Die Datenbank meiner App wird gefüllt und mit externen Datenquellen synchronisiert. Ich habe ein abstraktes Modell, von dem alle Modelle meiner Django 2.2-App abgeleitet sind und wie folgt definiert sind:
class CommonModel(models.Model):
# Auto-generated by Django, but included in this example for clarity.
# id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
ORIGIN_SOURCEA = '1'
ORIGIN_SOURCEB = '2'
ORIGIN_CHOICES = [
(ORIGIN_SOURCEA, 'Source A'),
(ORIGIN_SOURCEB, 'Source B'),
]
object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
object_id = models.IntegerField()
class A(CommonModel):
some_stuff = models.CharField()
class B(CommonModel):
other_stuff = models.IntegerField()
to_a_fk = models.ForeignKey("myapp.A", on_delete=models.CASCADE)
class C(CommonModel):
more_stuff = models.CharField()
b_m2m = models.ManyToManyField("myapp.B")
Das object_id
Feld kann nicht als eindeutig festgelegt werden, da jede Datenquelle, die ich in meiner App verwende, möglicherweise ein Objekt mit einem enthält object_id = 1
. Daher ist es notwendig, den Ursprung des Objekts anhand des Feldes aufzuspüren object_origin
.
Leider unterstützt Djangos ORM keine Fremdschlüssel mit mehr als einer Spalte.
Problem
Während halten die automatisch generierten Primärschlüssel in der Datenbank ( id
), würde Ich mag an meinem Fremdschlüssel und viele-zu-viele Beziehungen passieren auf beiden machen object_id
und object_origin
Felder anstelle des Primärschlüssels id
.
Was ich versucht habe
Ich dachte darüber nach, so etwas zu tun:
class CommonModel(models.Model):
# Auto-generated by Django, but included in this example for clarity.
# id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
ORIGIN_SOURCEA = '1'
ORIGIN_SOURCEB = '2'
ORIGIN_CHOICES = [
(ORIGIN_SOURCEA, 'Source A'),
(ORIGIN_SOURCEB, 'Source B'),
]
object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
object_id = models.IntegerField()
def _get_composed_object_origin_id(self):
return f"{self.object_origin}:{self.object_id}"
composed_object_origin_id = property(_get_composed_object_origin_id)
class A(CommonModel):
some_stuff = models.CharField()
class B(CommonModel):
other_stuff = models.IntegerField()
to_a_fk = models.ForeignKey("myapp.A", to_field="composed_object_origin_id", on_delete=models.CASCADE)
Aber Django beschwert sich darüber:
myapp.B.to_a_fk: (fields.E312) The to_field 'composed_object_origin_id' doesn't exist on the related model 'myapp.A'.
Und es klingt echt, Django hat das to_field
als Datenbankfeld angegebene Feld ausgenommen . Es ist jedoch nicht erforderlich, ein neues Feld zu meinem hinzuzufügen, CommonModel
da composed_object_type_id
es aus zwei nicht nullbaren Feldern besteht ...
quelle
Antworten:
Sie haben in Ihrem Kommentar in der anderen Antwort erwähnt, dass object_id nicht eindeutig ist, aber in Kombination mit object_type eindeutig. Könnten Sie also a
unique_together
in der Metaklasse verwenden? dhquelle
Haben / Können Sie das
unique
Attribut auf demobject_id
Feld setzen?Wenn dies nicht funktioniert, würde ich den Feldtyp in ein
uuid
Feld ändern :quelle
object_id
Kann leider nicht als eindeutig festgelegt werden, da es Fälle gibt, in denen es nicht eindeutig ist. In der externen Datenquelle, die mir die Daten bereitstellt, die ich in meiner App verwende, besteht der Primärschlüssel aus zwei Feldern:object_type
undobject_id
.object_id
nicht eindeutig ist, sollten Sie keinen Fremdschlüssel erstellen. Dies könnte zu Fehlern in der Datenbank führen, und das möchten Sie nicht. Wenn Sie stattdessen nicht das pk verwenden möchten, können Sie die Beziehung auch in den integriertenmodels.Model
Funktionen selbst verwalten.object_type
undobject_id
zusammen sind garantiert einzigartig. Aberobject_id
allein ist es nicht.Sie werden in Ihrer Frage als " Leider unterstützt Djangos ORM nicht mehr als eine Spalte Fremdschlüssel " erwähnt.
Ja, Django bietet diese Art von Unterstützung nicht an, da Django zuverlässiger ist als wir denken :)
Django bietet also eine Meta-Option, um diese Art von Problem zu lösen, und diese Option ist
unique_together
.Sie können Sätze von Feldnamen angeben, die zusammengenommen in Ihrem Fall eindeutig sein müssen ...
Sie können eine Liste der Liste, Sätze von Sätzen oder einfache Listen, einfache Sätze zu
unique_together
Optionen von bereitstellenclass meta:
.Ja, aber Django hat das gesagt ...
Sie können hinzufügen ,
UniqueConstraint
stattunique_together
in gleichenclass meta:
in Ihrem Fall , dass Sie , wie unten schreiben ...Die beste Vorgehensweise ist also, die
constraints
Option anstelle vonunique_together
zu verwendenclass meta:
.quelle
Sie können die Ursprungs-ID des zusammengesetzten Objekts zu einem Feld (
composed_object_origin_id
) machen, das aktualisiertsave
und als verwendet wirdto_field
.quelle