Django:在通用 DetailView 中实现表单

Posted

技术标签:

【中文标题】Django:在通用 DetailView 中实现表单【英文标题】:Django: Implementing a Form within a generic DetailView 【发布时间】:2018-01-21 10:25:36 【问题描述】:

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

我正在使用 Django 编写博客,该博客设置了第一个通用 ListView 以简要显示所有可用的博客文章,并使用第二个通用 DetailView 更详细地显示特定博客文章。我现在想在特定博客文章下方放置一个 add_comment_field,并在下方显示所有其他 cmets。当评论表单显示在单独的页面上但与 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"),
]

post.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

【问题讨论】:

【参考方案1】:

如果您想将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)

【讨论】:

可能有用:docs.djangoproject.com/en/1.11/topics/class-based-views/mixins/… 请注意,文档建议反对这种模式(根据@pjdavis) 基于github.com/django/django/blob/master/django/views/generic/…这里的源代码,我相信我们可以直接继承BaseFormView而不是FormMixin,因为post方法已经写好了。【参考方案2】:

有没有办法在用户添加评论后隐藏表单?这适用于基于函数的视图,方法是传递一个变量(例如 new_comment = True)并在模板中使用 if 子句。但是对于基于类的视图似乎无法弄清楚。即使在 form_valid() 函数中将变量设置为 true,表单仍然会弹出。

【讨论】:

【参考方案3】:

不必用初始值填充表单。我将扩展上述解决方案。

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)

【讨论】:

以上是关于Django:在通用 DetailView 中实现表单的主要内容,如果未能解决你的问题,请参考以下文章

django如何循环通过通用detailview传回的上下文对象?

将用户名传递给通用 DetailView 时出错 - django 1.4.3

将 Pk 或 Slug 传递给 Django 中的通用 DetailView?

是否有用于在 Django 中实现视图模型装饰器 ala Draper 的库?

Django Generic Views:教程中,DetailView如何自动提供变量?

点击时 UICollectionViewCell 不显示 DetailView