장고 추상 모델 대 일반 상속


87

구문 외에도 django 추상 모델을 사용하는 것과 django 모델에서 일반 Python 상속을 사용하는 것의 차이점은 무엇입니까? 장점과 단점?

업데이트 : 내 질문이 오해되었다고 생각하고 추상 모델과 django.db.models.Model에서 상속하는 클래스의 차이점에 대한 응답을 받았습니다. 실제로 django 추상 클래스 (Meta : abstract = True)에서 상속하는 모델 클래스와 'object'(model.Model이 아님)에서 상속하는 일반 Python 클래스의 차이점을 알고 싶습니다.

다음은 그 예입니다.

class User(object):
   first_name = models.CharField(..

   def get_username(self):
       return self.username

class User(models.Model):
   first_name = models.CharField(...

   def get_username(self):
       return self.username

   class Meta:
       abstract = True

class Employee(User):
   title = models.CharField(...

1
이것은 두 가지 상속 접근 방식을 사용하는 것 사이의 장단점에 대한 훌륭한 개요입니다. charlesleifer.com/blog/django-patterns-model-inheritance
jpotts18

답변:


155

실제로 django 추상 클래스 (Meta : abstract = True)에서 상속하는 모델 클래스와 'object'(model.Model이 아님)에서 상속하는 일반 Python 클래스의 차이점을 알고 싶습니다.

Django는의 하위 클래스에 대해서만 테이블을 생성 models.Model하므로 전자는 ...

class User(models.Model):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

   class Meta:
       abstract = True

class Employee(User):
   title = models.CharField(max_length=255)

...의 라인을 따라 단일 테이블이 생성됩니다.

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(255) NOT NULL,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

... 후자는 ...

class User(object):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

class Employee(User):
   title = models.CharField(max_length=255)

... 테이블이 생성되지 않습니다.

다중 상속을 사용하여 이와 같은 작업을 수행 할 수 있습니다.

class User(object):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

class Employee(User, models.Model):
   title = models.CharField(max_length=255)

... 테이블을 생성하지만 User클래스에 정의 된 필드를 무시 하므로 다음과 같은 테이블이 생성됩니다.

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

3
감사합니다. 제 질문에 대한 답변입니다. Employee에 대해 first_name이 생성되지 않는 이유는 아직 명확하지 않습니다.
rpq 2013-06-02

4
@rpq Django의 소스 코드를 확인해야하지만 IIRC는에서 일부 메타 클래스 해커 리를 사용 models.Model하므로 수퍼 클래스에 정의 된 필드가 선택되지 않습니다 (의 하위 클래스도 아닌 경우 models.Model).
Aya

1
@rpq 난 당신이 물었을 때이 7 년 전 알고 있지만에서 ModelBase__new__속성 값이있는 경우 검사 속성 호출,이 클래스의 초기 검사를 수행합니다 ' contribute_to_class속성 - Fields당신이 장고에 정의하는 것이 모든 수행을 그들이 그렇게, contributable_attrsDDL을 생성하기 위해 마이그레이션 중에 궁극적으로 전달 되는 특수 사전에 포함됩니다 .
Yu Chen

2
장고를 볼 때마다 울고 싶은데 왜 ????? 왜 모든 말도 안되는 이유, 프레임 워크가 언어와 완전히 다른 방식으로 동작해야하는 이유는 무엇입니까? 최소 경악의 원칙은 어떻습니까? 이 동작 (장고의 거의 모든 것)은 파이썬을 이해하는 사람이 기대하는 것과는 다릅니다.
E.Serra

36

추상 모델은 각 하위 하위 항목에 대한 전체 열 집합이있는 테이블을 생성하는 반면 "일반"Python 상속을 사용하면 연결된 테이블 집합 ( "다중 테이블 상속"이라고 함)이 생성됩니다. 두 가지 모델이있는 경우를 고려하십시오.

class Vehicle(models.Model):
  num_wheels = models.PositiveIntegerField()


class Car(Vehicle):
  make = models.CharField(…)
  year = models.PositiveIntegerField()

경우 Vehicle추상적 모델은, 당신은 하나의 테이블을해야합니다 :

app_car:
| id | num_wheels | make | year

그러나 일반 Python 상속을 사용하는 경우 두 개의 테이블이 있습니다.

app_vehicle:
| id | num_wheels

app_car:
| id | vehicle_id | make | model

자동차의 바퀴 수가 vehicle_id있는 행에 대한 링크는 어디에 있습니까 app_vehicle?

이제 Django는 이것을 객체 형태로 멋지게 조합 num_wheels하여에 속성으로 액세스 할 수 Car있지만 데이터베이스의 기본 표현은 다릅니다.


최신 정보

업데이트 된 질문을 해결하기 위해 Django 추상 클래스에서 상속 object하는 것과 Python에서 상속하는 것의 차이점 은 전자가 데이터베이스 개체로 취급되고 (따라서 테이블이 데이터베이스에 동기화 됨) Model. 일반 Python에서 상속 object하면 클래스 (및 하위 클래스)에 이러한 특성이 제공되지 않습니다.


1
하는 models.OneToOneField(Vehicle)것은 모델 클래스를 상속하는 것과도 동일 할 것입니다. 그러면 두 개의 별도 테이블이 생성되지 않습니까?
Rafay 2014-04-04

1
@MohammadRafayAleem : 예,하지만 상속을 사용할 때 Django는 예를 들어에 num_wheels속성을 만드는 car반면, a를 사용 OneToOneField하면 직접 역 참조해야합니다.
mipadi 2014.04.07

포인터가 아닌 필드 app_car도 포함하도록 테이블에 대해 모든 필드가 다시 생성 되도록 정규 상속을 적용 num_wheels하려면 vehicle_id어떻게해야합니까?
Don Grem

@DorinGrecu : 기본 클래스를 추상 모델로 만들고 상속합니다.
mipadi

@mipadi 문제는 기본 클래스를 추상적으로 만들 수 없다는 것입니다. 프로젝트 전체에서 사용되고 있습니다.
Don Grem

13

주요 차이점은 모델에 대한 데이터베이스 테이블이 생성되는 방식입니다. abstract = TrueDjango 없이 상속을 사용하면 각 모델에 정의 된 필드를 보유하는 부모 및 자식 모델 모두에 대해 별도의 테이블이 생성됩니다.

abstract = True기본 클래스에 사용 하는 경우 Django는 필드가 기본 클래스 또는 상속 클래스에 정의되어 있는지 여부에 관계없이 기본 클래스에서 상속하는 클래스에 대한 테이블 만 만듭니다.

장단점은 애플리케이션의 아키텍처에 따라 다릅니다. 다음 예제 모델이 주어집니다.

class Publishable(models.Model):
    title = models.CharField(...)
    date = models.DateField(....)

    class Meta:
        # abstract = True

class BlogEntry(Publishable):
    text = models.TextField()


class Image(Publishable):
    image = models.ImageField(...)

는 IF Publishable클래스는 추상적하지 장고는 열 publishables에 대한 테이블을 생성합니다 titledate및 별도의 테이블 BlogEntryImage. 이 솔루션의 장점 은 블로그 항목 또는 이미지에 관계없이 기본 모델에 정의 된 필드에 대해 모든 게시 가능 항목에서 쿼리 할 수 ​​있다는 것 입니다. 그러나 따라서 Django는 예를 들어 이미지에 대한 쿼리를 수행하는 경우 조인을 수행해야합니다. Publishable abstract = TrueDjango를 만들면 Publishable모든 필드 (또한 상속 된 필드)를 포함하는 블로그 항목 및 이미지에 대한 테이블이 생성되지 않습니다 . 이것은 get과 같은 작업에 조인이 필요하지 않기 때문에 편리합니다.

모델 상속에 대한 Django의 문서 도 참조하세요 .


9

다른 답변에서 보지 못한 것을 추가하고 싶었습니다.

파이썬 클래스와 달리 필드 이름 숨기기는 허용되지 않습니다. 모델 상속 .

예를 들어 다음과 같은 사용 사례에 대한 문제를 실험했습니다.

나는 django의 인증 PermissionMixin 에서 상속받은 모델을 가지고 있습니다 .

class PermissionsMixin(models.Model):
    """
    A mixin class that adds the fields and methods necessary to support
    Django's Group and Permission model using the ModelBackend.
    """
    is_superuser = models.BooleanField(_('superuser status'), default=False,
        help_text=_('Designates that this user has all permissions without '
                    'explicitly assigning them.'))
    groups = models.ManyToManyField(Group, verbose_name=_('groups'),
        blank=True, help_text=_('The groups this user belongs to. A user will '
                                'get all permissions granted to each of '
                                'his/her group.'))
    user_permissions = models.ManyToManyField(Permission,
        verbose_name=_('user permissions'), blank=True,
        help_text='Specific permissions for this user.')

    class Meta:
        abstract = True

    # ...

그럼 난 무엇보다도 나는 그것이 무시하고 싶어 내 믹스 인이 있었다 related_namegroups필드. 그래서 거의 다음과 같았습니다.

class WithManagedGroupMixin(object):
    groups = models.ManyToManyField(Group, verbose_name=_('groups'),
        related_name="%(app_label)s_%(class)s",
        blank=True, help_text=_('The groups this user belongs to. A user will '
                            'get all permissions granted to each of '
                            'his/her group.'))

나는 다음과 같이이 두 가지 믹스 인을 사용했습니다.

class Member(PermissionMixin, WithManagedGroupMixin):
    pass

네, 이것이 작동 할 것으로 예상했지만 작동하지 않았습니다. 그러나 문제는 더 심각했습니다. 제가받은 오류가 모델을 전혀 가리 키지 않았기 때문에 무엇이 잘못되었는지 전혀 몰랐습니다.

이 문제를 해결하는 동안 무작위로 믹스 인을 변경하고 추상 모델 믹스 인으로 변환하기로 결정했습니다. 오류가 다음과 같이 변경되었습니다.

django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'

보시다시피이 오류는 무슨 일이 일어나고 있는지 설명합니다.

내 의견으로는 이것은 큰 차이였습니다. :)


여기에 주요 질문과 관련이없는 질문이 있지만 마지막에 이것을 어떻게 구현 했습니까 (그룹 필드의 related_name을 재정의하기 위해)?
kalo

@kalo IIRC 방금 이름을 완전히 다른 것으로 변경했습니다. 상속 된 필드를 제거하거나 바꿀 수있는 방법은 없습니다.
Adrián

예, Contribute_to_class 메서드를 사용하여이를 수행하는 방법을 찾았을 때 저는 거의 포기할 준비가되었습니다.
kalo

1

가장 큰 차이점은 User 클래스를 상속 할 때입니다. 한 버전은 단순한 클래스처럼 작동하고 다른 버전은 Django 모델처럼 작동합니다.

기본 "객체"버전을 상속하는 경우 Employee 클래스는 표준 클래스 일 뿐이며 first_name은 데이터베이스 테이블의 일부가되지 않습니다. 양식을 만들거나 다른 Django 기능을 사용할 수 없습니다.

models.Model 버전을 상속하면 Employee 클래스는 Django Model 의 모든 메서드를 갖게 되며 first_name 필드를 양식에서 사용할 수있는 데이터베이스 필드로 상속합니다.

문서에 따르면 추상 모델은 "데이터베이스 수준에서 자식 모델 당 하나의 데이터베이스 테이블 만 생성하면서 Python 수준에서 공통 정보를 추출하는 방법을 제공합니다."


0

별도의 테이블을 만들지 않고 ORM이 데이터베이스에 조인을 만들 필요가 없기 때문에 대부분의 경우 추상 클래스를 선호합니다. 그리고 추상 클래스를 사용하는 것은 Django에서 매우 간단합니다.

class Vehicle(models.Model):
    title = models.CharField(...)
    Name = models.CharField(....)

    class Meta:
         abstract = True

class Car(Vehicle):
    color = models.CharField()

class Bike(Vehicle):
    feul_average = models.IntegerField(...)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.