티스토리 뷰
관계 설정
관계를 설정하는 객체 관계 종류는 다음 3가지가 있다.
일대다(1:M) - 고객 하나에는 계좌 여러개
필연적으로 계좌라는 테이블의 외부키를 만든다.
ForeignKey로 만든다.
class ForeignKey(to, on_delete, ""options)
to: 일대다(1:M) 관계에서 1에 해당하는 모델명을 지정한다.
on_delete : 필수 옵션이다. 참조하는 인스턴스가 삭제되었을 때 처리 방식을 지정한다.
CASCADE : 참조 인스턴스 삭제시 함께 삭제한다.
class Manufacturer(models.Model):
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
class Artist(models.Model):
name = models.CharField(max_length=10)
class Album(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
일대일 (1:1) - 고객 하나에는 하나의 주민등록번호
class OneToOneField(to, on_delete, **options)
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
다대다(M:M) - 주문 여러개, 상품 여러개
고객과 상품이라는 관계는 다대다 관계가 있다.
고객과 상품 사이에 주문을 만들어서 1대다 관계를 만들면, 관계를 다 풀지 않았으므로 하나 테이블을 넣어서 관계를 풀어줘야 한다.
class ManyToManyField(to,**options)
to에 모델명을 지정해준다.
from django.db import models
class Pizza(models.Model):
name = models.CharField(max_length=50)
class Topping(models.Model):
name = models.CharField(max_length=50)
class PizzaOrder(models.Model):
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
toppings = models.ManyToManyField(Topping)
quantity = models.IntegerField()
모델에서 관계를 설정하는 방법(1:M)
comment는 댓글을 저장할 목적으로 comment라는 모델을 쓴다.
해당 포스트와 연관이 된 글들을 들어가게 해야 하므로, 1:M 관계를 설정하게 만들어준다.
blog/models.py
class Comment(models.Model):
post= models.ForeignKey(Post, on_delete=models.CASCADE)
author= models.CharField(max_length=20)
message= models.TextField()
created= models.DateField(auto_now_add=True)
updated= models.DateTimeField(auto_now=True)
커맨드창에서 coment를 추가
python manage.py makemigrations blog
python manage.py migrate blog
blog/admin.py
from .models import *
admin.site.resister(Post)
admin.site.register(Comment)
이제 runserver를 한다.
python manage.py runserver
이러면 admin 페이지에서 DB에서 추가할 때 해당 관계를 설정 할 수 있다.
blog_comment 테이블을 보게 되면, post_id의 관계가 설정된 것을 볼 수 있다.
1:1 실습
blog/models.py에 다음과 같은 테이블들을 추가해준다.
class User(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=20)
address = models.CharField(max_length=50)
똑같이 migrate를 해준다.
python manage.py makemigrations blog
python manage.py migrate blog
같은 방식으로 admin.py에 또 추가해주면 된다.
admin.site.resister(User)
admin.site.register(Profile)
다시 runserver에서 admin 페이지로 들어가 보면, 아무것도 없다.
이름을 아무거나 홍길동 같은 걸로 만들고, profile에서 해당 유저에 대한 걸 하나 더 추가하려고 하면, 에러가 난다.
실제 db에서는 다음과 같이 들어간다.
실습(N:M)
원래 존재하던 Post 테이블에 tag라는 열을 추가해준다.
class Post(models.Model):
title = models.CharField(max_length=250)
body= models.TextField()
tag = models.ManyToManyField('Tag', null=True, blank=True) #공백도 허용한다.
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
python manage.py makemigrations blog
python manage.py migrate blog
그러면 해당 테이블이 추가된 걸 볼 수 있다.
이름도 관계에 대해서 추가된다.
당연히 이 녀석도 admin에 추가해주어야 관리자 페이지에서 쓸 수 있다.
admin.site.register(Tag)
마찬가지로 해당하는 tag의 목록을 만들고, post에 가보면, 태그를 쓸 수 있다.
그러면, post_id_tag 테이블이, 다대다 관계 설정을 위해 다음과 같이 채워진다.
모델 관계 이름
일대다(1:M)
관계 이름은 자동으로 설정된다.
1측의 인스턴스명.M축의 모델명 소문자_set으로 된다.
dept= DEPT.objects.get(id=1) # 부서 모델에서 인사부 인스턴스 추출
dept.emp_set #인사부 소속의 EMP의 모든 사원, emp는 EMP 모델명의 소문자
dept.emp_set_all() #인사부 소속 모든 사원 인스턴스 추출
모델명이 Post이고, 테이블명이 blog_post
모델명이 Comment이고, 테이블명이 blog_comment
from blog.models import *
post = Post.objects.get(id=5) #먼저 원하는 해당 인스턴스를 추출한다.
post_comments = post.comment_set.all() #comment_set이라는 comment를 전부 뽑는다.
for comm in post_comments :
print(comm.message)
print()
일대일(1:1) 관계 이름
1측 인스턴스명.1측 모델명 소문자
employee = EMP.objects.get(id=1) # 사원번호가11인 사원의 인스턴스를 추출
employee.computer
# 사원번호가11인 사원에 지급한 컴퓨터 접근
from blog.models import *
user = User.objects.get(id=1) #먼저 원하는 해당 인스턴스를 추출한다.
user_profile = user.profile #그 뒤에다
user_profile.address
다대다(N:M) 관계 이름
일단 ManyToManyField 필드가 설정된 모델의 인스턴스를 통해 접근한다.
인스턴스명.필드명
여기서는 Post.tag일 것이다.
from blog.models import *
post_ins = Post.objects.get(id=5)
post_ins
<Post:아이슬란드 오로라 (Iceland post_ins.tag.all()
<QuerySet [<Tag:오로라>, <Tag: 겨울>
혹은, ManyTomanyField 필드가 설정되지 않은 모델의 인스턴스를 통해 접근한다.
인스턴스명.M측 모델명 소문자_set
여기서는 Tag.post_set이 될 것이다.
tag_list = Tag.objects.filter(name__contains='트레킹)
tag_ins = tag_list[0]
tag_ins.post_set.all()
<QuerySet [<Post:히말라야 (Himalayas)>, <Post: 산토리니>
사용자 정의 관계 이름
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE,related_name='comments')
author = models.CharField(max_length=20)
realated_name 자체를 comments로 지정해주었다.
from blog.models import *>>> post_ins = Post.objects.get(id=5)
post_ins.comment_set.all()
Traceback (most recent call last): File "<console>", line 1, in <module>
AttributeError: 'Post' object has no attribute 'comment_set’
post_ins.comments.all()
<QuerySet [<Comment: Comment object (1)>, <Comment: Comment object (2)>]>
1:M 관계임에도 불구하고, 그냥 comments를 써서 데이터를 뽑아낼 수 있다.