Как выразить отношения "один-ко-многим" в Django

Я определяю свои модели Django прямо сейчас, и я понял, что в типах полей модели не существует OneToManyField. Я уверен, что есть способ сделать это, поэтому я не уверен, что мне не хватает. У меня есть что-то вроде этого:

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

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

В этом случае каждый Dude может иметь несколько PhoneNumber s, но отношение должно быть однонаправленным, поскольку мне не нужно знать из PhoneNumber, которому Dude принадлежит сам по себе, поскольку у меня может быть много разных объектов, в которых есть экземпляры PhoneNumber, например Business:

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

Что бы я заменил OneToManyField (который не существует) в модели для представления такого рода отношений? Я иду из Hibernate/JPA, где объявление отношений "один ко многим" было так же просто:

@OneToMany
private List<PhoneNumber> phoneNumbers;

Как я могу выразить это в Django?

Ответ 1

Чтобы обрабатывать отношения "Один-ко-многим" в Django, вам нужно использовать ForeignKey.

Документация по ForeignKey очень всеобъемлющая и должна отвечать на все ваши вопросы:

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

Текущая структура в вашем примере позволяет каждому чуваку иметь один номер, а каждый номер принадлежит нескольким Dudes (то же самое с Business).

Если вам нужна обратная связь, вам нужно добавить два поля ForeignKey в вашу модель PhoneNumber, одну для Dude и one to Business. Это позволит каждому номеру принадлежать либо к одному чуваку, либо к одному бизнесу, и у Dudes и Business могут иметь несколько номеров. Я думаю, это может быть то, что вам нужно.

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

Ответ 2

В Django отношение "один ко многим" называется ForeignKey. Однако он работает только в одном направлении, поэтому вместо атрибута number класса Dude вам понадобится

class Dude(models.Model):
    ...

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

Многие модели могут иметь ForeignKey для одной другой модели, поэтому было бы справедливо иметь второй атрибут PhoneNumber такой, что

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

Вы можете получить доступ к PhoneNumber для объекта Dude d с помощью d.phonenumber_set.objects.all(), а затем сделать аналогично для объекта Business.

Ответ 3

Вы можете использовать либо внешний ключ со многими сторонами отношения OneToMany (т.е. отношение ManyToOne), либо использовать ManyToMany (с любой стороны) с уникальным ограничением.

Ответ 4

Чтобы быть более понятным - в Django нет OneToMany, только ManyToOne - это Foreignkey, описанный выше. Вы можете описать отношение OneToMany с помощью Foreignkey, но это очень невыразительно.

Хорошая статья об этом: https://amir.rachum.com/blog/2013/06/15/a-case-for-a-onetomany-relationship-in-django/

Ответ 5

django достаточно умен. На самом деле нам не нужно определять поле oneToMany. Он будет автоматически сгенерирован django для вас :-). Нам нужно только определить foreignKey в связанной таблице. Другими словами, нам нужно только определить отношение ManyToOne с помощью 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)  

если мы хотим получить список колес конкретного автомобиля. мы будем использовать python автоматически сгенерированный объект wheel_set. Для автомобиля c вы будете использовать c.wheel_set.all()

Ответ 6

Если модель "многие" не оправдывает создание модели как таковой (здесь это не так, но это может принести пользу другим людям), другой альтернативой может стать использование определенных типов данных PostgreSQL через пакет Django Contrib.

Postgres может работать с типами данных Array или JSON, и это может быть хорошим обходным путем для обработки One-To-Many, когда многие могут быть привязаны только к одному объекту.

Postgres позволяет получить доступ к отдельным элементам массива, что означает, что запросы могут быть очень быстрыми и избежать накладных расходов на уровне приложений. И, конечно же, Django реализует классный API для использования этой функции.

Он, очевидно, имеет недостаток, заключающийся в том, что он не переносим на другую базу данных, но я все же должен упомянуть об этом.

Надеюсь, что это может помочь некоторым людям, которые ищут идеи.

Ответ 7

Хотя Rolling Stone ответ хороший, простой и функциональный, я думаю, что есть две вещи, которые он не решает.

  1. Если ОП хотел ввести в действие номер телефона, он не может принадлежать как чуваку, так и бизнесу.
  2. Неизбежное чувство грусти в результате определения отношений в модели PhoneNumber, а не в моделях Dude/Business. Когда на Землю прибывают дополнительные земные существа, и мы хотим добавить модель Alien, нам нужно изменить PhoneNumber (при условии, что у ET есть номера телефонов) вместо простого добавления поля "phone_numbers" в модель Alien.

Представьте структуру типов контента, которая предоставляет некоторые объекты, которые позволяют нам создавать "универсальный внешний ключ" в модели PhoneNumber. Затем мы можем определить обратную связь между Чуваком и Бизнесом

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)

Подробнее см. в документации и, возможно, ознакомьтесь с этой статьей для быстрого обучения.

Кроме того, вот статья, в которой содержится аргумент против использования общих FK.