Django ModelForm 不显示字段错误

Posted

技术标签:

【中文标题】Django ModelForm 不显示字段错误【英文标题】:Django ModelForm not showing field errors 【发布时间】:2014-04-05 02:46:10 【问题描述】:

我有一个没有在模板中正确显示字段错误的 Django ModelForm。我有几个必填字段,以及我认为捕获错误并向用户显示错误的正确逻辑。

提交表单时出现以下错误:

Traceback (most recent call last):

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
   response = wrapped_callback(request, *callback_args, **callback_kwargs)

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/base.py", line 69, in view
   return self.dispatch(request, *args, **kwargs)

 File "/home/thevariable/webapps/va_jobs/va_jobs/jobs/views.py", line 29, in dispatch
   return super(ApplicationCreateView, self).dispatch(*args, **kwargs)

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/base.py", line 87, in dispatch
   return handler(request, *args, **kwargs)

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/edit.py", line 205, in post
   return super(BaseCreateView, self).post(request, *args, **kwargs)

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/views/generic/edit.py", line 170, in post
   if form.is_valid():

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 129, in is_valid
   return self.is_bound and not bool(self.errors)

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 121, in errors
   self.full_clean()

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 274, in full_clean
   self._clean_form()

 File "/home/thevariable/.virtualenvs/va_jobs/lib/python2.7/site-packages/django/forms/forms.py", line 300, in _clean_form
   self.cleaned_data = self.clean()

 File "/home/thevariable/webapps/va_jobs/va_jobs/jobs/forms.py", line 23, in clean
   resume_ext = resume.name.lower().split('.')[1]

AttributeError: 'NoneType' object has no attribute 'name'

这是我的forms.py:

from django.forms import ModelForm

from  .models import Application

class ApplicationForm(ModelForm):
    class Meta:
        model = Application
        fields = [
            'first_name',
            'last_name',
            'email_address',
            'phone_number',
            'salary_requirement',
            'resume',
            'portfolio_url',
            'description',
            'can_relocate',
            'start_date',
        ]

    def __init__(self, *args, **kwargs):
        super(ApplicationForm, self).__init__(*args, **kwargs)
        self.fields['first_name'].required = True
        self.fields['last_name'].required = True
        self.fields['email_address'].required = True
        self.fields['phone_number'].required = True
        self.fields['salary_requirement'].required = False
        self.fields['resume'].required = True
        self.fields['portfolio_url'].required = False
        self.fields['description'].required = False
        self.fields['can_relocate'].required = True
        self.fields['start_date'].required = False

    def clean(self):
        cleaned_data = super(ApplicationForm, self).clean()
        resume = cleaned_data.get('resume')
        resume_ext = resume.name.lower().split('.')[1]
        if not resume_ext in ('pdf', 'doc', 'docx'):
            del cleaned_data["resume"]
            msg = u"Your file must be a PDF or DOC file type."
            raise forms.ValidationError(msg)
            #self._errors["resume"] = self.error_class([msg])       
        return cleaned_data

这是我的看法:

class ApplicationCreateView(CreateView):
    model = Application
    form_class = ApplicationForm
    success_url = 'submitted/'

    def dispatch(self, *args, **kwargs):
        self.job = get_object_or_404(Job, slug=kwargs['slug'])
        return super(ApplicationCreateView, self).dispatch(*args, **kwargs)

    def form_valid(self, form):
        #Get associated job and save
        self.object = form.save(commit=False)
        self.object.job = self.job
        self.object.save()

        # Gather cleaned data for email send
        first_name = form.cleaned_data.get('first_name')
        last_name = form.cleaned_data.get('last_name')
        email_address = form.cleaned_data.get('email_address')
        phone_number = form.cleaned_data.get('phone_number')
        salary_requirement = form.cleaned_data.get('salary_requirement')
        description = form.cleaned_data.get('description')
        portfolio_url = form.cleaned_data.get('portfolio_url')
        can_relocate = form.cleaned_data.get('can_relocate')
        start_date = form.cleaned_data.get('start_date')
        resume = self.object.resume
        job = self.object.job

        #Compose message
        email = EmailMessage()
        email.body = 'Name: ' + first_name + last_name + '\n' + 'Email: '  + email_address + '\n' + 'Phone number: ' + str(phone_number) + '\n' + 'Salary requirement: ' + str(salary_requirement) + '\n' + 'Description: ' + description + '\n' + 'Portfolio URL: ' + portfolio_url + '\n' + 'Can relocate: ' + str(can_relocate) + '\n' + 'Start date: ' + str(start_date)
        email.subject = 'A new application has been submitted for %s' % (job)
        email.from_email = 'noreply@abc.com'
        email.to = ['jobs@abc.com',]
        email.bcc = ['abc@abc.com',]
        email.attach(resume.name, resume.read())
        email.send()
        return HttpResponseRedirect(self.get_success_url())

    def get_context_data(self, *args, **kwargs):
        context_data = super(ApplicationCreateView, self).get_context_data(*args, **kwargs)
        context_data.update('job': self.job)
        return context_data

