I have a very simple web application. A student comes in and he creates a note in one of the subjects. Each subject has number of subscribers. Similar to stackoverflow tags, once a tag has been added to a question, the subscribers are notified.
Similary in my app, I want to add subscribers, so once a note is created for a particular subject, all the subscribers of that subject are mailed.
I have my db models as follows -
class Subject(models.Model):
//some vlaues here
class Note(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
author = models.ForeignKey(User)
subject = models.ForeignKey(Subject)
class SubscriptionModel(models.Model):
subject = models.ForeignKey(Subject)
subscriber = models.ForeignKey(User)
There are couple of ways of implementing it -
Once a note is created, a post_save signal can be fired to email all the subscribers of that subjecct.
Something with pub-sub can be done.
Something with rss feeds can be done.
I am not sure what is a scalable approach to implement it, any help or pointers would be appreciated, thanks.
Signal are synchronous, so it will be bad to have mailing done in the save signal because it will slowdown the save process and will not scale.
I suggest to use asynchronous task via queue like django-rq or celery. And you can just put task in the queue in the post_save signal. This approach will scale well and wont interfere with normal site functioning.
Related
I have a NOTIFICATION and an USER app in Django.
The code goes something like :
class Notification(models.Model):
user = models.ForeignKey(User , related_name = "notification"
....
....
and
class User(models.Model):
notifications = models.OneToManyField(Notification , related_name = "user"
....
....
Now I know that models.OneToManyField doesn't exist in Django.
I get the fact that I can simply access the user from the notification instance of the model. But I suppose that would somehow slow my system because in production I would keep all the instances of Notification Model.
For example : I'm expecting around 500+ notifications per user once the system is in production for a significant amount of time.
I suppose, it would just be easier to access all the notifications of one user directly rather than sifting through the whole Notification Table to find notifications of a specific user.
I've read this and the documentation to an extent and I'm not able to find a solution to my problem.
Also I'm not sure about the processing constraints of a processor to obtain all the Notifications from the whole Notification Table. I'm just assuming that it'll be somewhat slower.
OneToManyField doesn't exist in Django because it is just the reverse relationship of a ForeignKey. So you don't need the notifications field on the User model here, just remove it.
Don't worry prematurely about performance of filtering on the notifications. Querying relations is what SQL was designed for, and that's what relational databases are good at doing.
I am trying to create a simple Notification/Message model for my Django app. This will store notifications from the site to the user, and messages from one user to another. A working model I had been using looks like this:
class Notification(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='sender_notification')
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name='recipient_notification')
message = models.TextField()
read = models.BooleanField(default=False)
recieved_date = models.DateTimeField(auto_now_add=True)
A message will have 0-1 senders (0 if the message is a notification from the site to the user), and typically one recipient (when the notification is meant for one specific user - e.g., "there is a comment on your post" - or when a message has been sent from one user to another). This had been working for me. However, it occurred to me that in some cases I want to send a notification to every user. I could potentially create Notification objects in a loop:
for user in User.objects.all():
Notification.objects.create(recipient=user, message='message for all uses')
But this seems like it will be pretty inefficient, and create unnecessary clutter in the database.
I experimented with updating my model like this:
class Notification(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='sender_notification')
recipient = models.ManyToManyField(User)
message = models.TextField()
read = models.BooleanField(default=False)
recieved_date = models.DateTimeField(auto_now_add=True)
But this requires a sender (for reasons that I don't understand), and since there is only one record for each notification, when one recipient reads the notification and I set read = True, it will show as read for all recipients.
Is there an easy solution that I am missing where I can use a single model to handle notifications/messages, or am I going to have to add some additional models (or duplicate notifications in the notifications table)?
Forgive my ignorance of database design. It is definitely a weakness of mine that I am working on.
You're worried it will create clutter in your DB, but the fact is if you want to store information about whether each user has read a given message, for each message you need to store one record per user. There's no way around it.
How you want to store these messages is up to you. I think your original model is fine. Admdebian's solution is also fine, but it requires an additional model. The only upside is that it uses less space (but not fewer records) in the DB for your mass notifications, but this doesn't matter unless your app has a ton of users.
Also, in your original model I would change the received_date field to sent_date. received_date is misleading, because I'm guessing you're creating the Notification record in the backend before you've received confirmation that it's been read, and you're setting that field to the current timestamp with auto_now_add.
So, change received_date to sent_date, and add a read_date column, which you can update when the user actually reads the message. Better yet, replace the read column with read_date, which is either None (message hasn't been read) or has some datetime in it (message was read at that time). You're now storing more information with the same number of fields.
My solution is to create a ReadFlag model.
class ReadFlag(models.Model):
user = models.ForeignKey(User)
message = models.ForeignKey(Message)
created = ...
I am designing an application where users send/receive records and I would like deletes to be separated for each user listed in the record (one user's delete will not hide the record from the other user).
My base model design looks like this:
class BasePrivateMessage(TimeStampedModel):
accepted = models.NullBooleanField(default=None, null=True, blank=True)
# fields in question
archived_by_recipient = models.BooleanField(default=False)
archived_by_sender = models.BooleanField(default=False)
read = models.BooleanField(default=False,
help_text='Recipient has viewed.')
recipient = models.ForeignKey('accounts.CommonUserProfile',
related_name='%(class)s_received')
sender = models.ForeignKey('accounts.CommonUserProfile',
related_name='%(class)s_sent')
message_body = models.TextField()
Would it be an improvement to replace the archived_by_xxxx fields with a ManyToManyField to accounts.CommonUserProfile that is responsible for storing a list of users who have hidden (soft-deleted) the record? It seems that would make client-side code simpler down the line. How is soft-delete typically implemented on a per-user basis?
I don't think there is a general industry-standard solution for this. Just do whatever you feel will get the job done for you specific application.
From what I understood, your message can only be viewed by a sender as well as a recipient, in which case I see no reason to add a M2M field. It will only slow down your application as it will use extra resources to do the extra lookups. However if you will need to extend you application where many users will be able to see a single message (e.g. group conversation), then it would make sense to add M2M field.
Bottom line is do whatever fits you application needs. Like always, its a compromise between abstraction (more flexibility) to performance.
If I wanted to setup comments for a blog in Django, and I wanted people to be able to reply to comments (like a normal blog), meaning each comment would have to know if it's a comment on another comment or not, would I set the model fields like this?
from django.db import models
from django.contrib.auth.models import User
class Comment(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(User)
text = models.TextField()
date = models.DateTimeField()
reply_to = models.ForeignKey(Comment, blank=True, null=True)
Is that correct? And how would I display them in a template?
Writing a hierarchical comments application seems too easy at first look but believe me it is not that simple. There are too many edge cases and security issues. So if this is a real project i would suggest you to use disqus, any other hosted solution or (now deprecated) comments framework.
On the other hand if you are just trying to learn how things done or playing around, your code seems fair enough so far. But you should consider Django's built-in content types framework instead of a direct foreign key relationship. That way you can relate a comment object to any other object. (a blog post or another comment). Take a look at comment frameworks models.py and you will see it.
class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
Also take a look at RenderCommentListNodein comment framework template tags. You should write a recursive function in order to get and display hierarchical comments.
You have to consider cases like:
What will happen if a user deletes a comment?
How should we delete comments? Should we actually remove it from database or should we set an attribute like deleted
How should we deal with permissions and level of user access?
If we let anonymous users to comment, what information do we need from them.
How to check human validation? Is captcha enough?
Happy hacking.
I'm writing a simple forum-like application on Google App Engine and trying to avoid scalability issues. I'm new to this non-RBDMS approach, i'd like to avoid pitfalls from the beginning.
The forum design is pretty simple, posts and replies will be the only concepts. What will be the best approach to the problem if the forum have millions of posts?
The model so far (stripped from useless properties):
class Message(db.Model):
user = db.StringProperty() # will be a google account user_id
text = db.TextProperty() # the text of the message
reply_to = db.SelfReferenceProperty() # if null is a post, if not null a reply (useful for reply-to-reply)
Splitting the model, i think it's faster because it will query less items when retrieving "all posts":
class Post(db.Model):
user = db.StringProperty() # will be a google account user_id
text = db.TextProperty() # the text of the message
class Reply(db.Model):
user = db.StringProperty() # will be a google account user_id
text = db.TextProperty() # the text of the message
reply_to = db.ReferenceProperty(Post)
This is a many-to-one relation in a RDBMS world, should a ListProperty be used instead? If so, how?
Edit:
Jaiku uses something like this
class StreamEntry(DeletedMarkerModel):
...
entry = models.StringProperty() # ref - the parent of this, should it be a comment
...
Firstly, why don't you use user = db.UserProperty() instead of user = db.StringProperty()?
Secondly, I'm quite sure you should use whatever it works and is more readable and test the performance later, for three reasons:
KISS (Keep it simple)
Early optimizations are bad
You can't improve what you can't measure
So when you are ready to measure, then start the optimizations.
I'm not saying this because I don't know nothing about RDBMS, No-SQL DBMS or Google Datastore performance optimizations, but because I usually get all my knowledge about it from testing, which seems to contradict previous assumptions more usually than I expected.
You might want to take a look at a good tutorial on creating a php forum from scratch. Sure that one is about PHP but it also covers the general overview of forum design.
Basically, don't split posts and replies or threads and posts. It will lead to some really awkward queries later on. A thread is simply a post that isn't replying to anything.