Django Query: How to find all posts from people you follow - django

I'm currently building a website with the Django Framework. I want on the homepage of my website to display all posts made by people the user is following. Here are the classes for Profile, Story and Follow:
class Profile(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30, null=True)
last_name = models.CharField(max_length=30, null=True)
class Follow(models.Model):
following = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="following")
follower = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="follower")
follow_time = models.DateTimeField(auto_now=True)
class Story(models.Model):
author = models.ForeignKey('accounts.Profile', on_delete=models.CASCADE, related_name="author")
title = models.CharField(max_length=50)
content = models.TextField(max_length=10000)
As you can see Follow uses two Foreign Keys to represent the following and the follower. Is there a way to query all stories from people the user is following?
I really don't know what to filter. Or is this maybe a job for aggregation? If someone could help me, that would be awesome!
following_feed = Story.object.filter(???).order_by('-creation_date')

One can use double underscores (__) to look "through" relations (like ForeignKeys, etc.).
So here we can filter like:
Story.objects.filter(
author__following__follower=my_profile
)
So by using author we obtain a reference to the Profile of the author, then with following we look at the Follow model and then finally with follower we again obtain a reference to Profile(s): the profile(s) of the follower(s).
my_profile of course need to be substituted with a Profile object (the profile of the person that is a follower of the authors of the Storys you wish to obtain).
This will generate a query like:
SELECT s.*
FROM story AS s
JOIN follow AS f ON f.following_id = s.author_id
WHERE f.follower_id = 123
where 123 is the id of the my_profile.
If a person is following another person multiple times (here this can happen since you do not enforce that the follower, following tuples are unique in the Follow model), then the corresponding Storys will be yielded multiple times.
It is therefore probably better to add a unique_together constraint in the Follow model:
class Follow(models.Model):
following = models.ForeignKey(
'Profile',
on_delete=models.CASCADE,
related_name="following"
)
follower = models.ForeignKey(
'Profile',
on_delete=models.CASCADE,
related_name="follower"
)
follow_time = models.DateTimeField(auto_now=True)
class Meta:
unique_together = (('following', 'follower'), )
It might also be worth to see the Follow model as the through model of a ManyToManyField [Django-doc].

Note that I haven't tested the code I am posting so tell me if something is missing.
First, you need to get all the Profiles that your users follow. Then you have to get the Stories that they have.
followed_people = Follow.objects.filter(follower=current_user).values('following')
stories = Story.objects.filter(author__in=followed_people)

Related

Django ORM Query Optimization Issue

I am making a blog website and I am facing some issues with the Query performance.
I have 3 models
User Model -> Users (To store user email, Password etc)
Post Model -> Actual Posts
people Model -> (To store users extra information)
Post Model ->
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
title = models.CharField(max_length=255,null=True)
description = models.CharField(max_length=1000,null=True)
Likes = models.ManyToManyField(to=User, related_name='Post_likes')
favourites = models.ManyToManyField(to=User,blank=True,related_name="favourite")
People Model ->
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.ImageField(upload_to='profile_pics', blank=True,null=True)
Phone_number = models.CharField(max_length=255,null=True,blank=True)
Birth_Date = models.DateField(null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
Now as both of these models are connected to User model. I want to query the Post model and get the user photo in the template. Now when I use post.user.people.photo then for every post it generates a seperate query to DB resulting in slowness. I would like to use Join here to combines multiple tables and fetch all the records at once.
I am currently using following Query ->
posts = Post.objects.select_related().prefetch_related('images_set').annotate(comments_Count = Count('comments_post',distinct=True)).annotate(Count('Likes',distinct=True)).all().order_by('-id')
You can perform a .select_related(…) [Django-doc] on the user and the people with user__people, so:
posts = Post.objects.select_related(
'user__people', 'category'
).prefetch_related('images_set').annotate(
comments_Count = Count('comments_post',distinct=True),
Count('Likes',distinct=True)
).order_by('-id')
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django access manytomany field from related_name in a view

I have what i think is a simple question but I am struggling to find out how it works. I get how related name works for foreign keys but with many to many fields it seems to break my brain.
I have two 3 models at play here. A User, TeamMember and Team Model as seen below.
User model is the built in django model.
#TeamMember Model
class TeamMember(models.Model):
member = models.ForeignKey(User, on_delete=models.SET(get_default_team_member), verbose_name='Member Name', related_name="team_members")
...
#Team Model
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="managers", null=True, blank=True)
team_lead = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tls", null=True, blank=True)
tps = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tps", null=True, blank=True)
members = models.ManyToManyField(TeamMember, blank=True, related_name="members")
...
Now in a view i want to access a specific users team. I thought i could do this by doing something like this:
member = TeamMember.objects.get(pk=1)
member_team = member.members.name
However if I print member_name than it prints nothing. If I try to access any of the other fields on that model like member.members.team_lead.first_name it fails to find the team_lead field. I understand that this has a .all() attribute but i thought it was tied to the team object through the members field. So if that member matches the team it would give me the team. So I thought it might be an issue if the same member was linked to more than one team (which is possible) so i tired something like this member.members.all().first().name and i get an error that states it cannot get name from NoneType.
Is there an easy way to get the team name from a relationship like this or am i better off just doing a team query with the user?
Thanks,
jAC
First of all, I would like to point out that you are not using the related_name (and related_query_name parameters in a proper way). I think this SO post will help you to understand the concept in a better way.
So, I would change the related_name (and related_query_name) values in the Team model as below,
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
team_lead = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
tps = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
members = models.ManyToManyField(
TeamMember, blank=True, related_name="teams", related_query_name="team"
)
...
Now in a view i want to access a specific user's team.
Since the Team and TeamMember models are connected via ManyToManyField, you may have "zero or more" Teams associated with a single TeamMember
So, the following query will get you all the teams associated with a particular TeamMemeber
team_member = TeamMember.objects.get(pk=1)
all_teams = team_member.teams.all()
You can also iterate over the QuerySet as,
team_member = TeamMember.objects.get(pk=1)
for team in team_member.teams.all():
print(team.name)
For anyone wondering what I did based on JPG's advice was the for loop option
team_member = TeamMember.objects.get(pk=1)
teams = [t.name for t in team_member.members.all()]
I personally do not care which team i get as my need in this case is just to pass a team through even if it is none. So i just use team = team[0] if teams.count() > 0 else "No team"

