Grouping posts by the user who created them - python

I am trying to create a page on my site (social media type) where the posts would be grouped and displayed by the user that created them.
I tried creating a following context item and then accessing the posts through each of the users in that group but it is not showing any results.
I also tried filtering posts by the users in following. However, it does not show any results. I don't know if I am using the filter function correctly.
This is my View:
class CommunityListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'community/community.html'
context_object_name = 'posts'
ordering = ['-date_added']
def get_context_data(self, **kwargs):
context = super(CommunityListView, self).get_context_data(**kwargs)
active_user = self.request.user
active_user_following = active_user.following.values_list('user_id', flat=True)
following_user_objects = []
context['following'] = following_user_objects
context['followed_user_pots'] = Post.objects.filter(user__in=following_user_objects)
for id in active_user_following:
followed = User.objects.get(id=id)
following_user_objects.append(followed)
return context
This is my HTML code:
{% for user in following %}
{{user}}
{% for post in user.post %}
{{post}}
{% endfor %}
{% endfor%}
All the above HTML displays is the username of the users in following. Is there some other way I need to access the posts?
This is the Post model:
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, null=True)
post = models.CharField(max_length=200)
date_added = models.DateTimeField(default=timezone.now)

First I think in values_list you have to put id not user_id, because you want to get ids of the following users. and also that's much better practice when you get query logic in get_queryset method. so you don't need to add followed_user_pots in context it's better to remove it and instead use this code for getting what you want.
def get_queryset(self):
qs = super().get_queryset()
active_user = self.request.user
active_user_following = active_user.following.values_list('id', flat=True)
return Post.objects.filter(user__id__in=following_user_objects)
in template you can access posts by iterating through object_list
{% for post in object_list %}
{{post}}
{% endfor %}

Related

Django Forum App, comments don't update on user-side, but can be seen through admin

