๐ฉ ๋ฌธ์ ๋ฐ์
์ฐ์ , ์ ๊ฐ ๋ถ๋ชํ์๋ ์ด์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ ์ ๊ฐ ๊ฒ์๊ธ์ ์์ฑํ๋ ์์ฒญ์ ํ ๋, ์นดํ ๊ณ ๋ฆฌ๋ฅผ ์ค์ ํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฐ์ผ๋ก '๊ธฐํ'๋ผ๋ ๊ฐ์ผ๋ก ์ ์ฅํ๋ ค๊ณ ํ์ต๋๋ค.
๋ฌธ์ ๋ '๊ธฐํ'๋ผ๋ ๊ฐ์ ์ ์ด์ DB ์ ์ฅ๋ ๊ฐ์ด ์๋๊ธฐ ๋๋ฌธ์ NOT NULL ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋ ๊ฒ์ด์ฃ .
- models.py
class Article(CommonModel):
author = models.ForeignKey("users.User", on_delete = models.CASCADE, related_name="articles",)
title = models.CharField(max_length=100, blank=False, null=False)
context = models.TextField()
category = models.ForeignKey("boards.Category", on_delete=models.CASCADE, related_name="category")
class Category(models.Model):
name = models.CharField(max_length=10, default='๊ธฐํ')
# ๊ฐ๋
์ฑ์ ์ํด ๊ด๋ จ์๋ ์ฝ๋๋ ์๋ตํ์ต๋๋ค.
๋ง์ฝ ์ด์ํ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๊ฒ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
django.db.utils.IntegrityError: NOT NULL constraint failed: boards_article.category_id
ํด๋น ์๋ฌ๋ ๊ฒ์๊ธ์ ์์ฑํ ๋ ์นดํ ๊ณ ๋ฆฌ ํ๋์ ๋ํ ์ ํจํ ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ์นดํ ๊ณ ๋ฆฌ๋ผ๋ ํ ์ด๋ธ์ ๊ฐ ์์ฒด๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ง๋ค์ง ๋ชปํ๋ ๊ฒ์ ๋๋ค. ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก๋ ์นดํ ๊ณ ๋ฆฌ ํ๋์ ๊ฐ์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ, NULL์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
๋ง์ฝ ์ด๋ฏธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋์ดํฌ ์ ์๋ ์ํฉ์ด๋ผ๋ฉด Null์ ํ์ฉํ๊ณ Blank๋ฅผ True๋ก ํด๋๋ ์ ๋ต์ ์ฌ์ฉํฉ๋๋ค.
์ ๋ ์์ง ์ด๊ธฐํ๋ฅผ ํ์ง ์๋ ์ํฉ์ด๊ธฐ ๋๋ฌธ์ `default` ๊ฐ์ ๋ฃ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ view ํจ์์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋น ๊ฐ์ผ ๊ฒฝ์ฐ ๊ธฐํ๋ฅผ ๋ฐ๊ฒ๋ํด์ ๋ง์ฝ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์๋ ๊ฒฝ์ฐ ์์ฑํ ์ ์๊ฒ๋ ํจ์๋ฅผ ์์ฑํ์ต๋๋ค.
def exist_category(request):
category_name = request.data.get('category', '๊ธฐํ')
category, _ = Category.objects.get_or_create(name=category_name)
return category.id
๐ค ๊ฐ์ฒด ๊ฐ ์ฐธ์กฐ ๊ด๊ณ?
์ค๋ ๋ฌธ์ ๋ฐ์์ ๋ํด ์๊ฐํด์ผํ ๋ถ๋ถ์ ๊ฐ์ฒด ๊ฐ ์ฐธ์กฐ ๊ด๊ณ์ ๋๋ค.
๊ฐ์ฒด ๊ฐ ์ฐธ์กฐ ๊ด๊ณ๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ธ๋ํค(Foregin Key)๊ฐ ํ์ํฉ๋๋ค.
๐ก ์ธ๋ํค(Foreign Key)
์ธ๋ํค๋ ๋ค๋ฅธ ํ ์ด๋ธ์ ํ์ ์๋ณํ ์ ์๋ ํค๋ฅผ ์๋ฏธํ๋ฉฐ ์ฐธ์กฐ๋๋ ๋ค๋ฅธ ํ ์ด๋ธ์ ๊ธฐ๋ณธ ํค(Primary Key)๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
์ฐธ์กฐ๋๋ ํ ์ด๋ธ์ ํ์ ์ฐธ์กฐํ๋ ์ธ๋ํค(FK)๊ฐ ์กด์ฌํ๋ ํ ์ญ์ ๋ ์ ์๊ณ ๊ธฐ๋ณธํค(PK) ๋ํ ๋ณ๊ฒฝ๋ ์ ์๋ค. (์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ)
๋ํ์ ์ผ๋ก, 1:N ๊ด๊ณ๋ฅผ ๊ฐ์ง๋ ์๋ ๊ฒ์๋ฌผ๊ณผ ๋๊ธ์ ๊ด๊ณ ํน์ ์ ์ ์ ๊ฒ์๊ธ ๊ด๊ณ๊ฐ ์๋ค. ํ ๋ช ์ ์ ์ ๊ฐ ์ฌ๋ฌ ๊ฒ์๋ฌผ์ ์์ฑํ ์ ์์ผ๋ฉฐ ํ ๊ฒ์๋ฌผ์๋ ์ฌ๋ฌ๊ฐ์ ๋๊ธ์ด ๋ฌ๋ฆด ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ง์ฝ, ํ๋์ ์นดํ ๊ณ ๋ฆฌ์ ์ฌ๋ฌ ๊ฐ์ ๊ฒ์๋ฌผ์ ํฌํจํ๊ณ ์ถ์ผ๋ฉด 1:N์ ๊ด๊ณ์ด๊ณ ํ ๊ฒ์๋ฌผ์ ์ฌ๋ฌ ๊ฐ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๊ฐ์ง๋ค๋ฉด ํ ์นดํ ๊ณ ๋ฆฌ๋ ์ฌ๋ฌ ๊ฐ์ ๊ฒ์๋ฌผ์ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์ N:M์ ๊ด๊ณ๋ฅผ ๊ฐ์ง๋๋ค.
์ํฉ์ ๋ฐ๋ผ ๊ด๊ณ๋ฅผ ๋ค๋ฅด๊ฒ ์ค์ ํ ์ ์์ต๋๋ค. ์ถ๊ฐ๋ก ์ ์ ์ ์ ์ ์ ํ๋กํ ๋ชจ๋ธ์ธ 1:1 ๋์ ๊ด๊ณ ํ ์ด๋ธ๋ ์กด์ฌํฉ๋๋ค.
๐ ๋ชจ๋ธ์์ 1:N ๊ตฌํํ๊ธฐ
๋ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด Memo๋ผ๋ ๊ฐ์ฒด์ Category๋ผ๋ ๊ฐ์ฒด๊ฐ 1:N ๊ด๊ณ๋ฅผ ๋งบ๊ฒ๋ ํ๋ ค๊ณ ํฉ๋๋ค.
์ฆ, ํ๋์ ์นดํ ๊ณ ๋ฆฌ๋ ์ฌ๋ฌ๊ฐ์ ๋ฉ๋ชจ๋ฅผ ๊ฐ์ง ์ ์๋ค๋ ๊ฒ์ ๋๋ค.
ํด๋น ๊ฐ์ฒด์ ์ธ๋ํค๋ฅผ ๊ฐ๋ ์์ฑ์ ํด๋น ์ต์ ๋ค์ ์ค์ ํด์ผ ํฉ๋๋ค.
์ค๋ช | ||
์ฐธ์กฐํ ํ ์ด๋ธ | ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ก ์ฐ๊ฒฐํ ๋ชจ๋ธ ํด๋์ค๋ฅผ ๊ฐ๋ฆฌํค๋ฉฐ ํด๋น ์ธ๋ํค์์ ์ด๋ค ํ ์ด๋ธ์ ์ฐธ์กฐํ ์ง๋ฅผ ์๋ฏธํ๋ค. | |
๊ด๊ณ์ ์ด๋ฆ(`related_name`) | ๊ฐ์ฒด ๊ด๊ณ์ ์ฌ์ฉํ ์ด๋ฆ์ ๋ํ๋ด๋๋ฐ ์ญ๋ฐฉํฅ์์ ์ฐธ์กฐํ ๋ ์ฌ์ฉํ๋ ์ด๋ฆ์ด๋ค. | |
๊ฐ์ฒด ์์ ์ญ์ ์ ์ํ ๋์(`on_delete / on_update`) | ์นดํ ๊ณ ๋ฆฌ ๋ชจ๋ธ์ ์ญ์ ๋ ๋ Memo ๋ชจ๋ธ์ด ๋ฏธ์น๋ ์ํฅ์ ๋งํ๋ค. | |
DB ํ๋ ์ด๋ฆ(`db_colum`) | ํ ์ด๋ธ์ ์ ์๋๋ ์ด๋ฆ์ ๋งํ๋ฉฐ Memo ํ ์ด๋ธ์ category ํ๋๊ฐ "categoty"๋ผ๋ ์ด๋ฆ์ผ๋ก ์ง์ ๋๋ค. |
๊ด๊ณ์ ์ด๋ฆ?
์ฌ๊ธฐ์, ๊ด๊ณ์ ์ด๋ฆ?์ด๋ผ๋ ๋ง์ ์ดํดํ๊ธฐ ์ด๋ ค์ฐ์ค ๊ฒ์ ๋๋ค.
๋ง์ฝ Memo๋ผ๋ ๋ชจ๋ธ์ด Category ๋ชจ๋ธ์ ์ฐธ์กฐํ๊ฒ๋๋ค๋ฉด ์ด๋ฅผ ์ ์ฐธ์กฐ๋ผ๊ณ ํ๊ณ ๋ฐ๋๋ก, Category ๋ชจ๋ธ์ด Memo ๋ชจ๋ธ์ ์ฐธ์กฐํ๋ ๊ฒ์ ์ญ์ฐธ์กฐ๋ผ๊ณ ํฉ๋๋ค.
์ ์ฐธ์กฐ
์ ์ฐธ์กฐ๋ ํน๋ณํ๊ฒ ๋ญ ์์ด ๋ค์ ์ฟผ๋ฆฌ์ API๋ก ํ๋๋ฅผ ์ ๊ทผํ ์ ์์ต๋๋ค.
memo = Memo.objects.get(pk=10)
memo.category.name # ์ผ์
์ญ์ฐธ์กฐ
๋ฐ๋๋ก ์ญ์ฐธ์กฐ๋ `ํ ์ด๋ธ๋ช _set`์ ํตํด ๋ฐ์ดํฐ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ฑฐ๋ ์๊น ๋์๋ `related_name` ์ต์ ์ ํตํด ๋ค์๊ณผ ๊ฐ์ด ์ฐธ์กฐํ ์ ์๋ค.
cate = Category.objects.get(pk=1)
cate.memos.all() # <QuerySet [<Memo: Memo object (9)>, <Memo: Memo object (10)>]>
cate.memos.get(pk=9) # <Memo: Memo object (9)>
์ค์ต
์ด๋ฒ์๋ ์ค์ ์ผ๋ก ์นดํ ๊ณ ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์์ ๋ฐ ์ญ์ ๋ฅผ ํด๋ณด๋ ์ค์ต์ ์งํํด๋ณด๊ฒ ์ต๋๋ค.
์ค์ํ๊ฑด "์ด๋"์ด๋ผ๋ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๋ง๋ค์์ ๋ ํด๋น ์นดํ ๊ณ ๋ฆฌ๋ฅผ `memo1`์ด๋ผ๋ ๋ฉ๋ชจ ๊ฐ์ฒด์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๋ฃ์ด์ค๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฅผ ์์ฒญํ๋ฉด `memo1`์ด๋ผ๋ ๊ฐ์ฒด์ ์ด๋์ด๋ผ๋ ์นดํ ๊ณ ๋ฆฌ๊ฐ ๋ฑ๋ก๋ฉ๋๋ค.
ํ์ง๋ง, ์ฌ๊ธฐ์ ์ค์ํ ๊ฒ ์์ด์!
๋ฉ๋ชจ๋ฅผ ์์ฑํ ๋ "์ด๋"์ด๋ผ๋ ์นดํ ๊ณ ๋ฆฌ๋ ์๋ก ์์ฑํ๋ ค ํ๋๋ฐ ์ด๋ฏธ "์ด๋"์ด๋ผ๋ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์กด์ฌํ์ ๋์ ๊ฒฝ์ฐ์ ๋๋ค.
๋ง์ฝ ์ด๋์ด๋ผ๋ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ๋ถ๊ตฌํ๊ณ ์๋ก ๋ง๋ค๊ฒ ๋๋ฉด Uniqueํ ๊ฐ๊ณผ ์ ํจํ์ง ์๊ธฐ ๋๋ฌธ์ ํํฐ๋ง์ ํ ํ์๊ฐ ์์ต๋๋ค.
์ฌ๊ธฐ์, `get()` ๋์ `first()`๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ์ด์ ๋ ๋ง์ฝ์ ์กด์ฌํ๋ ์นดํ ๊ณ ๋ฆฌ๊ฐ ์๋ ๊ฒฝ์ฐ `first`์ผ ๊ฒฝ์ฐ None์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๐ M:N ๊ด๊ณ
์ด๋ฒ์๋ ํ๋์ ํ ์ด๋ธ์ ํ๋ ์ด์ ๋ ์ฝ๋๊ฐ ๋ค๋ฅธ ํ ์ด๋ธ์ ํ๋ ์ด์์ ๋ ์ฝ๋์ ์ฐธ์กฐํ๋ ๊ด๊ณ(ManyToMany)์ ๋๋ค. ๋ํ์ ์ผ๋ก, ์ข์์ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ ์ ๊ฐ ์ฌ๋ฌ ๊ฒ์๋ฌผ์ ๋๋ฅผ ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ์ฃ .
์ธ๋ํค๋ก M:N๋ฅผ ๊ตฌํํ๋ค๊ณ ?
๋ง์ฝ ํ์์ด ์์ด ์์ ๋ ๋ฃ๊ณ ์ปดํจํฐ ์์ ์ ๋ฃ๋๋ค๊ณ ๊ฐ์ ํ ๋ ์ธ๋ํค ์ฐธ์กฐ ๊ด๊ณ๋ฅผ ๋ง๋ค๋ฉด ์ด๋ค ์ผ์ด ๋ฒ์ด์ง๊น์?
lecture1 = Lecture.objects.create(name='english')
lecture2 = Lecture.objects.create(name='computer')
student1 = Stuendt.objects.create(name='dustin', lecture=lecture1)
student1 = Stuendt.objects.create(name='dustin', lecture=lecture2) #?
1NF ์๋ฐ์ด ๋์ด ๋ฒ๋ฆฝ๋๋ค. ํ๋์ ์์ฑ์ ์ฌ๋ฌ๊ฐ ๊ฐ์ ๋ฃ๋ ๊ฒ์ ์ ์ด์ ๋ถ๊ฐ๋ฅํ ์ผ์ ๋๋ค.
M:N ๊ด๊ณ๋ฅผ ๊ตฌํํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ด ์กด์ฌํฉ๋๋ค.
- ์ค๊ฐ ํ ์ด๋ธ
- ManyToManyField
์ค๊ฐ ํ ์ด๋ธ
ํ์๊ณผ ์์ ์ฌ์ด์ ์ค๊ฐํ ์ด๋ธ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด `Student`๋ `Lecture` ํ ์ด๋ธ์์ ์ญ์ฐธ์กฐ๋ฅผ ํตํด ๊ฐ์ฒด ๊ด๊ณ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
# models.py
class StuLec(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
lecture = models.ForeignKey(Lecture, on_delete=models.CASCADE)
# views.py
StuLec.objects.create(student=student1, lecture=lecture1)
StuLec.objects.create(student=student1, lecture=lecture2)
student1.StuLec_set.all()
lecture1.StuLec_set.all()
ManyToMany
์ด๋ฒ์๋ ์ค๊ฐํ ์ด๋ธ์ ๋ง๋ค์ง ์์๋ ์๋์ผ๋ก ํ ์ด๋ธ์ ์์ฑํด์ฃผ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
๊ตณ์ด ์ค๊ฐํ ์ด๋ธ์ ๋ง๋ค์ง ์์๋ ๋ฐ๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ์ฃ .
class Student(models.Model):
lecture = models.ManyToManyField(Lecture, related_name="students")
name = models.TextField(null=True)
์ค๋ช | ||
์ฐธ์กฐํ ํ ์ด๋ธ | ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ก ์ด๋ค ํ ์ด๋ธ์ ์ฐธ์กฐํ ์ง๋ฅผ ์๋ฏธํ๋ค. | |
๊ด๊ณ์ ์ด๋ฆ(`related_name`) | ๊ฐ์ฒด ๊ด๊ณ์ ์ฌ์ฉํ ์ด๋ฆ์ ๋ํ๋ด๋๋ฐ ์ญ๋ฐฉํฅ์์ ์ฐธ์กฐํ ๋ ์ฌ์ฉํ๋ ์ด๋ฆ์ด๋ค. | |
through | ์ปค์คํ ์ผ๋ก ์ค๊ฐํ ์ด๋ธ์ ์ง์ ํด์ค ์ ์๋ ์ต์ ์ด๋ค. (๊ทธ๋ฅ ์ค๊ฐํ ์ด๋ธ์ ๋ง๋ค ๋ ์ฐ์ ๋๋ค.) | |
through_field | ๊ตฌ์ฒด์ ์ธ ํ๋๋ฅผ ์ค์ ํ ๋ ์ฐ์ธ๋ค. `through_fields = (A.field, B.field) |
์ ์ํ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๊ฒ๋๋ฉด ์๋์ฒ๋ผ `app_student_lecture`๋ผ๋ ์๋ก์ด ํ ์ด๋ธ์ด ์์ฑ๋ฉ๋๋ค.
๊ด๊ณ ํ์ฑํ๊ธฐ
`add()`์ `remove()`๋ฅผ ํตํด ๊ด๊ณ๋ฅผ ํ์ฑํ๊ณ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ด๊ณ๋ฅผ ์ ๊ฑฐํ์ ๋ ํด๋น ๋ฐ์ดํฐ๋ ์ฌ๋ผ์ง์ง์๊ณ ๊ด๊ณ๋ง ์ ๊ฑฐ๋ฉ๋๋ค.
๋ง์ฝ ๋์ค ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
ํด๋น ์๋ฌ๋ M2M ํ ์ด๋ธ์์ ์ค๊ฐํ ์ด๋ธ๋ก ๋ณ๊ฒฝํ์ ๋ ๋ฐ์ํ๋ ์๋ฌ์ ๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์๋ `app.Student.lecture` ์ปฌ๋ผ์ ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์์ ์ง์ฐ๊ณ ๋ค์ ์์ฑํ๋ฉด ํด๊ฒฐ๋ฉ๋๋ค. ๋ฌผ๋ก ๊ธฐ์กด ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ค๋ฉด ์ฐ์ dumpํ๊ณ importํ๋ ๋ฐฉ์์ผ๋ก๋ ํด๊ฒฐํ ์ ์์ ๊ฒ ๊ฐ์ต๋๋ค.
ValueError: Cannot alter field app.Student.lecture into app.Student.lecture - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
๐ 1:1 ๊ด๊ณ(OnetoOneField)
1:1 ๊ด๊ณ(์ผ๋์ผ ๊ด๊ณ)๋ ํ๊ฐ์ ๋ชจ๋ธ์ด ๋ ๋ค๋ฅธ ํ๊ฐ์ ๋ชจ๋ธ๊ณผ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉํฉ๋๋ค.
์๋ฅผ ๋ค์ด, User๋ผ๋ ๋ชจ๋ธ์ด ์์ ๋, User๋ชจ๋ธ์ UserProfile ๋ชจ๋ธ์ ์์ฑํ๋ ๊ฒฝ์ฐ์ ๋๋ค.
1:N๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ญ์ฐธ์กฐ ๊ด๊ณ๋ฅผ ๊ฐ๋ ๊ฒฝ์ฐ์ `related_name`์ ํตํด ์ง์ ํ ์ ์์ต๋๋ค.
class User(models.Model):
username = models.CharField(max_length=100)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) # 1:1
bio = models.TextField()
๐ GenericForeignKey(GFK)
์ธ๋ํค์ด๊ธด ํ๋ฐ Genericํ ์ธ๋ํค? ์ด๊ฒ์ ๋ฌด์์ผ๊น์?
์ด๋ ํ ๋ฐ์ดํฐ ํ์์ ์์กดํ์ง ์๊ณ ์ฌ๋ฌ ํ์ ์ ๊ฐ์ง ์ ์๋ค๋ ์ ์ ๋์ด ์ธ๋ํค๋ฅผ ๋ชจ๋ ๋ชจ๋ธ์ ์ฐ๊ฒฐํด์ค ์ ์๋ค๋ ์๋ฏธ์ ๋๋ค.
์๋ ๊ทธ๋ฆผ์ฒ๋ผ, ์ปค๋ค๋ ๋ฉ๋ชจ๋ณด๋์ ๋ฉ๋ชจ๋ฅผ ๋ถ์ผ ์ ์๊ณ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ผ์๋ ์์ต๋๋ค. ๊ทธ ๋ฉ๋ชจ์ง๋ ์ด๋ฏธ์ง์ ์ฝ๋ฉํธ๋ฅผ ๋ถ์ผ ์ ์๋ค๊ณ ๋ชจ๋ธ์ ์ค๊ณํ๋ค๋ฉด `comment`๋ผ๋ ๋ชจ๋ธ์ `Memo`์ `Image`๋ชจ๋ธ์๋ ๊ฐ๊ฐ ์กด์ฌํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค.
class Memo(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
class Image(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(upload_to='images/')
class comment(models.Model):
text = models.CharField(max_length=100)
ForeginKey๋ฅผ ์์ฑํ์ฌ Memo๋ Image์ ๋ง๋ถ์ผ ์ ์๊ฒ ๋ง๋ ๋ค๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ์ปฌ๋ผ์ ์๋์น ์๊ฒ ์์ฑํด์ผํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด GenericForeignKey๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ContentType
Django์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก GFK๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด Contenttypes๋ฅผ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
์ฒ์ ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๊ฒ๋๋ฉด `django_content_type`์ด๋ผ๋ ํ ์ด๋ธ์ ์์ฑ๋ฉ๋๋ค. ์, ์ด Contenttype์ด๋ผ๋ ๊ฒ์ ์๋ง ํ์ฉํ๋ฉด ์ผ์ผํ ๋ชจ๋ธ๊ณผ FK๋ฅผ ๋งบ์ ํ์ ์์ด ContentType๊ณผ FK๋ฅผ ๋งบ์ผ๋ฉด ๊ด๊ณ๋ฅผ ์ฝ๊ฒ ๋งบ์ ์ ์์ต๋๋ค.
์ค์ต
์์ ๋ชจ๋ธ๋ค์ GFK์ ContentType ๋ชจ๋ธ์ ์ด์ฉํด ์ฌ์ ์ ํด๋ณด์์ต๋๋ค. ์์ง๊น์ง ์ดํดํ๊ธฐ ์ด๋ ค์ฐ์ค ๊ฒ์ ๋๋ค.
์ฐ์ , ๊ธฐ์กด์ ๋ชจ๋ธ์ `GenericRelation`์ด๋ผ๋ ๊ฒ์ด ์๊ฒจ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ด๋ ์ฝ๋ฉํธ ๊ฐ์ฒด๋ฅผ ์ญ์ฐธ์กฐํ ๋ ์ฌ์ฉํฉ๋๋ค. ๋ฐ๋์ ํ์ํ ๊ฒ์ ์๋๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ง์ ์ฝ๋ฉํธ ๊ฐ์ฒด์ ๊ธฐ์กด `text` ํ๋๋ฅผ ์ ์ธํ ์ธ๊ฐ์ง์ ํน๋ณํ ํ๋๊ฐ ์๊ฒจ๋ฌ์ต๋๋ค.
- content_type : ContentType ๋ชจ๋ธ๊ณผ FK๋ก ์ฐ๊ฒฐ๋๋ ํ๋์ด๋ค.
- object_id : ๊ด๋ จ๋ ๋ชจ๋ธ์ PK์ ์ ์ฅํ ์ ์๋ ํ๋ ์ด๋ค.
- content_object : ์ ๋ ํ๋์ ์ด๋ฆ์ ์ธ์๋ก ๋๊ฒจ์ค๋ค. ๊ธฐ๋ณธ ๊ฐ์ด๋ฏ๋ก ์๋ต์ด ๊ฐ๋ฅํ๋ค.
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
class Memo(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
comment = GenericRelation('comment', related_query_name='memo') # ์ญ์ฐธ์กฐํ ๋ ์ฌ์ฉ
class Image(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(upload_to='images/')
comment = GenericRelation('comment', related_query_name='image') # ์ญ์ฐธ์กฐํ ๋ ์ฌ์ฉ
class comment(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
text = models.CharField(max_length=100)
๐ก content_object๋ DB์ ๋ฐ์๋์ง ์๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก GenericForeignKey๋ DB Table์ ์ปฌ๋ผ์ผ๋ก ์กด์ฌํ์ง ์๋๋ค.
์ค์ ๋ก ๋ชจ๋ธ์ ์ฌ์ฉํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ์ ์์ต๋๋ค.
comment = Comment.objects.filter(memo__title='1์์ ํ์ํฉ๋๋ค:)')
comment = Comment.objects.create(content_object=memo, text="3์๋ก ๋ณ๊ฒฝ")
๊ทธ๋ฌ๋ฉด..
์ฐ์ , GFK๋ฅผ ์ดํดํ๊ณ ๋์ ๋ค์๋ ์๊ฐ์ ContentType์ ๊ฑฐ์ณ ํน์ ๋ชจ๋ธ์ ์ฐพ์๊ฐ์ผํ๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ FK ๋ณด๋ค๋ ์ฑ๋ฅ์ด ์ข์ง ์์ ๊ฒ์ด๋ผ ์๊ฐํ๋ค. ๊ฐ์ฒด๋ฅผ ํ๋ฒ์ ๊ฐ์ ธ์ค์ง ๋ชปํ๊ณ ์ฌ๋ฌ๋ฒ ๊ฑฐ์ณ ๊ฐ์ ธ์์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ฉด `prefetch_related`๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ง ์์๊น? ์์ ์ค๋ช ํ๋ฏ์ด GFK๋ ์๋ ํ๋์ด๊ธฐ ๋๋ฌธ์ ์คํจํ ๊ฒ์ด๋ค. ์ฆ, ๋ฌด๋ถ๋ณํ๊ฒ ์ฌ์ฉํ๋ฉด ์๋ ๊ฒ ๊ฐ๋ค. ์ผ๋จ ์ธ์งํ๊ธฐ๋ง ํ์..
โ๏ธ ํฌ์คํ ์ด ๋์์ด ๋์๋ ์๋ฃ
- GenericForeginKey์ ๋ฌธ์ ์ - Leffe_pt๋์ ๋ธ๋ก๊ทธ
- Avoid Django's GenericForeignKey - Luke Plant
- ForeignKey - Django Docs
- Generic Relations - Django Docs
์ค๋๋ ์ ์ ํฌ์คํธ๋ฅผ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
์ค๋ช ์ด ๋ถ์กฑํ๊ฑฐ๋ ์ดํดํ๊ธฐ ์ด๋ ต๊ฑฐ๋ ์๋ชป๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ๋ถ๋ด์์ด ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
'Web > Django' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Django] DB ์ฟผ๋ฆฌ ์ต์ ํ๋ฅผ ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ์ ๋ฆฌํด๋ณด์๋ค. (0) | 2023.12.20 |
---|---|
[Django] ์ฟผ๋ฆฌ์ ์ผ๋ก ๋ฐ์ดํฐ ์กฐํํ๊ธฐ (0) | 2023.12.20 |
[Django] CRUD ๊ตฌํ (0) | 2023.11.29 |
User Model ์ปค์คํ ํ๊ธฐ (0) | 2023.11.28 |
Static ํ์ผ ๊ด๋ฆฌํ๊ธฐ with AWS S3 ์ฐ๋ํ๊ธฐ (0) | 2023.11.27 |