Django - Comments - python

I am new to Django and I'm having problems with comment forms. I just want to add a text field for comment on a specific Post's page, but it is hidden. Here is my models.py file in blog app:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=100)
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('post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['date_posted']
def __str__(self):
return '{} - {}'.format(self.author, self.date_posted)
forms.py:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',)
views.py file:
from django.db import models
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.models import User
from django.shortcuts import redirect, render
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from django.views.generic import DetailView
from django.views.generic import CreateView
from django.views.generic import UpdateView
from django.views.generic import DeleteView
from .forms import CommentForm
from .models import Post
from .models import Comment
def home(request):
context = {
'title': 'Home',
'posts': Post.objects.all()
}
return render(request, 'blog/home.html', context)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name='posts'
ordering = ['-date_posted']
paginate_by = 7
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html'
context_object_name='posts'
paginate_by = 7
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
def post_detail(request, slug):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'form': comment_form})
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def about(request):
return render(request, 'blog/about.html', {'title': 'About'})
and finally, the post_detail.html template where everything should be displayed:
{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}
{% 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" }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-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>
<div>
<strong><h2>Comments Section</h2></strong>
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Leave A Comment</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Leave A Comment</button>
</div>
</form>
<ul>
{% for comment in object.comments.all %}
<div>
<img class="rounded-circle article-img" src="{{ post.author.profile.image.url }}">
{{ comment.author }} - {{ comment.date_posted | date:"F d, Y" }}
<br>
{{ comment.content }}
</div>
{% endfor %}
</ul>
</div>
{% endblock content %}
I am stuck with the problem. Any suggestions are appreciated.

Related

Django: child model edit comment not working

I got problem with my edit comments when i press the edit comment from the template i get no error but is redirected to the top of the same page. Anyone got any idea how i can get it to allow me to edit the comment?
This post is edited with only the code needed and i fixed the delete comment from before so that code is removed.
Here is my code:
views.py:
from django.shortcuts import render, redirect, reverse, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.db.models.functions import Lower
from .models import Product, Category, Review
from .forms import ProductForm, ReviewForm
def product_detail(request, product_id):
product = get_object_or_404(Product, pk=product_id)
if request.method == 'POST':
rating = request.POST.get('rating', 3)
content = request.POST.get('content', '')
Review.objects.create(
product=product,
rating=rating,
content=content,
created_by=request.user
)
# redirect to the same page
return redirect('product_detail', product_id=product_id)
reviews = Review.objects.filter(product=product)
context = {
'product': product,
'reviews': reviews
}
return render(request, 'products/product_detail.html', context)
#login_required
def edit_review(request, review_id):
"""
Saves review form edited by user
"""
review = get_object_or_404(Review, pk=review_id)
product = Product.objects.get(name=review.product)
if request.method == 'POST':
review_form = ReviewForm(request.POST or None, instance=review)
if review_form.is_valid():
review_form.save()
messages.success(request, 'Successfully updated product!')
return redirect(reverse('product_detail', args=[product.id]))
# Success message if added
messages.success(request, 'Thank You! Review was edited')
else:
# Error message if form was invalid
messages.error(request, 'Something went wrong. '
'Make sure the form is valid.')
form = ReviewForm(instance=review)
messages.info(request, f'You are editing {review_id}')
template = 'products/edit_review.html'
context = {
'form': form,
'product': review,
}
return redirect(reverse('product_detail', args=[product.id]))
models.py:
from django.db import models
from django.contrib.auth.models import User
class Review(models.Model):
product = models.ForeignKey(Product, related_name='reviews', on_delete=models.CASCADE)
rating = models.IntegerField(default=3)
content = models.TextField()
created_by = models.ForeignKey(User, related_name='reviews', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.product.name, self.created_by)
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.all_products, name='products'),
path('<int:product_id>/', views.product_detail, name='product_detail'),
path('add/', views.add_product, name='add_product'),
path('edit/<int:product_id>/', views.edit_product, name='edit_product'),
path('delete/<int:product_id>/', views.delete_product, name='delete_product'),
path('delete_review/<int:review_id>/delete_review', views.delete_review, name='delete-review'),
path('edit_review/<review_id>', views.edit_review, name="edit_review"),
]
forms.py:
from django import forms
from .widgets import CustomClearableFileInput
from .models import Product, Category, Review
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ('content', 'rating')
widgets = {
'content': forms.Textarea(attrs={'class': 'form-control'}),
'rating': forms.Select(attrs={'class': 'form-control'}),
}
edit_review.html template
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-12 col-md-6">
<hr>
<h2 class="logo-font mb-4">Reviews</h2>
<h5 class="text-muted">Edit you're Review</h5>
<hr>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<form method="POST" action="{% url 'edit_review' review.id %}" class="form mb-2" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
{% if field.name != 'image' %}
{{ field | as_crispy_field }}
{% else %}
{{ field }}
{% endif %}
{% endfor %}
<div class="text-right">
<a class="btn btn-outline-black rounded-0" href="{% url 'reviews' %}">Cancel</a>
<button class="btn btn-black rounded-0" type="submit">Update Review</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
for the first problem in the delete view you must refer to the model with capital Review
#login_required
def delete_review(request, review_id):
review = Review.objects.filter(review_id).last()
For the second problem you can see detailed information about this error and how to handle it, from Here

