He creado un módulo de mensajería privada muy básico para mi proyecto Django.
Tengo un modelo de mensaje que consta de:
sender (Foreign key to the member model) recipient (Foreign key to the member model) message date (Datetime of which the message was created)Ahora mi problema es que me gustaría crear una nueva vista que devuelva una lista de conversaciones basadas en estos mensajes.
Estoy tratando de escribir una consulta que devuelva los mensajes más recientes pero únicos donde el remitente = usuario actual O el destinatario = usuario actual. Esto es para tener una lista de los mensajes más recientes, que debería ser el equivalente a una lista de conversaciones. ¿Lo he entendido bien o estoy completamente pensando en cosas?
conversations = Message.objects.filter(Q(recipient=request.user) | Q(sender=request.user)).annotate(max=Max('date'))pero esto está devolviendo conversaciones duplicadas. Intenté esto desde otra publicación de desbordamiento de pila:
conversations = Message.objects.order_by('recipient', 'sender', 'date').distinct('recipient', 'sender')pero recibo este error "Los campos DISTINCT ON no son compatibles con este backend de base de datos"
Cualquier ayuda sería muy apreciada.
Como dijo @Grimmy en los comentarios, publique más información. Específicamente, agregue a su respuesta la consulta que intentó (que ya tiene en los comentarios) y el resultado, y qué está mal con el resultado (usted dice "conversaciones duplicadas; ¿quiere decir que cada conversación aparece solo dos veces o más que dos veces?)
Dos opciones vienen a la mente para esta situación:
Conversation que contiene información sobre la conversación, incluidos los miembros, la fecha de inicio, etc., y agregar una clave externa a cada mensaje asignándole una conversación (muchos a uno de mensaje a conversación). Esto simplificaría sus consultas y sería fácilmente extensible para acomodar "chats grupales" si fuera necesario (solo agregue más miembros a la conversación), o cosas como una imagen o un título para la conversación en sí.Cuando publique más información, abordaré la opción uno con más detalle si lo desea. Por ahora, solo diré que si obtiene conversaciones duplicadas donde cada conversación solo aparece dos veces (una donde el usuario es el remitente y otra donde el usuario es el destinatario), entonces parece que su consulta es lo suficientemente buena y simplemente puede escribir un bucle for en python que clasifique las conversaciones resultantes y elimine los duplicados más antiguos. Siempre que cada usuario no esté en más de cien o doscientas conversaciones, lo que parece ser el caso, eso no debería ralentizar demasiado el rendimiento.
Pero en cualquier caso, recomiendo la opción dos. Tuve que implementar una característica similar en un proyecto de Django, y elegí la opción 2. Simplifica enormemente su consulta para que se vea así:
# get the conversations the user is in conversation_list = Message.objects.filter(conversation__participants=user).order_by('-date') # get a list of the most recent message of each conversation message_list = conversation_list.values('conversation').annotate( first_msg=Max('conversation__message') )Para que esa segunda línea ordene los mensajes correctamente, agregue lo siguiente a su modelo de mensaje:
class Message(models.Model): # sender field is unchanged # date field is unchanged (maybe rename to avoid name collisions with date module) # message field is unchanged (maybe rename to 'content' to avoid confusion) # make recipient many-to-many because I'd recommend having both the sender and the recipient listed as recipients, # but you don't have to do that recipient = models.ManyToManyField(User) # conversation foreign key conversation = models.ForeignKey(Conversation, blank=False, null=False) # this simplifies sorting so the "Max('conversation__message')" line # sorts on date rather than primary key ordering = ["-date"]Aquí está el modelo de conversación:
class Conversation(models.Model): participants = models.ManyToManyField(User) # example functionality you may wish to add later group_name = models.CharField(max_length=512, default="Group", blank=False, null=False) profile_picture = models.FileField(upload_to='uploads/', default='uploads/GroupChatIcon.jpg')