#14234 closed (fixed)
Validation bug when using ModelForms
| Reported by: | David Reynolds | Owned by: | Jeremy Dunck |
|---|---|---|---|
| Component: | Forms | Version: | 1.2 |
| Severity: | Keywords: | ||
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
I am running into a validation problem with ModelForms - here is a quick summary of what is happening
Take a simple model - here we call Model.full_clean as part of the custom save method to enforce the validation:
from django.db import models
class Stuff(models.Model):
"""(Stuff description)"""
name = models.CharField(max_length=100)
age = models.IntegerField()
description = models.TextField(blank=True)
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
self.full_clean()
return super(Stuff, self).save(*args, **kwargs)
Simple ModelForm for the above - nothing unusual here:
from django import forms
class StuffModelForm(forms.ModelForm):
class Meta:
model = Stuff
# Instantiate the form with some data
form = StuffModelForm({
'name': 'Fred',
'age': '56',
'description': 'Old Fred is 57',
})
# Check the form is valid
if form.is_valid():
obj = form.save(commit=False) # save but don't commit
obj.age = 27 # Change the age
obj.save() # Do a full save of the object
obj.name = 'Ted' # Change the name
obj.save() # Attempt to update the db record
# you get this error:
ValidationError: {'id': [u'Stuff with this ID already exists.']}
This happens because the ModelForm sets ModelForm.instance._adding to True and never cleans it up. This is then tested for in Model._perform_unique_checks and the uniqueness checks are executed if it is set to True which it always is after calling ModelForm.save()
Attachments (3)
Change History (10)
comment:1 by , 15 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
by , 15 years ago
| Attachment: | dj14234-signal.diff added |
|---|
comment:2 by , 15 years ago
| Has patch: | set |
|---|---|
| Needs tests: | set |
| Patch needs improvement: | set |
Set flags I missed.
comment:3 by , 15 years ago
| Has patch: | unset |
|---|---|
| Needs tests: | unset |
| Patch needs improvement: | unset |
Jeremy, it seemed massively overkill to use a signal for this, so I went with the simpler version and added a test for it.
comment:4 by , 15 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Given that a core committer proposed a patch, I'm assuming this should be flagged as "Accepted"
comment:5 by , 15 years ago
| Has patch: | set |
|---|
I'm not a core committer - just a long-time contributor. Even so, yes, I think this is ready for review.
comment:6 by , 15 years ago
| Resolution: | → fixed |
|---|---|
| Status: | assigned → closed |
Though I am not sure putting full_clean in a model's .save is a good approach, I do agree with your assessment of the bug - that _adding should be reset.
Given that _adding is only set by BaseModelForm, I think it should be responsible for cleaning it up. That would suggest using a signal on post_save from BaseModelForm. Unfortunately, that's bad for performance since it means a lot of housekeeping, and maybe some apps have many active forms.
A slightly hackier way, but less code and definitely faster, would be just having Model.save_base reset ._adding.
I've attached (untested) patches for both. I'd lean toward the fast/hacky way, but if it works for you, please add tests so it can be committed.