Error using base class field in subclass unique_together meta option

Using the following code:

class Organization(models.Model): name = models.CharField(max_length="100",) alias = models.SlugField() ... class Division(Organization): parent_org = models.ForeignKey(Organization) class Meta: unique_together=['parent_org', 'alias'] ... 

Trying syncdb to give me this error:

 Error: One or more models did not validate: organizations.division: "unique_together" refers to alias. This is not in the same model as the unique_together statement. 

Any help is appreciated

Thanks,

Eric

+8
django django-models
source share
5 answers

This is by design. Reading the documentation for the unique_together option, she states that:

It is used by the Django admin, and applied at the database level .

If you look at the table that the subclass creates, you will see that in fact it does not have the fields that its parent has. Instead, it gets a soft foreign key to the parent table with the name of the field called [field]_ptr_id , where [field] is the name of the table that you inherit, with the exception of the application name. So your partition table has a primary foreign key called organization_ptr_id .

Now, since unique_together is applied at the database level using the UNIQUE , I don’t know how to do this so that the database really applies this to a field not contained in the table.

It’s best to use Validators at your business logic level or revise your database schema to support constraints.

Edit: as Manoah pointed out, you can also try using Model Validators , such as validate_unique .

+14
source share

[Model] Validators will work for you. Perhaps the easiest would be to use:

 class BaseOrganization(models.Model): name = models.CharField(max_length="100",) alias = models.SlugField() class Meta: abstract = True class Organization(BaseOrganization): pass class Division(BaseOrganization): parent_org = models.ForeignKey(Organization) class Meta: unique_together=['parent_org', 'alias'] 

Note. As with your current code, you cannot have organizational units.

+3
source share

This is the solution I recently used in Django 1.6 (thanks to Manoj Govindan for this idea):

 class Organization(models.Model): name = models.CharField(max_length="100",) alias = models.SlugField() ... class Division(Organization): parent_org = models.ForeignKey(Organization) # override Model.validate_unique def validate_unique(self, exclude=None): # these next 5 lines are directly from the Model.validate_unique source code unique_checks, date_checks = self._get_unique_checks(exclude=exclude) errors = self._perform_unique_checks(unique_checks) date_errors = self._perform_date_checks(date_checks) for k, v in date_errors.items(): errors.setdefault(k, []).extend(v) # here I get a list of all pairs of parent_org, alias from the database (returned # as a list of tuples) & check for a match, in which case you add a non-field # error to the error list pairs = Division.objects.exclude(pk=self.pk).values_list('parent_org', 'alias') if (self.parent_org, self.alias) in pairs: errors.setdefault(NON_FIELD_ERRORS, []).append('parent_org and alias must be unique') # finally you raise the ValidationError that includes all validation errors, # including your new unique constraint if errors: raise ValidationError(errors) 
+2
source share

This is not strictly related to the issue, but is very closely related; unique_together will work if the base class is abstract. You can mark abstract model classes as such using:

 class Meta(): abstract = True 

This will prevent django tables from being created for the class, and its fields will be directly included in any subclasses. In this situation, unique_together possible because all fields are in the same table.

https://docs.djangoproject.com/en/1.5/topics/db/models/#abstract-base-classes

+2
source share

I had a similar problem when trying to apply unique_together to the permission group created by this client.

 class ClientGroup(Group): client = models.ForeignKey(Client, on_delete=models.CASCADE) is_active = models.BooleanField() class Meta(): # looking at the db i found a field called group_ptr_id. # adding group_ptr did solve the problem. unique_together = ('group_ptr', 'client', 'is_active') 
0
source share

All Articles