В наследовании Django-Model-позволяет ли он переопределить атрибут родительской модели?



Я ищу, чтобы сделать это:



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



это поддерживается в Django? Если нет, то есть ли способ достичь подобных результатов?

521   9  

9 ответов:

обновленный ответ: как люди отметили в комментариях, оригинальный ответ не был должным образом ответить на вопрос. Действительно, только 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. Я лично рекомендовал бы уход: Если суб.х переопределяет супер.x, убедитесь, что Sub.x-подкласс Супер.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 работает нормально. Я не пробовал этот пример, в моем случае я просто переопределяю параметр ограничения так ... подожди и увидишь !

Я знаю, что это старый вопрос, но у меня была аналогичная проблема и нашел обходной путь:

у меня были следующие занятия:

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

    class Meta:
        abstract = True

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

но я хотел, чтобы наследуемое поле изображения года требовалось при сохранении поля изображения суперкласса nullable. В конце концов я использовал 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')})

сообщение об ошибке затем захватывается полем формы с именем "email"

Comments

    Ничего не найдено.