관계를 표현하는 모델 필드

guava·2021년 11월 14일
0
post-custom-banner

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.

외래키 (ForeignKey)

django.db.models.ForeignKey

  • 1:N 관계에서 N측에 명시한다. (Post:Comment, User:Post, User:Comment)
  • ForeignKey의 to 인자와 on_delete 인자
    • to: 대상 모델을 지정 (클래스명 또는 클래스 명을 문자열로 지정한다.
    • on_delete: Record 삭제 시 룰이다.
      • CASCADE : FK로 참조하는 다른 모델의 Record도 삭제한다.
      • PROTECT: ProtectedError(IntegrityError 상속)를 발생시킨다. 삭제 방지 설정
      • SET_NULL: null로 대체된다. null=True 옵션 필수
      • SET_DEFAULT: 디폴트 값으로 대체된다. 필드에 default값 지정 필수
      • SET: 대체할 값이나 함수 지정. 함수의 경우 호출하여 리턴 값을 사용
      • DO_NOTHING: 어떠한 액션 X. DB에 따라 오류가 발생할 수도 있다.

외래키에서의 reverse_name

  • reverse 접근 시의 속성명의 디폴트값은 "모델명소문자_set"이다.

모델 정의

# 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()

관계 모델로 접근

# 정방향 접근 (N -> 1)
$ Comment.objects.first()
$ comment.post # = Post.objects.get(pk=comment.post_id)

# 역방향 접근 (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의 디폴트는 모델명소문자_set이다.

reverse_name 충돌 시

  • reverse_name은 앱이름이 아닌 모델명만 고려되므로 충돌 우려가 있다. (다른 앱에 같은 모델명이 존재할 경우)
  • 충돌이 나면 makemigrations 명령이 실패한다.
  • ForeignKey 필드에 related_name 속성에서 reverse_name을 지정해줄 수 있다. ( ex. "blog_post_set")
  • reverse_name을 포기할수 있다. ForeignKey 필드에 reverse_name로 "+"를 넘긴다.

관계 항목의 노출 제한

  • limit_chices_to에 해당 필드에 제한 조건을 부여한다.
  • admin등의 폼에서도 선택 항목이 제한된다.
class Comment(models.Model):
    post = models.ForeignKey(to='instagram.Post', on_delete=models.CASCADE,
                             limit_choices_to={'is_publish': True})

OneToOneField

django.db.models.OneToOneField

  • 1:1관계에서 어디에나 지정 가능
  • ForeignKey(unique=True)와 비슷하며, reverse 접근할 때 차이가 있다.
    • User:Profile, FK일 때 → profile.user_set.first() → user
    • User:Profile, OneToOne일 때 → profile.user → user
  • to, on_delete 인자가 있다. (ForeignKey와 동일)

ManyToManyField

django.db.models.ManyToManyField

  • M:N 관계에서 어느 쪽이라도 필드 지정이 가능하다
    ManyToManyField(to, blank=Falase)
  • M:N중 활용하는 쪽에서 정의하는게 의미에 맞아보인다.

모델에 정의하기

class Post(models.Model):
    # ...
    tag_set = models.ManyToManyField('instagram.Tag', blank=True) # 활용하는 쪽(Post에서 Tag를 활용)에서 지정할 때
    # ...

 
class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    # post_set = models.ManyToManyField('Post', blank=True)  # 활용되는 쪽에서 지정할 때
    def __str__(self):
        return self.name

ManyToManyField활용하기

# 포스트에 하나의 태그를 추가하기
tag = Tag.objects.get(name='장고')
post.tag_set.add(tag)

# 포스트에 여러개의 태그를 추가하기
tag_qs = Tag.objects.all()
post.tag_set.add(*tag_qs)
post.tag_set.all()
post-custom-banner

0개의 댓글