How do I use django formset_factories?

I am trying to work on a django-based project/website that logs who took out our dogs last. I am very new to django so please bear with lack of knowledge in the field. I currently have my project set up so that I have models based on users/family members, the families' dogs, posts (logs of when and who took out the dogs), and actions, which is a child class that uses the dog and post models as foreign keys to see if the dogs peed and or pooped when they were taken out. The part that I am stuck on is trying to figure out how I create the post form. Since we have two dogs I need the form/post page to display a set of check boxes (for peeing and pooping actions) for each dog model that exists. While I can successfully display one action set, I run into the difficulty of trying to display the correct number of action sets and also to post the set of actions and the post itself. I have been trying to work with the formset_factory function but I am confused as to how I get it to function properly. Can anyone help? I have been stuck on this problem for quite a while and any support would be greatly appreciated.
models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Dog(models.Model):
name = models.CharField(max_length = 20)
class Post(models.Model):
walker = models.ForeignKey(User, on_delete = models.CASCADE)
time_posted = models.DateTimeField(default = timezone.now)
issues = models.TextField(max_length = 300)
class Action(models.Model):
post = models.ForeignKey(Post, on_delete = models.CASCADE)
dog = models.ForeignKey(Dog, on_delete = models.CASCADE)
peed = models.BooleanField(default = False)
pooped = models.BooleanField(default = False)
forms.py:
from django import forms
from django.forms import formset_factory
from dog_log_app.models import *
class Log_Form(forms.ModelForm):
class Meta:
model = Post
fields = ("issues",)
class Action_Form(forms.Form):
peed = forms.BooleanField(initial = False, required = False)
pooped = forms.BooleanField(initial = False, required = False)
views.py:
from django.shortcuts import render
from django.forms import formset_factory, modelformset_factory
from .models import Post, Dog, Action
from dog_log_app.forms import *
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
def home(request):
context = {
'posts': Post.objects.all().order_by('-time_posted'),
'actions': Action.objects.all(),
'dogs': Dog.objects.all()
}
return render(request, 'dog_log_app/home.html', context)
def post(request):
form_post = Log_Form(request.POST or None)
form_actions = modelformset_factory(Action, fields = ('peed', 'pooped'), extra = Dog.objects.count())
if request.method == 'POST':
form_post = Log_Form(request.POST)
if form_post.is_valid() and form_actions.is_valid():
post_save = form_post.save(commit = False)
post_save.walker = request.user
post_save.save()
form_actions.save()
return HttpResponseRedirect('..')
context = {
'post': form_post,
'action': formset_factory(Action_Form, extra = Dog.objects.count()),
'dogs': Dog.objects.all()
}
return render(request, 'dog_log_app/post_form.html', context)
post_form.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post</title>
</head>
<body>
<div>
<form method="POST">
{% csrf_token %}
<fieldset>
<legend>Create Post</legend>
<h3>{{dogs.name}}</h3>
<p>{{action.as_p}}</p>
<p>{{post.as_p}}</p>
</fieldset>
<div>
<button type="submit" value="Ok">Post</button>
</div>
</form>
</div>
</body>
</html>
what you can try would be to use ajax to add the amount you want, what I usually do is use the form:
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
class PostForm(ModelForm):
class Meta:
model = Post
fields = (
'issues',
)
class ActionForm(ModelForm):
class Meta:
model = Action
fields = (
'peed', 'pooped'
)
ActionFormSet = inlineformset_factory(
Post, Action, form=ActionForm,
fields = ['peed', 'pooped'], extra=1, can_delete=True,
)
With the forms created, we move on to the views, which will be class-based for convenience:
from django.db import transaction
from django.views.generic import CreateView, UpdateView
from .models import Post
from .forms import PostForm, ActionFormSet
class PostCreateView(CreateView):
template_name = 'your template'
form_class = PostForm
success_url = reverse_lazy('your url')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['formset'] = ActionFormSet(self.request.POST)
else:
data['formset'] = ActionFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
with transaction.atomic():
self.object = form.save()
if formset.is_valid():
formset.instance = self.object
formset.instance.walker = self.request.user
formset.save()
return super().form_valid(form)
class PostUpdateView(UpdateView):
template_name = 'your template'
form_class = PostForm
model = Post
success_url = reverse_lazy('your url')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['formset'] = ActionFormSet(
self.request.POST, instance=self.object)
else:
data['formset'] = ActionFormSet(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
with transaction.atomic():
self.object = form.save()
if formset.is_valid():
formset.instance = self.object
formset.save()
return super().form_valid(form)
and to display the information you can use a jquery library for the dynamic information on saving:
enter link description here
With that the configuration is much easier
An example template would be:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-content">
<h2>Agregar Proyecto</h2>
<div class="col-md-4">
<form id="form-container" method="post">
{% csrf_token %}
{{ form }}
<h2>Actions</h2>
<table>
{{ formset.management_form }}
{% for form in formset %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr id="projects_data">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<button type="submit" class="btn btn-success">{{ message }}</button>
Cancel
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$(function () {
$('#projects_data').formset({
prefix: '{{ formset.prefix }}',
addText: 'create',
deleteText: 'remove',
});
})
</script>
{% endblock %}
and in that way add data dynamically.

How to add comments on django

I am trying to add a comment system to my project, all the code looks fine but I am getting this error "ValueError at /
The QuerySet value for an exact lookup must be limited to one result using slicing". I dont know what is wrong but the error might be on the views.py file.
views.py
def imagelist(request):
images = Post.objects.all()
post = get_object_or_404(Post)
comments = Comment.objects.filter(post=images)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
contentt = request.POST.get('content')
comment = Comment.objects.create(post=images, user=request.user, content=content)
comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
context2 = {
"images": images,
"comments": comments,
"comment_form": comment_form,
}
return render(request, 'imagelist.html', context2)
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=160)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.post.title.str(self.user.username))
class Post(models.Model):
text = models.CharField(max_length=200)
posti = models.ImageField(upload_to='media/images', null=True, blank="True")
video = models.FileField(upload_to='media/images', null=True, blank="True")
user = models.ForeignKey(User, related_name='imageuser', on_delete=models.CASCADE, default='username')
liked = models.ManyToManyField(User, default=None, blank=True, related_name='liked')
updated = models.DateTimeField(auto_now=True)
created =models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.tittle)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',)
You need to pass to the create mathod of the comments a single Post since the corresponding fields is a ForeignKey and you're passing an entire queryset (Post.objects.all())
You need get just the post where the comment should live.
single_post = Post.objects.get(pk=the_post_pk)
comment = Comment.objects.create(post=single_post, user=request.user, content=content)
The problem is that you write:
images = Post.objects.all()
comments = Comment.objects.filter(post=images)
Here images is a set of Post objects, not a single Post object, hence you can not filter on that. But you actually do not need to do this anyway.
Furthermore there is also a small mistake in the __str__ of your Comment model:
class Comment(models.Model):
# …
def __str__(self):
return '{}-{}'.format(self.post.text, self.user.username)
But the view itself looks "odd", since you here write a list of Posts, but if you make a POST request, you will somehow need to know to what post you want to submit the Comment, therefore at least the view that accepts the POST request, will need to know the primary key of the Post to comment on. You can for example encode that in the urls.py:
# appname/urls.py
from django.urls import path
from app import views
urlpatterns = [
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/', views.post_list, name='post_detail')
]
In that view, we can then fetch the item, for example with get_object_or_404, and in case of a POST set the post and user objects in the view:
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
#login_required
def post_detail(request, pk):
image = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
comment_form = CommentForm(request.POST, request.FILES)
if comment_form.is_valid():
comment_form.instance.user = request.user
comment_form.instance.post = image
comment_form.save()
return redirect(post)
else:
comment_form = CommentForm()
context = {
'post': image,
'comment_form': comment_form,
}
return render(request, 'imagedetail.html', context)
In your template, you can render the comments of the post with:
{{ post }}
{% for comment in post.comment_set.all %}
{{ comment }}
{% endfor %}
<form method="post" action="{% url 'post_detail' pk=post.pk %}">
{% csrf_token %}
{{ comment_form }}
</form>
you can also make a view that renders a list:
#login_required
def post_list(request, pk):
images = Post.objects.prefetch_related('comment_set')
comment_form = CommentForm()
context = {
'image': images,
'comment_form': comment_form,
}
return render(request, 'imagelist.html', context)
in the template for the list, you can render it with:
{% for post in images %}
{{ post }}
{% for comment in post.comment_set.all %}
{{ comment }}
{% endfor %}
<form method="post" action="{% url 'post-detail' pk=post.pk %}">
{% csrf_token %}
{{ comment_form }}
</form>
{% endfor %}
here we thus make a POST request to the post-detail view.
After 4 hours of searching for answers, this is how I achieve it. All I did was add this new view, method, url and html. Hope this helps!
views.py
def imagedetail(request, pk):
post = get_object_or_404(Post, pk=pk)
comments = Comment.objects.filter(post=post)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
comment = Comment.objects.create(post=post, user=request.user, content=content)
comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
context2 = {
"comments": comments,
"comment_form": comment_form,
}
return render(request, 'imagedetail.html', context2)
models.py (on Post model)
def get_absolute_url(self):
return reverse('imagedetail', args=[self.id])
urls.py (for new view which is imagedetail)
path('imagedetail/<int:pk>/', views.imagedetail, name='imagedetail'),
imagelist.html (to redirect to imagedetail, btw it is a bootstrap button)
<a role="button" class="btn btn-primary" href="{% url 'imagedetail' pk=image.pk %}"></a>
imagedetail.html (it only shows comments)
<form method="post">
{% csrf_token %}
{{comment_form.as_p}}
{% if request.user.is_authenticated %}
<input type="submit" value="Submit" class="btn btn-outline-succes">
{% else %}
<input type="submit" value="Submit" class="btn btn-outline-succes" disabled>
{% endif %}
</form>
<div class="main-comment-section">
{{ comments.count }}
{% for comment in comments %}
<blockquote class="blockquote">
<p class="mb-0">{{ comment.content }}</p>
<footer class="blockquote-footer">by <cite title="Source Title">{{ comment.user }}</cite></footer>
</blockquote>
{% endfor %}
</div>
2 - forms.py icine elave ele:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'body')
# overriding default form setting and adding bootstrap class
def __init__(self, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs = {'placeholder': 'Enter name', 'class': 'form-control'}
self.fields['body'].widget.attrs = {'placeholder': 'Comment here...', 'class': 'form-control', 'rows': '5'}
3 - models.py elave ele :
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
name = models.CharField(max_length=50)
parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return self.name
def get_comments(self):
return Comment.objects.filter(parent=self).filter(active=True)
4 - views.py post bele gorunmelidi :
def post (request,slug):
post = Post.objects.get(slug = slug)
latest = Post.objects.order_by('-timestamp')[:3]
comments = post.comments.filter(active=True)
new_comment = None
comment_form = CommentForm()
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
comment_form = CommentForm()
context = {
'post': post,
'latest': latest,
'comments': comments,
'comment_form': comment_form
}
return render(request, 'post.html', context)
5 - post.html de author altina divider den sonra elave ele :
<hr/>
<h3>Add Comment</h3>
<form method="post" action="">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit" class="btn btn-primary">Comment</button>
</form>
{% with comments.count as total_comments %}
<h3 class="mt-5">
{{ total_comments }} comment{{ total_comments|pluralize }}
</h3>
{% endwith %}
{% if not post.comments.all %}
No comments yet
{% else %}
{% for comment in post.get_comments %}
{% include 'comment.html' with comment=comment %}
{% endfor %}
{% endif %}
</div>
6 - admin.py elave ele :
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display=('name', 'post', 'created', 'active')
list_filter = ('active', 'created', 'updated')
search_fields = ('name', 'body')
7 - templates de comment.html yarat ve icine elave et :
<div class="border-0 border-start border-2 ps-2" id="{{comment.id}}">
<div class="mt-3">
<strong>{{comment.name}}</strong>
{% if comment.parent.name%} to <strong>{{comment.parent.name}}</strong>{% endif %}
<small class="text-muted">On {{ comment.created.date }}</small>
</div>
<div class="border p-2 rounded">
<p>{{comment.body}}</p>
<button class="btn btn-primary btn-sm" onclick="handleReply({{comment.id}})">Reply</button>
<div id="reply-form-container-{{comment.id}}" style="display:none">
</div>
</div>
{% for comment in comment.get_comments %}
{% include 'comment.html' with comment=comment %}
{% endfor %}
8 - models de post un altina elave ele
def get_comments(self):
return self.comments.filter(parent=None).filter(active=True)

Comments not loading in Django

I have done a lot of experiments and very much feel out of my depth. I can't get existing comments to appear (I added some via the admin page) or the comment form. I have tried to Google the problem and rearrange my code. Unfortunately I don't have the foggiest idea what is causing the problem. So I don't know if any of my experiments were remotely along the right lines.
After posting the original question I tried using dispatch() in my code. I got error messages I tried to Google but I was just trying to apply band aid on top of band aid.
post_detail.html
{% 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="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y" }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-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>
<article class="media content-section">
<!-- comments -->
<h3>{{ comments.count }} Comments</h3>
{% for comment in comments %}
<div class="media-body">
<a class="mr-2" href="#">{{ comment.name }}</a>
<small class="text-muted">{{ comment.created_on|date:"F d, Y" }}</small>
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ comment.body | linebreaks }}</p>
{% endfor %}
</article>
{% endblock content %}
views.py
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Post
from .forms import CommentForm
from django.shortcuts import render, get_object_or_404
def home(request):
context = {
'posts': Post.objects.all()
}
return render(request, 'blog/home.html', context)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
#class PostDetailView(DetailView):
# model = Post
class PostDetailView(DetailView):
model = Post
def dispatch():
post = get_object_or_404(Post)
comments = post.comments.filter(active=True, slug=slug)
new_comment = None
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
return render(request, post_detail.html, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def about(request):
return render(request, 'blog/about.html')
forms.py
from .models import Comment
from django import forms
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now())
author = models.ForeignKey(User, on_delete=models.CASCADE)
url= models.SlugField(max_length=500, unique=True, blank=True)
def save(self, *args, **kwargs):
self.url= slugify(self.title)
super().save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', kwargs={'slug': self.slug})
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
class Meta:
ordering = ['created_on']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
urls.py (in Blog app)
from django.urls import path
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView
)
from. import views
from django.views.generic import TemplateView
urlpatterns = [
path('', PostListView.as_view(), name='blog-home'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
path('about/', views.about, name='blog-about'),
path('facebook/',TemplateView.as_view(template_name='blog/index.html'), name="facebook")
]
signals.py (in Users app)
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#reciever(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#reciever(post_save, sender=User)
def save_profile(sender, instance, created, **kwargs):
instance.profile.save()
correct your indentation of the code
class PostDetailView(DetailView):
model = Post
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})

