Además de la sintaxis, ¿cuál es la diferencia entre usar un modelo abstracto de django y usar la herencia simple de Python con modelos de django? ¿Pros y contras?
ACTUALIZACIÓN: Creo que mi pregunta fue mal entendida y recibí respuestas para la diferencia entre un modelo abstracto y una clase que hereda de django.db.models.Model. De hecho, quiero saber la diferencia entre una clase modelo que hereda de una clase abstracta de django (Meta: abstract = True) y una clase Python simple que hereda de digamos, 'objeto' (y no modelos.Modelo).
Aquí hay un ejemplo:
class User(object):
first_name = models.CharField(..
def get_username(self):
return self.username
class User(models.Model):
first_name = models.CharField(...
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(...
Respuestas:
Django solo generará tablas para subclases de
models.Model
, por lo que el primero ...class User(models.Model): first_name = models.CharField(max_length=255) def get_username(self): return self.username class Meta: abstract = True class Employee(User): title = models.CharField(max_length=255)
... hará que se genere una sola tabla, en la línea de ...
CREATE TABLE myapp_employee ( id INT NOT NULL AUTO_INCREMENT, first_name VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL, PRIMARY KEY (id) );
... mientras que el último ...
class User(object): first_name = models.CharField(max_length=255) def get_username(self): return self.username class Employee(User): title = models.CharField(max_length=255)
... no hará que se generen tablas.
Podría usar herencia múltiple para hacer algo como esto ...
class User(object): first_name = models.CharField(max_length=255) def get_username(self): return self.username class Employee(User, models.Model): title = models.CharField(max_length=255)
... que crearía una tabla, pero ignorará los campos definidos en la
User
clase, por lo que terminará con una tabla como esta ...CREATE TABLE myapp_employee ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, PRIMARY KEY (id) );
fuente
models.Model
, por lo que los campos definidos en la superclase no serán seleccionados (a menos que también sean subclases demodels.Model
).ModelBase
la__new__
llamada, realiza una verificación inicial de los atributos de la clase, verificando si el valor del atributo tiene uncontribute_to_class
atributo, elFields
que definiste en Django, todos lo tienen, por lo que se incluyen en un diccionario especial llamadocontributable_attrs
que finalmente se pasa durante la migración para generar el DDL.Un modelo abstracto crea una tabla con el conjunto completo de columnas para cada subhijo, mientras que el uso de la herencia "simple" de Python crea un conjunto de tablas vinculadas (también conocido como "herencia de múltiples tablas"). Considere el caso en el que tiene dos modelos:
class Vehicle(models.Model): num_wheels = models.PositiveIntegerField() class Car(Vehicle): make = models.CharField(…) year = models.PositiveIntegerField()
Si
Vehicle
es un modelo abstracto, tendrá una sola tabla:Sin embargo, si usa la herencia simple de Python, tendrá dos tablas:
¿Dónde
vehicle_id
hay un enlace a una fila enapp_vehicle
que también tendría el número de ruedas del automóvil?Ahora, Django reunirá esto muy bien en forma de objeto para que pueda acceder
num_wheels
como un atributoCar
, pero la representación subyacente en la base de datos será diferente.Actualizar
Para abordar su pregunta actualizada, la diferencia entre heredar de una clase abstracta de Django y heredar de Python
object
es que el primero se trata como un objeto de base de datos (por lo que las tablas para él se sincronizan con la base de datos) y tiene el comportamiento de unModel
. Heredar de un Python simpleobject
le da a la clase (y sus subclases) ninguna de esas cualidades.fuente
models.OneToOneField(Vehicle)
también sería equivalente a heredar una clase modelo, ¿verdad? Y eso daría como resultado dos tablas separadas, ¿no es así?num_wheels
un atributo en acar
, mientras que con aOneToOneField
tendrás que hacerlo desreferenciando tú mismo.app_car
tabla para que también tenga elnum_wheels
campo, en lugar de tener elvehicle_id
puntero?La principal diferencia es cómo se crean las tablas de bases de datos para los modelos. Si usa la herencia sin
abstract = True
Django, se creará una tabla separada para el modelo padre y el hijo, que contiene los campos definidos en cada modelo.Si usa
abstract = True
para la clase base, Django solo creará una tabla para las clases que heredan de la clase base, sin importar si los campos están definidos en la clase base o en la clase heredada.Los pros y los contras dependen de la arquitectura de su aplicación. Dados los siguientes modelos de ejemplo:
class Publishable(models.Model): title = models.CharField(...) date = models.DateField(....) class Meta: # abstract = True class BlogEntry(Publishable): text = models.TextField() class Image(Publishable): image = models.ImageField(...)
Si la
Publishable
clase no es abstracta, Django creará una tabla para publicables con las columnastitle
ydate
tablas separadas paraBlogEntry
yImage
. La ventaja de esta solución sería que puede realizar consultas en todos los elementos publicables para los campos definidos en el modelo base, sin importar si son entradas de blog o imágenes. Pero, por lo tanto, Django tendrá que hacer uniones si, por ejemplo, haces consultas de imágenes ... Si al hacerPublishable
abstract = True
Django no se creará una tabla paraPublishable
, sino solo para, entradas de blog e imágenes, que contenga todos los campos (también los heredados). Esto sería útil porque no se necesitarían combinaciones para una operación como get.Consulte también la documentación de Django sobre la herencia de modelos .
fuente
Solo quería agregar algo que no he visto en otras respuestas.
A diferencia de las clases de Python, la ocultación del nombre de campo no está permitida con la herencia del modelo.
Por ejemplo, he experimentado problemas con un caso de uso de la siguiente manera:
Tenía un modelo que heredaba de la autenticación PermissionMixin de django :
class PermissionsMixin(models.Model): """ A mixin class that adds the fields and methods necessary to support Django's Group and Permission model using the ModelBackend. """ is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_('Designates that this user has all permissions without ' 'explicitly assigning them.')) groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, help_text=_('The groups this user belongs to. A user will ' 'get all permissions granted to each of ' 'his/her group.')) user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, help_text='Specific permissions for this user.') class Meta: abstract = True # ...
Luego tuve mi mezcla que, entre otras cosas, quería que anulara la
related_name
delgroups
campo. Entonces fue más o menos así:class WithManagedGroupMixin(object): groups = models.ManyToManyField(Group, verbose_name=_('groups'), related_name="%(app_label)s_%(class)s", blank=True, help_text=_('The groups this user belongs to. A user will ' 'get all permissions granted to each of ' 'his/her group.'))
Estaba usando estos 2 mixins de la siguiente manera:
class Member(PermissionMixin, WithManagedGroupMixin): pass
Así que sí, esperaba que esto funcionara, pero no fue así. Pero el problema era más serio porque el error que estaba obteniendo no apuntaba a los modelos en absoluto, no tenía idea de lo que estaba saliendo mal.
Mientras trataba de resolver esto, decidí al azar cambiar mi mezcla y convertirla en una mezcla de modelo abstracto. El error cambió a esto:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
Como puede ver, este error explica lo que está sucediendo.
Esta fue una gran diferencia, en mi opinión :)
fuente
La principal diferencia es cuando hereda la clase User. Una versión se comportará como una clase simple y la otra se comportará como un modelo de Django.
Si hereda la versión base del "objeto", su clase de Empleado será simplemente una clase estándar y first_name no se convertirá en parte de una tabla de base de datos. No puede crear un formulario ni usar ninguna otra característica de Django con él.
Si hereda la versión de models.Model, su clase de Empleado tendrá todos los métodos de un Modelo de Django y heredará el campo first_name como un campo de base de datos que se puede usar en un formulario.
De acuerdo con la documentación, un modelo abstracto "proporciona una manera de factorizar la información común a nivel de Python, mientras se crea una sola tabla de base de datos por modelo hijo a nivel de base de datos".
fuente
Preferiré la clase abstracta en la mayoría de los casos porque no crea una tabla separada y el ORM no necesita crear uniones en la base de datos. Y usar la clase abstracta es bastante simple en Django
class Vehicle(models.Model): title = models.CharField(...) Name = models.CharField(....) class Meta: abstract = True class Car(Vehicle): color = models.CharField() class Bike(Vehicle): feul_average = models.IntegerField(...)
fuente