Wie man eine Eins-zu-Viele-Beziehung in Django ausdrückt

167

Ich definiere gerade meine Django-Modelle und habe festgestellt, dass es OneToManyFieldin den Modellfeldtypen keine gibt . Ich bin mir sicher, dass es einen Weg gibt, also bin ich mir nicht sicher, was mir fehlt. Ich habe im Wesentlichen so etwas:

class Dude(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

class PhoneNumber(models.Model):
    number = models.CharField()

In diesem Fall kann jedes Dudemehrere PhoneNumbers haben, aber die Beziehung sollte unidirektional sein, da ich nicht wissen muss PhoneNumber, Dudewem es per se gehört, da ich möglicherweise viele verschiedene Objekte habe, die PhoneNumberInstanzen besitzen, wie z. B. ein Businessfor Beispiel:

class Business(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

Was würde ich OneToManyFieldim Modell ersetzen (was nicht existiert), um diese Art von Beziehung darzustellen? Ich komme aus Hibernate / JPA, wo es so einfach war, eine Eins-zu-Viele-Beziehung zu erklären:

@OneToMany
private List<PhoneNumber> phoneNumbers;

Wie kann ich das in Django ausdrücken?

Naftuli Kay
quelle

Antworten:

132

Um Eins-zu-Viele-Beziehungen in Django zu verwalten, müssen Sie verwenden ForeignKey.

Die Dokumentation zu ForeignKey ist sehr umfassend und sollte alle Ihre Fragen beantworten:

https://docs.djangoproject.com/de/dev/ref/models/fields/#foreignkey

Die aktuelle Struktur in Ihrem Beispiel ermöglicht es jedem Typen, eine Nummer zu haben, und jede Nummer kann mehreren Typen gehören (wie bei Business).

Wenn Sie die umgekehrte Beziehung wünschen, müssen Sie Ihrem PhoneNumber-Modell zwei ForeignKey-Felder hinzufügen, eines für Dude und eines für Business. Dies würde es jeder Nummer ermöglichen, entweder einem Kerl oder einem Unternehmen zu gehören, und Jungs und Unternehmen könnten mehrere Nummern besitzen. Ich denke, das könnte das sein, wonach du suchst.

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)
rollender Stein
quelle
3
Können Sie mir ein Beispiel für das obige Problem geben? Ich vermisse es wahrscheinlich nur komplett, aber ich lese seit einiger Zeit die Django-Dokumentation und bin mir immer noch nicht sicher, wie ich diese Art von Beziehung herstellen soll.
Naftuli Kay
4
Möglicherweise möchten Sie beide ForeignKeys nicht erforderlich machen (leer = True, null = True) oder eine benutzerdefinierte Validierung hinzufügen, um sicherzustellen, dass mindestens die eine oder andere vorhanden ist. Was ist mit einem Unternehmen mit einer generischen Nummer? oder ein arbeitsloser Typ?
j_syk
1
@j_syk guter Punkt zur benutzerdefinierten Validierung. Aber es scheint etwas hackig zu sein, sowohl einen Fremdschlüssel für den Kerl als auch einen Fremdschlüssel für das Geschäft einzuschließen und dann eine benutzerdefinierte Validierung (außerhalb der Modelldefinition) durchzuführen. Es scheint, als müsste es einen saubereren Weg geben, aber ich kann es auch nicht herausfinden.
Andy
In dieser Situation ist es angebracht, das ContentTypes Framework zu verwenden, das generische Beziehungen zu verschiedenen Objekten ermöglicht. Die Verwendung von Inhaltstypen kann jedoch sehr schnell sehr komplex werden. Wenn dies Ihre einzige Notwendigkeit ist, ist dieser einfachere (wenn auch hackige) Ansatz möglicherweise wünschenswert.
Ball
57
Eine relevante Sache zu erwähnen ist das Argument "related_name" für ForeignKey. In der PhoneNumber-Klasse, die Sie haben würden dude = models.ForeignKey(Dude, related_name='numbers'), können Sie dann some_dude_object.numbers.all()alle zugehörigen Nummern abrufen (wenn Sie keinen "verwandten_Namen" angeben, wird standardmäßig "number_set" verwendet).
Markshep
40

In Django heißt eine Eins-zu-Viele-Beziehung ForeignKey. Es funktioniert jedoch nur in eine Richtung, sodass Sie kein numberKlassenattribut Dudebenötigen

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)

Viele Modelle können eine haben , ForeignKeyum ein anderes Modell, so dass es gültig wäre , ein zweites Attribut haben , PhoneNumberso dass

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

Sie können auf das PhoneNumbers für ein DudeObjekt dmit zugreifen d.phonenumber_set.objects.all()und dann für ein BusinessObjekt ähnlich vorgehen.

