On my social media platform, the news feed (called post_list) seems to be hidden from any user that isn't an admin.
Even when text is entered into the box and 'post' is pressed, the page refreshes but the text box isn't emptied and no posts appear belowhand.
If I log in as an admin, the feed appears immediately and so do all the posts that I just posted from the non-admin account that didn't appear on the non-admin account. As shown here.
I have just recently added the ability to share posts, so maybe that has influenced this issue in some manner.
(The news feed) post_list.html:
> {% extends 'landing/base.html' %} {% load crispy_forms_tags %}
>
> {% block content %} <div class="container">
> <div class="row justify-content-center mt-3">
> <div class="col-md-5 col-sm-12 border-bottom">
> <h5>Create a Post</h5>
> </div>
> </div>
>
> <div class="row justify-content-center mt-3">
> <div class="col-md-5 col-sm-12 border-bottom">
> <form method="POST" enctype="multipart/form-data">
> {% csrf_token %}
> {{ form | crispy }}
> <div class="d-grid gap-2">
> <button class="btn btn-light" style="background-color: #CC64C3; mt-3 mb-5">Post</button>
> </div>
> </form>
> </div>
> </div>
>
> {% for post in post_list %}
> <div class="row justify-content-center mt-3">
> <div class="col-md-5 col-sm-12 border-bottom position-relative">
> <div>
> <a href="{% url 'profile' post.author.profile.pk %}">
> <img class="rounded-circle post-img" height="30" width="30" src="{{ post.author.profile.picture.url }}" />
> </a>
> <p class="post-text">
> <a class="text-primary post-link" href="{% url 'profile' post.author.profile.pk %}">#{{ post.author }}</a> {{
> post.created_on }}
> </p>
> </div>
> <div class="position-relative">
> <p>{{ post.body }}</p>
>
> {% if post.image.count > 0 %}
> <div class="row">
> {% for img in post.image.all %}
> <div class="col-md-4 col-xs-12">
> <img src="{{ img.image.url }}" class="post-image" />
> </div>
> {% endfor %}
> </div>
> {% endif %}
> <form method="POST" action="{% url 'share-post' post.pk %}" class="d-none" id="{{ post.pk }}">
> {% csrf_token %}
> {{ shareform | crispy }}
> <div class="d-grid gap-2">
> <button class="btn btn-light" style="background-color: #CC64C3; mt-3 mb-5">Share</button>
> </div>
> </form>
> </div>
>
> <div class="d-flex flex-row">
> <form method="POST" action="{% url 'like' post.pk %}">
> {% csrf_token %}
> <input type="hidden" name="next" value="{{ request.path }}">
> <button class="remove-default-btn" type="submit">
> <i class="far fa-thumbs-up"><span>{{ post.likes.all.count }}</span></i>
> </button>
> </form>
>
> <form method="POST" action="{% url 'dislike' post.pk %}">
> {% csrf_token %}
> <input type="hidden" name="next" value="{{ request.path }}">
> <button class="remove-default-btn" type="submit">
> <i class="far fa-thumbs-down"><span>{{ post.dislikes.all.count }}</span></i>
> </button>
> </form>
> <span onclick="shareToggle('{{ post.pk }}')"><i class="far fa-share-square share-btn"></i></span>
> </div>
> </div>
> {% endfor %} </div> {% endblock content %}
views.py:
from django.shortcuts import render, redirect
from django.db.models import Q
from django.utils import timezone
from django.urls import reverse_lazy
from django.shortcuts import redirect
from django.contrib import messages
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.views import View
from django.views.generic.edit import UpdateView, DeleteView
from .models import MessageModel, Post, Comment, UserProfile, Notification, ThreadModel, MessageModel, Image
from .forms import PostForm, CommentForm,ThreadForm, MessageForm, ShareForm
class PostListView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
logged_in_user = request.user
posts = Post.objects.filter(
author__profile__followers__in=[logged_in_user.id]
).order_by('-created_on')
form = PostForm()
share_form = ShareForm()
context = {
'post_list': posts,
'shareform': share_form,
'form': form,
}
return render(request, 'social/post_list.html', context)
def post(self, request, *args, **kwargs):
logged_in_user = request.user
posts = Post.objects.filter(
author__profile__followers__in=[logged_in_user.id]
).order_by('-created_on')
form = PostForm(request.POST, request.FILES)
files = request.FILES.getlist('image')
share_form = ShareForm()
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = request.user
new_post.save()
for f in files:
img = Image(image=f)
img.save()
new_post.image.add(img)
new_post.save()
context = {
'post_list': posts,
'share_form': share_form,
'form': form,
}
return render(request, 'social/post_list.html', context)
class PostDetailView(LoginRequiredMixin, View):
def get(self, request, pk, *args, **kwargs):
post = Post.objects.get(pk=pk)
form = CommentForm()
comments = Comment.objects.filter(post=post).order_by('-created_on')
context = {
'post': post,
'form': form,
'comments': comments,
}
return render(request, 'social/post_detail.html', context)
def post(self, request, pk, *args, **kwargs):
post = Post.objects.get(pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.author = request.user
new_comment.post = post
new_comment.save()
comments = Comment.objects.filter(post=post).order_by('-created_on')
notification = Notification.objects.create(notification_type=2, from_user=request.user, to_user=post.author, post=post)
context = {
'post': post,
'form': form,
'comments': comments,
}
return render(request, 'social/post_detail.html', context)
class CommentReplyView(LoginRequiredMixin, View):
def post(self, request, post_pk, pk, *args, **kwargs):
post = Post.objects.get(pk=post_pk)
parent_comment = Comment.objects.get(pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.author = request.user
new_comment.post = post
new_comment.parent = parent_comment
new_comment.save()
notification = Notification.objects.create(notification_type=2, from_user=request.user, to_user=parent_comment.author, comment=new_comment)
return redirect('post-detail', pk=post_pk)
class PostEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields =['body']
template_name = 'social/post_edit.html'
def get_success_url(self):
pk = self.kwargs['pk']
return reverse_lazy('post-detail', kwargs={'pk': pk})
def test_func(self):
post = self.get_object()
return self.request.user == post.author
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
template_name = 'social/post_delete.html'
success_url = reverse_lazy('post-list')
def test_func(self):
post = self.get_object()
return self.request.user == post.author
class CommentDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Comment
template_name = 'social/comment_delete.html'
def get_success_url(self):
pk = self.kwargs['post_pk']
return reverse_lazy('post-detail', kwargs={'pk': pk})
def test_func(self):
post = self.get_object()
return self.request.user == post.author
class ProfileView(View):
def get(self, request, pk, *args, **kwargs):
profile = UserProfile.objects.get(pk=pk)
user = profile.user
post = Post.objects.filter(author=user).order_by('-created_on')
followers = profile.followers.all()
if len(followers) == 0:
is_following = False
for follower in followers:
if follower == request.user:
is_following = True
break
else:
is_following = False
number_of_followers = len(followers)
context = {
'user': user,
'profile': profile,
'posts': post,
'number_of_followers': number_of_followers,
'is_following': is_following,
}
return render(request, 'social/profile.html', context)
class ProfileEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = UserProfile
fields = ['name', 'bio', 'birth_date','location','picture']
template_name = 'social/profile_edit.html'
def get_success_url(self):
pk = self.kwargs['pk']
return reverse_lazy('profile', kwargs={'pk':pk})
def test_func(self):
profile = self.get_object()
return self.request.user == profile.user
class AddFollower(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
profile = UserProfile.objects.get(pk=pk)
profile.followers.add(request.user)
notification = Notification.objects.create(notification_type=3, from_user=request.user, to_user=profile.user)
return redirect('profile', pk=profile.pk)
class RemoveFollower(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
profile = UserProfile.objects.get(pk=pk)
profile.followers.remove(request.user)
return redirect('profile', pk=profile.pk)
class AddLike(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
post = Post.objects.get(pk=pk)
is_dislike = False
for dislike in post.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if is_dislike:
post.dislikes.remove(request.user)
is_like = False
for like in post.likes.all():
if like == request.user:
is_like = True
break
if not is_like:
post.likes.add(request.user)
notification = Notification.objects.create(notification_type=1, from_user=request.user, to_user=post.author, post=post)
if is_like:
post.likes.remove(request.user)
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
class AddDislike(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
post = Post.objects.get(pk=pk)
is_like = False
for like in post.likes.all():
if like == request.user:
is_like = True
break
if is_like:
post.likes.remove(request.user)
is_dislike = False
for dislike in post.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if not is_dislike:
post.dislikes.add(request.user)
if is_dislike:
post.dislikes.remove(request.user)
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
class AddCommentLike(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
comment = Comment.objects.get(pk=pk)
is_dislike = False
for dislike in comment.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if is_dislike:
comment.dislikes.remove(request.user)
is_like = False
for like in comment.likes.all():
if like == request.user:
is_like = True
break
if not is_like:
comment.likes.add(request.user)
notification = Notification.objects.create(notification_type=1, from_user=request.user, to_user=comment.author, comment=comment)
if is_like:
comment.likes.remove(request.user)
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
class AddCommentDislike(LoginRequiredMixin, View):
def post(self, request, pk, *args, **kwargs):
comment = Comment.objects.get(pk=pk)
is_like = False
for like in comment.likes.all():
if like == request.user:
is_like = True
break
if is_like:
comment.likes.remove(request.user)
is_dislike = False
for dislike in comment.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if not is_dislike:
comment.dislikes.add(request.user)
if is_dislike:
comment.dislikes.remove(request.user)
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
class SharedPostView(View):
def post(self, request, pk, *args, **kwargs):
original_post = Post.objects.get(pk=pk)
form = ShareForm(request.POST)
if form.is_valid():
new_post = Post(
shared_body=self.request.POST.get('body'),
body=original_post.body,
author=original_post.author,
created_on=original_post.created_on,
shared_user=request.user,
shared_on=timezone.now(),
)
new_post.save()
for img in original_post.image.all():
new_post.image.add(img)
new_post.save()
return redirect ('post-list')
class UserSearch(View):
def get(self, request, *args, **kwargs):
query = self.request.GET.get('query')
profile_list = UserProfile.objects.filter(
Q(user__username__icontains=query)
)
context = {
'profile_list': profile_list,
}
return render(request, 'social/search.html', context)
class ListFollowers(View):
def get(self, request, pk, *args, **kwargs):
profile = UserProfile.objects.get(pk=pk)
followers = profile.followers.all()
context = {
'profile': profile,
'followers': followers,
}
return render(request, 'social/followers_list.html', context)
class PostNotification(View):
def get(self, request, notification_pk, post_pk, *args, **kwargs):
notification = Notification.objects.get(pk=notification_pk)
post = Post.objects.get(pk=post_pk)
notification.user_has_seen = True
notification.save()
return redirect('post-detail', pk=post_pk)
class FollowNotification(View):
def get(self, request, notification_pk, profile_pk, *args, **kwargs):
notification = Notification.objects.get(pk=notification_pk)
profile = UserProfile.objects.get(pk=profile_pk)
notification.user_has_seen = True
notification.save()
return redirect('profile', pk=profile_pk)
class ThreadNotification(View):
def get(self, request, notification_pk, object_pk, *args, **kwargs):
notification = Notification.objects.get(pk=notification_pk)
thread = ThreadModel.objects.get(pk=object_pk)
notification.user_has_seen = True
notification.save()
return redirect('thread', pk=object_pk)
class RemoveNotification(View):
def delete(self, request, notification_pk, *args, **kwargs):
notification = Notification.objects.get(pk=notification_pk)
notification.user_has_seen = True
notification.save()
return HttpResponse('Success', content_type='text/plain')
class ListThreads(View):
def get(self, request, *args, **kwargs):
threads = ThreadModel.objects.filter(Q(user=request.user) | Q(receiver=request.user))
context = {
'threads': threads
}
return render(request, 'social/inbox.html', context)
class CreateThread(View):
def get(self, request, *args, **kwargs):
form = ThreadForm()
context = {
'form': form
}
return render(request, 'social/create_thread.html', context)
def post(self, request, *args, **kwargs):
form = ThreadForm(request.POST)
username = request.POST.get('username')
try:
receiver = User.objects.get(username=username)
if ThreadModel.objects.filter(user=request.user, receiver=receiver).exists():
thread = ThreadModel.objects.filter(user=request.user, receiver=receiver)[0]
return redirect('thread', pk=thread.pk)
elif ThreadModel.objects.filter(user=receiver, receiver=request.user).exists():
thread = ThreadModel.objects.filter(user=receiver, receiver=request.user)[0]
return redirect('thread', pk=thread.pk)
if form.is_valid():
thread = ThreadModel(
user=request.user,
receiver=receiver
)
thread.save()
return redirect('thread', pk=thread.pk)
except:
messages.error(request, 'User not found')
return redirect('create-thread')
class ThreadView(View):
def get(self, request, pk, *args, **kwargs):
form = MessageForm()
thread = ThreadModel.objects.get(pk=pk)
message_list = MessageModel.objects.filter(thread__pk__contains=pk)
context = {
'thread': thread,
'form': form,
'message_list': message_list,
}
return render(request, 'social/thread.html', context)
class CreateMessage(View):
def post(self, request, pk, *args, **kwargs):
form = MessageForm(request.POST, request.FILES)
thread = ThreadModel.objects.get(pk=pk)
if thread.receiver == request.user:
receiver = thread.user
else:
receiver = thread.receiver
if form.is_valid():
message = form.save(commit=False)
message.thread = thread
message.sender_user = request.user
message.receiver_user = receiver
message.save()
notification = Notification.objects.create(
notification_type=4,
from_user=request.user,
to_user=receiver,
thread=thread
)
return redirect('thread', pk=pk)
models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Post(models.Model):
shared_body = models.TextField(blank=True, null=True)
body = models.TextField()
image = models.ManyToManyField('Image', blank=True)
created_on = models.DateTimeField(default=timezone.now)
shared_on = models.DateTimeField(blank=True, null=True)
shared_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='+')
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.ManyToManyField(User, blank=True, related_name='likes')
dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes')
class Meta:
ordering = ['-created_on', '-shared_on']
class Comment(models.Model):
comment = models.TextField()
created_on = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey('Post', on_delete=models.CASCADE, null=True)
likes = models.ManyToManyField(User, blank=True, related_name='comment_likes')
dislikes = models.ManyToManyField(User, blank=True, related_name='comment_dislikes')
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='+')
#property
def children(self):
return Comment.objects.filter(parent=self).order_by('-created_on').all()
#property
def is_parent(self):
if self.parent is None:
return True
return False
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, verbose_name='user', related_name='profile', on_delete=models.CASCADE)
name = models.CharField(max_length=30, blank=True, null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
birth_date = models.DateField(null=True, blank=True)
location = models.CharField(max_length=100, blank=True, null=True)
picture = models.ImageField(upload_to='uploads/profile_pictures', default='uploads/profile_pictures/default.jpeg', blank=True)
followers = models.ManyToManyField(User, blank=True, related_name='followers')
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
class Notification(models.Model):
# 1 = Like, 2 = Comment, 3 = Follow, 4= DM
notification_type = models.IntegerField()
to_user = models.ForeignKey(User, related_name='notification_to', on_delete=models.CASCADE, null=True)
from_user = models.ForeignKey(User, related_name='notification_from', on_delete=models.CASCADE, null=True)
post = models.ForeignKey('Post', on_delete=models.CASCADE, related_name='+', blank=True, null=True)
comment = models.ForeignKey('Comment', on_delete=models.CASCADE, related_name='+', blank=True, null=True)
thread = models.ForeignKey('ThreadModel', on_delete=models.CASCADE, related_name='+', blank=True, null=True)
date = models.DateTimeField(default=timezone.now)
user_has_seen = models.BooleanField(default=False)
class ThreadModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
class MessageModel(models.Model):
thread = models.ForeignKey('ThreadModel', related_name='+', on_delete=models.CASCADE, blank=True, null=True)
sender_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
receiver_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
body = models.CharField(max_length=10000)
image = models.ImageField(upload_to='uploads/message_photos', blank=True, null=True)
date = models.DateTimeField(default=timezone.now)
is_read = models.BooleanField(default=False)
class Image(models.Model):
image = models.ImageField(upload_to='uploads/post_photos', blank=True, null=True)
Any tips as to why the news feed may only be appearing on the admin's account/ only appears AFTER a post has been submitted (which would be a nuisance to have to submit a post every time you want to view your news feed) would be greatly appreciated!
Does this issue occur even when you remove the filter:
posts = Post.objects.filter(
author__profile__followers__in=[logged_in_user.id]
).order_by('-created_on')
It looks like it only shows posts for the followers of the user.id
Firstfully, you should not end post() method with render. That may cause weird stuff happening during site loading. Rather use return self.form_valid(form) or the same with invalid. It's possibly, that you might need to inherit also FormMixin. Then you need to specify get_success_url() but I can see you know how to do that.
Secondly, if you want PostListView to be a list view, do it :) Then use get_queryset() method:
class PostListView(LoginRequiredMixin, ListView):
...
def get_queryset(self):
return Post.objects.filter(...)
Thirdly, I am not sure about author__profile__followers__in=[logged_in_user.id] filter. For me it is confusing. Maybe try different approach here to fix filtering. Try .all() instead of .filter() and see if it shows all correctly.
It seems you may be confused with ManyToMany relations. You should not use related name same as field name, it works exactly in a opposite way, as you might think. Read about it in Django's Docs.
I want to create a DetailView page which displays the detail of a model but I want to add a Comment section in the DetailView page using ModelFormMixin.
This is my views.py code:
class PostDetailView(ModelFormMixin, DetailView):
model = UserPost
context_object_name='post_detail'
form_class = UserCommentForm
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data(*args, **kwargs)
context['form'] = self.get_form()
return context
def get_absolute_url(self):
return reverse(request, 'basic_app:post_detail', kwargs={'pk':self.pk})
But when I hit the submit button it shows the following error:
Edited part:
This is my views.py file
model.py
forms.py
userpost_detail.html
browser image before clicking enter button
browser image after clicking enter button
django admin page
I'm sorry for not uploading the actual code because it was too difficult to upload that bulk of code.
I think the following code will help you. I code it a long time ago, but it seems to solve your problem (I didn't use taggit, so I created a PostTag model):
Models:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.timezone import localtime
from django.urls import reverse
class User(AbstractUser):
pass
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class PostTag(models.Model):
name = models.CharField(max_length=40)
slug = models.SlugField(max_length=40)
def __str__(self):
return self.name
class Post(models.Model):
POST_STATUS = (('draft', 'Draft'), ('published', 'Published'))
title = models.CharField(max_length=100)
body = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
created = models.DateTimeField(auto_now_add=True)
publish = models.DateTimeField(auto_now=True)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=100, unique_for_date='publish')
status = models.CharField(max_length=10, choices=POST_STATUS, default='draft')
tags = models.ManyToManyField(PostTag, blank=True)
def get_absolute_url(self):
local_time = localtime(self.publish)
return reverse("blog:post_date_detail", args=[local_time.year, local_time.month, local_time.day, self.slug])
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
objects = models.Manager() # The default manager.
published = PublishedManager() # Custom manager.
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 = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
def get_absolute_url(self):
local_time = localtime(self.post.publish)
return reverse("blog:post_date_detail", args=[local_time.year, local_time.month, local_time.day, self.post.slug])
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.name} on {self.post}'
Views:
from django.shortcuts import render, get_object_or_404, HttpResponseRedirect
from django.views.generic import ListView, DetailView, View
from django.views.generic.edit import FormView, SingleObjectMixin, CreateView
from django.views.generic.dates import YearArchiveView, MonthArchiveView, DateDetailView
from django.core.mail import send_mail
from django.db.models import Count
from .models import Post, User, Comment, PostTag
from .forms import EmailPostForm, CommentForm
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 4
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# si hay kwargs se actualiza el context
if self.kwargs:
context["tag"] = self.kwargs['tag_slug']
return context
def get_queryset(self):
tag = None
if self.kwargs:
tag_slug = self.kwargs['tag_slug']
tag = get_object_or_404(PostTag, slug=tag_slug)
posts = self.queryset.filter(tags__in=[tag])
return posts
return self.queryset
class PostYearArchiveView(YearArchiveView):
queryset = Post.published.all()
template_name = 'blog/post_list.html'
date_field = "publish"
context_object_name = 'posts'
make_object_list = True
paginate_by = 4
class PostMonthArchiveView(MonthArchiveView):
queryset = Post.published.all()
template_name = 'blog/post_list.html'
date_field = "publish"
context_object_name = 'posts'
month_format='%m'
paginate_by = 4
class PostDateDetailView(DateDetailView):
queryset = Post.published.all()
date_field = "publish"
month_format='%m'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = self.get_object()
post_tags_pks = obj.tags.all().values_list('pk', flat=True)
related_posts = self.queryset.filter(tags__in=post_tags_pks).exclude(pk=obj.pk)
related_posts = related_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:3]
context["related_posts"] = related_posts
comments = obj.comments.filter(active=True)
context["comments"] = comments
context['post_id'] = obj.pk
context['form'] = CommentForm()
return context
class NewComment(CreateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
post = context["form"].instance.post
context['post'] = post
post_tags_pks = post.tags.all().values_list('pk', flat=True)
related_posts = Post.published.filter(tags__in=post_tags_pks).exclude(pk=post.pk)
related_posts = related_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:3]
context["related_posts"] = related_posts
comments = post.comments.filter(active=True)
context["comments"] = comments
return context
class PostDetail(View):
def get(self, request, *args, **kwargs):
view = PostDateDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = NewComment.as_view()
return view(request, *args, **kwargs)
class SharePostView(SingleObjectMixin, FormView):
form_class = EmailPostForm
template_name = 'blog/post_share.html'
success_url = '/blog'
context_object_name = 'posts'
queryset = Post.published.all()
def get(self, request, *args, **kwargs):
post = self.get_object()
context = {'post': post, 'form': self.form_class}
return render(request, self.template_name, context)
def form_valid(self, form):
post = self.get_object()
post_url = self.request.build_absolute_uri(post.get_absolute_url())
form.send_mail(post, post_url)
return super(SharePostView, self).form_valid(form)
Considerations: See get_context_data to add other data to the context: in this case tags, related_post, the form, etc...
I have used 3 views. DateDetailView (similar to your DetailView) to display the page (with the context needed).
CreateView to manage the POST method and save the comment into the DB. I have also added context to get the post information.
Finally a View with the intention of managing the same URL,but calling the appropiate view
when the request is GET or POST. See urls.py
URLS:
from django.urls import path
from blog import views
from blog.models import Post
app_name = 'blog'
urlpatterns = [
path("", views.PostListView.as_view(), name="post_list"),
path('tag/<slug:tag_slug>/', views.PostListView.as_view(), name='post_list_by_tag'),
path('<int:year>/', views.PostYearArchiveView.as_view(), name="post_year_archive"),
path('<int:year>/<int:month>/', views.PostMonthArchiveView.as_view(), name="post_month_numeric"),
path('<int:year>/<int:month>/<int:day>/<slug:slug>/', views.PostDetail.as_view(), name="post_date_detail"),
path('<int:pk>/share/', views.SharePostView.as_view(), name='post_share'),
]
Comment form:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body', 'post')
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Name'}),
'email': forms.EmailInput(attrs={'placeholder': 'E-mail'}),
'body': forms.Textarea(attrs={'placeholder': 'Write your message here'}),
'post' : forms.HiddenInput(),
}
form in template:
<form method="post" action="" novalidate>
{% csrf_token %}
<div class="input-group input-group-icon">
{{ form.name }}
<div class="input-icon"><i class="fas fa-user"></i></div>
{% if form.name.errors %}
{{form.name.errors}}
{% endif %}
</div>
<div class="input-group input-group-icon">
{{form.email }}
<div class="input-icon">
<i class="fas fa-envelope"></i>
</div>
{% if form.email.errors %}
{{form.email.errors}}
{% endif %}
</div>
<div class="msg">
<div class="input-group">{{form.body}}
{% if form.body.errors %}
{{form.body.errors}}
{% endif %}
</div>
</div>
<input type="hidden" name="post" value="{{ post.pk }}">
<div class="input-group send-reset">
<input type="submit" value="Enviar Comentario" />
</div>
</form>
Considerations: See the input type=hidden to get the post.pk. This is needed to save the comment.post field.
I think I covered all the issues you can have. Hope it helps!!