When to use each model relationship in Django? - python

I've been reading through the Django documentation and looking over some of the other answers on the site for a couple of hours now, yet I still can't get it to sink in. I know this isn't Django specific, but the examples I use will be from a Django project.
My question boils down to when is it appropriate to use each:
Many-to-many relationships
Many-to-one relationships
One-to-one relationships
One-to-one, more or less makes sense to me.
Now for the other two. While I understand the differences between them in isolation, when it comes to using them practically in a project, I get confused. Here is an example:
class User(AbstractUser):
pass
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField()
class Watchlist(models.Model):
user = models.ForeignKey(User, related_name='watchlist', on_delete=models.CASCADE)
item = models.ManyToManyField(Listing)
class Comment(models.Model):
user = models.ForeignKey(User, related_name='comments', on_delete=models.SET_NULL)
comment = models.TextField()
Would this be the correct use of Many-to-one(ForeignKey) and Many-to-many?
Should Watchlist.item be a ForeignKey? Or is M2M correct?
Wouldn't it simplify to make the 'Watchlist' part of the User class? (give them an empty list to populate with listing ID's)
Why is Watchlist.user not a One-to-one relationship, if each watchlist belongs to a single user, and a user can only have one list?
Apologies for my stupidity, I just can't get this to sink in!
Thank you.
edit: Context, the models are from a 'learning' project I was working on intended to be an auction site, similar to eBay. The watchlist is sort of a 'wish' list... for the user to watch an item, not for site to watch a user!

To explain it simply these django-models or objects represents tables in your database and the fields are like the columns in them. So with a one-to-one relation you can only have one row in one table relating to one row in another table. For example one user in the user table (represented by one row) can only relate to one row in a profile table. But your user can have many comments, so this would be a one-to-many/foreignkey relation (if you set unique=true on a fk, it will in practice function as 1:1). If the users can collaborate on writing comments, or for example as here on stackoverflow where users can edit other users comments, that would be a many-to-many relation.
Database design can be complicated/complex, especially using an ORM without basic knowledge of SQL and how it all works beneath. In general it requires a bit of planning even for a simple application.

Related

How can I access to data stored in a ManyToMany related table in Django?

I want to make a query to obtain all the users that another user is "following" for a learning project.
When I execute the following python code:
user = User.objects.get(id=user_id)
followers = user.follows.all()
I don't get an error but instead I get a list with an object ( called Follow object (4) ) from which I can't access the user related properties.
I've checked this documentation here: https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/
But I can't find where I'm failing to use properly this type of model-relation.
Here are my Django models:
class User(AbstractUser):
pass
class Follow(models.Model):
user = models.ForeignKey(User, null=False, on_delete=models.CASCADE, related_name="follows")
follow = models.ManyToManyField(User, blank=True, related_name="followed_by")
I searched in my Django DB to see what is happening and I saw that there is a relational table called "network_follow_follow" that in fact has all the info that I need and is related with the object that I get from my Query, here are two pictures about the table that I can access and the one that I don't know how to access:
Table data requested by my code:
Table that I need to query:
So now I'm looking a way to access that last table, but to know how can I access straight forward to the last table data would be much better I think.
Thanks in advance.
See if this works for you.
follow_object = Follow.objects.filter(user=user)
followers_of_user = follow_object.follow.all()
I think the problem is that the user object does not have a user.follows function. You have created a seperate table in the database with that relationship data. I'm sure there's a better way of doing it but that's what I have done with something similar in my project.

Django Python annotate ManyToMany Relationship with additional information

my problem is the following. I have two models in the database, which I link together using a ManyToMany relationship. For the admin page I currently use "admin.TabularInline" to bind different objects to one via the graphic. I still want to specify an order in the connections, preferably numbers which represent an order for processing. A bit more figuratively described I have the model "Survey" and the model "SurveyQuestion". So I connect many SurveyQuestions with the Survey. But I can't specify an order, because I don't have an additional field for it. It is not known before how many questions will be in a survey. Nor is it known which questions will be inserted. Usually they are built during the compilation of the survey and may be used later for another survey. I am grateful for every tip!
This can be achieved by defining a custom relationship table between the Survey and SurveyQuestion using through argument. For example you can define a relationship model:
class Question(models.Model):
question = models.CharField(max_length=256)
class Survey(models.Model):
name = models.CharField(max_length=256)
questions = models.ManyToManyField(Questions, through='Questionnaire')
class Questionnaire(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
question_order = models.IntegerField()
The details and example can be found here:
https://docs.djangoproject.com/en/3.1/topics/db/models/#extra-fields-on-many-to-many-relationships. If you do not want to mess up with the models, then you have to find out some hack like was proposed by Ronak Muhta.

Is there a way to have multiple instances of a model embedded into a single model field in Django?

I know the foreign key concept,Djongo Array field , however is there an alternative?
The issue with the foreign key concept is that I would need to make multiple hits to the database and the issue with Array field is the lack of information and the errors emerging without a known solution.
What I would like to do basically is in fact add multiple instances of a model say comments to a single field in another model but I would like to embed it rather than creating a new table for comments which I tried using an abstract model however it did not work out.
I would like to know any alternative solution.
You can use foreign keys, and to avoid making separate query for every related record you can extract them all at once using prefetch_related - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#prefetch-related :
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
Code example:
# models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post, models.CASCADE)
text = models.TextField()
# Somewhere else, fetch posts together with related comments:
# It will take 2 requests in total
# - one to fetch posts, another to fetch all related comments
for post in Post.objects.all().prefetch_related('comment_set'):
print([c.text for c in post.comment_set.all()])

Django: To use User model or Profile model for related models

General question here (more specifically for Django/Python), when creating a model such as a Company for example, and several users (employees) are going to be related to such model, how should the different models be related?
I.e. a user profile will be related to a user object via a OneToOneField. But should the Company object be related by a ForeignKey to the User object or the UserProfile object, and why is this the appropriate way?
So
class UserProfile(models.Model): # For the Company Employees
user = models.OneToOneField(UserModel, related_name='employeeprofilemodel', on_delete=models.CASCADE, blank=True,
null=True)
...
company_rel = models.ForeignKey(CompanyModel, related_name='companyrel', null=True)
or
class User(AbstractBaseUser): # For all Users
...
company_rel = models.ForeignKey(CompanyModel, related_name='companyrel', null=True)
This is more of an opinion based question, rather than technicality. I'd recommend the former way, which is creating a One-to-one relationship with the AUTH_USER_MODEL. Then, you could store the employee related data in the User Profile table which can be related to the Company table through a ForeignKey relationship. The advantage of this kind of database schema is that, you can alter the User table(which is AUTH_USER_MODEL ) in future (if needed, of course) without affecting the employee details which maybe sensitive in nature. The User table lives independent of the Company table in the database and could be used for the Authorisation. Also less data is carried during db queries and implicitly a small level of optimisation can be achieved. I assume this is more appropriate way than extending from the auth.User model.
The latter way could compromise the above said sensitive data whenever a database refactoring ever required. Also, the queries would be much more heavy, when the User models have more data than necessary regarding the users. Extra care for optimisation may be required when handling the db queries with user tables.

Setup Django Blog Comments

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.

Categories

Resources