I am unable to design a code to render one particular post and it's related comments. The issue is maybe in views.py or the url.
I have looked at multiple sources without any results. I am a novice to coding and feel like I am missing some essential point. Posts and comments are getting created correctly and all comments get the correct post_id assigned.
My models.py is set up like this:
class Post(models.Model):
title = models.CharField(max_length=1000)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog-home')
class Comment(models.Model):
cid = models.AutoField(primary_key=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField()
comment_date = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
super(Comment, self).save(*args, **kwargs)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('blog-home')
My views.py is set up like this:
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comment_list'] = Comment.objects.filter(post=WHAT SHOULD GO HERE?)
return context
I need to pass the Post.id or primary key of the post in the filter above. Can someone explain what it should be?
The url used to access the post detail is as follows:
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail')
I get the post detail view rendered out as the author, title and content of the post when I have the following in views.py:
class PostDetailView(DetailView):
model = Post
The template for that is as below:
{% extends "blog/base.html" %}
{% block content%}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{object.author.profile.image.url}}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' object.author.username %}">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y P e" }}</small>
{% if object.author == user %}
<div><a class="btn btn-secondary btn-sm m-1 mb-1" href="{% url 'post-update' object.id%}">Update</a>
<a class="btn btn-danger btn-sm m-1 mb-1" href="{% url 'post-delete' object.id%}">Delete</a></div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% for comment in comment_list %}
<div class='article-content'>
<p>{{comment}}</p>
</div>
{% endfor %}
{% endblock %}
How should I take the post.id or pk of the Post and use it to filter the comments related only to that particular post?
Also, what is a good way to set up a template for rendering the queryset?
You should be able to iterate over the reverse link from Post object to the Comment objects linked to it (by default as comment_set) in your template:
{% for comment in post.comment_set %}
If you want greater control you inject a queryset into the context, something like this, to get the most recent six comments only.
"comments": post.comment_set.order_by("-comment_date")[:6]
The post object selected should be available as self.object in a DetailView and will be default be injected into the render context as object. An invaluable resource for navigating the structure of Django Class-based views is the Classy CBV
site.
Warning: this is "off the top of my head" so don't assume it's all perfect.
A single object will have access to its related objects.
Try this:
class PostDetailView(DetailView):
model = Post
# There's no need to define get_context_data() here
Now your template will have the post available as post (and also object).
All of the comments that have this post are available on the template like this:
{% for comment in post.comment_set.all %}
<div class='article-content'>
<p>{{ comment }}</p>
</div>
{% endfor %}
Related
I am creating a web application that will serve as a grocery store. The way I set it up is so the customer can come onto the website, click on the items that they would like to purchase, and then click a submit button to purchase those items. The problem I am running into is having a views.py function to take the information of which products were selected and subtracting 1 from the quantity of the database.
"""models.py"""
class Post(models.Model):
title = models.CharField(max_length=100)
Price = models.DecimalField(max_digits=4, decimal_places=2,default=1)
Sale = models.DecimalField(max_digits=4, decimal_places=2,default=1)
quantity = models.IntegerField(default=1)
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = TreeForeignKey('Category',null=True,blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
views.py
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
def inventory(request):
products = request.POST.getlist('products')
for product in products:
a = Post.objects.get(title=product)
a.quantity = a.quantity -1
a.save()
print(products) # This line isn't necessary but it will show what is in your list from the terminal.
return redirect('blog-home')
urls.py
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
path('inventory', views.inventory, name='inventory'),
home.html
{% extends "blog/base.html" %}
{% block content %}
<form action="{% url 'inventory' %}" method="POST" id="menuForm">
{% for post in posts %}
{% if post.quantity > 0 %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2">{{ post.category }}</a>
</div>
<h2><a class="article-title" >{{ post.title }}</a></h2>
<p class="article-content"> Price: ${{ post.Price }}</p>
<p class="article-content"> Sale: ${{ post.Sale }}</p>
<input type="checkbox" id="product_{{ post.id }}" value="{{ post.title }}" form="menuForm" name="products" > Inventory count: {{ post.quantity }}
</input>
</div>
</article>
{% else %}
{% endif %}
{% endfor %}
<button type="submit" form="menuForm">Confirm Purchase</button>
</form>
{% endblock content %}
My goal is to click on the checkboxes, and when the customer clicks the button on the bottom of home.html, it triggers the inventory function to subtract "1" from the quantity. When I say print(products), then the terminal gives me the title of all the posts that are check-marked. However, it still isn't going into the database and subtracting 1 from the inventory. (I solved it)
Using Post.objects.get('products[x]') does not make sence, the .get(…) method [Django-doc] expects Q objects as positional parameters, or named parameters, so for example:
from django.shortcuts import redirect
def inventory(request):
products = request.POST.getlist('products')
Post.objects.filter(pk__in=products).update(
quantity=F('quantity')-1
)
return redirect('profile')
By using a .update(…) [Django-doc] you decrement all Posts in bulk which is more efficient than making two database queries per Post object.
Using HttpResponseRedirect("{% url 'profile'%}") will not work either. The "{% url 'profile'%}" is just a string, it does not perform any template rendering. Django will normally return a HTTP 302 response, with {% url 'profile' %} as link to go to, but the browser does not understand {% url 'profile' %}, it can only work with a URI.
I try to remove comment so, I tried the first time by the class-based view then I hashed it to try the second way by normal function to know what's going on here. so, when I try to delete the comment by ID nothing does happen it's just directing me to the web page without deleting the comment so in this case the program runs but doesn't remove the object so, what is going on here?
Note: the posts and comments on the same page and slug field on that page are following by post not comment.
if the title of the post is: new title so, the slug will be new-title depending on the post
question_view.html
<div class="user-answer">
<div class="row">
<div class="col-xs-12">
{% for comment in my_question.comment_set.all %}
<div class="comments">
<div class="col-xs-0">
<div class="avatar">
<a href="{% url 'account:view_profile' comment.username %}">
<img class="img-circle img-thumbnail" style="width:50px; height: 50px;" src="{{ comment.logo }}">
</a>
</div>
</div>
<div class="col-xs-10">
<!-- --Comment itself-- -->
<div class="user_comment">
<p>{{ comment }}</p>
<div class="fa fa-caret-left comment-arrow"></div>
</div>
<!-- start Options in comment -->
<div class="sub-options">
{% if request.user.username == comment.username %}
<!-- --Edit comment-- -->
<div class="edit-comment">
<a>Edit</a>
</div>
<!-- --Delete comment-- -->
<div class="delete-comment">
<form method="post" action="{% url 'community:delete_comment' comment.pk %}">
{% csrf_token %}
<input type="hidden" name="delete-comment" value="{{ comment.comment }}">
<input type="submit" value="delete">
</form>
</div>
{% endif %}
<!-- end Options in comment -->
<!-- --comment Date-- -->
<div style="display: inline-block;color: #8e8e8e" class="comment-date">
<p>{{ comment.date|time }}</p>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
{% endfor %}
</div>
</div>
</div>
views.py
# Delete post
# class DeleteComment(DeleteView, SingleObjectMixin):
# model = Comment
# pk_url_kwarg = 'pk'
# template_name = 'community/question_view.html'
# queryset = Comment.objects.all()
#
# def get_success_url(self):
# title = UserAsking.objects.get(title=self.object)
# slug = UserAsking.objects.get(title=title).ask_slug
# return reverse_lazy('community:question_view', kwargs={'user_slug': slug})
#
# def get_object(self, queryset=None):
# request_comment = self.request.POST['delete-comment']
# return self.get_queryset().filter(pk=request_comment).get()
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
urls.py
urlpatterns = [
# path('delete-comment/<int:pk>/', views.DeleteComment.as_view(), name="delete_comment"),
path('delete-comment/<int:pk>/', views.delete_comment, name="delete_comment"),
]
models.py
class Comment(models.Model):
userasking = models.ForeignKey(UserAsking, on_delete=models.CASCADE)
comment = models.TextField(max_length=500, blank=True)
date = models.DateTimeField(auto_now_add=True)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=30, blank=False, default='empty')
logo = models.ImageField(upload_to='images/', default='images/default-logo.jpg', blank=True)
username = models.CharField(max_length=30, blank=False, default='empty')
def __str__(self):
return self.comment
I hope if you can explain by class based-view what's happening I will appreciate that.
note that: if you could understand exactly what's happening you will know that is no error appears to me, therefore, I get no exception or traceback. thank you in advance.
Try this:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk).delete()
return redirect('community:user_questions')
Your function only deletes the object but not the database entry since you don't trigger a valid SQL operation (delete in this case).
You just delete the object comment again which was assigned previously, but you don't affect the database entry:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
More on delete() in the official docu:
Deleting objects¶
Update on your comment:
You can't use Python's del statement to interact with database entries in Django.
Pythons del statement is used to delete objects initially created by Python like lists, variables, etc. etc.
In order to interact with your database in Django, you have to use the toolbox of Django's built-in Model instance which basically translates raw SQL operations into easy to use methods.
So maybe you can adapt a proper wording to highlight the differences by calling database "objects" entries and python objects: well, objects..
However, Django still offers the option to use raw SQL as fallback.
I'd like to put like and comment section in my blog project with some info like no. of likes and comments and also show user who comment on it in django project but don't know how.
its a simple blog so i just want a like button and a comment button, above that its need to show how much likes and comment that post have and at least need to show one comment below that just like a normal blog have
her is my html code:
{% for post in post %}
<article class="media mt-4">
<div class="media-body">
<img class="rounded-circle article-img" id="dp" src="{{ post.author.profile.image.url }}">
<div class="article-metadata">
<a class="mr-2" href="{% url 'Love Travel-User' post.author.username %}">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"F d, Y" }}</small>
</div>
<h2><a class="article-title" href="{% url 'User-Posts-Details' post.id %}">{{ post.title }}</a></h2>
<img src="{{ post.img.url }}" class="article-img">
<p class="article-content">{{ post.content }}</p>
</div>
</article>
{% endfor %}
views.py
class PostListViews(ListView):
model = Post
template_name = 'userpost/posts.html'
context_object_name = 'post'
ordering = ['-date_posted']
paginate_by = 7
models.py
class Post(models.Model):
title= models.CharField(max_length=100)
img = models.ImageField(upload_to='pics')
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author= models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('User-Posts-Details', kwargs={'pk': self.pk})
any help is appreciated
I will not give the code but I will explain how to do it.
I will help you with the comments part.
You have to create a model Comment link with a Foreign key to Post model. This will allow you to create Comment objects, then you will be able to stock them in your database and display them.
For the view, I prefer Function Based View, it will be easier for you.
And you have to create a ModelForm. To simplify creation of comments you should create an other html page.
I hope it was clear,
Do not hesitate if you have any questions !
Im a newbie working on a news site (or at least trying to, a lot of "problems" in the last few days lol ) trying to learn Django the best I can.
This is what I want to do :
I have an Article Model, it used to have 6 image fields that I used to send to the template and render the images, each image field had its own name and all was well in the world.
Then I got tasked with puting the Article images in a separate Image model.
So I did this :
class Article(models.Model):
title = models.CharField('title', max_length=200, blank=True)
slug = AutoSlugField(populate_from='title', default="",
always_update=True, unique=True)
author = models.CharField('Author', max_length=200, default="")
description = models.TextField('Description', default="")
is_published = models.BooleanField(default=False)
article_text = models.TextField('Article text', default="")
pub_date = models.DateTimeField(default=datetime.now, blank=True)
article_category = models.ForeignKey(Category, on_delete="models.CASCADE", default="")
def __str__(self):
return self.title
class ArticleImages(models.Model):
article = models.ForeignKey(Article, on_delete="models.CASCADE", related_name="image")
image = models.ImageField("image")
name = models.CharField(max_length=50, blank=True)
But so far I wasnt able to access my images in my template using
{{ article.image.url }} or {{ article.image.image.url }}
or any other combination. Why is that ?
Did I set up my models correctly ? One person suggested that I should change the model field from ForeignKey to OneToOneField, but I didn't get much feedback on why and how ?
So, how would I make a for loop that loops through the Articles model and then gets the related images for each Article ? I essentially want it to behave like I still have the 6 different fields like I did before. ( I have to do it this way, it's a part of the task ).
here are my views and my "index" template that I used to loop through the Articles and display 6 latest news on my home page. (please ignore the tags,I am aware they aren't working like this..the template is just so you can understand what I am talking about )
my views.py:
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latest_article_list'
def get_queryset(self):
return Article.objects.all()
class CategoryView(generic.ListView):
template_name = 'news/categories.html'
context_object_name = 'category'
def get_queryset(self):
return Article.objects.filter(article_category__category_title="Politics")
class ArticlesView(generic.ListView):
context_object_name = 'latest_article_list'
template_name = 'news/articles.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
def get_queryset(self):
category_pk = self.request.GET.get('pk', None)
if category_pk:
return Article.objects.filter(article_category__pk=category_pk).order_by("-pub_date")
return Article.objects.order_by("-pub_date")
def article(request, article_id):
article = get_object_or_404(Article, pk=article_id)
context = {'article': article,
'article_category': article.article_category.category_title}
return render(request, 'news/article.html', context)
template that I used with my old model :
{% for article in latest_article_list %}
<img class="single-article-img" src="{{ article.image.name.url }}" alt="">
<div class="container row">
<!-- Start Left Blog -->
<div class="article mt-10 col-md-4 col-sm-4 col-xs-12">
<div class="single-blog" style="margin:10px auto;">
<div class="single-blog-img">
<a href="{% url 'news:article' article.id %}#article-title">
<img class="for-imgs" src="{{ article.image.url }}" alt="">
</a>
</div>
<div class="blog-meta">
<span class="date-type">
<i class="fa fa-calendar"></i>{{ article.pub_date }}
</span>
</div>
<div class="xx blog-text">
<h4>
{{ article.title }}
</h4>
<p>
{{ article.description|truncatewords:30 }}
</p>
</div>
<span>
Read more
</span>
</div>
</div>
{% endfor %}
Thank you !
You need to loop over the images as you have many images against a single article object. You can have something like below to show images in your template:
{% if latest_article_list.articleimages %}
{% for articleimage in latest_article_list.articleimages.all %}
<img src="{{ articleimage.image.url }}" class="d-block w-100" alt="...">
{% endfor %}
{% endif %}
I've tried looking at quite a few posts first, and none of them seem to have an answer to my solution (at least not an obvious one). I'm still very new to Django, so I'm still getting a hang of how everything relates (in terms of the models, views, forms, etc). The blog was originally a carbon-copy of the djangogirls blog from their tutorial, but I wanted to extend it by adding comments from another tutorial on the web. I ran into an error that I couldn't figure out afterwards.
Here's the source code for the comments:
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ["post"]
views.py
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
comments = Comment.objects.filter(post=post)
d = dict(post=post, comments=comments, form=CommentForm(),
user=request.user)
d.update(csrf(request))
return render_to_response('blog/post_detail.html', d)
def add_comment(request, pk):
"""Add a comment to a post."""
p = request.POST
if p.has_key("body") and p["body"]:
author = "Anonymous"
if p["author"]: author = p["author"]
comment = Comment(post=Post.objects.get(pk=pk))
cf = CommentForm(p, instance=comment)
cf.fields["author"].required = False
comment = cf.save(commit=False)
comment.author = author
comment.save()
return redirect("dbe.blog.views.post_detail", args=[pk])
models.py
class Comment(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
author = models.CharField(max_length=60)
body = models.TextField()
post = models.ForeignKey(Post)
def __unicode__(self):
return unicode("%s: %s" % (self.post, self.body[:60]))
class CommentAdmin(admin.ModelAdmin):
display_fields = ["post", "author", "created_date"]
url pattern (urls.py):
url(r'^add_comment/(\d+)/$', views.post_detail, name='add_comment')
post_detail.html:
<!-- Comments -->
{% if comments %}
<p>Comments:</p>
{% endif %}
{% for comment in comments %}
<div class="comment">
<div class="time">{{ comment.created_date }} | {{ comment.author }}</div>
<div class="body">{{ comment.body|linebreaks }}</div>
</div>
{% endfor %}
<div id="addc">Add a comment</div>
<!-- Comment form -->
<form action="{% url add_comment post.id %}" method="POST">{% csrf_token %}
<div id="cform">
Name: {{ form.author }}
<p>{{ form.body|linebreaks }}</p>
</div>
<div id="submit"><input type="submit" value="Submit"></div>
</form>
{% endblock %}
The error is highlighting this line in post_detail.html:
<form action="{% url add_comment post.id %}" method="POST">{% csrf_token %}
And the error itself says:
NoReverseMatch at /post/1/
Reverse for '' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
And the traceback from django is insanely unwieldy, but it first points back to this line in views.py:
return render_to_response('blog/post_detail.html', d)
If you are using Django newer than 1.4, you need to quote your add_comment:
<form action="{% url 'add_comment' post.id %}" method="POST">