Jak wyrazić relację jeden do wielu w Django

167

Definiuję teraz moje modele Django i zdałem sobie sprawę, że nie ma OneToManyFieldw modelu pól typów. Jestem pewien, że można to zrobić, więc nie wiem, czego mi brakuje. Zasadniczo mam coś takiego:

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

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

W tym przypadku, każdy Dudemoże mieć wiele PhoneNumbers, ale związek powinien być jednokierunkowy, że nie muszę wiedzieć, z PhoneNumberktórego Dudejest właścicielem, per se, jak mogę mieć wiele różnych obiektów, które własne PhoneNumberprzypadki, takie jak Businessna przykład:

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

Co zastąpiłbym OneToManyField(czego nie ma) w modelu, aby przedstawić ten rodzaj relacji? Pochodzę z Hibernate / JPA, gdzie zadeklarowanie relacji jeden do wielu było tak proste, jak:

@OneToMany
private List<PhoneNumber> phoneNumbers;

Jak mogę to wyrazić w Django?

Naftuli Kay
źródło

Odpowiedzi:

133

Aby obsłużyć relacje jeden do wielu w Django, musisz użyć ForeignKey.

Dokumentacja na ForeignKey jest bardzo obszerna i powinna odpowiedzieć na wszystkie Twoje pytania:

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

Obecna struktura w twoim przykładzie pozwala każdemu Kolesiowi mieć jeden numer, a każdy numer należy do wielu Kolesi (to samo z Biznesem).

Jeśli chcesz odwrócić relację, musisz dodać dwa pola ForeignKey do modelu PhoneNumber, jedno do Dude i jedno do Business. Dzięki temu każdy numer należałby do jednego gościa lub firmy, a kolesie i firmy mogliby posiadać wiele numerów. Myślę, że to może być to, czego szukasz.

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)
toczący się kamień
źródło
3
Czy mógłbyś podać przykład, biorąc pod uwagę powyższy problem? Prawdopodobnie całkowicie tego mi brakuje, ale czytam dokumentację Django od jakiegoś czasu i nadal nie jestem pewien, jak stworzyć tego rodzaju relację.
Naftuli Kay
4
może chcieć, aby oba klucze obce nie były wymagane (puste = True, null = True) lub dodać jakiś rodzaj niestandardowej weryfikacji, aby upewnić się, że istnieje co najmniej jeden lub drugi. A co w przypadku firmy posiadającej numer ogólny? czy bezrobotny koleś?
j_syk,
1
@j_syk Dobra uwaga na temat niestandardowej weryfikacji. ale wydaje się trochę hackem dołączanie klucza obcego do gościa i klucza obcego do biznesu, a następnie przeprowadzanie niestandardowej (zewnętrznej w stosunku do definicji modelu) walidacji. wydaje się, że musi być czystszy sposób, ale też nie mogę tego rozgryźć.
andy
W tej sytuacji należy użyć struktury ContentTypes, która umożliwia tworzenie ogólnych relacji z różnymi obiektami. Jednak korzystanie z typów zawartości może bardzo szybko się skomplikować, a jeśli jest to Twoja jedyna potrzeba, może być pożądane to prostsze (nawet hackerskie) podejście.
kball
57
Jedną istotną rzeczą, o której należy wspomnieć, jest argument „related_name” dla ForeignKey. Więc w klasie PhoneNumber, którą miałbyś, dude = models.ForeignKey(Dude, related_name='numbers')a następnie możesz użyć some_dude_object.numbers.all()do uzyskania wszystkich powiązanych numerów (jeśli nie określisz „related_name”, domyślnie zostanie to „number_set”).
markshep
40

W Django relacja jeden do wielu nazywa się ForeignKey. Działa jednak tylko w jednym kierunku, więc zamiast mieć numberatrybut klasy Dude, będziesz potrzebować

class Dude(models.Model):
    ...

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

Wiele modeli może mieć ForeignKeyjeden inny model, więc byłoby ważne, aby mieć drugi atrybut PhoneNumbertakiego typu

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

Możesz uzyskać dostęp do PhoneNumbers dla Dudeobiektu za dpomocą d.phonenumber_set.objects.all(), a następnie zrobić to samo dla Businessobiektu.

