Reputation: 31
I'm currently working on a django project where i need to do multiple legacy. The project in it self has an admin with multiple websites. In my admin part i've created a Member class containing all mandatory info for a member. Then all individual sites have an MemberExtra class created from the Member class from the admin where i add all complementary information. When i launch my server (python manage.py runserver...) i've that error :
Error: One or more models did not validate:
programsite.memberextra: Accessor for field 'member_ptr' clashes with related field 'Member.memberextra'. Add a related_name argument to the definition for 'member_ptr'.
programsite.memberextra: Reverse query name for field 'member_ptr' clashes with related field 'Member.memberextra'. Add a related_name argument to the definition for 'member_ptr'.
gourmandiz.memberextra: Accessor for field 'member_ptr' clashes with related field 'Member.memberextra'. Add a related_name argument to the definition for 'member_ptr'.
gourmandiz.memberextra: Reverse query name for field 'member_ptr' clashes with related field 'Member.memberextra'. Add a related_name argument to the definition for 'member_ptr'.
admin/models.py:
class Member(models.Model):
prog = models.ForeignKey(Program, verbose_name=_("Program"))
status = models.CharField(_("Status"), m status = models.CharField(_("Status"), max_length=1, choices=STATUS_CHOICE\
S)
points_avai = models.BigIntegerField(_("
Current Points"), null=True)
points_notavai = models.BigIntegerField(_("Future Points"), null=True)
cn = models.CharField(_("Company name"), max_length=250)
full_name = models.CharField(_("Full name"), max_length=250)
b_add = models.CharField(_("Billing address"), max_length=250)
b_city = models.CharField(_("Billing City"), max_length=250)
b_zip = models.CharField(_("Billing ZIP code"), max_length=250)
b_country = models.CharField(_("Billing country"), max_length=250)
prog_start_date = models.DateField(_("Program start date"), null=True)
prog_end_date = models.DateField(_("Program end date"), null=True)
member_id = models.CharField(_("Member ID"), max_length=250, primary_key=T\
rue)
client_id = models.CharField(_("Client ID"), max_length=250, help_text="Nu\
méro de client.")
user = models.OneToOneField(User)
def __unicode__(self):
return self.full_name + " (" + str(self.member_id) + ")"
class Meta:
verbose_name = _("Member")
verbose_name_plural = _("Members")
programsite/models.py:
class MemberExtra(Member):
email = models.EmailField(_("Email"), max_length=100, null=True)
tel = models.CharField(_("Tel"), max_length=100, null=True)
patrick = models.CharField(_("Patrick"), max_length=100, null=True)
test42 = models.CharField(_("Test42"), max_length=100, null=True)
gourmandiz/models.py:
class MemberExtra(Member):
email = models.EmailField(_("Email"), max_length=100, null=True)
Upvotes: 3
Views: 2123
Reputation: 2189
The problem here is that you inherit your model twice, and both the child models have the same name. This results in having twice the same related_name, which is a problem for Django.
Your solution to add
member = models.OneToOneField(Member, related_name="%(app_label)s_%(class)s_related")"
in your MemberExtra
model works, but you loose the implicit inheritance magic that Django do to let you access both your models in one:
With your solution you have to do:
from programsite.models import MemberExtra
m = MemberExtra.objects.get(member__full_name = "Foobar")
m.email # -> returns the email of your MemberExtra instance
m.member.b_add # -> returns the address of the inherited member instance
Where, with the Django native inheritance, you can do:
from programsite.models import MemberExtra
m = MemberExtra.objects.get(full_name = "Foobar")
m.email # -> returns the email of your MemberExtra instance
m.b_add # -> returns the address of the inherited member instance
Which is much cleaner in my opinion.
To manage the inheritance, Django actually creates a OneToOneField
(https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance).
This field is called <parentclass>_ptr
, member_ptr
in you case.
If you manually create a OneToOneField
named <parentclass>_ptr
, and give it a related_name, Django is still able to find the parent model, and will not complain about identical related_names.
In your case, just add
member_ptr = models.OneToOneField(Member, related_name="%(app_label)s_%(class)s_related")"
in both your MemberExtra
model definitions.
This solution works, but is not how it should be done. Django provides a flag parent_link
that, when set to true, will tell Django that this is the field that will be used to access the parent class.
So you can add a field
member = models.OneToOneField(Member, parent_link=True, related_name="%(app_label)s_%(class)s_related")"
which will still work if, for some reason, Django needs to rename the default pointer to the parent.
Upvotes: 2
Reputation: 239250
The related_name
for a FK has to be unique. When you have an FK with a default related_name
(unspecified), that is inherited by multiple other models, all of the models end up with the same related_name
. See the section in the Django docs entitled Be careful with related_name
.
The solution is to set the related_name
argument of the FK to something like:
prog = models.ForeignKey(Program, verbose_name=_("Program"), related_name="%(app_label)s_%(class)s_related")
Django will then sub the app label and module name into to the string, making the related_name
unique for each of the subclasses.
Upvotes: 1