Думаю, вам поможет следующий код. Я давно кодирую его как go, но, похоже, он решил вашу проблему (я не использовал тег git, поэтому я создал модель PostTag):
Модели:
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}'
Просмотры:
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)
Соображения: см. Get_context_data, чтобы добавить другие данные в контекст: в этом случае теги, related_post, форма и т. Д. c ... Я использовал 3 вида. DateDetailView (аналогично вашему DetailView) для отображения страницы (с необходимым контекстом). CreateView для управления методом POST и сохранения комментария в БД. Я также добавил контекст для получения информации о посте. Наконец, представление с намерением управлять тем же URL-адресом, но вызывает соответствующее представление, когда запрос GET или POST. См. 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'),
]
Форма комментария:
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 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>
Соображения: См. Input type = hidden, чтобы получить post.pk. Это необходимо для сохранения поля comment.post.
Думаю, я рассмотрел все возможные проблемы. Надеюсь, это поможет !!