Подтвердить что ты не робот

В чем разница между OneToOne, ManyToMany и полем ForeignKey в Django?

У меня есть немного трудностей, когда я обдумываю отношения в моделях Django.

Может кто-нибудь объяснить, какая разница между OneToOne, ManyToMany и ForeignKey?

4b9b3361

Ответ 1

Ну, здесь есть два вопроса:

  • В чем разница (в общем) между отношениями один к одному, от многих до многих и внешних отношений.
  • Каковы их отличия, характерные для Django.

На оба этих вопроса можно легко ответить простым поиском Google, но поскольку я не могу найти точный обман этого вопроса на SO, я продолжу и отвечу.

Обратите внимание, что в Django отношения должны быть определены только с одной стороны отношения.


ForeignKey

Отношение внешнего ключа обычно известно как отношение "много-к-одному". Обратите внимание, что обратная сторона этого отношения является "один ко многим" (Django предоставляет инструменты для доступа). Как следует из названия, многие объекты могут быть связаны с одним.

Person <--> Birthplace

В этом примере у человека может быть только одна родина, но место рождения может быть связано со многими людьми. Давайте посмотрим на этот пример в Django. Скажем, это наши модели:

class Birthplace(models.Model):
    city = models.CharField(max_length=75)
    state = models.CharField(max_length=25)

    def __unicode__(self):
        return "".join(self.city, ", ", self.state)

class Person(models.Model):
    name = models.CharField(max_length=50)
    birthplace = models.ForeignKey(Birthplace)

    def __unicode__(self):
        return self.name

Вы можете видеть, что никакие отношения не определены в модели Birthplace, а отношение ForeignKey определено в модели Person. Скажем, что мы создаем следующие модели (очевидно, не в синтаксисе Python):

  • Место рождения: Даллас, Техас.
  • Место рождения: Нью-Йорк, Нью-Йорк.
  • Лицо: Джон Смит, Место рождения: (Даллас, Техас)
  • Лицо: Мария Ли, Место рождения: (Даллас, Техас)
  • Лицо: Даниэль Ли, Место рождения: (Нью-Йорк, Нью-Йорк).

Теперь мы можем видеть, как Django позволяет использовать эти отношения (обратите внимание, что ./manage.py shell - ваш друг!):

>> from somewhere.models import Birthplace, Person
>> Person.objects.all()
[<Person: John Smith>, <Person: Maria Lee>, <Person: Daniel Lee>]
>> Birthplace.objects.all()
[<Birthplace: Dallas, Texas>, <Birthplace: New York City, New York>]

Вы можете увидеть наши созданные модели. Теперь позвольте проверить кого-то место рождения:

>> person = Person.object.get(name="John Smith")
>> person.birthplace
<Birthplace: Dallas, Texas>
>> person.birthplace.city
Dallas

Скажем, вы хотите увидеть всех людей с данным местом рождения. Как я сказал ранее, Django позволяет вам обращаться к обратным отношениям. По умолчанию Django создает менеджер (RelatedManager) для вашей модели, названный <model>_set, где <model> - ваша модель имя в нижнем регистре.

>> place = Birthplace.objects.get(city="Dallas")
>> place.person_set.all()
[<Person: John Smith>, <Person: Maria Lee>]

Обратите внимание, что мы можем изменить имя этого менеджера, установив аргумент ключевого слова related_name в нашем отношении модели. Итак, мы изменили бы поле Birthplace в модели Person на:

birthplace = models.ForeignKey(Birthplace, related_name="people")

Теперь мы можем получить доступ к этой обратной связи с красивым именем:

>> place.people.all()
[<Person: John Smith>, <Person: Maria Lee>]

Один-к-одному

