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
, а второй элемент - соответствующее количество.