How to create a shared model in Django?

I have a couple of models that have need of a common set of fields. It is basically a set of various different types of contacts:
# models I would like to share
class Address(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
street1 = models.CharField(_('street1'), max_length=125, blank=True)
street2 = models.CharField(_('street2'), max_length=125, blank=True)
city = models.CharField(_('city'), max_length=50, blank=True)
state = models.CharField(_('state'), max_length=2, blank=True)
zip_code = models.CharField(_('zip_code'), max_length=10, blank=True)
class Phone(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
phone = models.CharField(_('phone'), max_length=50, blank=True)
# these are the models that I would like to have addresses and phone numbers
class Contact(models.Model):
name = models.CharField(_('name'), max_length=50, blank=False)
class Organization(models.Model):
name = models.CharField(_('name'), max_length=255, blank=False)
email = models.CharField(_('email'), max_length=100, blank=True)
website = models.CharField(_('website'), max_length=255, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.CharField(_('photo'), max_length=255, blank=True)
I would like to share the Phone and Address models with the Contact, Organization and UserProfile models. My first attempt was to add ForeignKey on each of Contact, Organization and UserProfile but after more research I believe this is backwards, so I moved the ForeignKey to Address and Phone but then discovered that ForeignKey can belong to one and only one model. In addition to sharing this data structure between multiple different contact types, I would like the ability to add more than one address or phone number to a contact. A contact could have a home address, work address, mobile number, and work number for example. So I have basically 2 questions:
1) Is sharing a model in this way a reasonable thing to do?
2) How would I go about setting up the models?
If I've understood correctly; you want your Contact, Organization, and UserProfile models to all have fields for Address and Phone. Moreover, they can each have more than one address/phone.
Is this reasonable? Sounds so to me.
How could you go about setting up the models? Generic Relations spring to my mind.
Consider just Contact and Address for now. Your second attempt is correct: we have a Many-to-one relationship (one contact, many addresses), so we need to make Contact a ForeignKey field in the Address model, like so:
class Address(models.Model):
<other_fields>
contact = models.ForeignKey('Contact', on_delete=models.CASCADE)
This allows you to assign multiple addresses to each contact.
Moving on, you essentially want your Address model to have multiple foreign keys: Contact, Organization, and UserProfile. One way of achieving this is to use Generic Relations. This makes use of Django's built-in "contenttypes" framework, and allows you to create GenericForeignKey fields that point to more than one model. I encourage you to read the docs linked, since generic relations aren't so trivial. In the end, you'll have something like:
class Address(models.Model):
label = models.CharField(max_length=50, blank=True)
street_1 = models.CharField(max_length=125, blank=True)
street_2 = models.CharField(max_length=125, blank=True)
etc...
models_with_address = models.Q(app_label='app_name', model='contact') | \
models.Q(app_label='app_name', model='organization') | \
models.Q(app_label='app_name', model='userprofile')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=models_with_address)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
With this, you can create several addresses for each of the models specified in the models_with_address query. To be able to query the addresses for a given contact/organization/etc., you'll need a reverse generic relation. Setting this up involves adding the line address = GenericRelation(Address) to the respective models.
For further generalisation, you could create a ContactableModel (or whatever) class:
class ContactableModel(models.Model):
address = GenericRelation('Address')
phone = GenericRelation('Phone')
Any model with an address and phone number (Contact, Organization, etc.) could then inherit this so that you don't have to repeatedly include those two fields. You could also improve the models_with_address limit, so that we have something like limit_choices_to=<subclasses_of_ContactableModel>.
Hope this helps!
A simple solution would be to add ManyToManyField(Address|Phone) (i.e. two M2M fields)
to Contact, Organization and UserProfile.
But that would mean different contacts/organizations/users could share addresses.
While this looks tempting, the problem with such a solution is that if someone
edits an address of a contact, they also change the address for all the remaining objects in the system!
Another possible solution which avoids the above problem
and doesn't require M2M fields or generic relationships would be to use
multi-table inheritance:
class Address(models.Model):
entity = models.ForeignKey(Entity)
...
class Entity(models.Model):
pass
class Contact(Entity):
...
class Organization(Entity):
...
(Under the hood Django creates implicit OneToOneField-s
that point from Contact & Organization to Entity.)

