I have a Django form that will add an entry to a database with a unique constraint on one of the columns. If the submission happens to attempt creation of a duplicate, I get a django.db.utls.IntegrityError stating I violated the unique constraint (thankfully).
When processing Django forms, there are a number of ways to validate:
Override the form class' clean method
Override the form class' clean_<field> method
If the clear_<field> method raises a django.forms.ValidationError, the error message will be appended to a list in the _errors dictionary under the field's name. When using the clean method, errors should be manually inserted into the _errors dict.
Now, the clean and clean_<field> methods don't sound like a nice place to put code that has side effects, and I fear that checking at this moment whether the value is unique does not guarantee I won't get IntegrityErrors. I would prefer to actually try and create the row in the database and signal a form error when a constraint is violated.
My main problem is that forms expose a public property errors which is read-only. Therefore, I should, according to the docs, add a method in my form that does all the work, try to insert in the database and register an error when an IntegrityError is raised. Because the action that processes the form after validation requires lots of other data, I don't want to put the code inside the form class itself.
Is there any other (documented or not) way to go about adding an entry in the errors dict from outside the class, or should I simply access the _errors "private" variable and be happy with that? I know this sounds like an OOP breach, but creating an extra method just to access this "private" variable does not seem to me like it shields anybody from anything.
Edit:
Seems like someone else already raised a similar question, but it seems the answers pointed to manual insertion into _errors...
For now, a simple solution is the following:
try:
# Try to save model instance, which might throw `IntegrityError`.
# ...
except django.db.utils.IntegrityError:
# The form passed validation so it has no errors.
# Use the `setdefault` function just in case.
errors = django.forms.util.ErrorList()
errors = form._errors.setdefault(
django.forms.forms.NON_FIELD_ERRORS, errors)
errors.append('Sorry, this username is already in use.')
I think adding the method to the form is quite reasonable. The ModelForm class, which has a save() method which does the logic of saving the created instance to the database. If your form can inherit from ModelForm (or just add a save() method to your form), you'll still be well within "djangonian" standards.
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
Related
I'm implementing a custom Django form which just contains an email address field.
As well as the usual email field type validation, I want to add some extra checks, such as whether this email address has already been used for this user etc.
What is the best practice way of doing this using class-based views (FormView, in this case, obviously) ?
Should I put the validation code:-
in the form_valid() method of the FormView or
in the clean_email() method of the form class
Option 2 seems the neatest to me, but if I go that way, I need to pass the user into the form (and pop it in the init method) so that the clean_email method can use to do database lookups which doesn't seem quite right either.
Use the latter approach since you know it would be neatest one.
Another benefit of going via this approach will be that you can identify which function your validation error occurred which might be helpful when you want to add more validations in future.
And then what other ways do you have to check if user is already present in the system.
You have to add the logic in view then.
From the source code, you can see that Django 1.4's Form class has a has_changed() method and changed_data property which seem rather useful, but are undocumented. My question is: do these work as expected, i.e.:
In Form.clean(), Form.has_changed() returns True if any any form data has changed, otherwise False
In Form.clean(), Form.changed_data is a list of field names whose values have changed.
If so, are there any specific reasons not to use them, apart from the usual caveats/dangers about using undocumented features (i.e. subject to change, not supported, etc.)?
NOTE 1: For these to work with custom widgets, those widgets need to have a _has_changed() method, which is defined for built in widgets.
NOTE 2: Interestingly, the documentation does include an offhand mention of the Formset.has_changed() method, but not of Form.has_changed().
After studying the Django source further, with helpful hints from Florian's answer, I can report that has_changed and changed_data do work as described in my question as long as the form has a way to get the initial data to compare the new data against.
So the question is, how does a form created from POST data know what the initial values of the GET form were? The short answer is it doesn't -- unless you tell it somehow. There are two ways to tell it:
Via the initial keyword argument to the form and/or the initial keyword arguments to the fields, exactly the same way you tell the GET form the initial values. NOTE: If you do this, it's up to you to make sure you use the same values for your GET and POST forms. This is the only truly reliable way to do it, since you directly control what the initial values are.
You let Django do the work of remembering the initial values from your GET by setting the show_hidden_initial keyword argument to True for each applicable field. For these fields, Django renders a hidden input element with the initial value into the GET form's HTML. Later, when you call has_changed or changed_data on the POST form, for any field with show_hidden_initial as True Django will automatically get the initial values from the hidden input elements in the POST data (superseding any initial values from initial form or field arguments). NOTE: as with anything relying on POST data, this approach is ultimately unreliable, since the values for the hidden inputs could still be changed despite being hidden.
You can rely on those methods on two conditions:
You pass the initial data dict to the form constructor during post requests too.
You don't use fields with show_hidden_initial=True. (The issue with such fields is that a user can submit the initial value used for comparison too and as such it wouldn't be trustworthy.)
Here is a piece of code that does what #Ghopper21 is suggesting. To get the initial data, I use the values() function on the QueryDict. It returns a dictionary containing the data belonging to the object. An important caveat is that I haven't checked how it handles foreign key references.
saveStudentView(request,studentID):
existingData = Student.objects.filter(id=paperID).values()[0]
form = StudentForm(request.POST)
if form.is_valid():
form = StudentForm(request.POST, initial=existingData)
if not form.has_changed():
//Tell the user nothing has changed
else:
//Do other stuff
I need to override django models's save method. I have used filter_horizontal for a many-to-many field in admin.py. I need to access the contents of that many-to-many field in the save method. But the many-to-many field is empty always when save method is executing. So I tried using Timer thread, to execute the process little later, but throws up error related to thread. Threads are not allowed in most of server-side technologies to avoid some deadlock problem. Is there any way that I can run a set of code that will execute immediately after save method has completed execution. I read something about signal.post_save() that is called in models's save_base method but I dont know whether that will be useful.
Looks like you might want to use a custom model form as here: http://reinout.vanrees.org/weblog/2011/11/29/many-to-many-field-save-method.html
There are a lot of links to related SO questions and bugs there an in the comments.
I am trying to subclass CharField with a custom unicode method. Basically, I want to be able to call my custom crypto method on the value that is returned by the default model field. I tried the following, but it seems that admin is getting the values some other way. It seems to me that this would be the most pythonic way to implement this, but perhaps I'm wrong.
def PasswordCharField(models.CharField):
def __unicode__(self):
return crypt(super(PasswordCharField,self).__unicode__())
I'm still not really sure why you're trying to encrypt a field on output - that doesn't seem very secure, the usual approach (taken by Django's own password fields) is to hash the data on entry and store that hashed version on the database.
However, if you want to go down this route, there's still no reason to use __unicode__ on a custom field subclass. There's nothing in the documentation that implies that this is what any part of Django - whether the admin or anything else - uses to get the value of a field. In fact, the admin uses exactly the same method to get the value of a field as anything else.
Custom field subclasses are fully described in the documentation, linked from the contents page. Without really understanding what you're trying to do it's hard to tell, but it could be that the to_python method is what you need. Bear in mind though that this will affect the value of the field for all uses in Django.
I'm trying to solve a problem that I outlined in this question. At the moment it looks like I will have to override the to_python() method on the ForeignKey field. But as far as I can see in django's source code, the ForeignKey class doesn't actually have a to_python() method declared, so it must be inheriting it from the Field class, which would mean it looks like this:
def to_python(self, value):
"""
Converts the input value into the expected Python data type, raising
django.core.exceptions.ValidationError if the data can't be converted.
Returns the converted value. Subclasses should override this.
"""
return value
Only that can't be right... That means it's not throwing a ValidationError. And yet surely something must be throwing it... I mean the conversion of an id into the object must be happening somewhere and surely if the id was incorrect a ValidationError would be thrown?
Or perhaps the right question to ask is what other methods are called before the clean_<fieldname>() method on a form? Which of these may I want to override?
to_python is used to convert an item from a database value to a Python one, in those cases where types differ. But a foreign key field is actually just an integer - the ID of the related object. So there is no conversion necessary, hence the empty method. The actual getting/displaying of the related object is done via a descriptor.
However, I don't know why you think you need to override to_python on the ForeignKey. This has nothing whatever to do with the issue you mention in the linked question, ie having to create an object before linking to it. That is best done in the form's clean method, and I will add an answer there.