这是我的模板(使用 Django 小部件调整 https://pypi.python.org/pypi/django-widget-tweaks):

<p><em>Note: Fields with an asterisk are required.</em></p>
<form role="form" action="" enctype="multipart/form-data" method="post">
% csrf_token %
% for field in form.visible_fields %
    <div class="form-group">
        % if field.errors %
        <ul class="list-unstyled list-inline">
            % for error in field.errors %
            <li class="text-warning"><span class="glyphicon glyphicon-warning-sign"></span>  error|escape </li>
            % endfor %
        </ul>
        % endif %
         field.label_tag 
        % if field.name == "portfolio_url" %
            % if job.portfolio_required % <small><span class="glyphicon glyphicon-asterisk"></span></small>
            % endif %
        % endif %
        % if field.field.required % <small><span class="glyphicon glyphicon-asterisk"></span></small>% endif %
        % if field.name == "resume" or field.name == "can_relocate" %
             field|add_class:"form-control short" 
        % elif field.name == "start_date" %
            <input id="id_start_date" class="form-control short" name="start_date" type="date" /><input id="initial-id_start_date" name="initial-start_date" type="hidden" />
        % elif field.name == "portfolio_url" %
            % if job.portfolio_required %
                <input class="form-control" id="id_portfolio_url" maxlength="200" name="portfolio_url" type="url" required />
            % endif %
        % else %
         field|add_class:"form-control" 
        % endif %
        % if field.help_text %<p class="help-block"> field.help_text </p>% endif %
    </div>
% endfor %
    <input type="submit" class="btn btn-default" value="Apply">
</form>

【问题讨论】:

resume 对象由于某种原因正在评估为None。检查那里的流量。 基本上,如果未设置resume,则会抛出此错误,请尝试:resume_ext = resume.name.lower().split('.')[1] if resume and resume.name else '' 这也是我的想法。如果我做if cleaned_data.get('resume'):....else: return cleaned_data 是的.. 这也是干净的解决方案。 【参考方案1】:

为了记录,我的解决方案如下(forms.py):

def clean(self):
    cleaned_data = super(ApplicationForm, self).clean()
    if cleaned_data.get('resume'):
        resume = cleaned_data.get('resume')
        resume_ext = resume.name.lower().split('.')[1]
        if not resume_ext in ('pdf', 'doc', 'docx'):
            del cleaned_data["resume"]
            msg = u"Your file must be a PDF or DOC file type."
            self._errors["resume"] = self.error_class([msg])        
        return cleaned_data
    else:
        return cleaned_data

这是受到对原始问题 (Django ModelForm not showing field errors) 的评论的启发。

【讨论】:

你是对的。这可以写成self.add_error('resume', msg)【参考方案2】:

很简单,在堆栈跟踪中说:“对象resume 没有属性name

resume = cleaned_data.get('resume')

也许resume 不在已清理的数据中? 尝试打印它,或将 pudb 放在那里。

这里是例外,因为他是无:

resume_ext = resume.name.lower().split('.')[1]

【讨论】:

不是一个真正的答案。并对此嗤之以鼻。

以上是关于Django ModelForm 不显示字段错误的主要内容,如果未能解决你的问题,请参考以下文章

django modelform 为空显示空白

Django - UpdateView ModelForm 在查询集字段上设置初始值

如何在 Django 中向 ModelForm 添加非模型字段?

Django ModelForm booleanfield 必填字段不起作用

Django ModelForm 浮点字段不调用 Clean_Field 方法

具有 ForeignKey 字段的模型的 Django ModelForm 无法正确显示 selectBox