stillLearning
źródło
1
Zakładałem, że ForeignKeyoznacza to „jeden do jednego”. Korzystając z powyższego przykładu, powinienem mieć Dudewiele, PhoneNumbersprawda?
Naftuli Kay
6
Zredagowałem odpowiedź, aby to odzwierciedlić. Tak. ForeignKeyjest tylko jeden do jednego, jeśli określisz ForeignKey(Dude, unique=True), więc z powyższym kodem otrzymasz Dudez wieloma PhoneNumbers.
stillLearning
1
@rolling stone- dziękuję, dodawałem to po tym, jak zdałem sobie sprawę z mojego błędu w komentarzu. Unique = True nie działa dokładnie tak, jak OneToOneField, chciałem wyjaśnić, że ForeignKey używa tylko relacji jeden do jednego, jeśli określisz Unique = True.
stillLearning
8
+1 za zrobienie tego z PhoneNumber. Teraz zaczyna mieć sens. ForeignKeyjest w zasadzie wiele-do-jednego, więc musisz to zrobić wstecz, aby uzyskać jeden do wielu :)
Naftuli Kay
2
Czy ktoś może wyjaśnić znaczenie nazwy pola phonenumber_set? Nie widzę tego nigdzie zdefiniowanego. Czy jest to nazwa modelu, pisana małymi literami, z dopiskiem „_set”?
James Wierzba
15

Możesz użyć klucza obcego po wielu stronach OneToManyrelacji (tj. ManyToOneRelacji) lub użyć ManyToMany(po dowolnej stronie) z ograniczeniem unikalności.

Кирилл Лавров
źródło
8

djangojest wystarczająco inteligentny. Właściwie nie musimy definiować oneToManypola. Zostanie automatycznie wygenerowany przez django:-). Musimy tylko zdefiniować foreignKeyw powiązanej tabeli. Innymi słowy, wystarczy zdefiniować ManyToOnerelację za pomocą 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)  

jeśli chcemy uzyskać listę kół danego samochodu. użyjemy python'sautomatycznie wygenerowanego obiektu wheel_set. Do samochodu, cktórego będziesz używaćc.wheel_set.all()

Sheikh Abdul Wahid
źródło
5

Chociaż odpowiedź Rolling Stone jest dobra, prosta i funkcjonalna, myślę, że są dwie rzeczy, których nie rozwiązuje.

  1. Jeśli OP chciał wyegzekwować, numer telefonu nie może należeć jednocześnie do gościa i firmy
  2. Nieuniknione uczucie smutku w wyniku zdefiniowania relacji w modelu PhoneNumber, a nie w modelach Dude / Business. Kiedy na Ziemię przybywają dodatkowe istoty ziemskie i chcemy dodać model Obcy, musimy zmodyfikować PhoneNumber (zakładając, że ET mają numery telefonów) zamiast po prostu dodawać pole „phone_numbers” do modelu Alien.

Przedstaw strukturę typów zawartości , która udostępnia niektóre obiekty, które pozwalają nam utworzyć „ogólny klucz obcy” w modelu PhoneNumber. Następnie możemy zdefiniować odwrotną relację w przypadku gościa i biznesu

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)

Zobacz dokumentację, aby uzyskać szczegółowe informacje, i być może zapoznaj się z tym artykułem, aby uzyskać krótki samouczek.

Tutaj jest również artykuł, w którym argumentuje się przeciwko używaniu generycznych FK.

rabeye
źródło
0

Jeśli model „wiele” nie uzasadnia tworzenia modelu jako takiego (nie w tym przypadku, ale może przynieść korzyści innym osobom), inną alternatywą byłoby poleganie na określonych typach danych PostgreSQL za pośrednictwem pakietu Django Contrib

Postgres może radzić sobie z typami danych Array lub JSON i może to być przyjemne obejście do obsługi typu jeden do wielu, gdy wiele-ies można powiązać tylko z jedną jednostką jednego .

Postgres umożliwia dostęp do pojedynczych elementów tablicy, co oznacza, że ​​zapytania mogą być naprawdę szybkie i pozwalają uniknąć narzutów na poziomie aplikacji. Oczywiście Django implementuje fajne API, aby wykorzystać tę funkcję.

Oczywiście ma tę wadę, że nie można go przenosić na inne zaplecze bazy danych, ale myślę, że nadal warto o tym wspomnieć.

Mam nadzieję, że pomoże to niektórym osobom szukającym pomysłów.

edouardtheron
źródło
0

Przede wszystkim zwiedzamy:

01) relacja jeden do wielu:

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)
    ........
    ........

Uwaga: Django nie zapewnia żadnej relacji OneToMany. Więc nie możemy użyć metody górnej w Django. Ale musimy przejść do modelu relacyjnego. Więc co możemy zrobić? W tej sytuacji musimy przekształcić model relacyjny w odwrotny model relacyjny.

Tutaj:

model relacyjny = OneToMany

Zatem odwrotny model relacyjny = ManyToOne

Uwaga: Django obsługuje relacje ManyToOne, aw Django ManyToOne jest reprezentowane przez ForeignKey.

02) relacja wiele do jednego:

SOLVE:

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

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

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

NB: MYŚL PO PROSTU !!

odrodzony
źródło