Django: При расширении User лучше использовать OneToOneField (User) или ForeignKey (User, unique = True)?
Я нахожу противоречивую информацию о том, следует ли использовать OneToOneField (User) или ForeignKey (User, unique = True) при создании модели UserProfile путем расширения модели пользователя Django.
Лучше ли это использовать?:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
или это?:
class UserProfile(models.Model):
user = models.OneToOneField(User)
Django Doc указывает OneToOneField, а Пример книги Django использует ForeignKey.
Джеймс Беннетт также имеет две записи в блоге, в которых также приводятся конфликтующие примеры:
В предыдущем посте Беннетт дает некоторые причины, по которым он переключился на использование ForeignKey вместо OneToOneField, но я не совсем понял его, особенно когда вижу другие сообщения, которые рекомендуют обратное.
Мне любопытно узнать ваше предпочтение и почему. Или это даже имеет значение?
Ответы
Ответ 1
Единственная реальная причина, данная в статье, заключается в том, что ее можно настроить так, чтобы страница администратора для User
отображала как поля в User
, так и UserProfile
. Это может быть реплицировано с помощью OneToOneField
с небольшой консистентной смазкой, поэтому, если вы не склонны показывать его на странице администрирования без какой-либо работы за счет некоторой ясности ( "Мы можем создать несколько профилей на пользователя! О нет, подождите, он установит уникальный." ) Я бы использовал OneToOneField
.
Ответ 2
Помимо встроенной страницы администратора, другой причиной для решения ForeignKey
является то, что она позволяет использовать правильный менеджер баз данных по умолчанию при обращении к объектам с обратным отношением. Рассмотрим пример из подклассов подклассов. Скажем, что определение класса Post
из примера выглядит так:
class Post(ParentModel):
title = models.CharField(max_length=50)
onetoone = models.ForeignKey(SomeModel, unique=True)
children = ChildManager()
objects = models.Manager()
Вызывая somemodel_instance.post_set.all()[0]
, вы получаете нужные объекты подклассов класса Post
, как указано, определяя первый (по умолчанию) менеджер как ChildManager
. С другой стороны, с помощью OneToOneField
, вызывая somemodel_instance.post
, вы получаете экземпляр класса Post
. Вы всегда можете вызвать somemodel_instance.post.subclass_object
и получить тот же результат, но менеджер по умолчанию может выполнять любые другие трюки, а решения FK
скрывают их хорошо.
Если вы владеете и можете изменять код пользовательского менеджера, вы можете использовать атрибут use_for_related_fields
вместо использования FK вместо законного поля 1to1, но даже это может потерпеть неудачу из-за некоторых неизвестных мне неприятностей автоматических менеджеров. Насколько я помню, в приведенном выше примере это не сработает.
Ответ 3
Другие причины обычно не использовать OneToOneField
, связанные с обратными отношениями: при использовании обратных отношений, определенных через OneToOneField
, вы получаете экземпляр модели, противоположный обратному отношению Manager
для ForeignKey
и, как следствие, там всегда ударил БД. Это дорого, если вы делаете некоторые общие вещи в обратных отношениях (через _meta.get_all_related_objects()
) и не знаете и не заботитесь, будете ли вы использовать их все или нет.