Attualmente sto lavorando a un progetto Django in cuibisogno di fare eredità multipla. Il progetto in sé ha un amministratore con più siti Web. Nella mia parte di amministratore ho creato una classe Member contenente tutte le informazioni obbligatorie per un membro. Quindi tutti i singoli siti hanno una classe MemberExtra creata dalla classe Member dall'amministratore in cui aggiungo tutte le informazioni complementari. Quando avvio il mio server (Python Manage .py RunServer ...) Ho quell'errore:
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)
risposte:
2 per risposta № 1Il problema qui è che erediti il tuo modello due volte ed entrambi i modelli figlio hanno lo stesso nome. Ciò comporta il doppio dello stesso nome_relativo, il che è un problema per Django.
La tua soluzione da aggiungere
member = models.OneToOneField(Member, related_name="%(app_label)s_%(class)s_related")"
nel tuo MemberExtra
il modello funziona, ma perdi la magia dell'ereditarietà implicita che Django fa per permetterti di accedere a entrambi i tuoi modelli in uno:
Con la tua soluzione devi fare:
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
Dove, con l'eredità nativa di Django, puoi fare:
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
Il che è molto più pulito secondo me.
Per gestire l'eredità, Django crea effettivamente un OneToOneField
(https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance). Questo campo è chiamato <parentclass>_ptr
, member_ptr
nel tuo caso.
Se si crea manualmente un OneToOneField
di nome <parentclass>_ptr
e assegnagli un nome_relativo, Django è ancora in grado di trovare il modello principale e non si lamenterà di nomi_identici identici.
Nel tuo caso, basta aggiungere
member_ptr = models.OneToOneField(Member, related_name="%(app_label)s_%(class)s_related")"
in entrambi i tuoi MemberExtra
definizioni del modello.
</ Strike>
Questa soluzione funziona, ma non è come dovrebbe essere fatta. Django fornisce una bandiera parent_link
che, se impostato su true, dirà a Django che questo è il campo che verrà utilizzato per accedere alla classe genitore.
Quindi puoi aggiungere un campo
member = models.OneToOneField(Member, parent_link=True, related_name="%(app_label)s_%(class)s_related")"
che funzionerà comunque se, per qualche motivo, Django deve rinominare il puntatore predefinito sul genitore.
1 per risposta № 2
Il related_name
per un FK deve essere unico. Quando hai un FK con un valore predefinito related_name
(non specificato), ereditato da più altri modelli, tutti i modelli finiscono con lo stesso related_name
. Vedi la sezione dei documenti Django intitolata Stai attento related_name
.
La soluzione è impostare il related_name
argomento dell'FK a qualcosa del genere:
prog = models.ForeignKey(Program, verbose_name=_("Program"), related_name="%(app_label)s_%(class)s_related")
Django sottoporrà quindi l'etichetta dell'app e il nome del modulo alla stringa, rendendo il file related_name
unico per ciascuna delle sottoclassi.