Отношение один к одному (One to one)

Последнее обновление: 23.08.2022

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

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=20)

class Account(models.Model):
    login = models.CharField(max_length=20)
    password = models.CharField(max_length=20)
    user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key = True)

Для создания этого отношения один к одному применяется конструктор типа models.OneToOneField(). Его первый параметр указывает, с какой моделью будет ассоциирована данная сущность (в данном случае ассоциация с моделью User). Второй параметр on_delete = models.CASCADE говорит, что данные текущей модели (Account) будут удаляться в случае удаления связанного объекта главной модели (User). Третий параметр primary_key = True указывает, что внешний ключ (через который идет связь с главной моделью) в то же время будет выступать и в качестве первичного ключа. И соответственно создавать отдельное поле для первичного ключа не надо.

В результате миграции в базе данных SQLite будут создаваться следующие таблицы:

CREATE TABLE "hello_user" (
	"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
	"name" varchar(20) NOT NULL
)
CREATE TABLE "hello_account" (
	"login" varchar(20) NOT NULL, 
	"password" varchar(20) NOT NULL, 
	"user_id" bigint NOT NULL PRIMARY KEY REFERENCES "hello_user" ("id") DEFERRABLE INITIALLY DEFERRED
)

Операции с моделями

С помощью свойства user в модели Account мы можем манипулировать связанным объектом модели User:

# создадим пользователя
sam = User.objects.create(name="Sam")

# создадим аккаунт пользователя Sam
acc = Account.objects.create(login = "1234", password="6565", user=sam)

# изменяем имя пользователя
acc.user.name = "Bob"
# сохраняем изменения в бд
acc.user.save()

print(f"{acc.user.name}, login: {acc.login}, password: {acc.password}")
# Bob, login: 1234, password: 6565

Однако через модель User мы также можем оказывать влияние на связанный объект Account. Несмотря на то, что явным образом в модели User определено только одно свойство - name, при связи один к одному неявно создается еще одно свойство, которое называется по имени зависимой модели и которое указывает на связанный объект этой модели. То есть в данном случае это свойство будет называться "account":

# создадим пользователя
tom = User.objects.create(name="Tom")
    
# создадим аккаунт пользователя
acc = Account(login = "1234", password="6565")
tom.account = acc
tom.account.save()

# обновляем данные
tom.account.login = "qwerty"
tom.account.password = "123456"
tom.account.save()
print(f"{tom.name}, login: {tom.account.login}, password: {tom.account.password}")
# Tom, login: qwerty, password: 123456

Подобным образом можно выполнять фильтрацию по обоим моделям и их свойствам:

# получим пользователя
tom = User.objects.get(name="Tom")
        
# получим аккаунт пользователя
tom_acc = Account.objects.get(user=tom)
print(f"login: {tom_acc.login}, password: {tom_acc.password}")
# login: qwerty, password: 123456

# получим аккаунт по имени пользователя
bob_acc = Account.objects.get(user__name="Bob")
print(f"login: {bob_acc.login}, password: {bob_acc.password}")
    
# получим пользователя по логину
user = User.objects.get(account__login="qwerty")
print(user.name)
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850