django forms.ModelForm validation problem
I have a model with some fields.
I have a model form that represents a portion of the above model. I have to do some custom validation. Some fields depend on others. Password and password confirm fields must be the same for example. Its tied to a back end system.
I must check the login name ( ensure its not in the back end system and its not in the database for this project ). I must validate the length and so on.
I must then validate the password against the constraints set in the back end. I need to use an API for this. I must pass the login name in with the password to use this command ( its silly but I cannot change it. )
If the login name raised a validation error in its method then the password validation methid cannot use sel.cleaned_data['login']
here is the code.
class PEMUserDetails(models.Model):
#Keys and IDs
domain = models.OneToOneField('GIBOSite', primary_key=True)
domain_name = models.CharField(unique=True, max_length=100)
# Details
PEM_login_id = models.CharField(max_length=32, unique=True)
PEM_password = models.CharField(max_length=32)
PEM_password_confirm = models.CharField(max_length=32)
FTP_login = models.CharField(max_length=32, blank=True, null=True)
FTP_password = models.CharField(max_length=32, blank=True, null=True)
FTP_host_name = models.CharField(max_length=256, blank=True, null=True)
def __unicode__(self):
return self.domain_name
def link_to(self, gibo_site):
self.domain = gibo_site
self.domain_name = gibo_site.domain
class PEMUserDetailsForm(forms.ModelForm):
PEM_password = forms.CharField(widget=forms.PasswordInput)
PEM_password_confirm = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = PEMUserDetails
exclude = ('domain', 'domain_name', 'FTP_login', 'FTP_password', 'FTP_host_name')
def clean_PEM_login_id(self):
login = self.cleaned_data['PEM_login_id']
try:
g = PEMUserDetails.objects.get(PEM_login_id=login)
raise forms.ValidationError("This Login ID is already taken, please choose another.")
except PEMUserDetails.DoesNotExist:
p = PBA()
pem_account = p.BM.UserValidate_API(login)
if pem_account['AccountID'] == 0:
pass
else:
raise forms.ValidationError("This Login ID is already taken, please choose another.")
validation = p.BM.GetLoginSettings_API()
m = re.match(validation['ManualLoginMask'], login)
if m == None:
raise forms.ValidationError(validation['LoginAlert'])
valid_login = (login == m.group(0))
if len(login) > validation['LoginMinLength'] and len(login) < validation['LoginMaxLength'] and valid_login:
pass
else:
raise forms.ValidationError(validation['LoginAlert'])
return login
def clean_PEM_password(self):
p = PBA()
login = self.cleaned_data['PEM_login_id']
password = self.cleaned_data['PEM_password']
validation = p.BM.GetLoginSettings_API()
if len(password) > validation['PwdMinLength']:
pass
else:
raise forms.ValidationError("Password must be more than %s characters long" % validation['PwdMinLength'])
passwd_status = p.BM.UserForVendorValidate_API(login, password)
if passwd_status['passwordStrength'] == '':
pass
else:
raise forms.ValidationError(passwd_status['passwordStrength'])
return password
def clean_PEM_password_confirm(self):
p = self.cleaned_data['PEM_password']
c = self.cleaned_data['PEM_password_confirm']
if c == p:
pass
else:
raise forms.ValidationError("The passwords you submitted do not match")
return c
I got around this by using a regular form and matched it to the model class. Its sort of running away from the problem. Is this a limitation of modelforms in django 1.1
python shell shows this
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from gibo.deployment.models import PEMUserDetailsForm
>>> from django.http import QueryDict
>>> post = QueryDict("PEM_login_id=beefsupreme&PEM_password=beef1234&PEM_password_confirm=beef123")
>>> f = PEMUserDetailsForm(post)
>>> f.is_valid()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File 开发者_如何转开发"/usr/lib/pymodules/python2.6/django/forms/forms.py", line 120, in is_valid
return self.is_bound and not bool(self.errors)
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 111, in _get_errors
self.full_clean()
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 243, in full_clean
value = getattr(self, 'clean_%s' % name)()
File "/home/niall/public_html/projects/GIBO/dev/projects/signup/gibo/../gibo/deployment/models.py", line 776, in clean_PEM_password
login = self.cleaned_data['PEM_login_id']
KeyError: 'PEM_login_id'
>>> post = QueryDict("PEM_login_id=beefsupremexxc&PEM_password=beef1234&PEM_password_confirm=beef123")
>>> f = PEMUserDetailsForm(post)
>>> f.is_valid()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 120, in is_valid
return self.is_bound and not bool(self.errors)
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 111, in _get_errors
self.full_clean()
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 243, in full_clean
value = getattr(self, 'clean_%s' % name)()
File "/home/niall/public_html/projects/GIBO/dev/projects/signup/gibo/../gibo/deployment/models.py", line 791, in clean_PEM_password_confirm
p = self.cleaned_data['PEM_password']
KeyError: 'PEM_password'
>>> f.errors
{'PEM_password': [u'Password is based on personal information.']}
>>>
but it works this way
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from gibo.deployment.models import PEMUserDetailsForm
>>> from django.http import QueryDict
>>> post = QueryDict("PEM_login_id=beefsupreme&PEM_password=beef1234&PEM_password_confirm=beef123")
>>> f = PEMUserDetailsForm(post)
>>> f.errors
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 111, in _get_errors
self.full_clean()
File "/usr/lib/pymodules/python2.6/django/forms/forms.py", line 243, in full_clean
value = getattr(self, 'clean_%s' % name)()
File "/home/niall/public_html/projects/GIBO/dev/projects/signup/gibo/../gibo/deployment/models.py", line 777, in clean_PEM_password
login = self.cleaned_data['PEM_login_id']
KeyError: 'PEM_login_id'
>>> f.errors
{'PEM_login_id': [u'This Login ID is already taken, please choose another.']}
>>> f.is_valid()
False
>>>
Field-specific clean methods -- clean_PEM_password(), etc -- should only access that specific field from self.cleaned_data. Other fields aren't guaranteed to be set yet. If you need to validate against multiple fields, you should use the clean() method, as all of the field-specific validation methods will already have been run.
Cleaning and validating fields that depend on each other
You cannot access "cleaned_data" when doing in field validation functions as you only get access to cleaned_data once all fields have passed validation.
So you either find a way to work without "cleaned_data" or you define a clean() method where you can access "cleaned_data" and then write your custom validation logic and throw ValidationErrors when needed.
I tend to go with option b in my own projects unless I really create a new type of formfield where custom validation is necessary - especially when checking for existence of data in my database.
精彩评论