[python] 장고에서-모델 상속-부모 모델의 속성을 재정의 할 수 있습니까?

나는 이것을 찾고 있어요 :

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

이것은 내가 사용하고 싶은 버전입니다 (어떤 제안에도 열려 있지만) :
http://docs.djangoproject.com/en/dev/topics/db/models/#id7

장고에서 지원 되나요? 그렇지 않은 경우 유사한 결과를 얻을 수있는 방법이 있습니까?



답변

업데이트 된 답변 : 사람들이 댓글에서 언급했듯이 원래 답변은 질문에 제대로 답변하지 않았습니다. 실제로 LongNamedRestaurant데이터베이스에서 생성 된 모델 만 Place그렇지 않았습니다.

해결책은 “장소”를 나타내는 추상 모델을 만드는 것입니다. AbstractPlace, 상속 :

class AbstractPlace(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class Place(AbstractPlace):
    pass

class LongNamedRestaurant(AbstractPlace):
    name = models.CharField(max_length=255)
    food_type = models.CharField(max_length=25)

@Mark 답변 을 읽으십시오 . 그는 비추 상 클래스에서 상속 된 속성을 변경할 수없는 이유를 훌륭한 설명을 제공합니다.

(이것은 Django 1.10 이후에만 가능합니다. Django 1.10 이전에는 추상 클래스에서 상속 된 속성을 수정할 수 없었습니다.)

원래 답변

Django 1.10부터 가능합니다 ! 요청한대로해야합니다.

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)

답변

아니요, 아닙니다 .

필드 이름 “숨기기”는 허용되지 않습니다.

일반적인 Python 클래스 상속에서는 자식 클래스가 부모 클래스의 모든 속성을 재정의하는 것이 허용됩니다. Django에서는 Field인스턴스 인 속성에 대해 허용되지 않습니다 (적어도 현재는 아님). 기본 클래스에라는 필드가있는 경우 해당 기본 클래스 에서 상속되는 클래스에서 author호출 author되는 다른 모델 필드를 만들 수 없습니다 .


답변

그것은 추상이 아니면 불가능 LongNamedRestaurant하며 Place, 그 이유는 다음과 같습니다 . 클래스로서뿐만 아니라 데이터베이스에서도. place-table에는 모든 pure Place및 모든 LongNamedRestaurant. 장소 테이블에 대한 및 참조를 LongNamedRestaurant사용하여 추가 테이블을 만듭니다 food_type.

당신이한다면 Place.objects.all(), 당신은 또한 인 모든 장소를 얻습니다 LongNamedRestaurant. 그리고 그것은 Place(없이 food_type) 의 인스턴스가 될 것입니다 . 그래서 Place.nameLongNamedRestaurant.name같은 데이터베이스 열을 공유하기 때문에 같은 유형이어야합니다.

나는 이것이 일반 모델에게 의미가 있다고 생각한다. 모든 식당은 하나의 장소이고, 적어도 그 장소에있는 모든 것이 있어야한다. 이러한 일관성은 데이터베이스 문제를 일으키지는 않지만 1.10 이전의 추상 모델에서는 가능하지 않은 이유 일 수도 있습니다. @lampslave가 언급했듯이 1.10에서 가능했습니다. 개인적으로주의를 기울이는 것이 좋습니다. Sub.x가 Super.x를 재정의하는 경우 Sub.x가 Super.x의 하위 클래스인지 확인하고 그렇지 않으면 Sub를 Super 대신 사용할 수 없습니다.

해결 방법 : AUTH_USER_MODEL이메일 필드 만 변경해야하는 경우 상당한 코드 복제를 포함 하는 사용자 지정 사용자 모델 ( )을 만들 수 있습니다 . 또는 이메일을 그대로두고 모든 양식에 필요한지 확인할 수 있습니다. 다른 응용 프로그램에서 사용하는 경우 데이터베이스 무결성을 보장하지 않으며 다른 방식으로 작동하지 않습니다 (사용자 이름이 필요하지 않게하려면).


답변

참조 https://stackoverflow.com/a/6379556/15690를 :

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True


답변

코드를 새 앱에 붙여넣고 INSTALLED_APPS에 앱을 추가하고 syncdb를 실행했습니다.

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

Django가 지원하지 않는 것 같습니다.


답변

이 멋진 코드 조각을 사용하면 추상 부모 클래스의 필드를 ‘재정의’할 수 있습니다.

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

추상 부모 클래스에서 필드가 제거되면 필요에 따라 자유롭게 재정의 할 수 있습니다.

이것은 내 작업이 아닙니다. 여기에서 원본 코드 : https://gist.github.com/specialunderwear/9d917ddacf3547b646ba


답변

Contribute_to_class를 다룰 수 있습니다.

class LongNamedRestaurant(Place):

    food_type = models.CharField(max_length=25)

    def __init__(self, *args, **kwargs):
        super(LongNamedRestaurant, self).__init__(*args, **kwargs)
        name = models.CharField(max_length=255)
        name.contribute_to_class(self, 'name')

Syncdb가 잘 작동합니다. 이 예제를 시도하지 않았습니다. 제 경우에는 제약 조건 매개 변수를 재정의하므로 … 기다립니다!