Django 1.9: Field clashes with the field of non-existing field in parent model
Asked Answered
E

3

37

I have some simple models, Profile, Certifier and Designer, the two latter inheriting from Profile (multi table inheritance). In Designer there’s a foreign key to Certifier.

class Profile(models.Model):
    TYPES = (
        ('admin', _('Administrator')),
        ('certifier', _('Certifier')),
        ('designer', _('Designer'))
    )
    
    user = models.OneToOneField(User)
    type = models.CharField(max_length=9, choices=TYPES)
    
    def __str__(self):
        return self.user.username + ' (' + self.type + ')'

class Admin(Profile):
    pass
class Certifier(Profile):
    pass
class Designer(Profile):
    certifier = models.ForeignKey(Certifier)

In Django 1.8 this works perfectly, but in 1.9 I get;

django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:

check.Designer.certifier: (models.E006) The field 'certifier' clashes with the field 'certifier' from model 'check.profile'.

(Profile.type is irrelevant in this case, I just need it to distinguish logged in user profile types).

check.profile obviously doesn’t have a field 'certifier'. Is this a bug or do I miss something? The same thing happens in another project.

Elishaelision answered 4/12, 2015 at 10:56 Comment(5)
Do you have conflicting migration files (006 perhaps)?, are you sharing a database? you mention that it happens in another projectSerenata
@Serenata E006 is the error code from the system checks framework, not a migration number.Joannajoanne
@Joannajoanne - Ah of course it is, Still think the problem is (was) possibly a conflicting migrationSerenata
There were no migration problems in 1.8 , and I don’t share the database with other programs. After renaming the field, the migrations caused by the 1.9 upgrade in admin and auth also worked fine. The other occurrence of this issue is in another project, but also with a case of multi table inheritance with a foreign key: parent model Component, both Window and Spacer inherit from Component, and there’s a foreign key pointing from Window to a Spacer, and the field name is spacer.Elishaelision
This seems to be poor design in 1.9, or simply a bug. I now have a perfectly functional database created under 1.8 that errors on makemigrations in 1.9. Plus, related_name seems the obvious solution to resolve the issue but it does not.Ampulla
T
31

I think that you shouldn't use name certifier for that foreign key relation because class Profile actually has certifier, admin and designer fields(although by descriptor) according to docs and in that case names actually would clash.

from django.contrib.auth.models import User

c = Certifier.objects.create(
    type='admin',
    user=User.objects.latest('date_joined'),
)

p = c.profile_ptr
print(p.certifier) #username (admin)

Change to something like certifier_field = models.ForeignKey(Certifier)

As it was pointed out in comments, you could rename the models to CertifierProfile, AdminProfile etc to avoid the clash.

Or you could also silence the check by adding SILENCED_SYSTEM_CHECKS = ['models.E006'] to your settings, but this is not a good approach.

Terrell answered 4/12, 2015 at 11:42 Comment(6)
I also wasn't glad to adjust my projects. I just tryed to explain reasons for django developers to impose this new requirement.Terrell
It solves the problem in 1.9, but nevertheless, it’s not nice to have such field names, especially because everything worked until 1.8 with the field name certifier.Elishaelision
The Django docs say that you can go from the parent model Profile to its child Certifier with profile.certifier. Defining another certifier field is ambiguous, so a check for this has been added to 1.9. If you really wanted to, you could silence the check by adding SILENCED_SYSTEM_CHECKS = ['models.E006'] to your settings, but I wouldn't recommend this.Joannajoanne
If you don't like certifier_field, you could rename the models to CertifierProfile, AdminProfile etc to avoid the clash. Or, you could try setting the parent field with a related_name.Joannajoanne
Okay, thanks for the solution, explanation and comments!Elishaelision
In my case (using django_model_utils InheritanceManager), silencing the check seems to be the best solution. Applying irrational names to the fields as a work around was not appealing. Everything seems to work fine, including the attribute name on the superclass and the subclass (not that I needed the former).Garling
K
30

You can specify Profile is an abstract class. This will stop the check from being confused with your parent fields.

class Meta:
    abstract = True
Kinsfolk answered 8/8, 2016 at 15:9 Comment(2)
I think it's probably a good rule of thumb to "never have a field name match the model name", however doing this can help avoid a major rewrite if you have a ton of logic tied to the field name (ie the field exists on API endpoints and is relied on by consuming apps)Freehold
Thanks, this works and I don't have to rename anything.Baggett
C
0

It is possible to change the clashed name by overriding the automatically-created OneToOneField

class Certifier(Profile):
    profile_ptr = models.OneToOneField(
        Profile,
        on_delete=models.CASCADE,
        parent_link=True,
        primary_key=True,
        related_name="certifier_ptr",
    )
Causation answered 18/7 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.