ich lerne immernoch
quelle
1
Ich war unter der Annahme, ForeignKeydass "eins zu eins" bedeutete. Mit Ihrem obigen Beispiel sollte ich eine haben Dude, die viele PhoneNumbersrichtig hat?
Naftuli Kay
6
Ich habe meine Antwort bearbeitet, um dies widerzuspiegeln. Ja. ForeignKeyist nur eins zu eins, wenn Sie angeben ForeignKey(Dude, unique=True), so dass Sie mit dem obigen Code ein Dudemit mehreren PhoneNumbers erhalten.
StillLearning
1
@rolling stone- danke, ich habe das hinzugefügt, nachdem ich meinen Fehler bemerkt habe, als du ihn kommentiert hast. Unique = True funktioniert nicht genau wie OneToOneField. Ich wollte erklären, dass ForeignKey nur dann eine Eins-zu-Eins-Beziehung verwendet, wenn Sie Unique = True angeben.
StillLearning
8
+1 dafür aus dem PhoneNumber. Jetzt macht es langsam Sinn. ForeignKeyist im Wesentlichen viele zu eins, also müssen Sie es rückwärts machen, um eins zu viele zu bekommen :)
Naftuli Kay
2
Kann jemand die Bedeutung des Feldnamens erklären phonenumber_set? Ich sehe es nirgendwo definiert. Ist es der Name des Modells, der in Kleinbuchstaben mit "_set" versehen ist?
James Wierzba
15

Sie können entweder Fremdschlüssel auf vielen Seiten der OneToManyBeziehung (dh ManyToOneBeziehung) oder ManyToMany(auf jeder Seite) mit eindeutiger Einschränkung verwenden.

Кирилл Лавров
quelle
8

djangoist klug genug. Eigentlich müssen wir kein oneToManyFeld definieren . Es wird automatisch von djangofür Sie generiert :-). Wir müssen nur foreignKeyin der zugehörigen Tabelle definieren . Mit anderen Worten, wir müssen die ManyToOneBeziehung nur mit definieren foreignKey.

class Car(models.Model):
    // wheels = models.oneToMany() to get wheels of this car [**it is not required to define**].


class Wheel(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)  

wenn wir die Liste der Räder eines bestimmten Autos erhalten wollen. Wir werden python'sautomatisch generierte Objekte verwenden wheel_set. Für das Auto werden cSie verwendenc.wheel_set.all()

Scheich Abdul Wahid
quelle
5

Die Antwort von Rolling Stone ist zwar gut, unkompliziert und funktional, aber ich denke, es gibt zwei Dinge, die es nicht löst.

  1. Wenn OP eine Telefonnummer erzwingen wollte, kann sie nicht sowohl einem Kerl als auch einem Unternehmen gehören
  2. Das unausweichliche Gefühl der Traurigkeit als Ergebnis der Definition der Beziehung im PhoneNumber-Modell und nicht im Dude / Business-Modell. Wenn zusätzliche Erdbewohner auf die Erde kommen und wir ein Alien-Modell hinzufügen möchten, müssen wir die Telefonnummer ändern (vorausgesetzt, die ETs haben Telefonnummern), anstatt dem Alien-Modell einfach ein Feld "phone_numbers" hinzuzufügen.

Führen Sie das Framework für Inhaltstypen ein , das einige Objekte verfügbar macht, mit denen wir einen "generischen Fremdschlüssel" für das PhoneNumber-Modell erstellen können. Dann können wir die umgekehrte Beziehung für Dude und Business definieren

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

class PhoneNumber(models.Model):
    number = models.CharField()

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    owner = GenericForeignKey()

class Dude(models.Model):
    numbers = GenericRelation(PhoneNumber)

class Business(models.Model):
    numbers = GenericRelation(PhoneNumber)

Weitere Informationen finden Sie in den Dokumenten. In diesem Artikel finden Sie möglicherweise ein kurzes Tutorial.

Hier ist auch ein Artikel , der gegen die Verwendung von generischen FKs spricht.

Rabeye
quelle
0

Wenn das "viele" -Modell die Erstellung eines Modells an sich nicht rechtfertigt (hier nicht der Fall, aber es könnte anderen Menschen zugute kommen), besteht eine andere Alternative darin, sich über das Django Contrib-Paket auf bestimmte PostgreSQL-Datentypen zu verlassen

Postgres kann mit Array- oder JSON- Datentypen umgehen , und dies kann eine gute Problemumgehung für One-to-Many sein, wenn die Many -ies nur an eine einzelne Entität des einen gebunden werden können .

Mit Postgres können Sie auf einzelne Elemente des Arrays zugreifen. Dies bedeutet, dass Abfragen sehr schnell durchgeführt werden können und Overheads auf Anwendungsebene vermieden werden. Und natürlich implementiert Django eine coole API , um diese Funktion zu nutzen.

Es hat offensichtlich den Nachteil, dass es nicht für andere Datenbank-Backends portierbar ist, aber ich denke, es ist immer noch erwähnenswert.

Hoffe, es kann einigen Leuten helfen, nach Ideen zu suchen.

Edouardtheron
quelle
0

Zunächst machen wir eine Tour:

01) Eins-zu-Viele-Beziehung:

ASSUME:

class Business(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class Dude(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=20)
    ........
    ........

NB: Django bietet keine OneToMany-Beziehung. Daher können wir in Django keine obere Methode verwenden. Aber wir müssen in ein relationales Modell konvertieren. Also was können wir tun? In dieser Situation müssen wir das relationale Modell in ein umgekehrtes relationales Modell umwandeln.

Hier:

relationales Modell = OneToMany

Also, umgekehrtes relationales Modell = ManyToOne

NB: Django unterstützt die ManyToOne-Beziehung und in Django wird ManyToOne von ForeignKey vertreten.

02) Viele-zu-Eins-Beziehung:

SOLVE:

class Business(models.Model):
    .........
    .........

class Dude(models.Model):
    .........
    .........

class PhoneNumber(models.Model):
    ........
    ........
    business = models.ForeignKey(Business)
    dude = models.ForeignKey(Dude)

NB: EINFACH DENKEN !!

wiedergeboren
quelle