Class Views and NoReverseMatch

I am new to coding and especially new to working with Django. I want to build a small "forum" for me and the other students and it works pretty fine.
There is just one error I encounter over and over:
Exception Type: NoReverseMatch
Exception Value: Reverse for 'thread-create' with
arguments '('',)' not found. 1 pattern(s) tried: ['forum/(?P<pk>[0-9]+)/add/$']
I can work around it using functions instead of class based Views, but I doubt that's how it's supposed to be done.
I have the feeling I am missing something very important while working with class views.
I really would appreciate if someone could help me out :-)
It's the links that are not working:
{% url 'user-posts' thread.author.username %}
and more importantly:
{% url 'forum:thread-create' thread.id %}
What's also not working while using classes is:
{% for thread in thema.thread_set %} (works with the functions)
(I only want to see the threads that are bind to a certain thema. That also works pretty well with functions).
And you might also be asking why I don't just use the function based views:
I want to know what am I doing wrong
I want to be able to easy paginate my site.
Code:
# models.py
from django.db import models
from django.urls import reverse
from PIL import Image
from django.utils import timezone
from django.contrib.auth.models import User
class Thema(models.Model):
themengebiet = models.CharField(max_length=350)
beschreibung = models.TextField()
themen_logo = models.FileField(max_length=350, upload_to="logos", default='default.jpg')
erstellt_am = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.themengebiet
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.themen_logo.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.themen_logo.path)
class Thread(models.Model):
thema = models.ForeignKey(Thema, on_delete=models.CASCADE)
titel = models.CharField(max_length=350)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
erstellt_am = models.DateTimeField(default=timezone.now)
thread_logo = models.ImageField(upload_to="logos", d efault='default.jpg')
def get_absolute_url(self):
return reverse('forum:thread-page', kwargs={'pk': self.thema.id})
def __str__(self):
return self.titel
class Posting(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
titel = models.CharField(max_length=350)
erstellt_am = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
inhalt = models.TextField()
def __str__(self):
return self.titel
views.py
from django.views import generic
from django.views.generic import CreateView, UpdateView, DeleteView, ListView
from .models import Thema, Thread, Posting
from django.shortcuts import render, get_object_or_404
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.core.paginator import Paginator
class ThemenView(ListView):
template_name = 'forum/posts_original.html'
context_object_name = 'all_themen'
def get_queryset(self):
return Thema.objects.all()
class ThemaCreate(CreateView):
model = Thema
fields = ['themengebiet', 'beschreibung', 'themen_logo']
success_url = "/forum/"
class ThreadCreate(CreateView):
model = Thread
fields = ['thema', 'titel', 'erstellt_am']
success_url = reverse_lazy('forum:posts_original')
def get_object(self):
return Thread.objects.get(pk=self.kwargs['pk'])
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostCreate(CreateView):
model = Posting
fields = ['thread', 'titel', 'inhalt', 'erstellt_am']
success_url = "/forum/"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ThreadView(ListView):
model = Thread
context_object_name = 'threads'
template_name = 'forum/thread.html'
success_url = reverse_lazy('forum:posts_original')
def get_object(self):
return Thread.objects.get(pk=self.kwargs['pk'])
class PostView(ListView):
model = Posting
context_object_name = 'posts'
template_name = 'forum/posts.html'
urls.py
from django.urls import path
from . import views
app_name = 'forum'
urlpatterns = [
# /forum/
path('', views.ThemenView.as_view(), name='posts_original'),
# forum/id/
path('<int:pk>/', views.ThreadView.as_view(), name='thread-page'),
# forum/thread/add
#path('<int:pk>/add/', views.thread_create, name='thread-create'),
# forum/id//id
path('thread/<int:pk>/', views.PostView.as_view(), name='post-page'),
path('add/', views.ThemaCreate.as_view(), name='thema-create'),
path('<int:pk>/add/', views.ThreadCreate.as_view(), name='thread-create'),
path('thread/<int:pk>/add/', views.PostCreate.as_view(), name='post-create'),
]
'''
def thread(request, pk):
try:
thema = get_object_or_404(Thema, pk=pk)
except Thema.DoesNotExist:
raise Http404("Thema existitiert nicht")
return render(request, 'forum/thread.html', {'thema': thema})
def post(request, pk):
try:
thread = get_object_or_404(Thread, pk=pk)
except Thread.DoesNotExist:
raise Http404("Thema existitiert nicht!")
return render(request, 'forum/posts.html', {'thread': thread})
'''
{% extends "blog/base.html" %}
{{ threads }}
{% block content %}
Zurück zur Themenübersicht
{% for thread in threads %}
<article class="media content-section">
<img class="rounded-circle article-img" src= "{{ thread.thread_logo.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' thread.author.username %}">{{ thread.author }}</a>
<small class="text-muted">{{ thread.erstellt_am|date:"d. F Y" }}</small>
</div>
<h2><a class="article-title" href=" {% url 'forum:post-page' thread.id %}">{{ thread.titel }}</a></h2>
<p class="article-content">Thema: {{ thread.thema }}</p>
</div>
</article>
{% endfor %}
<form action="{% url 'forum:thread-create' thread.id %}" method="post" enctype="multipart/form-data" style="display: inline;">
{% csrf_token %}
Thread hinzufügen
</form>
{% endif %}
{% endblock content %}

Categories

Resources