1

Is there a better way to design the Message model ?

I have a Message model:

class Message(models.Model):
    """
    message
    """
    title = models.CharField(max_length=64, help_text="title")
    content = models.CharField(max_length=1024, help_text="content")

    is_read = models.BooleanField(default=False, help_text="whether message is read")

    create_user = models.ForeignKey(User, related_name="messages",help_text="creator")
    receive_user = models.CharField(max_length=1024, help_text="receive users' id")

    def __str__(self):
        return self.title
    def __unicode__(self):
        return self.title

You see, I use models.CharField to store the users' id, so I can know the users who should receive this row message.

I don't know whether this design type is good. or is there a better way to do that?

I have considered use ManyToMany Field, but I think if user is too many, the admin create one message will create as many as users count, so I think this is not a good idea.

fanhualuojin154873
  • 521
  • 1
  • 6
  • 15
  • 1
    you can update **receive_user** to many-to-many field as there is a possibility of message being received by more than one user. – Vijesh Venugopal Dec 06 '17 at 08:53
  • 1
    receive_user can also be a foreign key to User model. – Sandeep Balagopal Dec 06 '17 at 08:53
  • I have considered ManyToMany field, because if do like this, every time the admin create one message, the system will create many same contents message use for-loop in there right? – fanhualuojin154873 Dec 06 '17 at 09:09
  • On another note, you may want to have `is_read` as a DateTimeField instead of BooleanField. This way, you can keep track of when a message was read. You can still write a boolean wrapper around it as well. Just an idea. :-) Happy coding – Sebastian Fleck Dec 06 '17 at 09:13

2 Answers2

1

I would definitely use ManyToManyField for your receive_user. You're going to find that keeping a CharField updated and sanitised with user_ids is going to be a nightmare that will involve re-implementing vast swathes of existing Django functionality.

I'm not sure if I understand your potential issue to using ManyToManyField, users of the admin will be able to select which users are to be recipients of the message, it doesn't automatically a message for each user.

e: Also, depending on which version of python you're using (2 or 3) you only need one of either __str__ or __unicode__

__unicode__ is the method to use for python2, __str__ for python3: See this answer for more details

ptr
  • 3,292
  • 2
  • 24
  • 48
0

So it actually depends on your needs in which direction I would change your message Model.

General Changes

Based on the guess: you don't ever need an index on the content field

I would change the content to a TextField (alse because the length of 1024 is already to large for a propper index on mysql for example) https://docs.djangoproject.com/en/1.11/ref/databases/#textfield-limitations here some more infos about this topic.

I would pbly increase the size of the title field just because it seems convenient to me.

1. Simple -> One User to One User

The single read field indicates a one to one message:

I would change the Receiver to also be a Foreign key and adapt the related names of the sender and receiver field to represent these connections to something like sent-messages and received-messages.

Like @sebastian-fleck already suggested I'd also change the read field to a datetime field, it only changes your querysets from filter(read=True) to filter(read__isnull=False) to get the same results and you could create a property representing the read as boolean for conveniance, e.g.

@property
def read(self):
    return bool(self.read_datetime)  # assumed read as a datetime is read_datetime

2. More Complex: One User to Multiple User

This can get a lot more complex, here the least complex solution I could think of.

Conditions: - there are only messages and no conversation like strukture - a message should have a read status for every receiver

(I removed descriptions for an easier overview and changed the models according to my opinions from before, this is based on my experience and the business needs I assumed from your example and answers)

@python_2_unicode_compatible
class Message(models.Model):
    title = models.CharField(max_length=160)
    content = models.TextField()

    create_user = models.ForeignKey(User, related_name="sent-messages")
    receive_users = models.ManyToManyField(User, through=MessageReceiver)

    def __str__(self):
        return 'Message: %s' % self.title

@python_2_unicode_compatible
class MessageReceiver(models.Model):
    is_read = models.Datetime(null=True, blank=True)
    receiver = models.ForeignKey(User)
    message = models.ForeignKey(Message)

This structure is using the power of ManyToMany with a custom through Model, check this out, it very mighty: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.ManyToManyField.through.

tldr: we want every receiver to have a read status, so we modeled this in a separate object

Longer version: we utilize the power of a custom ManyToMany through model to have a separate read status for every receiver. This means we need to change some parts of our code to work for the many to many structure, e.g. if we want to know if a message was read by all receivers:

def did_all_receiver_read_the_message(message)
    unread_count = my_message.receive_users.filter(is_read__isnull=True).count()
    if unread_count > 0:
        return True
    return False

if we want to know if a specific user read a specific message:

def did_user_read_this_message(user, message)
    receiver = message.receive_users.get(receiver=user)
    return bool(receiver.is_read)

3. Conversations + Messages + Participants

This is something that would exceed my time limit but some short hints:

  • Conversation holds everything together
  • Message is written by a Participant and holds a created timestamp
  • Participant allows access to a conversation and links a User to the Conversation object
  • the Participant holds a last_read timestamp with can be used to calculate if a message was read or not using the messages created timestamps (-> annoyingly complex part & milliseconds are important)

Everything else pbly would need to be adapted to your specific business needs. This scenario is pbly the most flexible but it's a lot of work (based on personal experience) and adds quite a bit of complexity to your architecture - I only recommend this if it's really really needed ^^.

Disclaimer:

  • This could be an overall structure, most design decisions I made for the examples are based on assumptions, I could only mentioned some or the text would to long, but feel free to ask.
  • Please excuse any typos and errors, I didn't had the chance to run the code.