在Django表单中验证ModelChoiceField

1 投票
2 回答
3865 浏览
提问于 2025-04-15 20:09

我正在尝试验证一个包含 ModelChoiceField 的表单:

forms.py:

from django import forms

from modelchoicetest.models import SomeObject

class SomeObjectAddForm(forms.ModelForm):
    class Meta:
        model = SomeObject

models.py:

from django.db import models

class SomeChoice(models.Model):
    name = models.CharField(max_length=16)

    def __unicode__(self):
        return self.name

class SomeObject(models.Model):
    choice = models.ForeignKey(SomeChoice)

views.py:

from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from forms import SomeObjectAddForm

def add(request):
    if request.method == 'POST':
        form = SomeObjectAddForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('modelchoicetest_add'))
    else:
        form = SomeObjectAddForm()

    return render_to_response('modelchoicetest/index.html',
                              {'form': form},
                              context_instance=RequestContext(request))

在正常情况下使用时,一切都很顺利。但我想要保护这个表单,避免无效输入。很明显,当我在这个字段中输入无效值时,我应该得到 forms.ValidationError,对吧?但是如果我尝试提交一个在 'somechoice' 字段中填入 'invalid' 的表单,我得到的是:

ValueError: invalid literal for int() with base 10: 'invalid'

而不是预期中的 forms.ValidationError。我该怎么办呢?我尝试在这里放一个 def clean_somechoice(self) 来检查这个字段,但那并没有奏效:ValueError 在它到达 clean_somechoice() 之前就发生了。

而且我觉得这不是一个好的解决方案,应该有更简单的方法,但我就是没想到。

这是完整的错误追踪信息:

Traceback:
File "/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py" in get_response
  101.                     response = callback(request, *callback_args, **callback_kwargs)
File "/home/andrey/public_html/example/modelchoicetest/views.py" in add
  11.         if form.is_valid():
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in is_valid
  120.         return self.is_bound and not bool(self.errors)
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in _get_errors
  111.             self.full_clean()
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in full_clean
  276.                     value = field.clean(value)
File "/usr/local/lib/python2.6/dist-packages/django/forms/fields.py" in clean
  154.         value = self.to_python(value)
File "/usr/local/lib/python2.6/dist-packages/django/forms/models.py" in to_python
  911.             value = self.queryset.get(**{key: value})
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in get
  330.         clone = self.filter(*args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in filter
  536.         return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in _filter_or_exclude
  554.             clone.query.add_q(Q(*args, **kwargs))
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py" in add_q
  1109.                             can_reuse=used_aliases)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py" in add_filter
  1048.                 connector)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/where.py" in add
  66.             value = obj.prepare(lookup_type, value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/where.py" in prepare
  267.             return self.field.get_prep_lookup(lookup_type, value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/__init__.py" in get_prep_lookup
  314.             return self.get_prep_value(value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/__init__.py" in get_prep_value
  496.         return int(value)

Exception Type: ValueError at /
Exception Value: invalid literal for int() with base 10: 'invalid'

2 个回答

0

这是一个已知的Django错误:

http://code.djangoproject.com/ticket/11716

在这个错误没有被修复之前,你只能手动处理ValueError这个错误。

1

看起来这个错误是由实际的 ModelChoiceField 对象的 clean 方法引发的。因为这是一个外键,Django 期待的是一个 int 类型的值,这个值代表了 SomeChoice 的主键(pk)。你到底是怎么把 invalid 传递给表单的呢?

对评论的回应

如果你真的觉得需要处理这个问题,可以尝试重写默认的 ModelChoiceField,创建一个新的字段叫 choice,并在 ModelChoiceField__init__ 方法中传入 to_field_name 这个参数。这样 Django 就不会根据主键进行过滤,也就不会引发那个错误了。

不过我个人不建议使用这个方法。没有必要去迎合那些试图破解你表单的用户。

撰写回答