Milano
Milano

Reputation: 18735

Django model: Conversation between two users

I'm trying to create a Model which represents a conversation between two users (only two).

Can't figure out how to create two fields because users are equivalent.

class Conversation(models.Model):
    user_one = ForeignKey('auth.User')
    user_two = ForeignKey('auth.User')

    class Meta:
         unique_together = ('user_one','user_two')

Is this the best way I can design a model?

And then manager method:

def get_conversation(user_one,user_two):
    c = Conversation.objects.filter(Q(user_one=user_one,user_two=user_two)|Q(user_one=user_one,user_two=user_two))
    return c

Or is there a more comfortable way to handle such model? For example using ManyToManyField and check if there are two and only two users?:

users = ManyToManyField('auth.User') 

Upvotes: 1

Views: 580

Answers (2)

Jessie
Jessie

Reputation: 2475

Use the related_name field when you have more than 1 foreign key to the same model. Because you often don't care who specifically is user_one and user_two, you can simply make sure that user_one and user_two are consistent. In this case, I'm using the user's id field to say which user will be user_one and which will be user_two. This makes querying simpler because you don't need to do a query for the two pairs (A, B) and (B, A)

class Conversation(models.Model):
    user_one = ForeignKey('auth.User', related_name="user_one")
    user_two = ForeignKey('auth.User', related_name="user_two")

    class Meta:
        unique_together = ('user_one','user_two')

    def clean(self):
        # Ensure that user_one's id is always less than user_two's
        if self.user_one and self.user_two and self.user_one.id > self.user_two.id:
            (self.user_one, self.user_two) = (self.user_two, self.user_one)

    @classmethod
    def get(cls, userA, userB):
        """ Gets all conversations between userA and userB
        """

        if userA.id > userB.id:
            (userA, userB) = (userB, userA)

        return cls.objects.filter(user_one=userA, user_two=userB)

Upvotes: 2

VMatić
VMatić

Reputation: 996

If you are using postgres you could use an ArrayField:

class Conversation(models.Model):
    users = ArrayField(
        ForeignKey('auth.User'),
        size=2,
    )

That would help with lookups. However note what the documentation currently says about the size parameter:

This is an optional argument. If passed, the array will have a maximum size as specified. This will be passed to the database, although PostgreSQL at present does not enforce the restriction.

Upvotes: 0

Related Questions