List of posts not displaying - django - python

I have created a post model and would like to view the posts in post_list. While creating the new post, it is redirecting to post_list but not displaying any post.
Also, in my post_form I have rendered the fields manually by using django templates. I couldnt figure out where I have made the mistake. Can someone please help me out. Thanks
models.py
class Post(models.Model):
author = models.ForeignKey(User, on_delete = models.CASCADE)
slug = models.SlugField(unique=True, blank=True, default=uuid.uuid1)

By default the context_object_name is object_list
Either you access your Posts list in template with object_list
{% for post in object_list %}
{{ post }} <!-- with lowercase -->
{% endfor %}
Or you change the context_object_name to post_list, so that way you will be able to access the post list with post_list in template
class PostListView(ListView):
model = Post
context_object_name = 'post_list'

You probably need to use lowercase variable name in your post_list.html.
For instance, {{ Post.title }} should probably be lowercase {{ post.title }}.
There are a few places to change that.

Related

Django show same content (of a model) in sidebar of every page (also in different apps)

I have two apps: blog and mysite.
In the project folder, I have a template which includes a sidebar template. This sidebar is shown on every page of the project (index pages, mysite pages, blog pages).
One part of this sidebar should show a list of the latest x blog entries (independent of the page where the user is).
blog/models.py
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=264)
text = RichTextField(config_name='detail_text_field', default='')
created_date = models.DateTimeField(default=timezone.now)
blog/views.py
class LatestBlogEntriesListView(ListView):
model = Post
template_name = 'blog/_latest_blog_entries_list.html'
def get_queryset(self):
return Post.objects.all().order_by('created_date')[-3:]
sidebar.html
<div class="row">
{% include 'blog/_latest_blog_entries_list.html' %}
</div>
_latest_blog_entries_list.html
<h4>Latest Blog Entries</h4>
{% for post in objects %}
{{ post.title }}
{% endfor %}
Unfortunately, this does not work. My sidebar only shows the h4 "Latest Blog Entries", but not the posts. How can I do this?
Any help is highly appreciated!
I found to use a context processor, as described in this post: https://dev.to/harveyhalwin/using-context-processor-in-django-to-create-dynamic-footer-45k4
This allows to access a context variable within all pages.
When using ListView the default object is called object_list. Try changing your code to this:
{% for post in object_list %}
{{ post.title }}
{% endfor %}
You can also change this variable name in the view, so that when you catch it on the template, it's a custom name.
class LatestBlogEntriesListView(ListView):
model = Post
template_name = 'blog/_latest_blog_entries_list.html'
context_object_name = "your_new_object_name"

How do I get reverse reference in Django template?

