Django пользовательские `get_or_create` на модели с M2M через модель

Как сказано в заголовке, я пытаюсь реализовать get_or_create метод, который может получить или создать экземпляр относительно through модели.

Модели:

class Component(models.Model):
    name = CharField()

class ConfigComponent(models.Model):
    config = models.ForeignKey(Config)
    component = models.ForeignKey(Component)
    quantity = models.PositiveIntegerField()

class Config(models.Model):
    components = models.ManyToManyField(Component, through="ConfigComponent")

По сути, я хочу проверить, существуют ли Config подходящие входные данные, и использовать их, если да. В противном случае - создать новый. Но как учесть quantity каждого Component? Найти Config с точным количеством компонентов не проблема, но может оказаться, что компоненты совпадают, но их количество разное.

def get_or_create(
    self, 
    components: list[tuple[Component, int]]
) -> tuple[Config, bool]:
    
    component_names = [component[0] for component in components]
    components_count = len(components)

    configs_match = Config.objects.annotate(
                                      total_components=Count('components'),
                                      matching_components=Count(
                                         'components',
                                         filter=Q(components__in=component_names)
                                      ))\
                                  .filter(
                                      total_components=components_count, 
                                      matching_components=components_count
                                  )
               

                             

Использование:

from django.db.models import Count, Q

configs_match = Config.objects.alias(
    count=Count('components'),
    nfilter=Count('components', filter=Q(components__in=my_components)),
).filter(
    count=len(my_components),
    nfilter=len(components),
)

Здесь будет искаться точное совпадение, так как Config должны иметь то же количество components, что и len(my_components), и , это также должно выполняться для всех components в my_components.

Или чтобы также учитывать целые числа:

from django.db.models import Count, Q

configs_match = Config.objects.alias(
    count=Count('components'),
    nfilter=Count(
        'components',
        filter=Q(
            *[
                Q(component=c, configcomponent__quantity=q)
                for c, q in my_components
            ],
            _connector=Q.OR
        ),
    ),
).filter(
    count=len(my_components),
    nfilter=len(my_components)),
)

со my_components список из двух кортежей, первый элемент которого Component, а второй элемент - соответствующее количество.

Вернуться на верх