2 models in 1 view and 1 template in Django? - python

So i have 2 models,
class Post(models.Model):
topic = models.CharField(max_length=200)
description = models.TextField()
created_by = models.ForeignKey(User, related_name='posts')
created_on = models.DateTimeField()
def __str__(self):
return self.topic
class Comment(models.Model):
commented_by = models.ForeignKey(User, related_name='comments')
commented_on = models.ForeignKey(Post, related_name='comments')
commented_text = models.CharField(max_length=500)
commented_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.commented_text
and i want to display post and comment on single template. So, i created this view:
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
comment_list = Comment.objects.all()
return render(request, 'post_desc.html', {'post': post, 'comment_list':comment_list})
and try to display them in template:
{% if post.created_on %}
{{ post.created_on }}
{% endif %}
<h1>{{ post.topic }}</h1>
<p>{{ post.description }}</p>
<h4>Comments</h4>
{% if comment_list.commented_on == post.topic %}
{{ comment_list.commented_by }}{{ comment_list.commented_text }}
{% endif %}
But it is only displaying post and not comment section. What i did wrong here?

the template code is wrong so the view, your view should be like this:
def post_desc(request, pk):
post = Post.objects.get(id=pk)
return render(request, 'post_desc.html', {'post': post})
and the template should be
{% for comment in post.comments.all %}
<p>{{comment.text}}</p>
{% endfor %}
Where comments is the related name to access the Comment objects from Post.

You have to iterate the comments.
{% for comment in comment_list %}
<do your thing>
{% endfor %}
There is one notable issues in the code.
No need to pass all the comments to template. Only pass the comments that belong to the Post.

Related

Like button is working but not changing to Unlike in django

I've been trying to debug this issue for a day together with my expert coder but we were unable to identify the issue here. The like button works perfectly all right and I am able to query the total likes, but the Clap button does not change to unlike and we are unsure why. I believe the error lies in either the html line:
{% if liked and post.slug == blog_post.slug %} Unlike {% else %} Like {% endif %}
or the django line:
context["liked"] = True if blog_post.likes.filter(id=request.user.id).exists() else False
Big thanks if you can identify the issue here!
views.py
def home_feed_view(request, *args, **kwargs):
context = {}
blog_posts = BlogPost.objects.all()
context['blog_posts'] = blog_posts
if request.method=="POST":
slug=request.POST["submit_slug"]
blog_post = get_object_or_404(BlogPost, slug=slug)
context['blog_post'] = blog_post
context["liked"] = True if blog_post.likes.filter(id=request.user.id).exists() else False
context['total_likes'] = blog_post.total_likes()
type_of_post = TypeofPostFilter(request.GET, queryset=BlogPost.objects.all())
context['type_of_post'] = type_of_post
paginated_type_of_post = Paginator(type_of_post.qs, 13 )
page = request.GET.get('page')
post_page_obj = paginated_type_of_post.get_page(page)
context['post_page_obj'] = post_page_obj
return render(request, "HomeFeed/snippets/home.html", context)
def LikeView(request, slug):
context = {}
user = request.user
if not user.is_authenticated:
return redirect('must_authenticate')
post = get_object_or_404(BlogPost, slug=slug)
liked = False
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
liked = False
else:
post.likes.add(request.user)
liked = True
return redirect(request.META.get('HTTP_REFERER', ''))
<td class="table-primary">
<form action="{% url 'HomeFeed:like_post' post.slug %}" method="POST">
{% csrf_token %}
<button type="submit" name="submit_slug" value="{{ post.slug }}" class='btn btn-primary btn-sm'>
{% if liked and post.slug == blog_post.slug %} Unlike {% else %} Like
{% endif %}
</button>
{{ post.total_likes }} Clap {{ post.total_likes|pluralize }}
</form>
</td>
urls.py
path('<slug>/like/', LikeView, name='like_post'),
models.py
class BlogPost(models.Model):
chief_title = models.CharField(max_length=50, null=False, blank=False)
body = models.TextField(max_length=5000, null=False, blank=False)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='blog_posts', blank=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
Ok so here's a much simpler way to perform a check if a model exist in a m2m field in another model:
html
{% if request.user in blog_post.likes.all %} Unlike {% else %} Like {% endif %}
view
if request.user in blog_post.likes.all():
P.S. In your model you should rename the field likes to likers since its a relation with a User model not a Like model :)
EDIT
So, to easily display a list of posts and their like buttons accordingly on a single page you wanna do this in your template:
views.py
def posts(request):
blog_posts = BlogPost.objects.all()
return render(request, 'index.html', {'blog_posts': blog_posts})
index.html
{% for post in blog_posts %}
<h1>{{ post.chief_title }}</h1>
<p>{{ post.author }} says: </p>
<b>{{ post.body }}</b>
<p> This post is liked by: </p>
{% for user in post.likes %}
<p>{{ user.username }}</p>
{% endfor %}
{% if request.user not in post.likes.all %}
Like
{% else %}
Unlike
{% endif %}
{% endfor %}

