I have a simple notification model:
class Notification(models.Model):
user = models.ForeignKey(User)
sender = models.ForeignKey(User)
model = '''What to put here?'''
comment = models.CharField(max_length=200)
created = models.DateTimeField(auto_now=False,auto_now_add=True)
I need the notification to relate to several different models, for example; posts, user follows, etc
Is there anyway in django you can relate to several models instead of creating a notification model for each one?
I want to avoid models like this:
PostLikeNotification, UserFollowNotification, etc.
So does django have this functionality? I couldn't find it anywhere in the docs.
You could use Content Types/Generic Relations
class Notification(models.Model):
user = models.ForeignKey(User)
sender = models.ForeignKey(User)
object_id = models.PositiveIntegerField(default=None, null=True)
content_type = models.ForeignKey(ContentType, default=None, null=True)
comment = models.CharField(max_length=200)
created = models.DateTimeField(auto_now=False,auto_now_add=True)
#property
def model_object(self):
content_type = self.content_type
object_id = self.object_id
if content_type is not None and object_id is not None:
MyClass = content_type.model_class()
model_object = MyClass.objects.filter(pk=object_id)
if model_object.exists():
return model_object.first()
return None
Here we are storing the Model (Using the Content Types framework) and Primary Key (must be an Integer in this example) of the related object in the Notification model, then adding a property method to fetch the related object.
With this you can relate your notifications to any other model. You could also use the ForeignKey.limit_choices_to argument on the content_type field to validate that it only accepts certain models.
Django need to know the model before creating a relation, you can store the model in char field like post:23 user_follow:41 and define a get_model method that will parse that field and return the right model object
All depends on your design, you have several options. Different options depend on the size of your database:
How many notifications are there?
Do you need to update the notifications often?
Or most of the notifications are inserted once and then read often?
Use an abstract model
Use an abstract model and actually create the PostLikeNotification and UserFollowNotification and other models of such a kind.
class Notification(models.Model):
# ...
class Meta:
abstract = True
class PostLikeNotification(Notification):
model = models.ForeignKey(SomePost)
class UserFollowNotification(Notifcation):
model = models.ForeignKey(Follower)
# ...
This has several advantages:
You keep your relations in your (relational) database.
You have strong foreign keys to prevent inconsistent data.
It is "Djangoic": relations in the database, starting with a normalised database, and no early optimisations are django's way of doing things.
And, of course, this has some disadvantages:
If you need to search all notifications for something the query will be complex.
Moreover, a query over all notifications will be slow, since it filters several tables.
Use a CharField
You can use a simple CharField and store in it the model name and id. Or two fields one for the name and another for the id.
class Notification(models.Model):
model_type = models.CharField(max_len=48)
model_id = models.PositiveInteger()
Advantages:
You have a single table, querying is faster if you have the right indexes.
You can get one of the types of notifications with a simple comparison (index model_type for extra speed).
Disadvantages:
Inconsistent data may appear.
You will need to add extra code at a higher level to deal with possible inconsistent data.
Parallel writes (that may need to lock the entire table) may be a problem.
The middle ground, use several foreign keys
This is just one way of implementing a middle ground between the two options below: You add several nullable foreign keys. Other ways of achieving middle ground exist.
class Notification(models.Model):
model_post = models.ForeignKey(SomePost, null=True, blank=True)
model_follow = models.ForeignKey(Follower, null=True, blank=True)
Advantage:
Verification of inconsistent data can be made without searching other tables (foreign keys are foreign keys, the database takes care of their consistency).
Disadvantage:
It has most of the disadvantages of the other two methods but to a lesser extent (at least in most of them).
Conclusion
If you're just starting a project, and you do not know (or are not worried) about the volume of data then do create several tables. Abstract models were created for this purpose.
On the other hand if you have a lot of notifications to be read and filtered (by a lot, I mean millions) then you have good reasons to create a single notification table and process the relations at a higher level. Note that this incurs locking problems, you shall (almost) never lock notifications if you have a single table.
Related
I have the following code for models:
class Tag(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
class Activity(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, through='TagBinding')
class TagBinding(models.Model):
tag = models.ForeignKey(Tag)
activity = models.ForeignKey(Activity)
I want to write a database constraint on the TagBinding model using a new Django 2.2 syntax. This constraint should check that tag and activity fields of the TagBinding model have the same user. What I've tried to do:
class TagBinding(models.Model):
tag = models.ForeignKey(Tag)
activity = models.ForeignKey(Activity)
class Meta:
constraints = [
models.CheckConstraint(
name='user_equality',
check=Q(tag__user=F('activity__user')),
)
]
But this doesn't work because Django doesn't allow to use joins inside of the F function. Also Subquery with OuterRef didn't work for me because models that were referenced in a query were not registered.
Is there any way I can implement this constraint using a new syntax without raw SQL?
Update
It seems like some SQL backends don't support joins in constraints definition, so the question now: is it even possible to implement this behavior in the relational database?
In Postgres, there are two types of constraints (other than things like unique and foreign key constraints), CHECK CONSTRAINTS and EXCLUDE constraints.
Check constraints can only apply to a single row.
Exclusion constraints can only apply to a single table.
You will not be able to use either of these to enforce the constraint you want, which crosses table boundaries to ensure consistency.
What you could use instead are trigger-based constraints, that can perform other queries in order to validate the data.
For instance, you could have a BEFORE INSERT OR UPDATE trigger on the various tables that checks the users match. I have some similar code that runs on same self-relation tree code that ensures a parent and child both have the same "category" as one another.
In this case, it's going to be a bit trickier, because you would need some mechanism of preventing the check until all tables involved have been updated.
I have gone through the question, best way to implement privacy on each field in model django and Its answers doesn't seem solve my problem so I am asking some what related question here,
well, I have a User model. I want the user to make possible to control the privacy of each and every field of their profile (may be gender, education, interests etc . ..).
The privacy options must not to be limited to just private or public, but as descriptive as
public
friends
only me
friend List 1 (User.friendlist.one)
friend List 2 (User.friendlist.two)
friend List 3 (User.friendlist.three)
another infinte lists that user may create.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
so If I have the UserModel,
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
How do I setup a privacy setting here? I am using postgres, can I map a JSON field or Hstore even an ArrayField?
what is the best solution that people used to do with Django with same problem?
update:
I have n model fields. What I really want is to store the privacy settings of each instance on itself or some other convenient way.
I have worked on my issue, tried solutions with permissions and other relations. I have a Relationship Model and all other relationship lists are derived from the Relationship model, so I don't want to maintain a separate list of Relationships.
So my pick was to go with a Postgres JSONField or HStoreField. Since Django has good support for postgres freatures, I found these points pro for the choice I made.
JSON/HashStore can be queried with Django ORM.
The configurations are plain JSON/HashStore which are easy to edit and maintain than permissions and relations.
I found database query time taken are larger with permissions than with JSON/HStore. (hits are higher with permissions)
Adding and validating permissions per field are complex than adding/validating JSON.
At some point in future if comes a more simple or hassle free solution, I can migrate to it having whole configuration at a single field.
So My choice was to go with a configuration model.
class UserConfiguration(models.Model):
user = # link to the user model
configuration = #either an HStore of JSONFeild
Then wrote a validator to make sure configuration data model is not messed up while saving and updating. I grouped up the fields to minimize the validation fields. Then wrote a simple parser that takes the users and finds the relationship between them, then maps with the configuration to return the allowed field data (logged at 2-4ms in an unoptimized implementation, which is enough for now). (With permission's I would need a separate list of friends to be maintained and should update all the group permissions on updation of privacy configuration, then I still have to validate the permissions and process it, which may take lesser time than this, but for the cost of complex system).
I think this method is scalable as well, as most of the processing is done in Python and database calls are cut down to the least as possible.
Update
I have skinned down database queries further. In the previous implementation the relations between users where iterated, which timed around 1-2ms, changing this implementation to .value_list('relations', flat=True) cut down the query time to 400-520µs.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
I would advice you to decouple the privacy objects from the UserModel, to not mess your users data together with those options. To minimize the amount of database queries, use djangos select_related and prefetch_related.
The requirements you have defined IMO lead to a set of privacy related objects, which are bound to the UserModel. django.contrib.auth is a good point to start with in this case. It is build to be extendable. Read the docs on that topic.
If you expect a large amount of users and therefore also an even larger amount of groups you might want to consider writing the permissions resolved for one user in a redis based session to be able to fetch them quickly on each page load.
UPDATE:
I thought a little more about your requirements and came to the conclusion that you need per object permission as implemented in django-guardian. You should start reading their samples and code first. They build that on top of django.contrib.auth but without depending on it, which makes it also usable with custom implementations that follow the interfaces in django.contrib.auth.
What about something like this?
class EditorList(models.Model):
name = models.CharField(...)
user = models.ForeignKey(User)
editor = models.ManyToManyField(User)
class UserPermission(models.Model):
user = models.ForeignKey(User)
name = models.BooleanField(default=False)
email = models.BooleanField(default=False)
phone = models.BooleanField(default=False)
...
editor = models.ManyToManyField(User)
editor_list = models.ManyToManyField(EditorList)
If a user wants to give 'email' permissions to public, then she creates a UserPermission with editor=None and editor_list=None and email=True.
If she wants to allow user 'rivadiz' to edit her email, then she creates a UserPermission with editor='rivadiz' and email=True.
If she wants to create a list of friends that can edit her phone, then she creates and populates an EditorList called 'my_friends', then creates a UserPermission with editor_list='my_friends' and phone=True
You should then be able to query all the users that have permission to edit any field on any user.
You could define some properties in the User model for easily checking which fields are editable, given a User and an editor.
You would first need to get all the EditorLists an editor belonged to, then do something like
perms = UserPermissions.objects.filter(user=self).filter(Q(editor=editor) | Q(editor_list=editor_list))
First of all, in my opinion you should go for multiple models and for making the queries faster, as already mentioned in other answers, you can use caching or select_related or prefetch_related as per your usecase.
So here is my proposed solution:
User model
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
...
public_allowed_read_fields = ArrayField(models.IntegerField())
friends_allowed_read_fields = ArrayField(models.IntegerField())
me_allowed_read_fields = ArrayField(models.IntegerField())
friends = models.ManyToManyField(User)
part_of = models.ManyToManyField(Group, through=GroupPrivacy)
Group(friends list) model
class Group(models.Model):
name = models.CharField()
Through model
class GroupPrivacy(models.Model):
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
allowed_read_fields = ArrayField(models.IntegerField())
User Model fields mapping to integers
USER_FIELDS_MAPPING = (
(1, User._meta.get_field('name')),
(2, User._meta.get_field('email')),
(3, User._meta.get_field('phone')),
...
)
HOW DOES THIS HELPS??
for each of public, friends and me, you can have a field in the User model itself as already mentioned above i.e. public_allowed_read_fields, friends_allowed_read_fields and me_allowed_read_fields respectively. Each of this field will contain a list of integers mapped to the ones inside USER_FIELDS_MAPPING(explained in detail below)
for friend_list_1, you will have group named friend_list_1. Now the point is the user wants to show or hide a specific set of fields to this friends list. That's where the through model, GroupPrivacy comes into the play. Using this through model you define a M2M relation between a user and a group with some additional properties which are unique to this relation. In this GroupPrivacy model you can see allowed_read_fields field, it is used to store an array of integers corresponding to the ones in the USER_FIELDS_MAPPING. So lets say, for group friend_list_1 and user A, the allowed_read_fields = [1,2]. Now, if you map this to USER_FIELDS_MAPPING, you will know that user A wants to show only name and email to the friends in this list. Similarly different users in friend_list_1 group will have different values in allowed_read_fields for their corresponding GroupPrivacy model instance.
This will be similar for multiple groups.
This will be much more cumbersome without a separate permissions model. The fact that you can associate a given field of an individual user's profile with more than one friend list implies a Many to Many table, and you're better off just letting Django handle that for you.
I'm thinking something more like:
class Visibility(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
field = models.CharField(max_length=32)
public = models.BooleanField(default=False)
friends = models.BooleanField(default=False)
lists = models.ManyToManyField(FriendList)
#staticmethod
def visible_profile(request_user, profile_user):
"""Get a dictionary of profile_user's profile, as
should be visible to request_user..."""
(I'll leave the details of such a method as an exercise, but it's not
too complex.)
I'll caution that the UI involved for a user to set those permissions is likely to be a challenge because of the many-to-many connection to friend lists. Not impossible, definitely, but a little tedious.
A key advantage of the M2M table here is that it'll be self-maintaining if the user or any friend list is removed -- with one exception. The idea in this scheme is that without any Visibility records, all data is private (to allow everyone to see your name, you'd add a Visibility record with user=(yourself), field="name", and public=True. Since a Visibility record where public=False, friends=False, and lists=[] is pointless, I'd check for that situation after the user edits it and remove that record entirely.
Another valid strategy is to have two special FriendList records: one for "public", and one for "all friends". This simplifies the Visibility model quite a bit at the expense of a little more code elsewhere.
as the question states, how do I do create a many-to-one relationship in django models?
Basically, I have two models: Article and Comic, I want to have one Comment model which will have a relationship with both Article and Comic, but not both.
So if a Comment object has a relationship with an Article object, then it wont have a relationship with a Comic object.
I am currently doing it the following way, which does not work:
class Article(models.Model):
#other fields
class Comic(models.Model):
#other fields
class Comment(models.Model):
article = models.ForeignKey(Article)
comic = models.ForeignKey(Comic)
I would really appreciate some help.
This is tricky. I think there are a couple ways you could model this.
Using your current way you could enforce your uniqueness constraint in the application.
class Comment(models.Model):
article = models.ForeignKey(Article)
comic = models.ForeignKey(Comic)
def save(self, *args, **kwargs):
# assert that there is either comic OR article but not both
super(Comment, self).save(*args, **kwargs)
with this way, what happens if you add another model that you want Comment to reference?? You will have to manually add the conditional for the new type in your save method and perform a migration.
Django provides GenericForeignKey field that would allow you to reference any model from Comment. https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/#generic-relations
This would allow you to create a generic reference from Comment to either Article or Comic, and since it is only one field, would by default be mutually exclusive. I find querying and using GenericeForeignKeys awkward; but they are still an option, that might work fine for your use case.
Another powerful option, (my favorite) could be to create a polymorphic model, which would also be mutually exclusive.
Each Comment could reference a generic Piece of Content, using model inheritance. (I did not test the following, so it will probably not work as copied/pasted)
class Content(models.Model):
objects = InheritanceManager()
# shared content fields could be stored in this model
class Article(Content):
# article specific fields
class Comic(Content):
# comic specific fields
class Comment(models.Model):
content = models.OneToOneField(Content)
This is a powerful way to model the relationship of Comment to any Content. This DOES add additional query overhead, and should warrant an audit for your use case.
InheritanceManager is a utility provided by django-model-utils package, and is pretty lightweight. I have used in in production environment and it is performant, as long as you understand the additional query overheard involved with modeling your data using it. https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager
The query overhead is explained in the documentation.
If you think you will add additional Content subclasses in the future this could be a scalable way to model your relationship, and provides more flexibility in filtering then GenericForeignKeys.
Well, you can add another field to you Comment model. Like
class Comment(models.Model):
article = models.ForeignKey(Article, null = True)
comic = models.ForeignKey(Comic, null = True)
assigned = models.BooleanField(initial=False)
Once a comment object is created, put either article or comic to point at another object and make assigned = True.
I've pored over the Django docs regarding the contenttypes framework several times, and I simply don't understand it well enough to implement generic relations in my project. I've looked for online examples or tutorials regarding the matter, but I can't find a single one. Call me dumb, but I need some help on this one (please don't answer by simply linking to the docs). Based on the lack of resources online, I believe if you answer this question with a thorough example, your answer may be the most helpful example online to date regarding django generic relations (bonus!).
So, my question is: can someone show a simple example of the models and maybe a couple lines of code showing how to interact with instances of a generic model?
As inspiration, here is what I believe would be a very common situation:
A site has media items that are largely treated the same, but are slightly different. For example, let's say there are image and video items, and users can "like" an item or "comment" on an item. The likes and comments should be treated the same, regardless of whether they are posted on an image or video item. So, if there is an ItemView for viewing an image or a video in a user's album, the following kinds of calls would be possible : mediaitem.comments.all() or len(mediaitem.likes.all()) or comment.user_who_commented, without having to know which kind of media item it is (image or video).
I believe you would need six models for this:
Abstract MediaItem class
Two different types of media item: ImageItem and VideoItem
Abstract MediaItemActions class
Two different types of actions that can be done towards media items: Like and Comment
If you know how to use this Django feature, please show us a full example! I feel like it would be an extremely powerful tool and am aching to use it in my application. The more explicit, the better.
Your use case sounds very similar to the (now deprecated) Django comments framework. If you check out the models, you'll see how to use a generic relation in BaseCommentAbstractModel--note that you need all three fields, a ForeignKey to ContentType, a field to hold the objects' pks, and the GenericForeignKey field.
As for how to query for objects by GenericForeignKey, you can see some examples in the template tags in that project. See for example the get_query_set method in BaseCommentNode, which retrieves comments by querying on the content type and pk of the target object.
def get_query_set(self, context):
ctype, object_pk = self.get_target_ctype_pk(context)
if not object_pk:
return self.comment_model.objects.none()
qs = self.comment_model.objects.filter(
content_type = ctype,
object_pk = smart_text(object_pk),
site__pk = settings.SITE_ID,
)
I actually have a very similar situation on one of my projects, with various media types.
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class ReviewedItem(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
review = models.ForeignKey("Review")
class CreativeWork(models.Model):
#other fields
keywords = generic.GenericRelation("TaggedItem",null=True, blank=True, default=None)
reviews = generic.GenericRelation("ReviewedItem",null=True, blank=True, default=None)
class MediaObject(CreativeWork):
#fields
class VideoObject(MediaObject):
#fields
class AudioObject(MediaObject):
#fields
Every Video or Audio is a MediaObject, which is a CreativeWork.
CreativeWorks have a GenericRelation to tags and Reviews. So now anything can be tagged or reviewed.
All you need is for the 'action' to have a ForeignKey to ContentType.
Than add a GenericRelation to your model. I actually found the django.docs to be pretty helpful :)
But if not hope this helps.
Another option is Polymorphic Models. I won't say it is the way you should go, but that it could perhaps be an option.
I am a fan of both generic foreign keys and Polymorphic Models. Polymorphic Models work best in those scenarios where there is a lot of similarity in the models.
I want to model an article with revisions in Django:
I have following in my article's models.py:
class Article(models.Model):
title = models.CharField(blank=False, max_length=80)
slug = models.SlugField(max_length=80)
def __unicode__(self):
return self.title
class ArticleRevision(models.Model):
article = models.ForeignKey(Article)
revision_nr = models.PositiveSmallIntegerField(blank=True, null=True)
body = models.TextField(blank=False)
On the artlcle model I want to have 2 direct references to a revision - one would point to a published revision and another to a revision that is being actively edited. However from what I understand, OneToOne and ForeignKey references generate a backreference on the other side of the model reference, so my question is, how do i create a one-way one-to-one reference in Django?
Is there some special incantation for that or do I have to fake it by including state into revision and custom implementations of the fields that ask for a revision in specific state?
Edit: I guess, I've done somewhat poor job of explaining my intent. Let's try it on a higher abstraction level:
My original intent was to implement a sort of revisioned article model, where each article may have multiple revisions, where one of those revisions may be "published" and one actively edited.
This means that the article will have one-to-many relationship to revisions (represented by ForeignKey(Article) reference in ArticleRevision class) and two one way references from Article to revision: published_revision and edited_revision.
My question is mainly, how can I model this with Django's ORM.
The back-references that Django produces are programatic, and do not affect the underlying Database schema. In other words, if you have a one-to-one or foreign key field on your Article pointing to your Revision, a column will be added to the Article table in the database, but not to the Revision table.
Thus, removing the reverse relationship from the revision to the article is unnecessary. If you really feel strongly about it, and want to document in your code that the backlink is never used, a fairly common Django idiom is to give the fields a related_name attribute like _unused_1. So your Article model might look like the following:
class Article(models.Model):
title = models.CharField(blank=False, max_length=80)
slug = models.SlugField(max_length=80)
revision_1 = models.OneToOneField(ArticleRevision, related_name='_unused_1')
revision_2 = models.OneToOneField(ArticleRevision, related_name='_unused_2')
def __unicode__(self):
return self.title
That said, it's rare that a one-to-one relationship is actually useful in an application (unless you're optimizing for some reason) and I'd suggest carefully reviewing your DB schema to make sure this is really what you want. It may make sense to keep a single ForeignKey field on your ArticleRevision pointing back to an Article (since an ArticleRevision will, presumably, always need to be associated with an Article) and adding another column to Revision indicating whether it's published.
What is wrong with the link going both ways? I would think that the OneToOneField would be the perfect choice here. Is there a specific reason why this will be a detriment to your application? If you don't need the backreference why can't you just ignore it?