TIL - 0209(수정중)

[summary]

  • 모델 관련 자료 진도 다감
  • Melon artist form 이용해서 데이터 받는 것 연습
  • 다음 시간에는 sql과 쿼리셋 진도 나감

질문

pby = Person.objects.create('박보영')
이 경우 오류로 `TypeError: create() takes 1 positional argument but 2 were given`
이런 메시지가 뜨더라구요. 저는 하나의 인자를 넘겨줬는데 왜 2개를 받았다고 하는건가요?

항상 첫번째 위치인자로 self가 넘어가기 때문 create 메서드는 create(** kwargs) 이런식으로 키워드 인자들만 받도록 되어있는데 이 앞에 create(self, ** kwargs) 이런 식으로 self 위치인자가 기본적으로 넘어가서 실제로 create 메서드에 self 라는 인자를 아무값이나 넘겨보면 여러 개의 값이 self 로 넘어왔다고 에러


django_extensions

<!-- 우분투의 경우, graphviz 외에 두개 더 설치  -->
sudo apt-get install graphviz libgraphviz-dev pkg-config
pip install pygraphviz
pip install pydot

공식홈페이지-Graph models

<!-- 선택한 테이블 만 출력 -->
./manage.py graph_models multi_table -o erd.png
<!-- 전체 그룹 테이블 출력 -->
./manage.py graph_models -a -g -o erd.png
<!-- 특정 그룹 테이블 출력 -->
./manage.py graph_models -g multi_table abstract_base_classes -o erd.png

해당 파일에 가서 확인 하면, ER다이어그램을 볼 수 있다.

Multi-table inheritance

  • abstract를 하는 경우, 부모 클래스가 테이블이 만들어지지 않는다. 그러나 이 모델의 경우 부모 클래스도 테이블이 생긴다.
  • 다중 테이브르 상속은 one_to_one 필드를 사용하여, 하위와 상위를 연결하기 때문에, 상위에서 하위로 이동이 가능 (Place -> Restaurant)
<!-- ERD 이미지로 바꾸는 것 -->
(fc-django-document)  django git:(master*)./manage.py graph_models multi_table -o erd.png

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):
        return f'Place {self.name} | {self.address}'

class Restaurant(Place):
    # one_to_one field가 되기 때문에, place에 id를 참조
    # 무언가를 꺼낼때, 속도가 느리다.
    # 최대 2단계만 속도가 느려지기 때문에
    # 장점 : Place.name 으로 접근 할 수 있음
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)


    # related_query_name이 같은 경우가 생기기 때문에, 항상 이렇게 설정
    nearby_places = models.ManyToManyField(
        Place,
        related_query_name = 'near_restaurant'
    )

    def __str__(self):
        return f'Restaurant {self.name} | {self.address}'
  • 역참조의 경우, 소문자class명_set이렇게 사용했는데, one_to_one 필드의 경우, 소문자class명_속성 이런식으로 접근이 가능
# related_name과 related_query_name이 같다
# related_name
p2.restaurant
# related_query_name
Place.objects.filter(restaurant__address = '신사역')

Proxy models

참고사이트 참고사이트

  • multi-table 상속을 하는 경우, 각각 자식 클래스들 마다 테이블이 생성된다. 그러나 부모 모델의 메소드만 재정의하던가 새로 추가하고 싶을 때 Proxy를 사용한다. 즉, 테이블을 가지는 모델을 상송박되, 자식모델은 테이블을 만들 필요가 없는 경우 사용
  • 추상 클래스에 메소드만 있을 경우, 이는 사용할 수 있다.

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=50)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_block = models.BooleanField(default=False)

    def __str__(self):
        return self.name


class Admin(User):
    class Meta:
        proxy = True

    def __str__(self):
        return f'{self.name} (관리자)'

    def drop(self, user):
        user.delete()


class Staff(User):
    class Meta:
        proxy = True

    def __str__(self):
        return f'{self.name} (스태프)'

    def block(self, user):
        user.is_block = True
        user.save()

  • Staff와 Admin 테이블이 없고, User테이블과 proxy inheritance로 연결된 것을 볼 수 있다.
from django.db import models
from django.db.models.manager import Manager

class User(models.Model):
    name = models.CharField(max_length=50)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_block = models.BooleanField(default=False)

    def __str__(self):
        return self.name

# 커스텀 매니저
class AdminManager(Manager):
    # 기본적으로 가져오는 queryset을 변경할 때 사용
    # super().get_queryset() = 클래스의 전체 데이터 가져
    def get_queryset(self):
        return super().get_queryset().filter(is_admin=True)

class Admin(User):
    # Admin.objects.all()하면 기본 쿼리셋으로 지정한 데이터셋 출
    objects = AdminManager()

    class Meta:
        proxy = True

    def __str__(self):
        return f'{self.name} (관리자)'

    def drop(self, user):
        user.delete()

class StaffManager(Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_staff=True)

class Staff(User):
    objects = StaffManager()

    class Meta:
        proxy = True

    def __str__(self):
        return f'{self.name} (스태프)'

    def block(self, user):
        user.is_block = True
        user.save()
  • 프록시 모델에 모델 관리자를 지정하지 않으면, 부모클래스로부터 관리자를 상속 받는다.
  • 프록시 모델에서 정의를 하면, 부모클래스의 관리자 역시 사용할 수 있음
  • objects말고 다른이름으로 관리자를 설정하면 objects는 자동으로 생기지 않는다.

# `objects` 매니저도 사용하고, 커스텀 매니저도 사용하고 싶을 때
class ExtraManagers(model.Model):
  secondary = NewManger()

  class Meta:
    abstract = True

class MyPerson(Person, ExtraManagers):
  class Meta:
    proxy = True

Mix-in

클래스에 추가적이 속성이나 메소드를 제공하는 것을 말함 자체적으로는 실행하지 않는다. Django는 특정 속성이 있을 수도 있다. 필드를 추가 할 수도 있어서.. ps_ptr 을 피하기 위해서.

hiding

추상 클래스의 경우, 테이블이 없기 때문에 상속을 받아서 사용했을 때 속성의 이름을 바꿀 수 있다.

기타

  • 캡슐화 추상 기본 클래스에서는 objects를 사용해서 접근 할 이유가 없다.
<!-- 특정 파일 제외 시킬 때 -->
git reset HEAD inheritance/proxy