Opened 13 years ago
Closed 12 years ago
#17615 closed Bug (fixed)
Unique field validation with multi-table inheritance
Reported by: | David Bennett | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.5 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | yes | UI/UX: | no |
Description
I've encountered an issue where if you have a Parent class with a unique field and a Child class that specifies its own primary_key, the unique field will fail validation when using a Child ModelForm.
I'm attaching a patch that includes a test and fix.
Attachments (2)
Change History (10)
by , 13 years ago
Attachment: | unique_field_with_mti.diff added |
---|
comment:1 by , 13 years ago
Triage Stage: | Unreviewed → Accepted |
---|
Confirmed and attached is a cleaned up version of the patch. I think this is ready for commit, but I am not going to tick that box as I have modified the patch.
comment:4 by , 12 years ago
Patch needs improvement: | set |
---|---|
Resolution: | fixed |
Status: | closed → new |
Version: | 1.3 → 1.5 |
Got an error:
AttributeError: 'NoneType' object has no attribute 'attname'
Because a model can be 'abstract = True' doesn't have a pk, but a form (also 'abstract = True') can be used for validation 'is_valid()'. I use it for Ajax validation of a single item in a form with multiple items.
row 823-825:
# model_class_pk = self._get_pk_val(model_class._meta)
# if not self._state.adding and model_class_pk is not None:
# qs = qs.exclude(pk=model_class_pk)
comment:5 by , 12 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Please open a new ticket with a full and understandable description of what you are reporting. Thanks.
follow-up: 8 comment:6 by , 12 years ago
Resolution: | fixed |
---|---|
Status: | closed → new |
I'm sorry didn't find the time yesterday to create a complete example, but here it is:
a table with some values:
CREATE TABLE test_contact ( `id` int(11) NOT NULL auto_increment, `subject` varchar(64) NOT NULL, `email` varchar(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `subject` (`subject`) ) ENGINE=InnoDB; INSERT INTO test_contact VALUES (1,'aaa','aaa@test.com'); INSERT INTO test_contact VALUES (2,'bbb','bbb@test.com'); INSERT INTO test_contact VALUES (3,'ccc','ccc@test.com');
python manage.py shell
import django django.get_version() '1.5' from django.db import models class ContactSubjectManager(models.Manager): def get_query_set(self): return Contact.objects.values('id','subject').all() class ContactSubject(models.Model): subject = models.CharField(max_length=64, unique=True) objects = ContactSubjectManager() class Meta: abstract = True app_label = 'test' class ContactEmailManager(models.Manager): def get_query_set(self): return Contact.objects.values('id','email').all() class ContactEmail(models.Model): email = models.CharField(max_length=64) objects = ContactEmailManager() class Meta: abstract = True app_label = 'test' class Contact(ContactSubject,ContactEmail): objects = models.Manager() class Meta: app_label = 'test' def __unicode__(self): return u'%s - %s' % (self.subject, self.email) from django import forms class ContactSubjectForm(forms.ModelForm): subject = forms.RegexField( regex = r'^[ 0-9a-zA-Z()-]+$', max_length = 30, min_length = 3, error_messages = {'invalid': u'Please enter a valid subject.'}) class Meta: abstract = True model = ContactSubject class ContactEmailForm(forms.ModelForm): email = forms.EmailField( error_messages = {'invalid': u'Please enter a valid email address.'}) class Meta: abstract = True model = ContactEmail class ContactForm(ContactSubjectForm,ContactEmailForm): class Meta: model = Contact f = Contact.objects.all() f [<Contact: aaa - aaa@test.com>, <Contact: bbb - bbb@test.com>, <Contact: ccc - ccc@test.com>] data = { 'subject': 'aaa' , 'email' : 'aaa@test.com' } f = ContactForm(data) f.is_valid() False f.errors {'subject': [u'Contact with this Subject already exists.']} data = { 'subject': 'aaa'} f = ContactSubjectForm(data) f.is_valid() Traceback (most recent call last): File "<console>", line 1, in <module> File "…/lib/python2.7/site-packages/django/forms/forms.py", line 126, in is_valid return self.is_bound and not bool(self.errors) File "…/lib/python2.7/site-packages/django/forms/forms.py", line 117, in _get_errors self.full_clean() File "…l/lib/python2.7/site-packages/django/forms/forms.py", line 274, in full_clean self._post_clean() File "…/lib/python2.7/site-packages/django/forms/models.py", line 344, in _post_clean self.validate_unique() File "…/lib/python2.7/site-packages/django/forms/models.py", line 353, in validate_unique self.instance.validate_unique(exclude=exclude) File "…/lib/python2.7/site-packages/django/db/models/base.py", line 731, in validate_unique errors = self._perform_unique_checks(unique_checks) File "…/lib/python2.7/site-packages/django/db/models/base.py", line 823, in _perform_unique_checks model_class_pk = self._get_pk_val(model_class._meta) File "…/lib/python2.7/site-packages/django/db/models/base.py", line 466, in _get_pk_val return getattr(self, meta.pk.attname) AttributeError: 'NoneType' object has no attribute 'attname'
The output should be:
f.is_valid() False f.errors {'subject': [u'Contact subject with this Subject already exists.']}
comment:7 by , 12 years ago
Solution:
In function _perform_unique_checks "lib/python2.7/site-packages/django/db/models/base.py" remove comment on lines 817-822
# Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) # Note that we need to use the pk as defined by model_class, not # self.pk. These can be different fields because model inheritance # allows single model to have effectively multiple primary keys. # Refs #17615.
remove code lines:
823 model_class_pk = self._get_pk_val(model_class._meta) 824 if not self._state.adding and model_class_pk is not None: 825 qs = qs.exclude(pk=model_class_pk)
add these 2 lines (same as in Django 1.4)
if not self._state.adding and self.pk is not None: qs = qs.exclude(pk=self.pk)
comment:8 by , 12 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Replying to johan.holman@…:
I'm sorry didn't find the time yesterday to create a complete example, but here it is:
For our workflow - please open this in a new ticket with your two comments summarized in the description. You can then cross link to it being related to this ticket by putting the ticket number #17615 in the description. The original ticket was closed, and we generally don't revise committed fixes by reopening tickets. Thanks.
Test and fix.