Just a little on my application so you have an idea of what’s going on. I have a forum where I have posts made by users and users can follow other users.
I have two modals I am trying to join them, they are called Post
and Follow
and I want to display fields from both tables. Below is the Post modal.
class Post(models.Model):
header = models.CharField(max_length=100)
body = models.TextField()
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.header
And my follow modal below.
class Follow(models.Model):
followed = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='followed')
follower = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='follower')
def __str__(self):
return f"{str(self.id)} {self.follower} follows {self.followed}"
In the html I want to be able to display something like post.header post.body post.follow.followed
.
The reason why I need to be able to do something like this above is because currently in the html, I am looping through the posts and then I looping through the follows to see who I am and am not following. This is a causing a duplicate issue. So I thought joining the data on Post and Follow modal would be ideal.
Example of the looping issue below:
{% for post in posts %}
{% include 'base/components/post_component.html' with post=post followers=followers %}
{% endfor %}
{% for i in followers %}
{% if i.followed_id == post.user_id and i.follower_id == user_id %}
<p> "testing loop issue" </p>
{% else%}
<p> "testing loop issue" </p>
{% endif %}
{% endfor %}
I appreciate any comments or suggestions.
You don’t want to do that sort of test or filtering in your template. You want to identify the data you want in your view, and only pass the desired data to the template.
For example, if you have a User
object named user
, then the set of Follow
objects with the followed
field referring to user
would be user.followed.all()
You would then retrieve that list in your view and pass it to your template in the context.
You view is where this work should be done, not the template.
Makes sense to only pass and display data in the html. Okay, view is for the heavy lifting.
Regarding your example of the user and follow. How would I do this? I’m mainly aware of joining modals together which have a foreign key but apart from that I am unsure as how to return a join Post and Follow query set.
You don’t need to do anything special - that’s one of the benefits of Django’s ORM.
Any reference to a Post
implicitly gives you access to the User
object via the user
field. Having a User
object automatically gives you access to the set of the Follow
objects that refer to that user
in the followed
field.
So, given an object named post
(an instance of Post
), the set of all Follow
objects with a followed
field being the User
in the user
field of post
would be post.user.followed.all
. (The followed
in this specific case is not a reference to the field, it’s a reference to the implicitly-created related manager, which means you have all the queryset methods available to it as well. Having the related_name = followed
potentially creates some confusion here.)
1 Like
I did not know about the post.user.followed.all
, that’s very handy to know and I’m starting to see the appeal of Django’s ORM.
Do you know where I can read / watch more on this?
I may have another question on this.
The related object managers are documented here: Related objects reference | Django documentation | Django.
That may not make sense if you’re not familiar or comfortable with model managers, which are documented here: Managers | Django documentation | Django
(A RelatedManager is a special type of a general manager, but they effectively work the same.)
Thanks, this should be helpful.
I think for now, my last question. How do you suggest that I tackle this issue…
As you know, I have a Post and Follow modal. Basically, I’m trying to display all posts on the home page - which is fine. I wanted to implement a follow / unfollow button which is why I created the Follow modal.
I get all the posts and follow objects from the view pass it to my html - home page. I loop the posts becuase I want to display all posts and then within that loop, I make another for loop for the follow object. If the post.user_id == follow.followed_id and follow.follower_id == request.user_id then display the “unfollow” button because are following them, else add a “follow” button.
The issue with this logic above is that if a user is following, let’s say 3 people, then one button for follow / unfollow will not be displayed and it will be 3. Because of the double loop, it is causing duplicates. I did play around with you idea of post.user.followed.all
but the issue remains the same because I have two different query sets and the double loop will cause a duplicate. I did think to try and merge the data somehow.
It sounds like you need to filter the results of the query.
You have a Post. The Post was written by a User.
Someone is reading the Post. That is a different person, one whose identity is identified as request.user.
My understanding from what you’ve written is that you don’t want followed.all
- you want followed.filter
, where the filter is checking to see if the follower
is the person reading the Post. Is my understanding correct?
Yes, you are correct in regarding to finding posts that a user has followed with a filter, thankfully this is fine to do. However, this is the meat of the issue. I want to display all posts on the same page as you scroll down (like twitter) and view them all, regardless of, if or if not followed.
Currently, I do believe in order to do this, all the right data must in one joined or union type object and then running a loop and if condition in the html tags should be fine.
I am thinking of some type of query merge or creating a list from the two queries and merging the, based on if conditions.
This is a completely different problem, and not at all related to the primary topic here.
You’re looking for something commonly referred to as an “infinite scroll”, and it involves some JavaScript accessing a view designed to work with this.
This is precisely not the way to do this.
Side question: Have you worked your way through either the Official Django Tutorial or the Django Girls Tutorial?
I believe I have not written properly what I wanted to convey. Allow me to re-write it.
I want to get all posts from the Post modal - which is fine to do. I want to grab all from the Follow modal where follower_id == request.user_id, now I have an object that tells me what users the request user is following. Initially, I made a loop for the post object and displayed all posts - which works fine.
I wanted to add a follow / unfollow button based on the request user of who they are and are not following, the button toggles correctly and follows/unfollows users however I needed to create another loop within the post loop go through my list based on the follow modal and the issue is this…
The button is duplicating itself based on the amount of users a user is following, I believe this to be due to the double loop I have because when I follow one user it is fine but when I follow a second user, the follow/unfollow button duplicates to two and so with with 3, 4, etc.
I did believe by doing some type of merge of the Post and Follow modal would solve this issue since we would only need to loop once (the post modal) and just do html if else tags based on if a request user following another user or not. Although you have enlightened me that this is not the way to do it.
Not specifically those tutorials but I will add them to my list, thank you for the links.
We’re now back to the previous point.
What you want to do is apply the proper filter on your query to retrieve the Follow objects.
Lets start from the base case.
You have a Post.
That Post has an FK to a User.
There are one or more instances of Follow with the field named followed referencing that User.
If your Follow model is created correctly, each of those instances of Follow reference a different User in the follower field.
You want one row of Follow.
Which of the Follow do you want?
My impression is that you want the instance of Follow that refers to the User referenced by the Post in the followed field that also has the follower field matching the person who is currently logged on.
If that’s correct, then you write your query to retrieve that one row of Follow.
Just want to say thanks for your help on this, you got me thinking on the right track + those tutorials are good so far. My apologies for not clearly writing the issue. I do want to add a bit below to explain how I solved my double loop issue in hope it may be useful to others with the same issue.
Assuming you have read above and got an idea of what’s going on. My issue was a double loop in the html which was causing duplicates. I solved the issue mostly in the view, which I kept the posts the same however I filtered the Follow modal on the follower_id == request.user_id
and then grabbing all followed_id
and putting the values into the list - now we know who our request.user is following. I passed this along to the html (and of course the posts), looped through my posts and had an if statement that went something like if post.user_id in followed_list then display unfollow else display follow
.
Hope this helps anyone who is having a duplicate issue. If unclear, let me know and I’ll write some code to show you.