Django form.is_valid() 失败的基于类的视图 - Form、SingleObject、DetailMixins

Posted

技术标签:

【中文标题】Django form.is_valid() 失败的基于类的视图 - Form、SingleObject、DetailMixins【英文标题】:Django form.is_valid() failing class based views - Form, SingleObject, DetailMixins 【发布时间】:2020-04-23 08:07:02 【问题描述】:

我有两个应用,这里我们称它们为 blog 和 cmets。

Comments 有一个 Comment 模型。博客有一个博客模型。评论有一个评论表格。博客有一个 DetailView。

我希望我的 CommentForm 出现在 Blog DetailView 上,这样人们就可以从博客详细信息页面提交 cmets。

表单呈现正常 - 它发出一个 POST 请求,它重定向到 get_success_url() 但是(我在views.py中添加了一些打印 - 见下文)在views.py中进行测试以查看表单数据收到我看到 form.is_valid() 路径不符合,我不明白为什么。

我基本上是在尝试遵循“替代更好的解决方案”: https://docs.djangoproject.com/en/2.2/topics/class-based-views/mixins/#using-formmixin-with-detailview

blog/views.py

class CommentLooker(SingleObjectMixin, FormView):
    template_name = 'blogs/blog_detail.html'
    form_class = CommentForm
    model = blog

    def get_object(self):
        #self.team = get_object_or_404(team, team_id=self.kwargs['team_id'])
        #queryset_list = blog.objects.filter(team = self.team)
        team_id_ = self.kwargs.get("team_id")
        blog_id_ = self.kwargs.get("blog_id")
        return get_object_or_404(blog, blog_id=blog_id_, team=team_id_)

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        self.object = self.get_object()
        return super(CommentLooker, self).post(request, *args, **kwargs)

    def get_success_url(self):
        return reverse('blogs:teams')

class blogDisplay(View):
    def get(self,request,*args,**kwargs):
        view = blogFromteamContentView.as_view()
        return view(request, *args, **kwargs)
    def post(self,request,*args,**kwargs):
        view = CommentLooker.as_view()
        return view(request,*args,**kwargs)

class blogFromteamContentView(LoginRequiredMixin, DetailView):
    model = blog
    template_name = 'blogs/blog_detail.html'
    # override get_object so we can use blog_id when we use this class in urls.py
    # otherwise DetailViews expect 'pk' which defaults to the primary key of the model.

    def get_object(self):
        team_id_ = self.kwargs.get("team_id")
        blog_id_ = self.kwargs.get("blog_id")
        return get_object_or_404(blog, blog_id=blog_id_, team=team_id_)

    def get_context_data(self, **kwargs):
        context = super(blogFromteamContentView, self).get_context_data(**kwargs)
        team_id_ = self.kwargs.get("team_id")
        blog_id_ = self.kwargs.get("blog_id")

        # get the list of blogs for a given blog id and team id combination.
        context['queryset'] = get_object_or_404(blog, blog_id=blog_id_, team=team_id_)

        # get and set things related to ability to associate comments to a blog.
        initial_data = 
                "content_type": blog.get_content_type,
                "object_id": blog.blog_id
            
        comments        = blog.comments # uses the @property set in this class.
        comment_form    = CommentForm(self.request.POST or None, initial=initial_data)
        if comment_form.is_valid():
            print(comment_form.cleaned_data)
        else:
            print('invalido!')
        context['comment_form'] = comment_form
        return context

blog/models.py

class Blog(models.Model):
    team= models.ForeignKey(Team, on_delete=CASCADE)
    blog_id = models.AutoField(primary_key=True)
    blog_name = models.CharField(
        max_length=100, verbose_name='Blog Name')

blog/urls.py

path('teams/<int:team_id>/blogs/<int:blog_id>/', blog.blogDisplay.as_view(), name='detail'),

blog_detail.html

<div> 
<p class="lead"> Comments </p>
<form method="POST" action="."> % csrf_token %
     comment_form 
    <input type="submit" value="Post Comment" class="btn btn-primary">
</form>
<hr/>
% for comment in blog.comments.all %

<blockquote class="blockquote">
    <p> comment.content </p>
    <footer class="blockquote-footer">  comment.user  |  comment.timestamp|timesince  ago </footer>
</blockquote>

<hr/>
% endfor %

cmets/forms.py

从 django 导入表单

class CommentForm(forms.Form):
    content_type = forms.CharField(widget=forms.HiddenInput)
    object_id = forms.IntegerField(widget=forms.HiddenInput)
    parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
    content = forms.CharField(widget=forms.Textarea)

编辑:

使用 print(comment_form.errors) 后:

object_id 输入一个整数。

建议我的 initial_data 可能是问题所在。事实上,我的 initial_data 中的 content_type 和 object_id 都是问题。我在问 blog.blog_id - 即使用类,而不是实例。所以我改变了

get_context_data:

