7.관계를 표현하는 모델 필드
관계를 표현하는 모델 필드
RDBMS에서의 관계 예시
- 1:N 관계(ForeignKey) -> models.ForeignKey.Key로 표현
- User : Post, User : comment, Post : comment
- 1:1 관계(OneToOneField) -> models.OneToOneField로 표현
- User : Profile
- M:N 관계(ManyToManyField) -> models.ManyToManyField로 표현
- Post : Tag
ForeignKey
1:N 관계에서 N측에 명시한다.
- ForeignKey(to, on_delete) // 필수 지정
- to: 대상 모델
- 클래스 직접 지정 (post = models.ForeignKey(Post, on_delete=~~~))
- 클래스 명을 문자열로 지정 (post = models.ForeignKey(‘instagram.Post’, on_delete=~~~))
- on_delete: Record 삭제 시 Rule
- CASCADE : FK로 참조하는 다른 모델의 Record도 삭제
- PROTECT: ProtectedError(IntegrityError 상속)를 발생시킴 (삭제 방지 설정)
- SET_NULL: null로 대체된다. null=True 옵션 필수
- SET_DEFAULT: 디폴트 값으로 대체 (필드에 default값 지정 필수)
- SET: 대체할 값이나 함수 지정 (함수의 경우 호출하여 리턴 값을 사용)
- DO_NOTHING: 어떠한 액션 X (DB에 따라 오류가 발생할 수도 있다.)
- to: 대상 모델
올바른 User 모델 지정
- django는 User 모델을 제공하나, User 모델이 변경될 수 있기 때문에 확실한 방법이 필요함
django.contrib.auth.models import User
- 앱에 안에 User 모델을 만들시에 settings.py 변수를 만들고 그것을 활용해야함
- settings.py 설정
# project/settings.py
AUTH_USER_MODEL = 'auth.User' # 항상 현재 활성화된 유저를 가르킴
- 앱내 models.py 설정
class Post(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) title = models.CharField(max_length=100) # ...
- db 접근 방법
>>> user.post_set.all()
FK에서의 reverse_name
reverse 접근 시의 속성명 : default -> “모델명소문자_set”
- models.py
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
message = models.TextField()
- 정방향 접근 방법 (comment(N) -> post(1))
Comment.object.all()
# <QuerySet [<Comment: Comment object (1)>, <Comment: Comment object (2)>]>
comment = Comment.objects.first()
comment.post # Post.objects.get(pk=comment.post_id)
# <Post : 네 번째 포스팅>
- 역방향 접근 방법 (1 -> N)
post = Post.objects.first()
# 아래는 역방향 접근 방법으로 전부 같은 동작을 한다.
comment.objects.filter(post_id=4)
comment.objects.filter(post__id=4)
comment.objects.filter(post=post)
post.comment_set.all() # reverse_name=comment_set
reverse_name 이름 충돌 발생시
- reverse_name default 명은 앱이름 고려 X, 모델명만 고려
- instagram.User 모델, author = FK(User)
- blog.User 모델, author = FK(User)
- 이름 충돌이 날 때, makemigrations 명령이 실패
- 이름 충돌 피하는 방법
- 어느 한 쪽의 FK에 대해, reverse_name 포기 -> reated_name=’+’ (custom name)
- 어느 한 쪽의 (혹은 모두) FK의 reverse_name을 변경
- ex) FK(User, …, related_name=”blog_post_set”)
- ex) FK(User, …, related_name=”instagram_post_set”)
ForeignKey.limit_choices_to 옵션
관계항목 노출 제한
class Post(models.Model):
is_public = models.BooleanField(default=False, verbose_name='공개여부')
# ...
class Comment(models.Model):
post = models.ForeignKey(to='instagram.Post', on_delete=models.CASCADE,
limit_choices_to={'is_publish': True})
OneToOneField
1:1관계에서 어느 쪽이라도 가능
- ForeignKey(unique=True)와 유사하지만, reverse 차이
- User:Profile을 FK로 지정한다면 -> profile.user_set.first() -> user
- User:Profile을 O2O로 지정한다면 -> profile.user -> user
- OneToOneField(to, on_delete)
ManyToManyField
M:N 관계에서 어느 쪽이라도 필드 지정 가능
-
ManyToManyField(to, blank=False)
-
방법 1(활용 하는 쪽에서 지정)
class Post(models.Model):
tag_set = models.ManyToManyField('Tag', blank=True)
class Article(models.Model):
tag_set = models.ManyToManyField('Tag', blank=True)
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
- 방법 2(활용 되는 쪽에 지정)
class Post(models.Model):
# ...
class Article(models.Model):
# ...
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
post_set = models.ManyToManyField('Post', blank=True)
article_set = models.ManyToManyField('Article', blank=True)
댓글남기기