고급 Django 모델:Python 개발 개선

모델은 Django 프레임워크의 핵심 개념이다.Djangodesign philosophies for models에 따르면 우리는 가능한 한 필드의 명칭과 기능을 명확히 하고 모델 자체에 보기나 다른 곳이 아닌 모델과 관련된 모든 기능을 포함해야 한다.만약 이전에 Ruby on Rails를 사용한 적이 있다면, 이러한 디자인 이념은 신선하지 않은 것 같습니다. 왜냐하면 Rails와 Django는 모두 대상 관계 매핑 (ORM) 시스템을 위해 Active Record pattern 데이터 저장을 처리했기 때문입니다.
이 글에서 우리는 이러한 이념, Django의 핵심 특성, 심지어 일부 라이브러리를 이용하여 우리가 모델을 개선하는 방법을 연구할 것이다.

getter/setter/deleter 속성


파이썬의 2.2 버전 이래의 기능으로서 속성의 사용법은 하나의 속성처럼 보이지만 실제로는 하나의 방법이다.비록 모델에서 속성을 사용하는 것이 그렇게 선진적이지는 않지만, 우리는 파이톤 속성의 일부 충분히 이용되지 않은 특성을 사용하여 우리의 모델을 더욱 강하게 할 수 있다.
Django의 내장 인증을 사용하거나 AbstractBaseUser 사용자 정의 인증을 사용하면 last_login 모델에 정의된 User 필드를 잘 알 수 있습니다. 이것은 사용자가 지난번에 프로그램에 로그인했을 때 저장한 시간 스탬프입니다.last_login를 사용하고 싶지만 last_seen라는 필드를 캐시에 더 자주 저장하면 쉽게 할 수 있다.
우선, 우리는 Python 속성을 만들어서 캐시에서 값을 찾을 것입니다. 만약 찾지 못하면 데이터베이스에서 값을 되돌려줍니다.
accounts/models.py
from django.contrib.auth.base_user import AbstractBaseUser
from django.core.cache import cache


class User(AbstractBaseUser):
    ...

    @property
    def last_seen(self):
        """
        Returns the 'last_seen' value from the cache for a User.
        """
        last_seen = cache.get('last_seen_{0}'.format(self.pk))

        # Check cache result, otherwise return the database value
        if last_seen:
            return last_seen

        return self.last_login
주의: 나는 이미 모델을 간소화했다. 왜냐하면 이 블로그에 단독 강좌 전문 소개customizing the built-in Django user model가 있기 때문이다.
위의 속성은 캐시에 있는 사용자의 last_seen 값을 검사합니다. 내용을 찾지 못하면 모델에서 사용자가 저장한 last_login 값을 되돌려줍니다.참고<instance>.last_seen는 현재 매우 간단한 인터페이스 뒤에 우리 모델에 더욱 맞춤형 속성을 제공한다.
값을 속성some_user.last_seen = some_date_time에 할당하거나 속성del some_user.last_seen에서 값을 삭제할 때의 사용자 정의 행동으로 확장할 수 있습니다.
...

@last_seen.setter
def last_seen(self, value):
    """
    Sets the 'last_seen_[uuid]' value in the cache for a User.
    """
    now = value

    # Save in the cache
    cache.set('last_seen_{0}'.format(self.pk), now)

@last_seen.deleter
def last_seen(self):
    """
    Removes the 'last_seen' value from the cache.
    """
    # Delete the cache key
    cache.delete('last_seen_{0}'.format(self.pk))

...
현재, 값이 last_seen 속성에 분배될 때마다, 우리는 그것을 캐시에 저장하고, 값이 del 삭제될 때 캐시에서 삭제합니다.파이썬 문서에는 사용setterdeleter가 기술되어 있지만 Django 모델을 연구할 때 야외에서 보기 드물다.
전통적인 데이터베이스에 오래 지속될 필요가 없는 것들을 저장하거나 성능 때문에 전통적인 데이터베이스에 오래 지속되어서는 안 된다는 용례가 있을 수 있습니다.상술한 예시 중의 사용자 정의 속성을 사용하는 것은 좋은 해결 방안이다.
유사한 용례에서 python-social-auth 라이브러리는 제3자 플랫폼(예를 들어 GitHub과 트위터)을 이용하여 사용자의 신분 검증을 관리하는 도구로 사용자가 로그인한 플랫폼의 정보에 따라 데이터베이스에 업데이트된 정보를 만들고 관리한다.어떤 경우, 되돌아오는 정보가 데이터베이스에 있는 필드와 일치하지 않습니다.예를 들어 python-social-auth 라이브러리는 사용자를 만들 때 fullname 키워드 파라미터를 전달한다.만약에 저희 데이터베이스에서 full_name를 속성명으로 사용한다면 저희는 곤경에 빠질 수 있습니다.
이 문제를 해결하는 간단한 방법 중 하나는 위의 getter/setter 모드를 사용하는 것입니다.
@property
def fullname(self) -> str:
    return self.full_name

