Django:在通用 DetailView 中实现表单



在浏览了几个谷歌搜索结果页面后,我仍然绝望地陷入同样的问题。我正在尝试在博客文章下方实现一个评论字段。我非常感谢任何提示和建议!

我正在 Django 中开发一个博客,该博客设置了第一个通用 ListView 以简要显示所有可用的博客文章,并使用第二个通用 DetailView 更详细地显示特定的博客文章。我现在想在特定博客文章下方放置一个add_comment_field,并在下面显示所有其他评论。当注释表单显示在单独的页面上但与 DetailView 不在同一页面上时,它起作用,这是所需的结果。

我怀疑这与 views.py 和 forms.py 之间的相互作用有关,但我无法弄清楚问题所在。

再次,非常感谢您的帮助!

views.py

from django.shortcuts import render, get_object_or_404, redirect
from .models import Post, Comment
from .forms import CommentForm
from django.views.generic.detail import DetailView
class ParticularPost(DetailView):
template_name='blog/post.html'
model = Post
def add_comment_to_post(self, pk):
post = get_object_or_404(Post, pk=pk)
if self.method == "POST":
form = CommentForm(self.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return {'form': form}

urls.py

from django.conf.urls import url, include
from django.views.generic import ListView, DetailView
from .models import Post, Comment
from .views import ParticularPost
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date")[:25], template_name="blog/blog.html")),
url(r'^(?P<pk>d+)$', ParticularPost.as_view(), name="post_detail"),
]

帖子.html

{% extends "personal/header.html" %}
{% load staticfiles %}
{% block content %}
<div class="container-fluid background_design2 ">
<div class="header_spacing"></div>
<div class="container post_spacing">
<div class="row background_design1 blog_post_spacing inline-headers">
<h3><a href="/blog/{{post.id}}">{{ post.title }}</a></h3>
<h6> on {{ post.date }}</h6>
<div class = "blog_text">
{{ post.body|safe|linebreaks}}
</div>
<br><br>
</div>
<div>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Send</button>
</form>
</div>
<div class=" row post_spacing background_design1 ">
<hr>
{% for comment in post.comments.all %}
<div class=" col-md-12 comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

forms.py

from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)

models.py

from django.db import models
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField()
date = models.DateTimeField()
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('blog.Post', related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.text

如果要组合DetailView和表单,请使用FormMixin

from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin
from django.urls import reverse
from .models import Post, Comment
from .forms import CommentForm

class ParticularPost(FormMixin, DetailView):
template_name='blog/post.html'
model = Post
form_class = CommentForm
def get_success_url(self):
return reverse('post_detail', kwargs={'pk': self.object.id})
def get_context_data(self, **kwargs):
context = super(ParticularPost, self).get_context_data(**kwargs)
context['form'] = CommentForm(initial={'post': self.object})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super(ParticularPost, self).form_valid(form)

并且不要忘记将post字段添加到表单中(您可以隐藏(:

class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text', 'post',)

添加创建日期的更好方法 - 使用auto_now_add=True

created_date = models.DateTimeField(auto_now_add=True)

正如一些人在安东·舒拉绍夫的答案的评论中提到的那样,虽然提供的解决方案有效,但并不是开发人员在 Django 文档中推荐的解决方案。

我遵循了文档中给出的替代解决方案,用于一个看起来与 OP 非常相似的项目。希望此解决方案对尝试解决相同问题的任何其他人有用。

首先,我创建了 DetailView,并定义了自己的get_context_data方法来将表单添加到上下文中:

from django.shortcuts import render
from django.views import View
from django.views.generic import ListView, DetailView
from django.views.generic.edit import FormView
from django.views.generic.detail import SingleObjectMixin
from django.http import Http404, HttpResponseForbidden
from .models import BlogPost, Comment
from users.models import BlogUser
from .forms import CommentForm
class BlogPostDetailView(DetailView):
"""
Shows each individual blog post 
and relevant information. 
"""
model = BlogPost
template_name = 'blog/blogpost_detail.html'
context_object_name = 'blog_post'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = CommentForm()
return context

然后,在我的 FormView 视图中,我定义了用户提交表单(添加新注释(时要运行的 post 方法。 (请注意,我设置了success_url = '#',以便表单将保持在同一页面上。有无数种方法可以做到这一点,但这对我来说是最简单的方法。

class CommentFormView(SingleObjectMixin, FormView):
"""
View for the comment form, which allows users to 
leave comments user a blog post if logged in.
"""
template_name = 'blog/blogpost_detail.html'
form_class = CommentForm
model = Comment
success_url = '#'
def post(self, request, *args, **kwargs):
"""
Posts the comment only if the user is logged in.
"""
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super().post(request, *args, **kwargs)

最后一个视图将所有内容汇集在一起,是一个简单的视图,其中 get 方法调用 BlogPostDetailView(详细信息视图(,帖子方法调用 CommentFormView。

在 post 方法中,我还创建了一个表单对象,以自动将当前用户设置为评论的作者,并将博客文章设置为页面正在显示的当前博客文章。

class PostView(View):
def get(self, request, *args, **kwargs):
view = BlogPostDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = CommentFormView.as_view()
form = CommentForm(request.POST)
# Set the current user
# to the comment_author field
form.instance.comment_author = request.user
# Set the blog post as the current blogpost
form.instance.post = BlogPost.objects.get(id=self.kwargs['pk'])
if form.is_valid():
form.save()
return view(request, *args, **kwargs)

在我的 forms.py 中,我像这样定义了我的 CommentForm 模型(我将标签设置为空字符串,以便标签"内容"不会显示在新评论上方(:

from django import forms
from ckeditor.widgets import CKEditorWidget
from .models import Comment

class CommentForm(forms.ModelForm):
"""
Gives the option to add a comment to the bottom of a Blog Post, 
but only for logged in users.
"""
content = forms.CharField(widget=CKEditorWidget(), label='')
class Meta:
model = Comment
fields = [ 'content',]

class Media:
css = {
'all': ('forms.css',)
}

没有必要用 initials 填充表单。我将扩展上述解决方案。

def form_valid(self, form):
post = self.get_object()
myform = form.save(commit=False)
myform.post =  post
form.save()
return super(ParticularPost, self).form_valid(form)

最新更新