Django视图向表单传递数据失败及视图类方法执行机制咨询(含UpdateView POST请求405问题)
嘿,让我一步步帮你理清这些Django开发里的问题:
get_form_kwargs未执行的原因 Django类视图的执行核心是dispatch方法,它会根据请求方法(GET/POST等)分发到对应的处理函数,不同的视图类有不同的内置流程。
你在PostDetailView里写的get_form_kwargs根本不会被调用——因为这个方法是FormMixin提供的,只有继承了FormMixin的视图(比如FormView、CreateView这类专门处理表单的视图)才会在实例化表单时触发它。而DetailView的定位是展示模型详情,它本身没有内置的表单处理逻辑,哪怕你定义了form属性,也不会自动调用get_form_kwargs。
我分两种场景给你整理解决方案:
场景A:在帖子详情页直接处理评论提交
如果想在PostDetailView里同时展示帖子和评论表单,只需给DetailView混入FormMixin,这样就能用上表单相关的方法了:
from django.views.generic.detail import DetailView from django.views.generic.edit import FormMixin from django.urls import reverse from .models import Post from .forms import CommentForm class PostDetailView(DetailView, FormMixin): model = Post form_class = CommentForm # 注意用`form_class`而非`form`,这是FormMixin的要求 def get_form_kwargs(self): kwargs = super().get_form_kwargs() # 直接传递用户对象,比用户名更灵活 kwargs['user'] = self.request.user return kwargs def post(self, request, *args, **kwargs): self.object = self.get_object() # DetailView需要先获取当前帖子对象 form = self.get_form() # 用get_form()实例化表单,才会触发get_form_kwargs if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) def form_valid(self, form): # 自动给评论关联当前帖子和登录用户 form.instance.post = self.object form.instance.user = self.request.user form.save() return super().form_valid(form) def get_success_url(self): # 提交成功后返回帖子详情页 return reverse("post-detail", args=[self.object.pk])
然后修正你的CommentForm——你之前的表单存在逻辑混乱:手动定义了user选择字段,但Meta类里的fields却写了author,而且评论用户应该是登录用户,不需要手动选择:
from django import forms from .models import Comment from djrichtextfield.widgets import RichTextWidget class CommentForm(forms.ModelForm): content = forms.CharField(widget=RichTextWidget(attrs={ 'class': 'md-textarea form-control', 'placeholder': 'Comment here ...', 'rows': '4', })) def __init__(self, *args, **kwargs): # 从视图传递过来的用户信息 self.current_user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # 这里可以根据用户做自定义逻辑,比如限制未登录用户提交 # if not self.current_user.is_authenticated: # self.fields['content'].widget.attrs['disabled'] = True class Meta: model = Comment fields = ('content',) # 不需要user字段,视图里自动赋值
模板里注意把表单的href改成action(表单的提交地址用action属性):
<form method="POST" action="{% url 'post-detail' post.id %}"> {% csrf_token %} <div class="form-group"> {{ form.content }} </div> <button class="btn btn-primary btn-block" type="submit">Comment</button> </form>
场景B:用独立视图处理评论提交
如果想把评论功能拆成独立视图,绝对不要用UpdateView——它是用来更新现有模型实例的,完全不匹配你创建新评论的需求,应该用CreateView:
from django.views.generic.edit import CreateView from django.urls import reverse from .models import Comment, Post from .forms import CommentForm class AddCommentView(CreateView): model = Comment form_class = CommentForm def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def form_valid(self, form): # 获取当前帖子对象 post = Post.objects.get(pk=self.kwargs['pk']) form.instance.post = post form.instance.user = self.request.user return super().form_valid(form) def get_success_url(self): # 提交成功返回帖子详情页 return reverse("post-detail", args=[self.kwargs['pk']])
URL配置要区分详情页和评论提交页:
urlpatterns = [ path('post/<int:pk>/', PostDetailView.as_view(), name="post-detail"), path('post/<int:pk>/add-comment/', AddCommentView.as_view(), name="add-comment"), ]
模板里的表单要指向独立的评论URL:
<form method="POST" action="{% url 'add-comment' post.id %}"> {% csrf_token %} <div class="form-group"> {{ form.content }} </div> <button class="btn btn-primary btn-block" type="submit">Comment</button> </form>
你遇到405(方法不允许)错误的核心原因是:
UpdateView的定位是更新现有模型实例,它的默认逻辑是针对某个已存在的对象做修改,和你创建新评论的需求完全不匹配;- 你在视图里用了
form而非form_class属性,这不符合UpdateView的要求,导致视图无法正确初始化表单逻辑。
换成我上面推荐的CreateView就能解决这个问题。
内容的提问来源于stack exchange,提问作者disguisedtoast

