python django django-inheritance

python - En Django-Modelo de herencia: ¿le permite anular el atributo de un modelo principal?



django-inheritance (9)

¡Desde Django 1.10 es posible ! Solo tienes que hacer lo que pediste:

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)

Estoy buscando hacer esto:

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)

Esta es la versión que me gustaría usar (aunque estoy abierto a cualquier sugerencia): http://docs.djangoproject.com/en/dev/topics/db/models/#id7

¿Esto es compatible con Django? Si no, ¿hay alguna manera de lograr resultados similares?


Eso no es posible a menos que sea abstracto, y aquí está el por qué: LongNamedRestaurant es también un Place , no solo como una clase sino también en la base de datos. La tabla de lugares contiene una entrada para cada Place puro y para cada LongNamedRestaurant . LongNamedRestaurant solo crea una tabla adicional con food_type y una referencia a la tabla de lugares.

Si hace Place.objects.all() , también obtendrá cada lugar que sea un LongNamedRestaurant , y será una instancia de Place (sin el tipo de food_type ). Así que Place.name y LongNamedRestaurant.name comparten la misma columna de base de datos y, por lo tanto, deben ser del mismo tipo.

Creo que esto tiene sentido para los modelos normales: cada restaurante es un lugar, y debe tener al menos todo lo que tiene ese lugar. Tal vez esta coherencia es también la razón por la cual no fue posible para los modelos abstractos antes de 1.10, aunque no daría problemas en la base de datos allí. Como comenta @lampslave, fue posible en 1.10. Yo personalmente recomendaría la atención: si Sub.x anula Super.x, asegúrese de que Sub.x sea una subclase de Super.x, de lo contrario, Sub no se puede usar en lugar de Super.

Soluciones AUTH_USER_MODEL : puede crear un modelo de usuario personalizado ( AUTH_USER_MODEL ) que implique una gran cantidad de duplicación de código si solo necesita cambiar el campo de correo electrónico. De forma alternativa, puede dejar el correo electrónico tal como está y asegurarse de que sea obligatorio en todos los formularios. Esto no garantiza la integridad de la base de datos si otras aplicaciones lo usan, y no funciona al revés (si desea hacer que el nombre de usuario no sea necesario).


Esta pieza supercool de código le permite ''anular'' campos en clases principales abstractas.

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")

Cuando los campos han sido eliminados de la clase principal abstracta, puede redefinirlos como lo necesite.

Este no es mi propio trabajo. Código original desde aquí: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba


No puede anular los campos del Modelo, pero se puede lograr fácilmente anulando / especificando el método clean (). Tuve el problema con el campo de correo electrónico y quería hacerlo único en el nivel de modelo y lo hice así:

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'')})

El mensaje de error es capturado por el campo Formulario con el nombre "correo electrónico"


No, no es :

El nombre de campo "ocultar" no está permitido

En la herencia de clase de Python normal, se permite que una clase secundaria anule cualquier atributo de la clase principal. En Django, esto no está permitido para los atributos que son instancias de Field (al menos, no en este momento). Si una clase base tiene un campo llamado author , no puede crear otro campo de modelo llamado author en ninguna clase que herede de esa clase base.


Pegué el código en una aplicación nueva, agregué una aplicación a INSTALLED_APPS y ejecuté syncdb:

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

Parece que Django no lo admite.


Sé que es una vieja pregunta, pero tuve un problema similar y encontré una solución:

Tenía las siguientes clases:

class CommonInfo(models.Model): image = models.ImageField(blank=True, null=True, default="") class Meta: abstract = True class Year(CommonInfo): year = models.IntegerField()

Pero quería que se requiriera el campo de imagen heredado de Year mientras se mantiene el campo de imagen de la superclase nulable. Al final utilicé ModelForms para aplicar la imagen en la etapa de validación:

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

Parece que esto solo es aplicable en algunas situaciones (sin duda, donde se necesita imponer reglas más estrictas en el campo de la subclase).

Alternativamente, puede utilizar el clean_<fieldname>() lugar de clean() , por ejemplo, si se debe completar una town campo:

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


Tal vez podrías tratar con 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 funciona bien. No probé este ejemplo, en mi caso simplemente anulo un parámetro de restricción así que ... ¡espera y mira!


Ver https://.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