how to get unique values in django

I have a category model and list of posts related to those category also some post with same category name but when i wanted to make list of category section in template,
it showing duplicate name of category as it related to posts like:
food,
food,
desert,
style,
desert,
but I want like:
food,
desert,
style,
here is my code:
views.py
class ListCategory(ListView):
model = Post
paginate_by = 2
template_name = 'shit.html'
context_object_name = 'queryset'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cate = Post.objects.all()
context['cate'] = cate
return context
models.py
class Category(models.Model):
title = models.CharField(max_length=20)
thumbnail = models.ImageField()
detail = models.TextField()
featured = models.BooleanField(default=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-category', kwargs={
'pk': self.pk
})
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
featured = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Author,on_delete=models.CASCADE)
thumbnail = models.ImageField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = TaggableManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={
'pk': self.pk
})
templates
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for cat in cate %}
<li>{{cat.category}}<span>(12)</span></li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Thank you so much!
Seems like you want to group your Posts, based on their category; so you can achieve that by iterating over the Category (instead of Post), and use the backward relationship to find out the related Post objects.
views.py
class ListCategory(ListView):
model = Category
paginate_by = 2
template_name = 'shit.html' # :)
context_object_name = 'queryset'
template:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for category in queryset %}
<li>{{category}}<span>{{ category.posts_set.count }}</span></li>
<ul>
{% for post in category.post_set.all %}
<li>{{ post }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</div>
{% endblock content %}
I also use {{ category.post_set.count }} instead of 12, since I think you are looking for the number of Post objects within each category.
You can use unique=True in desired field, to make every value unique. If you'll try to add new record with same value of unique field, a django.db.IntegrityError will be raised.
More about unique
More about model's fields options

account/list_of_post_by_subactegory.html TemplateDoesNotExist

I can't solve this problem. When you try to load the page, list_of_post_by_subcategory.html throws TemplatesDoesNotExist, Please help.
models.py
class Category(models.Model):
category = models.CharField(max_length=250)
slug = models.SlugField()
def __str__(self):
return self.category
def get_absolute_url(self):
return reverse('list_of_post_by_category', args=[self.slug])
class SubCategory(models.Model):
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, related_name="subcategories")
subcategory = models.CharField(max_length=250)
slug = models.SlugField()
def __str__(self):
return self.subcategory
def get_absolute_url(self):
return reverse('list_of_post_by_subcategory', args=[self.slug])
urls.py
url(r'^category/(?P<slug>[-\w]+)/$', views.list_of_post_by_category, name= 'list_of_post_by_category'),
url(r'^subcategory/(?P<slug>[-\w]+)/$', views.list_of_post_by_subcategory, name= 'list_of_post_by_subcategory'),
views.py
def list_of_post_by_category(request, slug):
categories = Category.objects.all()
category=get_object_or_404(Category, slug = slug)
subcategories = SubCategory.objects.filter(category=category)
posts = Post.objects.filter(category=category)
return render(request, "account/list_of_post_by_category.html", {'categories': categories, 'posts': posts, 'category': category, 'subcategories': subcategories})
def list_of_post_by_subcategory(request, slug):
subcategory=get_object_or_404(SubCategory, slug=slug)
posts = Post.objects.filter(subcategory=subcategory)
return render(request, "account/list_of_post_by_subcategory.html", {'posts': posts, 'subcategory': subcategory})
list_of_post_by_subcategory.html
{% extends "base.html" %}
{% block title %}{{ subcategory.subcategory }}{% endblock %}
{% block content %}
<h1>{{ subcategory.subcategory }}</h1>
{% for post in posts %}
<div class="single_post">
{{ post.title }}
<br/>
<br/>
<div>
{{ post.publish }}
{{ post.subcategory }}
</div>
</div>
{% endfor %}
<br/>
{% endblock %}
Similar template list_of_post_by_category.html loads correctly. But list_of_post_by_subcategory.html throws TemplateDoesNotExist.
The filenames don't match here. You posted your error as account/list_of_post_by_subactegory.html TemplateDoesNotExist Look at the name of html file you mentioned(subactegory). And the one you are calling has this name account/list_of_post_by_subcategory.html