Apologies if the title doesn't make much sense. I don't quite understand what I lack in knowledge.
I have a Post and Comment models in my Django project. What I'm trying to do is list out all the Blog posts, and show NUMBER OF COMMENTS OF EACH POST. Please see my codes below.
models.py
class Blog(models.Model):
objects = models.Manager()
title = models.CharField(max_length=100, blank=True)
body = models.CharField(max_length=10000, blank=True)
created_at = models.DateField(auto_now_add=False)
class Comment(models.Model):
objects = models.Manager()
post = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='comment')
views.py
def main_page(request):
all_blogs = Blog.objects.all()
context = {
'blog' : blog,
}
return render(request, 'main/home.html', context)
template
{% for b in blog %}
<div>
<p>{{b.title}}</p>
<div>
{{WHERE THE NUMBER OF THIS POST'S COMMENTS IS DISPLAYED}}
</div>
</div>
{% endfor %}
All I need is the number of the comments, but have no idea how to do it. Is there a way to make this possible in the template? Or do I have to add some codes in views.py?
You can annotate the Blog objects with the number of related Comments with:
from django.db.models import Count
def main_page(request):
all_blogs = Blog.objects.annotate(
num_comments=Count('comment')
)
context = {
'blogs' : blogs
}
return render(request, 'main/home.html', context)
The Blog objects that arise from that queryset will have an extra attribute .num_comments with the number of related comments:
{% for blog in blogs %}
<div>
<p>{{ blog.title }}</p>
<div>
{{ blog.num_comments }}
</div>
</div>
{% endfor %}

Django custom template tags

Why my custom template tag doesn't work?
templatetags.py:
from django import template
from ..models import User
register = template.Library()
#register.inclusion_tag('main/post_detail.html', takes_context=True)
def get_user_liked_posts():
request = context['request']
user = User.objects.get(username=request.user.username)
liked_posts = []
for post in user.liked_posts.all():
liked_posts.append(post.name)
return {'liked_posts': liked_posts}
post_detail.html:
{% load static %}
{% load templatetags %}
<nav class="blog-pagination" aria-label="Pagination">
<span id="likes_count">{{ post.likes_count }}</span>
{% if post.name in liked_posts %}
<button id="like_button" class="btn btn-outline-primary btn-primary text-
white">Like</button>
{% else %}
<button id="like_button" class="btn btn-outline-primary">Like</button>
{% endif %}
</nav>
views.py:
class PostDetailView(DetailView):
model = Post
slug_field = 'url'
class LikePostView(View):
def post(self, request, slug):
post = Post.objects.get(id=request.POST['id'])
user = User.objects.get(username=request.user.username)
if request.POST['like'] == 'true':
post.likes_count += 1
user.liked_posts.add(post)
else:
post.likes_count -= 1
user.liked_posts.remove(post)
user.save()
post.save()
return redirect('post_detail', slug)
models.py:
class Post(models.Model):
"""
This is post model
"""
name = models.CharField(max_length=150, blank=False)
article = models.TextField(blank=False)
image = models.ImageField(upload_to='uploads/', blank=True)
likes_count = models.IntegerField(default=0)
url = models.CharField(max_length=150, blank=False)
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug': self.url})
I want to check if the post is in the liked post of the current user, but it doesn't work.
It doesn't show any errors, it just does nothing.
User in my app must like or unlike posts. In models, I have many to many relationship user with the post. I want to check if the user likes this post
The problem is that you don't even use the template tag, furthermore this is not even needed as you can simply write something like so in the template:
{% if post in request.user.liked_posts.all %}
A Liked post
{% else %}
Not a liked post
{% endif %}
But this is a bit inefficient as we are getting all the posts liked by the user just to check if they like some post. Also if this were in a loop with multiple posts we would be making a query for each post.
Instead we can simply annotate whether the user likes a post in the view itself using an Exists subquery [Django docs] on the through model of the many to many:
from django.db.models import Exists, OuterRef
class PostDetailView(DetailView):
model = Post
slug_field = 'url'
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(
liked_by_user=Exists(
User.liked_posts.through.objects.filter(
post_id=OuterRef("pk"),
user_id=self.request.user.id
)
)
)
return queryset
Now in the template we can simply write:
{% if post.liked_by_user %}
A Liked post
{% else %}
Not a liked post
{% endif %}
Note: Your way of saving the count similarly can simply be turned into an annotation using the Count aggregation function [Django
docs].
Generally one should not store calculated attributes in a column since
that might lead to inconsistent data when updating and forgetting to update the related count, etc.

Django - show all images assigned to the post

I am creating blog application and I would like to show all images which were uploaded to the single blog post.
Here are my Models.py:
class Post(models.Model):
title = models.CharField(max_length=100)
content = RichTextField(blank=True, null=True)
class PostImage(models.Model):
post = models.ForeignKey(Post, default=None, on_delete=models.CASCADE, related_name='postimages')
image = models.ImageField(upload_to='gallery/')
Views.py
class PostGalleryView(DetailView):
model = Post
template_name = 'blog/gallery.html'
context_object_name = 'photos'
In the template if I put there {{ photos.postimages }} then it shows on the page: blog.PostImage.None
even though that 2 images are uploaded to the particular blog post. {{ photos.postimages.count }} shows number 2. If I tried to loop through photos.postimages it throws an error: 'RelatedManager' object is not iterable. Any idea how I can access the PostImage model in the template?
Try using loop in your template with all like:
{% for photo in photos.postimages.all %}
{{ photo.image }}
{% endfor %}

Check if a user has voted on a Post, in the template

Here's my Post model:
models
class Post(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
title = models.TextField(max_length=76)
content = models.TextField(null=True, blank=True)
...
class PostScore(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
post = models.ForeignKey(Post, related_name='score')
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
And here's my template. I want to do something like this... if the user has upvoted or downvoted the Post, then hide the upvote/downvote buttons:
{% if request.user in Post.has_answered %}
{% else %}
<img src="upvote.png" class="upvote" />
<img src="downvote.png" class="downvote" />
{% endif %}
I planned to do this by adding a ManyToManyField called has_answered to my Post model, but i'm unable to do that as I get this error:
post.Post.has_answered: (fields.E304) Reverse accessor for 'Post.has_answered' clashes with reverse accessor for 'Post.user'.
HINT: Add or change a related_name argument to the definition for 'Post.has_answered' or 'Post.user'.
post.Post.user: (fields.E304) Reverse accessor for 'Post.user' clashes with reverse accessor for 'Post.has_answered'.
HINT: Add or change a related_name argument to the definition for 'Post.user' or 'Post.has_answered'.
Any idea how I can fix this? I'm not too sure about the error message as I don't think I can alter my current user field.
You can change your models to like this. You might not need PostScore model.
class Post(models.Model):
# Other Fields i.e title, content, author ...
upvotes = models.ManyToMany(User)
downvotes = models.ManyToMany(User)
You can get upvotes on a Post using this.
upvotes = post_object.upvotes.count()
downvotes = post_object.downvotes.count()
To see whether user has upvoted or not,
if request.user in post_object.upvotes.all():
# This user has upvoted this post
Same for downvotes.
You can do similar thing in your template as well and hide/show buttons based on condition.
{% if request.user in post_object.upvotes.all %}
<!-- show upvote button highlighted -->
{% elif request.user in post_object.downvotes.all %}
<!-- show downvote button highlighted -->
{% else %}
<!-- Show both buttons (not highlighted) -->
{% endif %}
Hope this helps.
Have you tried send the flag from your django views to template like :
def myView(request):
parameters['is_user_voted'] = PostScore.objects.filter(user=self.request.user).exists()
.....
.....
send ```parameters``` to your template using render()
And change you template as:
{% if is_user_voted == 'True' %}
{% else %}
<img src="upvote.png" class="upvote" />
<img src="downvote.png" class="downvote" />
{% endif %}

Categories

Resources