def get_context_data(self, **kwargs):
context = super(blogFromteamContentView, self).get_context_data(**kwargs)
team_id_ = self.kwargs.get("team_id")
blog_id_ = self.kwargs.get("blog_id")

# get the list of blogs for a given blog id and team id combination.
context['queryset_list_recs'] = get_object_or_404(blog, blog_id=blog_id_, team=team_id_)

instance = get_object_or_404(blog, blog_id=blog_id_, team=team_id_)
initial_data = 
        "content_type": instance.get_content_type,
        "object_id": blog_id_
    

还有我的 views.py:

def post(self, request, *args, **kwargs):
    if not request.user.is_authenticated:
        return HttpResponseForbidden()
    self.object = self.get_object()
    comment_form = CommentForm(self.request.POST)
    if comment_form.is_valid():
        print('valido')
        c_type = comment_form.cleaned_data.get("content_type")
        content_type = ContentType.objects.get(model=c_type)
        obj_id = comment_form.cleaned_data.get('object_id')
        content_data = comment_form.cleaned_data.get("content")
        new_comment, created = Comment.objects.get_or_create(
            user = self.request.user,
            content_type = content_type,
            object_id = obj_id,
            content = content_data
        )
    else:
        print('postinvalido!')
    return super(CommentLooker, self).post(request, *args, **kwargs)

这个(不恰当的打印语句除外)现在似乎给出了预期的行为。

【问题讨论】:

我假设您的意思是您没有到达if comment_form.is_valid(): 中的blogFromteamContentView.get_context_data 行?在 POST 中,您使用的是 CommentLooker 视图而不是那个视图 当然,我真傻。谢谢。我将专注于 CommentLooker 的 post 方法。我不确定 post 如何获取它的上下文 - 即我将在此处保存表单。 所以如果我定义初始数据并将comment_form = CommentForm(self.request.POST, None, initial=initial_data) 添加到CommentLooker 的post 方法中,comment_form 将失败comment_form.is_valid() 刚才我的假设是有一些错误使我的表单无效 - 一旦那是已修复,然后我会将 `Comment.objects.get_or_create([fields]) 添加到 CommentLooker 的 post 方法中,以保存 cmets。 print(form.errors) 给了我:&lt;ul class="errorlist"&gt;&lt;li&gt;object_id&lt;ul class="errorlist"&gt;&lt;li&gt;Enter a whole number.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; 所以我想问题出在我的 initial_data 上。我将在正文中编辑说明。 主要问题文本已更新为接近解决方案。我不确定在 CommentLooker 的 post 方法中实例化 CommentForm(self.request.POST)。表格不是已经传入了吗?我不清楚如何访问它。 【参考方案1】:

使用 print(comment_form.errors) 后:

object_id 列表项

输入一个整数。

建议我的 initial_data 可能是问题所在。事实上,我的 initial_data 中的 content_type 和 object_id 都是问题。我在问 blog.blog_id - 即使用类,而不是实例。所以我改变了

get_context_data:

def get_context_data(self, **kwargs):
context = super(blogFromteamContentView, self).get_context_data(**kwargs)
team_id_ = self.kwargs.get("team_id")
blog_id_ = self.kwargs.get("blog_id")

# get the list of blogs for a given blog id and team id combination.
context['queryset_list_recs'] = get_object_or_404(blog, blog_id=blog_id_, team=team_id_)

instance = get_object_or_404(blog, blog_id=blog_id_, team=team_id_)
initial_data = 
        "content_type": instance.get_content_type,
        "object_id": blog_id_
    

还有我的views.py

def post(self, request, *args, **kwargs):
    if not request.user.is_authenticated:
        return HttpResponseForbidden()
    self.object = self.get_object()
    comment_form = CommentForm(self.request.POST)
    if comment_form.is_valid():
        print('valido')
        c_type = comment_form.cleaned_data.get("content_type")
        content_type = ContentType.objects.get(model=c_type)
        obj_id = comment_form.cleaned_data.get('object_id')
        content_data = comment_form.cleaned_data.get("content")
        new_comment, created = Comment.objects.get_or_create(
            user = self.request.user,
            content_type = content_type,
            object_id = obj_id,
            content = content_data
        )
    else:
        print('postinvalido!')
    return super(CommentLooker, self).post(request, *args, **kwargs)

这个(不适当的打印语句除外)现在似乎给出了预期的行为。我不清楚为什么需要在 post 方法中创建 CommentForm 的实例 - 感觉就像我在这里做错了什么。

【讨论】:

以上是关于Django form.is_valid() 失败的基于类的视图 - Form、SingleObject、DetailMixins的主要内容,如果未能解决你的问题,请参考以下文章

当值为空时,Django modelSerializer form.is_valid() 为真

Django 消费者(Django 频道)中的表单数据验证(相当于 form.is_valid)

Django:form.is_valid 检查后,cleaned_data 缺少图像字段

Django form.is_valid() 总是假的

django form.is_valid 返回 false

Form验证随笔 | Django