Запрос Django ORM для системы обмена сообщениями

#python #django #django-models

#python #django #django-модели

Вопрос:

У меня есть следующие модели для внутреннего приложения обмена сообщениями:

 class MessageThread(models.Model):
    subject = models.CharField(max_length=256, blank=False)

class Message(models.Model):
    thread = models.ForeignKey(MessageThread)
    content = models.CharField(max_length=5000, blank=False)
    timestamp = models.DateTimeField(auto_now_add=True, blank=False)
    sender = models.ForeignKey(User)

class MessageRecipient(models.Model):
    message = models.ForeignKey(Message)
    thread = models.ForeignKey(MessageThread)
    recipient = models.ForeignKey(User)
    status = models.CharField(max_length=20, choices=MESSAGE_STATUS, default="unread")
 

Для данного пользователя, чтобы получить список потоков, которые я делаю:

 distinct_threads=MessageThread.objects.filter(messagerecipient__recipient=user)
    .order_by('-message__timestamp').distinct()
 

Как мне избавиться от всех потоков с сообщением, содержащим статус «удалено» в ORM? (Другими словами, я хочу получить список всех потоков без потока, содержащего сообщение со статусом =»удалено».)

Комментарии:

1. Это одна из дыр в ORM Django.

Ответ №1:

Для этого я добавил метод в диспетчере моделей:

     def get_active_threads(self, user):
        all_threads = MessageRecipient.objects.filter(recipient=user).order_by('-message__timestamp')

        deleted_threads = []
        final_threads = []

        for thread in all_threads:
            if thread.thread not in (deleted_threads or final_threads):
                if thread.status == "deleted":
                    deleted_threads.append(thread.thread)
                else:
                    final_threads.append(thread)

        return final_threads
 

Ответ №2:

Вы можете либо сделать это на python (как предложено выше), либо позволить БД выполнять грязную работу за вас.

Вам нужно будет создать набор запросов, содержащий все потоки с удаленным сообщением для определенного пользователя, а затем второй набор запросов, который получает все потоки, не содержащиеся в первом:

 delthreads = MessageThread.objects.filter(messagerecipient__recipient=user,
    messagerecipient__status='deleted').distinct()
result = MessageThread.objects.filter(messagerecipient_recipient=user)
    .exclude(messagethread_in=delthreads).distinct()
 

Однако с точки зрения производительности (особенно для больших таблиц) это повредит вам. Ваша модель нуждается в некоторой реструктуризации, чтобы обеспечить более эффективные запросы. Рассмотрим:

 class MessageThread(models.Model):
    subject = models.CharField(max_length=256, blank=False)

class Message(models.Model):
    thread = models.ForeignKey(MessageThread)
    content = models.CharField(max_length=5000, blank=False)
    timestamp = models.DateTimeField(auto_now_add=True, blank=False)
    sender = models.ForeignKey(User)
    recipient = models.ForeignKey(User)
    status = models.CharField(max_length=20, choices=MESSAGE_STATUS, default="unread")
 

Если вы предполагаете, что будете часто выполнять этот запрос, вы можете добавить поле кэша в MessageThread, которое вы можете обновлять всякий раз, когда кто-то удаляет сообщение в этом потоке:

 class MessageThread(models.Model):
    subject = models.CharField(max_length=256, blank=False)
    dirty = models.BooleanField(default=False)
 

Тогда было бы очень легко извлечь список потоков без удаленных сообщений.

 nondelthreads = MessageThread.objects.filter(message__recipient=user, dirty=True).distinct()
 

Ответ №3:

Вкратце, почему у вас есть thread поле as внутри MessageRecipient ? Вы можете перейти к thread через message .

В любом случае, вам нужно использовать exclude()

 # breaking up onto multiple lines for readability, not valid python though
MessageThread.objects.filter(messagerecipient__recipient=user)
.exclude(messagerecipient__status='deleted')
.order_by('-message__timestamp').distinct()
 

Комментарии:

1. Разве это не позволило бы потоку оставаться в запросе, если, например, в потоке 5 сообщений, и только одно из этих сообщений помечено как удаленное? Ему необходимо удалить поток, если какое- либо сообщение в этом потоке удалено, а не если все.