Django comments not showing in template - python

I have a system setup to show comments on a post detail page, however the comments are not showing. I have been focusing on the template tags, because I have used this view code elsewhere and it has worked, however I may be wrong. No errors returning, just not showing the comment in the detail view.
userpost_detail.html:
{% extends 'base.html' %}
{% block content %}
<div class="main">
<h1 class="posttitle">{{ userpost.title }}</h1>
<p class="postcontent">{{ userpost.post_body }}</p>
{% if request.user.is_authenticated and request.user == post.author %}
<a class="link" href="{% url 'feed:edit_post' post.id %}">Edit Post</a>
{% endif %}
Add Comment
{% for comment in userpost.usercomment.all %}
{% if user.is_authenticated %}
{{ comment.create_date }}
<!--
<a class="btn btn-warning" href="{% url 'comment_remove' pk=comment.pk %}">
<span class="glyphicon glyphicon-remove"></span>
</a>
-->
<p>{{ comment.comment_body }}</p>
<p>Posted By: {{ comment.author }}</p>
{% endif %}
{% empty %}
<p>No Comments</p>
{% endfor %}
</div>
{% include 'feed/sidebar.html' %}
{% endblock %}
app PostDetailView:
class PostDetailView(DetailView):
model = UserPost
app add_comment_to_post view:
#login_required
def add_comment_to_post(request,pk):
post = get_object_or_404(UserPost,pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
return redirect('feed:post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request,'feed/comment_form.html',{'form':form})
app urls:
from django.conf.urls import url
from feed import views
app_name = 'feed'
urlpatterns = [
url(r'^new/$',views.CreatePostView.as_view(),name='new_post'),
url(r'^post/(?P<pk>\d+)$',views.PostDetailView.as_view(),name='post_detail'),
url(r'^post/(?P<pk>\d+)/edit/$',views.UpdatePostView.as_view(),name='edit_post'),
url(r'^post/(?P<pk>\d+)/delete/$',views.DeletePostView.as_view(),name='delete_post'),
url(r'^post/(?P<pk>\d+)/comment/$',views.add_comment_to_post,name='add_comment'),
]
Models.py:
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class UserPost(models.Model):
author = models.ForeignKey(User,related_name='userpost',null=True)
post_date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=150,blank=False)
post_body = models.TextField(max_length=1000,blank=False)
def publish(self):
self.save()
def get_absolute_url(self):
return reverse('index')
def __str__(self):
return self.title
class UserComment(models.Model):
post = models.ForeignKey('feed.UserPost',related_name='comments')
author = models.ForeignKey(User,related_name='usercomment')
comment_date = models.DateTimeField(auto_now_add=True)
comment_body = models.TextField(max_length=500)
def publish(self):
self.save()
def get_absolute_url(self):
return reverse("userpost_list")
def __str__(self):
return self.comment_body

As #user6731765 mentioned you need comments coz of related_name
{% for comment in userpost.comments.all %}
When you get comment_remove error
You need to define a url for comment_remove and define a view for that.
urlpatterns = [
. . . . . .
url(r'^comment/remove/(?P<pk>\d+)/$',views.DeleteCommentView.as_view(),name='comment_remove'),
]
Then in views.py
class DeleteCommentView(DeleteView):
model=UserComment

Due to the related_name you are using for posts in UserComment, try using
{% for comment in userpost.comments.all %}
in your template instead.

Related

Getting NoReverseMatch while click the comment button

I was getting that same error while click the like button, But the error was solved..
again after creating comment view and its other staff I'm getting that error again...When I click the comment button then the error appears..I'm very new to Django,,, help me please..
My project models.py, template page, urls.py, views.py are attached herewith
**models.py**
from email.policy import default
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Blog(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200, verbose_name="Put a Title")
blog_content = models.TextField(verbose_name="What is on your mind")
blog_image = models.ImageField(upload_to="blog_images", default = "/default.png")
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Comment(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_comment" )
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_comment")
comment = models.TextField()
comment_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.comment
class Like(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_liked")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_liked")
class Unlike(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name = "blog_unliked")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = "user_unliked")
**blog_page.html**
{% extends "main.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<div style="text-align:center;">
<h2>{{blog.title}}</h2>
<img src="{{blog.blog_image.url}}" alt="" width="630px" height="300px">
</div>
<div style="text-align:center;">
{{blog.blog_content|linebreaks}}
</div>
{% if not liked and not unliked %}
<h4> Like </h4>
<h4>Unlike</h4>
{% elif unliked %}
<h4> Like </h4>
{% elif liked %}
<h4>Unlike</h4>
{% endif %}
<div>
<h4>
Comments:
</h4>
{% for comment in comments %}
<div>
{{ user }} <br>
<h5>{{ comment }}</h5>
</div>
{% endfor %}
<!-- <h6>Add your comment:</h6> -->
<form action="" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<a class="btn btn-sm btn-info" href="{% url 'comment' %}">Comment</a>
</form>
</div>
{% endblock content %}
**urls.py**
from django.urls import path
from blog_app import views
urlpatterns = [
path("", views.home, name='home'),
path("blog_page/<str:pk>/", views.blog_view, name='blog_page'),
path("like/<str:pk>/", views.like, name="like"),
path("unlike/<str:pk>/", views.unlike, name="unlike"),
path("comment/", views.comment, name="comment"),
]
**views.py**
from django.shortcuts import render
from . models import Blog, Comment, Like, Unlike
from . forms import CommentForm
# Create your views here.
def home(request):
blogs = Blog.objects.all()
context = {'blogs': blogs}
return render(request, 'blog_app/home.html', context)
def blog_view(request, pk):
blog = Blog.objects.get(id=pk)
form = CommentForm()
comments = Comment.objects.filter(blog=blog)
context = {"blog": blog, "comments": comments, "form":form}
return render(request, 'blog_app/blog_page.html', context)
def like(request, pk):
blog = Blog.objects.get(id=pk)
user = request.user
liked, like = Like.objects.get_or_create(blog=blog, user=user)
context = {"liked" : liked, "blog": blog }
return render(request, "blog_app/blog_page.html", context)
def unlike(request, pk):
blog = Blog.objects.get(id=pk)
user = request.user
unliked, unlike = Unlike.objects.get_or_create(blog=blog, user=user)
context = {"unliked" : unliked, 'blog': blog}
return render(request, "blog_app/blog_page.html", context)
def comment(request):
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
context = {}
return render(request, "blog_app/blog_page.html", context)
Your comment button is just a link, is it normal ? I think, you want to submit your form when you click on?
<div>
<h4>
Comments:
</h4>
{% for comment in comments %}
<div>
{{ user }} <br>
<h5>{{ comment }}</h5>
</div>
{% endfor %}
<!-- <h6>Add your comment:</h6> -->
<form action="{% url 'comment' %}" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<button type="submit" class="btn btn-sm btn-info">Comment</button>
</form>
</div>
And i think, your problem occured because you dispolay this template from comment view without set blog in context data.
def blog_view(request, pk):
blog = Blog.objects.get(id=pk)
form = CommentForm()
comments = Comment.objects.filter(blog=blog)
context = {"blog": blog, "comments": comments, "form":form}
return render(request, 'blog_app/blog_page.html', context)
def comment(request):
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
return redirect("blog_page", pk=form.instance.blog.pk)
return HttpResponse(status_code=400) # error case
else:
return HttpResponse(status_code=501) # try to GET page
Better solution is to pass blog pk in the url for being able to render page with error:
path("blog/<int:pk>/comment/", views.comment, name="comment")
<form action="{% url 'comment' blog.pk %}" method="POST">
{% csrf_token %}
{{form|crispy}} <br>
<button type="submit" class="btn btn-sm btn-info">Comment</button>
</form>
def comment(request, pk):
blog = get_object_or_404(Blog, pk=pk)
form = CommentForm()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
return redirect("blog_page", pk=blog.pk)
return render(request, "...", {"blog": blog, "form": form})

How can I get my html to display my django model?

I'm trying to get my django model to be shown in the footer of my base.html but I can't get it to show up. I've looked at a few videos and I can't figure out where I'm going wrong. I know that the model works as I've made 4 entries in my database and I can view them on the admin page. The code also doesn't show any errors so I have nothing to go off of there. Here it is:
Models.py
class SocialMediaPlatform(models.Model):
name = models.CharField(max_length=50, blank=True, null=True)
font_awesome_class = models.CharField(max_length=50, blank=True, null=True)
base_url = models.CharField(max_length=50, blank=True, null=True,
default='https://instagram.com/ or https://tiktok.com/#')
def __str__(self):
return self.base_url
Views.py
def social_media_base_view(request):
context = {}
smbase = SocialMediaPlatform.objects.all()
context['smbase'] = smbase
return render(request, 'base.html', context)
Urls.py
urlpatterns = [
path('', views.social_media_base_view),
]
Admin.py
#admin.register(SocialMediaPlatform)
class SocialPlatformAdmin(admin.ModelAdmin):
list_display = ('name', 'font_awesome_class', 'base_url')
base.html
{% for smlink in smbase %}
<a href="{{ smlink.name }}">
<i class="{{ smlink.font_awesome_class }}">
</a>
{% endfor %}
Look what you are passing as context:
def social_media_base_view(request):
...
smbase = SocialMediaPlatform.objects.all()
context['smbase'] = smbase
return ...
It's QuerySet of SocialMediaPlatform objects. It means, that you can render them one by one with for loop:
{% for smlink in smbase %}
{{ smlink.name }}
{% endfor %}
You don't need to call SocialMediaPlatform model additionaly.
in your view you use:
return render(request, 'index.html', context)
i can not see, if you are defined index.html
Somethere in index you should have
{% extends base.htm %}
The second, i am agree with #Shabble:
{% for smlink in smbase %}
<a href="{{ smlink.name }}">
<i class="{{ smlink.font_awesome_class }}">
</a>
{% endfor %}
The last:
Try to use Django-GCBV TemplateView. Or ListView, in your case it is better:
somethere in views.py
Class MyListView(ListView):
model = SocialMediaPlatform
template_name = index.html
somethere in urls.py
urlpatterns = [
path('', MyListView.as_view()),
]
somethere in index.html
{% for smlink in object_list %}
<a href="{{ smlink.name }}">
<i class="{{ smlink.font_awesome_class }}">
</a>
{% endfor %}

Django - Form not saving when submitted

Good afternoon all,
One of my form does not seem to save when submitted. I cannot see why, in particular as I have a similar form working just fine using the same code.
For some reason it work just fine using the admin panel.
My assumption is that I am missing something that tells the form it needs to be saved. But cannot find what.
Any ideas?
Models
RATING=(
(1,'1'),
(2,'2'),
(3,'3'),
(4,'4'),
(5,'5'),
)
class ProductReview(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE)
product=models.ForeignKey(Product,related_name="comments", on_delete=models.CASCADE)
review_text=models.TextField(max_length=250)
review_rating=models.IntegerField(choices=RATING,max_length=150, default=0)
date_added = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
Views
def add_review(request, product_id):
product = Product.objects.get(pk=product_id)
form = ReviewAdd(request.POST or None, instance=product) #instance=product (populate field with existing information)
if form.is_valid():
form.save()
return redirect('product')
return render(request, 'main/add_review.html',{'form':form})
URL
from django.urls import path
from . import views
urlpatterns = [
...
path('product/add_review/<product_id>', views.add_review,name="add_review"),
]
Forms
class ReviewAdd(forms.ModelForm):
class Meta:
model = ProductReview
fields = ('review_text', 'review_rating')
labels ={
'review_text': '',
'review_rating': '',
}
widgets = {
'review_text': forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter Review'}),
}
Admin
from django.contrib import admin
from .models import Venue, User, Product, ProductReview
from django.urls import path
admin.site.register(User)
admin.site.register(ProductReview)
class ProductReview(admin.ModelAdmin):
list_display=['user','product','review_text','get_review_rating']
HTML Page
{% extends 'main/base.html' %}
{% load crispy_forms_tags %}
{% block title %}
{% endblock %}
{% block content %}
<center>
<h1>Add ReviewTo Database</h1>
<br/><br/>
{% if submitted %}
Success!
{% else %}
<form action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="Submit" value="Submit" class="btn btn-secondary">
</form>
{% endif %}
</center>
{% endblock %}
I detect 2 fixes
On your url, the parameter product_id might need the type of data it will going to receive
path('product/add_review/<int:product_id>', views.add_review,name="add_review"),
And in your view, you are sending an instance, and no data for a new rating.
In your view:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
#login_required
def add_review(request, product_id):
product = get_object_or_404(Product, pk=product_id)
form = ReviewAdd(request.POST or None)
if form.is_valid():
new_rating = form.save(commit=False)
new_rating.product = product
new_rating.user = request.user
new_rating.save()
return redirect('product')
return render(request, 'main/add_review.html',{'form':form})

Django: post form and post list on the same page

I make a site with multiple users, making posts with images and ability to add/remove friends.
So it's easy to make two different pages for post list and creating a new one. But of course it looks better when you can read posts and make new at the same place.
As I understand (learn django for less than a month), I can't connect 2 views to the same url, so the most logical way I see is to join 2 views in one, I also tried to play with template inheriting to render post form by including template, but actually it doesn't work.
Below you can see my views, Post model, and templates. Thank you for attention.
views.py:
from braces.views import SelectRelatedMixin
from . import models
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
class PostList(SelectRelatedMixin, generic.ListView):
model = models.Post
select_related = ('user',)
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('post_message', 'post_image')
model = models.Post
select_related = ('user',)
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
models.py:
import misaka
class Post(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE, related_name = 'posts')
posted_at = models.DateTimeField(auto_now = True)
post_message = models.TextField()
message_html = models.TextField(editable = False)
post_image = models.ImageField(upload_to = 'postpics', blank = True)
def __str__(self):
return self.post_message
def save(self, *args, **kwargs):
self.message_html = misaka.html(self.post_message)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('posts:all')
class Meta:
ordering = ['-posted_at']
unique_together = ['user', 'post_message']
urls.py:
app_name = 'posts'
urlpatterns = [
path('', views.PostList.as_view(), name = 'all'),
path('new/', views.CreatePost.as_view(), name = 'create'),
]
post_form.html (template, that allows to make a new post, which will be seen in post_list.html):
{% extends 'posts/post_base.html'%}
{% block post_content %}
<div class="post-form">
<form action="{% url 'posts:create' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.post_message }}</p>
<p>{{ form.post_image }}</p>
<input id='post-submit' type="submit" value="Post">
</form>
</div>
{% endblock %}
post_list.html:
{% extends 'posts/post_base.html'%}
{% block post_content %}
<div class="post-container">
{% for post in post_list %}
<div class="current-post-container">
{% include 'posts/_post.html'%}
</div>
{% endfor %}
</div>
{% endblock %}
_post.html(pages, which render by Misaka):
<div class="post-info">
<h5 id='post-owner' >{{post.user.first_name}} {{post.user.last_name}}</h5>
<h6>{{ post.posted_at }}</h6>
<p>{{ post.message_html|safe }}</p>
<div>
<img class='post-image' src="/media/{{ post.post_image }}" alt="">
<div>
{% if user.is_authenticated and post.user == user and not hide_delete %}
<a href="{% url 'posts:delete' pk=post.pk %}" title = 'delete'>Delete</a>
{% endif %}
</div>
</div>
</div>
post_base.html:
{% extends 'base.html' %}
{% block content%}
{% block prepost %}{% endblock %}
{% block post_content %}{% endblock %}
{% block post_post %}{% endblock %}
{% endblock %}
EDIT:
Task was solved. I added two template_name strings to both of my views, so now they look like:
CreatePost in views.py:
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('post_message', 'post_image')
model = models.Post
select_related = ('user',)
template_name = 'posts/post_list.html'
template_name = 'posts/post_form.html'
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
PostList in views.py:
class PostList(SelectRelatedMixin, generic.ListView):
model = models.Post
select_related = ('user',)
template_name = 'posts/post_list.html'
template_name = 'posts/post_form.html'
You can put the post_create_form on the same page as post_list_view there is no need to make a separate view for post creation but You need to make ones for editing and deleting.
You can give all of these views the same HTML page with different URLs.
Using template_name = 'example/example.html' ,in Class_Based_Views.
I hope I understand your problem if not clarify more why you can't join two views in one.
def posts(request):
posts = Post.objects.all()
form = PostForm(request.POST or None, request.FILES or None)
if request.method == "POST":
if form.is_valid():
...
context={
'posts' : page_obj,
'create_or_update_post_form' : form,
}
return render(request, 'post/posts.html',context)
Do you struggle to do this in Class-based-view?
You can do easily with django class based views.
Create views as
from django.views.generic import ListView
from django.views.generic.edit import CreateView
class ModelCreate(CreateView):
model = ModelName
fields = ['field1', 'field2']
template_name = 'same_page.html'
success_url = reverse_lazy('list_view')
class ModelList(CreateView, ListView):
model = ModelName
fields = ['field1', 'field2']
paginate_by = 5
template_name = 'same_page.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = self.get_form()
return context
# If form post redirect to ModelCreate View
def post(self, request, *args, **kwargs):
return ModelCreate.as_view()(request)
app/urls.py
from django.urls import path
from app import views
path('list', views.ModelList.as_view(), name='list_view'),
path('add', views.ModelCreate.as_view(), name='add_view'),
Finally in templates/same_page.html
<div class="row">
<div class="col-sm-5">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit" value="submit" class="btn btn-primary btn-sm float-right">Submit</button>
</form>
</div>
<div class="col-sm-5">
{% if object_list %}
{% for object in object_list %}
<p>{{object.field1}}</p>
<p>{{object.field2}}</p>
{% endfor %}
{% endif %}
</div>
</div>
Hope, this helps.

Django user posts to be filtered from all posts and displayed in user profile

I am new to Python and Django and now developing a blog.I have this model that users can log in to the site and add their posts and all posts are displayed in home. At the same time I want the posts by the user to be displayed in user profile.
My model for the blogpost is
from django.db import models
from django.utils import timezone
class Blogpost(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
and the views.py:
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from blogpost.models import Blogpost
def home(request):
context = {}
template = 'home.html'
return render(request,template,context)
def about(request):
context = {}
template = 'about.html'
return render(request,template,context)
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
context = {'user': user}
user_posts=Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request,template,context,{'user_posts':user_posts})
I am using this template to display the posts from users returned by the query set.
profile.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block blogprofile %}
{{ user.username }}
{{ user.email }}
{% for Blogpost in user_posts %}
<div class="post">
<div class="date">
<p>{{ Blogpost.published_date }}</p>
<p>{{ Blogpost.author }}</p>
</div>
<h1>{{ Blogpost.title }}</h1>
<p>{{ Blogpost.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}
Now I am getting only user name and email when I open the profile.html where as the post is not getting retrieved. Can anyone please correct me on where I am making mistake.
Change your view to this:
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
user_posts = Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request, template, {'user_posts':user_posts,'user': user})

Categories