You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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']

问题分析与解决方案

核心问题

  1. 无效表单处理错误:post方法的else分支错误调用了form_valid,无效表单不应进入保存流程,这会导致self.object被错误赋值为None。
  2. 视图响应缺失:表单有效时调用super().form_valid(form)后未返回结果,视图无法生成正确的跳转响应。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:17:53