Django整合DetailView与FormView报错:'TestDetails'无'object'属性
Django中DetailView与FormView组合使用时的AttributeError问题
点击考试表单提交按钮后,Django抛出错误:AttributeError: 'TestDetails' object has no attribute 'object'
视图代码:
class TestDetails(DetailView, FormView): model = Test form_class = QuestionForm context_object_name = 'test' pk_url_kwarg = 'test_id' template_name = 'app/views/test_details.html' def get_context_data(self, **kwargs): test_id = self.kwargs.get('test_id') context = super().get_context_data(**kwargs) context['title'] = f'Тест - {test_id}' context['questions'] = Question.objects.filter(test_id=test_id) return context def form_valid(self, form): print(form.cleaned_data) return super().form_valid(form) def form_invalid(self, form): print(form.cleaned_data) return super().form_invalid(form)
完整回溯信息:
Internal Server Error: /tests/1 Traceback (most recent call last): File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner response = get_response(request) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\views\generic\base.py", line 104, in view return self.dispatch(request, *args, **kwargs) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\views\generic\base.py", line 143, in dispatch return handler(request, *args, **kwargs) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\views\generic\edit.py", line 153, in post return self.form_invalid(form) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\cryptosite_server\app\views.py", line 98, in form_invalid return super().form_invalid(form) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\views\generic\edit.py", line 67, in form_invalid return self.render_to_response(self.get_context_data(form=form)) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\cryptosite_server\app\views.py", line 87, in get_context_data context = super().get_context_data(**kwargs) File "C:\Users\User\OneDrive\Рабочий стол\PycharmProjects\cryptosite\.venv\lib\site-packages\django\views\generic\detail.py", line 95, in get_context_data if self.object: AttributeError: 'TestDetails' object has no attribute 'object'
问题原因
直接同时继承DetailView和FormView时,POST请求的处理流程由FormView主导,不会触发DetailView初始化self.object的逻辑。当表单验证失败调用form_invalid,进而调用get_context_data时,DetailView的get_context_data尝试访问self.object,但该属性未被赋值,因此抛出错误。
解决方案
方案1:在dispatch方法中提前初始化object
重写dispatch方法,确保无论GET还是POST请求,都先获取对象并赋值给self.object:
class TestDetails(DetailView, FormView): model = Test form_class = QuestionForm context_object_name = 'test' pk_url_kwarg = 'test_id' template_name = 'app/views/test_details.html' def dispatch(self, request, *args, **kwargs): self.object = self.get_object() return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): test_id = self.kwargs.get('test_id') context = super().get_context_data(**kwargs) context['title'] = f'Тест - {test_id}' context['questions'] = Question.objects.filter(test_id=test_id) return context def form_valid(self, form): print(form.cleaned_data) return super().form_valid(form) def form_invalid(self, form): print(form.cleaned_data) return super().form_invalid(form)
方案2:改用SingleObjectMixin与FormView组合继承
避免多重继承可能的方法冲突,直接使用SingleObjectMixin(DetailView的底层混入类)和FormView组合,并在GET/POST方法中初始化object:
from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import FormView class TestDetails(SingleObjectMixin, FormView): model = Test form_class = QuestionForm context_object_name = 'test' pk_url_kwarg = 'test_id' template_name = 'app/views/test_details.html' def get(self, request, *args, **kwargs): self.object = self.get_object() return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.object = self.get_object() return super().post(request, *args, **kwargs) def get_context_data(self, **kwargs): test_id = self.kwargs.get('test_id') context = super().get_context_data(**kwargs) context['title'] = f'Тест - {test_id}' context['questions'] = Question.objects.filter(test_id=test_id) return context def form_valid(self, form): print(form.cleaned_data) return super().form_valid(form) def form_invalid(self, form): print(form.cleaned_data) return super().form_invalid(form)
方案3:在get_context_data中检查并初始化object
如果不想修改dispatch或继承结构,可以在get_context_data开头检查self.object是否存在,不存在则主动获取:
def get_context_data(self, **kwargs): if not hasattr(self, 'object'): self.object = self.get_object() test_id = self.kwargs.get('test_id') context = super().get_context_data(**kwargs) context['title'] = f'Тест - {test_id}' context['questions'] = Question.objects.filter(test_id=test_id) return context
内容的提问来源于stack exchange,提问作者Rasul Ahmetov
相关产品推荐
相关产品推荐

