티스토리 뷰

반응형

관계 설정

 

관계를 설정하는 객체 관계 종류는 다음 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를 써서 데이터를 뽑아낼 수 있다.

반응형