Django DetailView表单提交报错:'NoneType' object has no attribute 'id'
解决Django DetailView中创建WrongCards条目时的AttributeError错误
问题概述
在Django的DetailView中嵌入表单,尝试创建WrongCards条目时触发错误:AttributeError at /card/3/ 'NoneType' object has no attribute 'id',错误触发在views.py第19行的get_success_url方法中。
错误详情
AttributeError at /card/3/ 'NoneType' object has no attribute 'id' Request Method: POST Request URL: http://127.0.0.1:8000/card/3/ Django Version: 4.1.7 Exception Type: AttributeError Exception Value: 'NoneType' object has no attribute 'id' Exception Location: C:\Users\Super\Desktop\projects\пед проэкт\cards\views.py, line 19, in get_success_url Raised during: cards.views.CardDetailView Python Executable: C:\Users\Super\Desktop\projects\пед проэкт\venv\Scripts\python.exe Python Version: 3.10.6 Python Path: ['C:\\Users\\Super\\Desktop\\projects\\пед проэкт', 'C:\\Users\\Super\\AppData\\Local\\Programs\\Python\\Python310\\python310.zip', 'C:\\Users\\Super\\AppData\\Local\\Programs\\Python\\Python310\\DLLs', 'C:\\Users\\Super\\AppData\\Local\\Programs\\Python\\Python310\\lib', 'C:\\Users\\Super\\AppData\\Local\\Programs\\Python\\Python310', 'C:\\Users\\Super\\Desktop\\projects\\пед проэкт\\venv', 'C:\\Users\\Super\\Desktop\\projects\\пед проэкт\\venv\\lib\\site-packages'] Server time: Fri, 14 Apr 2023 10:00:23 +0000
相关代码
视图代码
class CardDetailView(SuccessMessageMixin, ModelFormMixin, DetailView): model = Card template_name = 'cards/detail.html' success_message = 'Ваше сообщение об ошибке получено. Мы обязательно займемся этим.' form_class = WrongCardsForm def get_success_url(self): return reverse_lazy('detail', kwargs={'pk': self.object.id}) def get_context_data(self, **kwargs): context = super(CardDetailView, self).get_context_data(**kwargs) context['form'] = self.get_form() return context def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.get_form() if form.is_valid(): form = form.save(commit=False) form.author = self.request.user form.card = self.get_object() form.save() super(CardDetailView, self).form_valid(form) else: return super(CardDetailView, self).form_valid(form)
表单代码
class WrongCardsForm(ModelForm): class Meta: model = WrongCards fields = ['mistake_in', 'error_text',] # fields = '__all__'
URL配置
urlpatterns = [ path('', views.CardsListView.as_view(), name='index'), path('<int:pk>/', views.CardDetailView.as_view(), name='detail'), ]
模型代码
class WrongCards(models.Model): """Карточки с ошибками""" CHOICE = ((None, 'Выберите в чем ошибка'), ('a', 'терминe'), ('b', 'определение'), ('c', 'произношение')) mistake_in = models.CharField('Ошибка в', choices=CHOICE, max_length=3) card = models.ForeignKey('Card', on_delete=models.CASCADE, verbose_name='Ошибка', related_name='wrong_cards') error_text = models.TextField('Подробнее об ошибки') author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='Автор') def __str__(self): return self.mistake_in class Card(models.Model): """Карточки слов""" term = models.CharField('Термин', max_length=100) transcription = models.CharField('Транскрипция', max_length=100) definition = models.CharField('Определение', max_length=100) audi = models.FileField(upload_to='audi/%Y/%m/%d', verbose_name='Произношение', validators=[validators.RegexValidator(regex=".mp3")], ) photo = models.ImageField(upload_to='photos/%Y/%m/%d', verbose_name='Фото') author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='Автор') created = models.DateTimeField('Дата создания', auto_now_add=True) updated = models.DateTimeField('Дата последнего редактирования', auto_now=True) def __str__(self): return f'{self.term} - {self.definition}' class Meta: verbose_name = "Карточку" verbose_name_plural = "Карточки" ordering = ['-created']
问题分析与解决方案
核心问题
- 无效表单处理错误:
post方法的else分支错误调用了form_valid,无效表单不应进入保存流程,这会导致self.object被错误赋值为None。 - 视图响应缺失:表单有效时调用
super().form_valid(form)后未返回结果,视图无法生成正确的跳转响应。 self.object被覆盖:ModelFormMixin.form_valid会将self.object替换为表单创建的WrongCards实例,导致get_success_url无法获取原Card对象的ID。
修正后的视图代码
class CardDetailView(SuccessMessageMixin, ModelFormMixin, DetailView): model = Card template_name = 'cards/detail.html' success_message = 'Ваше сообщение об ошибке получено. Мы обязательно займемся этим.' form_class = WrongCardsForm def get_success_url(self): # 直接获取当前Card对象的ID,避免self.object被替换 return reverse_lazy('detail', kwargs={'pk': self.get_object().id}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['form'] = self.get_form() return context def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.get_form() if form.is_valid(): form_instance = form.save(commit=False) form_instance.author = self.request.user form_instance.card = self.object form_instance.save() # 返回form_valid的结果,触发成功提示与跳转 return super().form_valid(form) else: # 无效表单返回form_invalid,渲染错误信息 return super().form_invalid(form)
额外优化建议
- 调整WrongCards模型的
mistake_in字段选项,将默认空选项改为空字符串,避免数据库存储None:
CHOICE = (('', 'Выберите в чем ошибка'), ('a', 'терминe'), ('b', 'определение'), ('c', 'произношение'))
内容的提问来源于stack exchange,提问作者vlad
相关产品推荐
相关产品推荐

