В наследовании 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? Если нет, то есть ли способ достичь подобных результатов?
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_dataadmin.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