7.관계를 표현하는 모델 필드

2 분 소요

관계를 표현하는 모델 필드

RDBMS에서의 관계 예시

  1. 1:N 관계(ForeignKey) -> models.ForeignKey.Key로 표현
    • User : Post, User : comment, Post : comment
  2. 1:1 관계(OneToOneField) -> models.OneToOneField로 표현
    • User : Profile
  3. 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에 따라 오류가 발생할 수도 있다.)

올바른 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 명령이 실패
  • 이름 충돌 피하는 방법
    1. 어느 한 쪽의 FK에 대해, reverse_name 포기 -> reated_name=’+’ (custom name)
    2. 어느 한 쪽의 (혹은 모두) 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)

댓글남기기