For reference, here are my models in my Forum app:
class Forum(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('forum-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
forum = models.ForeignKey(Forum, on_delete=models.CASCADE)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
To display the forum posts, I have a CBV 'ForumListView':
class ForumListView(ListView):
model = Forum
template_name = 'forum/forum.html'
context_object_name = 'forum_posts'
ordering = ['-created_at']
From this list, the user can click on any forum and it will lead them to 'forum-detail' with the CBV 'ForumDetailView':
class ForumDetailView(DetailView):
model = Forum
extra_context = {
'comments': Comment.objects.all().order_by('-created_at')}
Here is where I passed in the comments from my Comment model to be shown alongside the post. I think this is the reason why the comments don't update, but I'm not too sure how to fix this.
In the template for forum_detail.html, this is how I display all the comments made:
{% for comment in comments %}
{% if comment.forum == forum %}
<div class="content-section">
<p>{{ comment.description }}</p>
<small>{{ comment.user.username }}, on {{ comment.created_at|date:"F d, Y" }}</small>
</div>
{% endif %}
{% endfor %}
Note that the new comment made will be shown if I re-run
python manage.py runserver
and sometimes the new comment appears after a few minutes of waiting/refreshing the page.
Also, I think function-based views may fix this, however I have coded my entire app with CBVs and would love a fix that doesn't involve re-coding everything!
Any help is greatly appreciated, and can provide more info/code if needed!
Putting it in extra_context like this will cause the queryset to be evaluated when you define the view, and any values that it has at that time will be the only values that the view will get. That's why it is working when you restart the server. So it should be dynamic and fetched every time a new request comes. In that case you need to put it in get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.all().order_by('-created_at')}
return context

Django - Accessing Model Field in class based view

I have this model
class Post(models.Model):
title = models.CharField(max_length=100)
title2 = models.CharField( max_length=100)
content = models.TextField(default=timezone.now)
content2 = models.TextField(default=timezone.now)
post_image = models.ImageField(upload_to='post_pics')
post_image2 = models.ImageField(upload_to='post2_pics')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
and this function based view that uses the model:
class PostListView(ListView):
model = Post
template_name = 'front/front.html'
context_object_name = 'listings'
ordering = ['-date_posted']
def get_context_data(self, **kwargs):
check_for_zipcode = #where I want to access the author for the current instance
context = super().get_context_data(**kwargs)
context['zipcodes'] = check_for_zipcode
return context
All I want to know is how can I access the author field in the class-based view. I can access the author in my HTML like so"
{% for listings in listings %}
<h3>listings.author</h3>
{% endfor %}
With this, I'll get back the author field for every instance of that model.
How would I get the author for the instance in the variable check_for_zipcode? I tried self.author, self.listings.author, etc; but nothing works
In get_context_data method you have the object_list which is the result of get_queryset method. So you can override the get_queryset method. You can even do the same in get_context_data in object_list. Sample example as below:
def get_queryset(self):
return Post.objects.all().annotate(zipcode=F('author'))
Then in your template:
{% for listing in listings %}
<h3>listing.zipcode</h3>
{% endfor %}
This returns the id of author object since it is a foreignkey. If you want some other attribute like username then do author__username in the annotate function.

Filter and count QuerySet items with a variable

I am new to Django and am working on a very basic social media site as a practice project.
Right now, I am trying to figure out how to filter a QuerySet based on a variable and counting how many items in the QuerySet match the filter.
To demonstrate what I am trying to do, let's say I am looping through all the visible posts (like a Facebook post or something similar), and I am wanting to display the number of comments each post has.
This is how I would go about that:
{% post in all_posts %}
<h1> There are currently {{ HOW DO I FILTER AND COUNT? }} comments on this post</h1>
{% endfor %}
This is what the relevant section of my views.py file looks like:
def index(request):
all_posts = Posts.objects.order_by('-date_published')
all_comments = Comments.objects.order_by('-date_published')
context = {'all_posts': all_posts,
'all_comments': all_comments
}
return render(request, 'social/index.html', context)
The comments link to the posts through the postID variable. So, I know this doesn't work, but ideally I would like to replace my HOW DO I FILTER AND COUNT? part of the template with something like:
{{ all_comments.filter(postID=post).count }}
Is there an easy way to do this in my views.py or in the template itself? The main problem I have is that I do not know how I can pass the post variable in the template to some function that return the count I am looking for.
UPDATE:
Below are my Posts and Comments models:
class Posts(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
content = models.TextField()
date_published = models.DateTimeField('date posted')
class Comments(models.Model):
postID = models.ForeignKey(Posts, on_delete=models.CASCADE)
commenter = models.CharField(max_length=200)
email = models.EmailField(max_length=200)
content = models.TextField()
date_published = models.DateTimeField('date posted')
You can annotate the Posts model objects with the number of Comments with:
def index(request):
all_posts = Posts.objects.annotate(
ncomments=Count('comments')
).order_by('-date_published')
all_comments = Comments.objects.order_by('-date_published')
context = {
'all_posts': all_posts,
'all_comments': all_comments
}
return render(request, 'social/index.html', context)
In the template you can then render this with:
{% post in all_posts %}
<h1> There are currently {{ post.ncomments }} comments on this post</h1>
{% endfor %}
Note: normally a Django model is given a singular name, so Post instead of Posts.

How to query automatically in Django?

I can retrieve data by explicitly giving the 'Id' but I want to retrieve it automatically the top 3 'Id' data. How can I do that. please check my code below and help me out
this is models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.title
this is admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('id','title')
admin.site.register(Post, PostAdmin)
this is views.py
def index(request):
post3 = get_object_or_404(Post, id=3)
post2 = get_object_or_404(Post, id=2)
post1 = get_object_or_404(Post, id=1)
context = {
"post3": post3,
"post2": post2,
"post1": post1,
}
return render(request, 'contents/index.html', context)
as you can see in the above am giving the id number and it works fine but I want it to retrieve the top id automatically
this is my remplate
<h3>{{post3.title}}</h3>
<h3>{{post2.title}}</h3>
<h3>{{post1.title}}</h3>
You can just use slice on queryset (it will automatically add LIMIT to the SQL query) or take objects specified by index:
posts = Post.objects.all()[:3] # 3 first objects
posts = Post.objects.order_by("-id")[:3] # 3 last objects
post = Post.objects.last() # last object
post = Post.objects.order_by("-id")[0] # also last object, but it's better to use `last` in such case
post = Post.objects.order_by("-id")[1] # pre-last object
Pass it to the context:
context = {"posts": posts}
And then you can process this queryset in template:
{% for post in posts %}
<h3>{{ post.title }}</h3>
{% endfor %}
you should query in views for instance posts = Post.objects.all()[0:3] and then in template use for loop
{% for post in posts %}
{{ post.title }}
If you want to create DetailView you can pass post.id via template and add it as parameter in view, then Post = get_object_or_404(id=post_id)

Print all user's followers

I'm trying to print all the followers the person that owns a profile page has. Here is my following table that shows the following relationship:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
I am also using Django's auth User model.
views.py
class FollowersView(DetailView):
model = User
slug_field = 'username'
template_name = 'profile/followers.html'
def get_profile_followers(user):
return user.followers.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["follower_list"] = get_profile_followers(self.object) # self.object is user profile
return context
In the template, I am doing this:
{% for follower in follower_list %}
<h1>{{ follower }}</h1>
{% endfor %}
But, I get this instead: Chris is followed by John. This is correct, Chris is followed by John, however, I want to display only John's user and John's attributes like avatar, follower_count, etc. that are fields in the User table. How can I do this?
Based on text, I suppose you get Following instance as {{ follower }} value. So you can just use . syntax to get follower attributes like this:
{% for follower in follower_list %}
{{ follower.follower.username }}
{{ follower.follower.avatar }}
{% endfor %}
Note that accessing related objects through . required additional DB query. So you may use select_related in you queryset for optimization:
def get_profile_followers(user):
return user.followers.all().select_related('follower')

Categories