한국어로 옮기기 어색한 단어들은 영문으로 혹은 해석이 애매한 구절은 직역한 그대로 사용 하였습니다.
일대일 관계
일대일 관계를 정의하기 위해서는 OneToOneField
를 사용한다. 다른 Field
타입과 마찬가지로 모델의 클래스 속성으로 포함하여 사용한다.
객체가 어떤 방식으로든 다른 객체를 "확장" 할 때 객체의 기본 키에서 유용하다.
OneToOneField
는 positional argument
를 요구한다:모델과 관계된 클래스를 말한다
예를들어서, "place" 데이터베이스를 만들려고 한다면, 주소, 전화번호 등의 보통의 필드들을 데이터베이스 내에 만들 것이다. 그리고 나서 "place" 상위에 "restaurants" 데이터베이스를 만들길 원한다면, Restaurant
모델에 위의 필드들을 복사하는 등 반복하는 것 대신에, Place
와 일대일 관계를 가지는 Restaurant
모델을 만들 수 있을 것이다.(왜냐하면 restaurant은 place와 "is a"가 충족하기 때문이다; 사실 이를 처리할 때, 일반적으로는 암시적인 일대일 관계를 포함하는 상속을 사용한다)
외래키와 마찬가지로, 당신은 재귀적인 관계와, (다대다 관계가 있는 객체), 모델에 정의되지 않은 관계를 생성할 수 있다.
Sell also
풀 예제는 일대일 관계 모델 예제 를 보자
OneToOneField
는 또한 선택적으로 parent_link
argument를 허용한다.
OneToOneField
클래스들은 모델에서 자동으로 기본키가 되는데 사용된다. 하지만 이 사항은 더이상 사실이 아니다(원하는 경우에 primary_key argument를 수동으로 전달할 수 있다) 그러므로, 1개의 모델에서 여러개의 OneToOneField
타입을 가지는 것이 가능하다
내 마음대로 해설
Place와 Restaurant로 보는 OneToOneField의 예제
첫번째 테이블에 있는 하나의 레코드가 두번째 테이블에 있는 단 하나의 레코드에만 연결되고, 두번째 테이블에 있는 하나의 레코드가 첫 번째 테이블에 있는단 하나의 레코드에만 연결될 때, 일대일 관계에 놓인다고 표현한다.
개념적으로 일대다와 비슷하지만, 일대다 관계에서 unique=True
옵션이 들어간 것으로 이해하면 된다.
문서에서는 Place
와 Restaurant
의 예를 든다.
x
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Meta:
db_table = "place"
def __str__(self):
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, on_delete=models.CASCADE, primary_key=True)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
class Meta:
db_table = "place"
def __str__(self):
return "%s the restaurant" % self.place.name
내가 생각하기에 일대일 관계가 맺어져있을 때, 가장 큰 특징은 양쪽에서 각각 속성에 접근 가능하다는 것이다.
x
In [27]: p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
In [28]: p1.save()
INSERT INTO "place" ("name", "address")
VALUES ('Demon Dogs', '944 W. Fullerton')
Execution time: 0.002118s [Database: default]
In [29]: r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
In [30]: r.save()
INSERT INTO "restaurant" ("place_id", "serves_hot_dogs", "serves_pizza")
VALUES (4, 1, 0)
In [32]: p1.restaurant
Out[32]: <Restaurant: Demon Dogs the restaurant>
In [33]: r.place
Out[33]: <Place: Demon Dogs the place>
이 때 중요한 것은 OneToOneField
에 할당하기전에, 반드시 save를 먼저해야한다. 위 예제에서는 Restaurant 모델에 OneToOneField
로 지정해주기 전에 Place 인스턴스를 반드시 save() 부터 해주어야 한다. 그렇지 않으면 아래와 같은 에러가 발생한다.
xxxxxxxxxx
In [1]: p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
In [2]: r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=True)
In [3]: r.save()
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-3-f6d1b5693493> in <module>
----> 1 r.save()
~/.pyenv/versions/3.8.0/envs/py38/lib/python3.8/site-packages/django/db/models/base.py in save(self, force_insert, force_update, using, update_fields)
698 if not field.remote_field.multiple:
699 field.remote_field.delete_cached_value(obj)
--> 700 raise ValueError(
701 "save() prohibited to prevent data loss due to "
702 "unsaved related object '%s'." % field.name
ValueError: save() prohibited to prevent data loss due to unsaved related object 'place'.
모델 내 복수개의 OneToOneField를 가질 수 있다?
x
class MySpecialUser(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
supervisor = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='supervisor_of',
)
class Meta:
db_table = "my_special_user"
xxxxxxxxxx
In [7]: MySpecialUser.objects.create(user=user, supervisor=user)
INSERT INTO "my_special_user" ("user_id", "supervisor_id")
VALUES (1, 1)
In [20]: user
Out[20]: <User: thkwon>
In [21]: hasattr(user, 'supervisor_of')
Out[21]: True
In [22]: hasattr(user, 'myspecialuser')
Out[22]: True
In [23]: user.supervisor_of
Out[23]: <MySpecialUser: MySpecialUser object (1)>
In [24]: user.myspecialuser
Out[24]: <MySpecialUser: MySpecialUser object (1)>
In [26]: special_user.user
Out[26]: <User: thkwon>
In [27]: special_user.supervisor
Out[27]: <User: thkwon>
primary_key=True 지정 여부에 따른 테이블 구조
xxxxxxxxxx
# primary_key=True 지정 했을 때,
BEGIN;
--
-- Create model Place
--
CREATE TABLE "place" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(50) NOT NULL,
"address" varchar(80) NOT NULL
);
--
-- Create model Restaurant
--
CREATE TABLE "restaurant" (
"place_id" integer NOT NULL PRIMARY KEY REFERENCES "place" ("id") DEFERRABLE INITIALLY DEFERRED,
"serves_hot_dogs" bool NOT NULL,
"serves_pizza" bool NOT NULL
);
COMMIT;
x
# primary_key=True 지정을 안했을 때,
BEGIN;
--
-- Create model Place
--
CREATE TABLE "place" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(50) NOT NULL,
"address" varchar(80) NOT NULL
);
--
-- Create model Restaurant
--
CREATE TABLE "restaurant" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"serves_hot_dogs" bool NOT NULL,
"serves_pizza" bool NOT NULL,
"place_id" integer NOT NULL UNIQUE REFERENCES "place" ("id") DEFERRABLE INITIALLY DEFERRED
);
COMMIT;
테이블 상으로 보았을 때, ID 가 추가되는 것 이외에는 기능이나 동작상에서 다른 문제는 발견하지 못하였다. 어떤 경우에 primary_key=True
를 명시적으로 써주어야 하는지 따로 언급된 부분이 없는 걸로 보아선, 둘 중 선택해서 사용하면 될 것 같은데 내 개인적으로는 자동 생성되는 id
값을 따로 쓸 일이 없다면, primary_key=True
로 설정해주는 편이 좋을 것 같고, 위에서 나온 한 모델에 OneToOneField가 2개인 경우에는 primary_key=True
를 사용하지 않는 것이 좋을 것 같다. 아직 완벽히 리서치 된게 아니라서 일단 여기까지 정리한다.
'개발 > 장고' 카테고리의 다른 글
[해설과 함께 읽는 Django 문서] Models - 필드 이름 제한사항들 (0) | 2021.01.31 |
---|---|
[해설과 함께 읽는 Django 문서] Models - 파일 간 모델 (0) | 2021.01.31 |
[DRF] Serializers - 객체 역직렬화 하기 (0) | 2021.01.31 |
[DRF] Serializers - 객체 직렬화 하기 (0) | 2021.01.31 |
[DRF] Serializers - Serializers 클래스 선언하기 (0) | 2021.01.31 |