ManyToMany through other Model creation

Hey there!
I’m looking for a better way of doing some ManyToMany creation.
I have this set of models.

from django.db import models
from django.utils.translation import gettext_lazy as _

from app.base.models import AutoTimeStampModel, BaseModel
from users.models import User

class Activity(BaseModel):

    code = models.CharField(
        verbose_name=_("Activity code"),
        max_length=25,
        unique=True,
    )
    description = models.CharField(
        verbose_name=_("Activity description"),
        max_length=255,
    )


class Company(AutoTimeStampModel):
    federal_entity_id = models.CharField(
        verbose_name=_("Federal Entity ID"),
        help_text=_("The company federal entity id"),
        max_length=14,
        primary_key=True,
    )
    activities = models.ManyToManyField(
        to=Activity,
        through="CompanyActivity",
        verbose_name=_("Activities"),
    )
    # A lot of fields, not relevant here

class CompanyActivity(BaseModel):
    company = models.ForeignKey(
        verbose_name=_("Company"),
        to=Company,
        on_delete=models.CASCADE,
    )
    activity = models.ForeignKey(
        to=Activity,
        on_delete=models.CASCADE,
        verbose_name=_("Activity"),
    )
    primary = models.BooleanField(
        verbose_name=_("Primary Activity?"),
        default=False,
    )

Each Company will have many Activity that may be primary or not.

The “problem” i’m facing, it’s that i don’t know if there’s already a Activity, so i use get_or_create to create the Activity i set it to the activities many to many relation of the Company that will create primary with a default=False and after that i’m doing a update on the activities to update the primary field to True if there is any primary activities.

Here’s the referenced code.

# services.py

from companies import models

from companies.scrapers.factory import create_scraping_provider

def create_new_company_from_scraping_request(federal_entity_id: str) -> models.Company:
    scraper = create_scraping_provider()
    scraped_company = scraper.get_federal_entity_by_id(federal_entity_id)
    company: models.Company = models.Company.objects.create(**scraped_company.create_kwargs)

    models.Address.objects.create(company=company, **scraped_company.address_kwargs)

    activities = [
        models.Activity.objects.get_or_create(
            code=scraped_activity.code, defaults={"description": scraped_activity.description}
        )[0]
        for scraped_activity in scraped_company.activities
    ]
    company.activities.add(*activities)

    primary_activities = [actv.code for actv in scraped_company.activities if actv.primary]
    if primary_activities:
        company.activities.filter(activity__code__in=primary_activities).update(primary=True)

I’m looking for a better way of doing this creation, it seems repetitive and not idiomatic. I referenced to the Docs, but did not found any related case.
Thanks in advance.

I would say it’s fine. You are creating some extra queries, but I’m guessing the volume and frequence of such isn’t going to create a problem.

The only way I immediately see to reduce that would be for you create the instances of CompanyActivity directly rather than using the .add on the ManyToManyField. This would allow you to set the primary field directly at the time the object is being created.

Thanks @KenWhitesell !
I refactored to this:

    activities_primary = [
        (
            models.Activity.objects.get_or_create(
                code=scraped_activity.code, defaults={"description": scraped_activity.description}
            )[0],
            scraped_activity.primary,
        )
        for scraped_activity in scraped_company.activities
    ]
    models.CompanyActivity.objects.bulk_create(
        [
            models.CompanyActivity(company=company, activity=activity, primary=primary)
            for activity, primary in activities_primary
        ]
    )