Как фильтровать модель django по своим объектам во много-много поле (точное совпадение)?
У меня есть эта модель в моем коде:
class Conversation(models.Model):
participants = models.ManyToManyField(User, related_name="message_participants")
и мне нужно отфильтровать этот объект модели "Разговор" полем "участники" "многие ко многим".
значение: у меня есть, например, 3 пользовательских объекта, поэтому я хочу получить только объекты "Разговор", в которых есть 3 пользователя в этом поле "Участники".
Я попытался сделать это:
def get_exist_conv_or_none(sender,recipients):
conv = Conversation.objects.filter(participants=sender)
for rec in recipients:
conv = conv.filter(participants=rec)
где отправитель является объектом User, а "получатели" - это список объектов пользователя.
он не будет вызывать ошибки, но он дает мне неправильный Object of Conversation.
Спасибо.
изменить:
Более новая попытка привела меня к этому:
def get_exist_conv_or_none(sender,recipients):
participants=recipients
participants.append(sender)
conv = Conversation.objects.filter(participants__in=participants)
return conv
которые в основном имеют одинаковую проблему. Он дает объекты, в которых есть один или несколько "участников" в списке. но то, что я ищу, является точным соответствием объекта many-to-many.
Значение, объект с точными "пользователями" на нем отношение "многие ко многим".
edit 2: Моя последняя попытка. все равно не будет работать.
def get_exist_conv_or_none(sender,recipients):
recipients.append(sender)
recipients = list(set(recipients))
conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
for participant in recipients[1:]:
conv.filter(participants=participant)
conv.filter(count=len(recipients))
return conv
Ответы
Ответ 1
Хорошо, поэтому я нашел ответ:
Чтобы сделать точное совпадение, мне нужно связать фильтр с моделью, а затем убедиться, что она имеет точное количество аргументов, которые ей нужны, так что поле "многие ко многим" будет иметь в ней все требуемые объекты и не более.
Я проверю номер объекта с помощью аннотации: (https://docs.djangoproject.com/en/dev/topics/db/aggregation/)
закончил с этим кодом:
def get_exist_conv_or_none(recipients):
conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
for participant in recipients[1:]:
conv = conv.filter(participants=participant)
conv = conv.filter(count=len(recipients))
return conv
Ответ 2
Для быстрого поиска с использованием индекса базы данных я использую этот код:
class YandexWordstatQueue(models.Model):
regions = models.ManyToManyField(YandexRegion)
regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
phrase = models.ForeignKey(SearchPhrase, db_index=True)
tstamp = models.DateTimeField(auto_now_add=True)
class YandexWordstatRecord(models.Model):
regions = models.ManyToManyField(YandexRegion)
regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
phrase = models.ForeignKey(SearchPhrase, db_index=True)
Shows = models.IntegerField()
date = models.DateField(auto_now_add=True)
@receiver(m2m_changed, sender=YandexWordstatRecord.regions.through)
@receiver(m2m_changed, sender=YandexWordstatQueue.regions.through)
def yandexwordstat_regions_changed(sender, **kwargs):
if kwargs.get('action') in ['post_add', 'post_remove']:
instance = kwargs.get('instance')
l = list(instance.regions.values_list('RegionID', flat=True))
l.sort()
instance.regions_cached = json.dumps(l)
instance.save()
Это добавляет накладные расходы при сохранении, но теперь я могу выполнить быстрый фильтр с помощью этого фрагмента:
region_ids = [1, 2, 3] # or list(some_queryset.values_list(...))
region_ids.sort()
regions_cahed = json.dumps(region_ids)
YandexWordstatQueue.objects.filter(regions_cached=regions_cached)