Get all related field in Django model

I am struggling to understand how one-to-many and many-to-many relation works in Django model. My schema looks something like this so far, I am open for suggestions to make it better.
A many-to-many relation between users and team. Also, there will be schedules that belong to a particular user of a team.
This is how my model looks like so far,
class Team(models.Model):
tid = models.AutoField(primary_key=True)
team_name = models.CharField(max_length=30)
manager_name = models.CharField(max_length=30)
class Schedule(models.Model):
sid = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
pay_rate = models.CharField(max_length=30)
location = models.CharField(max_length=50)
class BelongsTo(models.Model):
bid = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE)
Question: I want to get the information of each user, what are their schedules and which team each schedule belongs to. How would I to do it? I have tried BelongsTo.objects.select_related().all(), but it is not working for me.
Note: I am open for suggestions, if something is wrong with my schema or model or the approach, please let me know.
BelongsTo is seems like utility table.So
BelongsTo.objects.all().values('user', 'team__team_name', 'schedule')
Your schema looks almost right, but I would modify it a little bit. In particular, I will change how Schedule is implemented. Instead of having a sid in the User Belongs To join-table, I would include the user and team in the Schedule table as foreign keys.
This is how the Django models should then look like:
class User(models.Model):
username = models.CharField(max_length = 200)
# put other fields like password etc. here
class Team(models.Model):
team_name = models.CharField(max_length=30)
manager_name = models.CharField(max_length=30)
user = models.ManyToManyField("User")
class Schedule(models.Model):
user = models.ForeignKey("User")
team = models.ForeignKey("Team")
date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
pay_rate = models.CharField(max_length=30)
location = models.CharField(max_length=50)
Note the following:
You don't need to have a primary key field in the models because Django adds a primary key field called pk or id automatically.
Note the absence of the User Belongs To model. Django implements join-tables like User Belongs To automatically when you use a ManyToManyField. See the Django docs on many-to-many relationships.
You also don't need on_delete = models.CASCADE on ForeignKey fields, because this is the default behavior.
To see how to get information about users, teams and schedule from this configuration of models, consult the Django documentation on making db queries. It's quite easy.

Django Multiple User Profiles with Class based views (Best Practices)

I have a website where there are two kinds of users (say) : students and tutors.
Both types have common login functionality, age, gender etc but have distinct attributes such as report cards for students and degree certificates for tutors.
I read this question : Django - Multiple User Profiles and setup my profiles as shown below:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
slug = models.SlugField()
...
And two other models that link to the above. Eg:
class StudentProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="user_profile")
#more custom attributes
class TutorProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="doctor_profile")
#more custom attributes
Now my questions:
1) SlugField is defined on the UserProfile attribute but will ideally use the User.username field. This means a join between these two tables will happen each time. Is this to be expected?
2) Assuming I am using class based views, editing/viewing the profile will depend on the UserProfile in question. But I want the user to be able to edit/view all his details on the same page. Thus, I will have to fetch TutorProfile / StudentProfile too and add custom logic to ensure updates happen to them too.
It seems to me that there should be a proper way of handling these situations (Since a lot of websites have similar requirements). What are the best practices to be followed in such a situation?
While searching for answers, I came across a solution which I think might suit my needs (posting here to welcome critique and help out others who might be looking for answers).
Taken from Django Design patterns and Best Practices
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
gender = models.IntegerField(null=True)
user_type = models.CharField(max_length=20, choices=UserTypes.CHOICES)
slg = models.SlugField()
class Meta(object):
abstract = True
class StudentProfile(models.Model):
report_card = models.FileField(upload_to="/temp")
house = models.CharField()
class Meta(object):
abstract = True
class TutorProfile(models.Model):
performance = models.IntegerField(default=0)
salary = models.IntegerField(default=0)
class Meta(object):
abstract = True
One base abstract class and two specific classes which cover the various user profiles. Keeping them separate like this makes it easy for us to reason about the various fields present in each user type.
Finally,
class Profile(UserProfile, StudentProfile, TutorProfile):
pass
This is the model used as the settings.AUTH_USER_MODEL.
Overall, the advantages I see:
Single DB call on user edit/view page.
Easier to think about overall.
Disadvantage : Lots of wasted space.
Anyone has any better suggestions?