Отношения "один-к-одному" очень похожи на отношение "один-к-одному", за исключением того, что он ограничивает два объекта наличием уникальной взаимосвязи. Примером этого может служить пользователь и профиль (в котором хранится информация о пользователе). Ни один из двух пользователей не использует один и тот же профиль. Давайте посмотрим на это в Django. Я не буду пытаться определить модель пользователя, поскольку Django определяет ее для нас. Однако обратите внимание, что Django предлагает использовать django.contrib.auth.get_user_model() для импорта пользователя, чтобы мы это сделали. Модель профиля может быть определена следующим образом:

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL) # Note that Django suggests getting the User from the settings for relationship definitions
    fruit = models.CharField(max_length=50, help_text="Favorite Fruit")
    facebook = models.CharField(max_length=100, help_text="Facebook Username")

    def __unicode__(self):
        return "".join(self.fruit, " ", self.facebook)

Нам нужен только один пользователь с профилем для проверки этого в оболочке:

  • Пользователь: johndt6
  • Профиль: user: johndt6, "Kiwi", "blah_blah"

Теперь вы можете легко получить доступ к профилю пользователя из модели пользователя:

>> user = User.objects.all()[0]
>> user.username
johndt6
>> user.profile
<Profile: Kiwi blah_blah>
>> user.profile.fruit
Kiwi
>> profile = Profile.objects.get(user=user)
>> profile.user
<User: johndt6>

Конечно, вы можете настроить имя обратного отношения, используя аргумент related_name, как указано выше.


Много-ко-многим

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

Отношение "многие ко многим" между двумя моделями определяет, что ноль, один или несколько объектов первой модели могут быть связаны с нулем, одним или несколькими объектами второй модели. В качестве примера давайте представим компанию, которая определяет их рабочий процесс через проекты. Проект может быть связан без заказов, только с одним заказом или с несколькими заказами. Заказ может быть связан не с проектами, ни с одним проектом, ни с другими. Пусть определит наши модели так:

class Order(models.Model):
    product = models.CharField(max_length=150)  # Note that in reality, this would probably be better served by a Product model
    customer = models.CharField(max_length=150)  # The same may be said for customers

    def __unicode__(self):
        return "".join(self.product, " for ", self.customer)

class Project(models.Model):
    orders = models.ManyToManyField(Order)

    def __unicode__(self):
        return "".join("Project ", str(self.id))

Обратите внимание, что Django создаст RelatedManager для поля orders для доступа к отношениям "многие ко многим".

Позвольте создать следующие объекты (в моем непоследовательном синтаксисе!):

  • Заказ: "Космический корабль" , "НАСА"
  • Заказ: "Подводная лодка", "ВМС США"
  • Заказ: "Race car", "NASCAR"
  • Проект: заказы: []
  • Проект: заказы: [(Заказ: "Космический корабль" , "НАСА" )]
  • Проект: заказы: [(Заказ: "Космический корабль" , "НАСА" ), (Заказ: "Гонка", "NASCAR" )]

Мы можем получить доступ к этим отношениям следующим образом:

>> Project.objects.all()
[<Project: Project 0>, <Project: Project 1>, <Project: Project 2>]
>> for proj in Project.objects.all():
..     print(proj)
..     proj.orders.all()  # Note that we must access the `orders`
..                        # field through its manager
..     print("")
Project 0
[]

Project 1
[<Order: Spaceship for NASA>]

Project 2
[<Order: Spaceship for NASA>, <Order: Race car for NASCAR>]

Обратите внимание, что заказ NASA связан с 2 проектами, а порядок ВМС США не связан ни с одной. Также обратите внимание, что один проект не имеет ордеров, а один имеет несколько.

Мы также можем получить доступ к обратному обращению так же, как и раньше:

>> order = Order.objects.filter(customer="NASA")[0]
>> order.project_set.all()
[<Project: Project 0>, <Project: Project 2>]

Ресурсы

Хорошее объяснение отношений db, предоставленное @MarcB

Страница Википедии о кардинальности

Django Docs:

models.ForeignKey

models.OneToOneField

models.ManyToManyField

Индивидуальные отношения

Отношения "многие ко многим"