@fullname.setter
def fullname(self, value: str):
    self.full_name = value
현재 python-social-auth 사용자의 fullname 를 모델 new_user.fullname = 'Some User' 에 저장할 때, 우리는 그것을 캡처하여 데이터베이스 필드 full_name 에 저장할 것입니다.

모델 관계식 통과하기


Djangomany-to-many relationships는 복잡한 대상 관계를 간단하게 처리하는 좋은 방법이지만, 이들이 만든 intermediate models에 사용자 정의 속성을 추가할 수 있는 능력을 제공할 수 없습니다.기본적으로, 이것은 대상을 연결하는 데 사용되는 식별자와 두 개의 외부 키 인용만 포함한다.
Django ManyToManyField through 매개변수를 사용하여 이 중간 모델을 직접 생성하고 필요한 다른 필드를 추가할 수 있습니다.
예를 들어 만약에 우리 프로그램이 사용자가 그룹에서 구성원 신분을 가지고 있어야 할 뿐만 아니라 구성원 신분이 언제 시작되는지 추적해야 한다면 우리는 맞춤형 중간 모델을 사용하여 이 점을 실현할 수 있다.
accounts/models.py
import uuid

from django.contrib.auth.base_user import AbstractBaseUser
from django.db import models
from django.utils.timezone import now


class User(AbstractBaseUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    

class Group(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    members = models.ManyToManyField(User, through='Membership')

class Membership(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    joined = models.DateTimeField(editable=False, default=now)
위의 예에서 우리는 여전히 ManyToManyField를 사용하여 사용자와 그룹 간의 관계를 처리하지만 Membership 키워드 매개 변수 전달through 모델을 사용하면 joined 사용자 정의 속성을 모델에 추가하여 구성원의 시작 시간을 추적할 수 있다.이 through 모델은 표준 Django 모델로 하나의 메인 키(우리는 여기서 UUID를 사용)와 두 개의 외부 키만 있으면 대상을 연결할 수 있다.
같은 3가지 모델을 사용하여 웹 사이트에 간단한 구독 데이터베이스를 만들 수 있습니다.
import uuid

from django.contrib.auth.base_user import AbstractBaseUser
from django.db import models
from django.utils.timezone import now


class User(AbstractBaseUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    ...

class Plan(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=50, unique=True, default='free')
    subscribers = models.ManyToManyField(User, through='Subscription', related_name='subscriptions', related_query_name='subscriptions')

class Subscription(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    plan = models.ForeignKey(Plan, on_delete=models.CASCADE)
    created = models.DateTimeField(editable=False, default=now)
    updated = models.DateTimeField(auto_now=True)
    cancelled = models.DateTimeField(blank=True, null=True)
여기서, 우리는 사용자가 처음으로 구독하는 시간, 그들이 구독을 업데이트하는 시간, 그리고 우리가 코드 경로를 추가하면, 사용자가 언제 우리 프로그램에 대한 구독을 취소했는지 추적할 수 있다.through모델을 ManyToManyField와 함께 사용하는 것은 우리의 중간 모델에 더 많은 데이터를 추가하고 사용자에게 더 전면적인 체험을 제공하는 좋은 방법으로 대량의 추가 작업을 할 필요가 없다.

에이전트 모델


일반적으로 Django에서 모델의 하위 클래스화(추상 모델 포함)를 새 클래스에 포함할 때 프레임워크는 이 클래스에 새로운 데이터베이스 테이블을 만들고 이를 OneToOneField해서 부모 데이터베이스 테이블에 연결합니다.Django는 "multi-table inheritance"이라고 하는데 이것은 기존 모델의 필드와 구조를 다시 사용하고 그 안에 자신의 데이터를 추가하는 좋은 방법입니다.Django 디자인 철학이 말한 것처럼 "너 자신을 되풀이하지 마라."
다중 테이블 상속 예:
from django.db import models

class Vehicle(models.Model):
    model = models.CharField(max_length=50)
    manufacturer = models.CharField(max_length=80)
    year = models.IntegerField(max_length=4)

class Airplane(Vehicle):
    is_cargo = models.BooleanField(default=False)
    is_passenger = models.BooleanField(default=True)
이 예는 창설될 것이다...
... continue with Proxy Models
자크 클랜시(Zac Clancy)는 전 세계 오물(재난 즉시 응답 팀)의 부회장이다.

좋은 웹페이지 즐겨찾기