programing

Django - Model Inheritance - 상위 모델의 속성을 재정의할 수 있습니까?

linuxpc 2023. 8. 14. 22:32
반응형

Django - Model Inheritance - 상위 모델의 속성을 재정의할 수 있습니까?

이 작업을 수행할 예정입니다.

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 이전에는 추상 클래스에서 상속된 속성을 수정할 수 없었기 때문에 가능합니다.)

원답

장고 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 클래스 상속에서는 자식 클래스가 부모 클래스의 모든 속성을 재정의할 수 있습니다.장고에서는 다음과 같은 속성에 대해 허용되지 않습니다.Field인스턴스(적어도 현재는 아님).기본 클래스에 다음 필드가 있는 경우author다음과 같은 다른 모델 필드를 만들 수 없습니다.author해당 기본 클래스에서 상속되는 모든 클래스에서.

추상적이지 않으면 불가능하며, 그 이유는 다음과 같습니다.LongNamedRestaurant또한Place클래스뿐만 아니라 데이터베이스에도 있습니다.플레이스 테이블에는 모든 순수 항목이 포함되어 있습니다.Place그리고 모두에게LongNamedRestaurant.LongNamedRestaurant그냥 추가 테이블을 만듭니다.food_type플레이스 테이블에 대한 참조입니다.

네가 한다면.Place.objects.all()당신은 또한 모든 장소를 얻을 수 있습니다.LongNamedRestaurant그리고 그것은 의 사례가 될 것입니다.Place(없이)food_type. 그래서.Place.name그리고.LongNamedRestaurant.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

내 솔루션은 다음과 같이 간단합니다.monkey patching내가 어떻게 변했는지 알아보세요.max_lengthname에 출전하다.LongNamedRestaurant측정 결과:

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

class LongNamedRestaurant(Place):
    food_type = models.CharField(max_length=25)
    Place._meta.get_field('name').max_length = 255

코드를 새 앱에 붙여넣고 앱을 설치_에 추가했습니다.APPS 및 실행 syncdb:

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

장고는 그것을 지원하지 않는 것 같습니다.

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

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는 잘 작동합니다.저는 이 예를 시도하지 않았습니다. 저의 경우 제약 조건 매개 변수를 재정의하기 때문에... 기다려 보십시오!

오래된 질문인 것은 알지만 비슷한 문제가 있었고 해결 방법을 찾았습니다.

저는 다음과 같은 수업을 들었습니다.

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

하지만 저는 슈퍼클래스의 이미지 필드를 무효로 유지하면서 연도의 상속된 이미지 필드가 필요하기를 원했습니다.마지막으로 검증 단계에서 ModelForms를 사용하여 이미지를 적용했습니다.

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

admin.py :

class YearAdmin(admin.ModelAdmin):
    form = YearForm

이것은 일부 상황(특히 하위 클래스 필드에 더 엄격한 규칙을 적용해야 하는 경우)에만 적용되는 것 같습니다.

또다음사수있다니습용할을 할 수 있습니다.clean_<fieldname>() 대신 clean()예를 들어 필드인 경우town다음을 입력해야 합니다.

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town

모형 필드를 재정의할 수는 없지만 clean() 메서드를 재정의/지정하여 쉽게 실행할 수 있습니다.이메일 필드에 문제가 있어서 모델 레벨에서 고유하게 만들고 싶었고 다음과 같이 했습니다.

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

그런 다음 "이메일"이라는 이름의 양식 필드에서 오류 메시지가 캡처됩니다.

언급URL : https://stackoverflow.com/questions/2344751/in-django-model-inheritance-does-it-allow-you-to-override-a-parent-models-a

반응형