Foreign key not rendering in template

I'm building a commenting system, which is working fine but i'm now trying to integrate voting. So I made another model to handle that and I tried to pair it using ForeignKey. Not too familiar with ForeignKey but i've looked at some other answers here to see how to render it in the template. I tried that using the nested for loop in my template below but {{ j.user }} doesn't render anything. Can anyone tell me what I'm doing wrong?
models.py
class Comment(models.Model):
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User)
comment = models.ForeignKey(Comment)
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
views.py
...
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.all()
context = {
'score': score,
'comment_list': comment_list,
}
return render(request, 'article.html', context)
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
{% for j in comment_list.score_set.all %}
{{ j.user }} #nothing comes up
{% endfor %}
<p>{{ i.comment_text }}</p>
</div>
{% endfor %}
when using _set, the reverse relationship lookup, you must provide the full model name, you must also specify which instance this list of related models this "set" is for so it should be
{% for j in i.commentscore_set.all %}
{% empty %}
No scores found
{% endfor %}
You may also wish to set a related_name for the foreign key
comment = models.ForeignKey(Comment, related_name='scores')
...
{% for j in i.scores.all %}

Unable to render ForeignKey fields in my template

I'm making a comment system for my django app and i've been told it's best to make a seperate model for comment-voting. So i've done that and here's my models.py:
class Comment(models.Model):
user = models.ForeignKey(User, default=1)
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User, default=1)
comment = models.ForeignKey(Comment, related_name='score')
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
def __str__(self):
return str(self.comment)
Here's my views.py where the comments are created:
def article(request, category, id):
name = resolve(request.path).kwargs['category']
for a, b in CATEGORY_CHOICES:
if b == name:
name = a
instance = get_object_or_404(Post, id=id, category=name)
allauth_login = LoginForm(request.POST or None)
allauth_signup = SignupForm(request.POST or None)
#comments
comment = CommentForm(request.POST or None)
ajax_comment = request.POST.get('text')
comment_length = len(str(ajax_comment))
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.filter(comment=comment_list)
if request.is_ajax():
if comment.is_valid():
comment = Comment.objects.create(comment_text=ajax_comment, author=str(request.user), destination=id)
print(comment)
comment.save()
score = CommentScore.objects.create(comment=comment)
score.save()
username = str(request.user)
return JsonResponse({'text': ajax_comment, 'text_length': comment_length, 'username': username})
else:
print(comment.errors)
context = {
'score': score,
'comment_list': comment_list,
'comment': comment,
'instance': instance,
'allauth_login': allauth_login,
'allauth_signup': allauth_signup
}
return render(request, 'article.html', context)
So the comment works fine, but as you can see a couple lines later i'm trying to then create a CommentScore instance to match with the comment. In my template, I've rendered each comment and it's fields (comment_text, author etc), but I want to render the upvotes field associated with that comment. How would I do this?
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
</div>
{% endfor %}
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [
'comment_text',
'id',
'author',
'destination',
]
I've already tried the following and they haven't worked;
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
{% for i in comment_list.score_set.all %}
{{ i.upvotes }} #renders nothing
{% endfor %}
</div>
{% endfor %}
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
{% for j in i.score %}
{{ j.upvotes }} #Error: 'RelatedManager' object is not iterable
{% endfor %}
</div>
{% endfor %}
Having a lot of trouble so help is appreciated.
Changing "i.score" to "i.score.all" resolves the problem as the RelatedManaager error usually happens when you are trying to iterate over the manager and not the objects selected by that manager. - Solved by #joe-j
So it works now but if someone could explain the 2nd line of this syntax that would be great:
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.filter(comment=comment_list)
What exactly is happening when I assign comment=comment_list here? I copied this code from someone else but i'm still abit unsure how it's working.

Categories