Além da sintaxe, qual é a diferença entre usar um modelo abstrato django e usar a herança simples do Python com modelos django? Prós e contras?
ATUALIZAÇÃO: Acho que minha pergunta foi mal interpretada e recebi respostas sobre a diferença entre um modelo abstrato e uma classe que herda de django.db.models.Model. Na verdade, eu quero saber a diferença entre uma classe de modelo que herda de uma classe abstrata django (Meta: abstract = True) e uma classe Python simples que herda digamos, 'objeto' (e não modelos.Modelo).
Aqui está um exemplo:
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(...
Respostas:
Django irá gerar apenas tabelas para subclasses de
models.Model
, então o anterior ...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)
... fará com que uma única tabela seja gerada, nos moldes 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) );
... enquanto o ú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)
... não fará com que nenhuma tabela seja gerada.
Você pode usar herança múltipla para fazer algo assim ...
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 criaria uma tabela, mas irá ignorar os campos definidos na
User
classe, então você acabará com uma tabela como esta ...CREATE TABLE myapp_employee ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, PRIMARY KEY (id) );
fonte
models.Model
, então os campos definidos na superclasse não serão selecionados (a menos que também sejam subclasses demodels.Model
).ModelBase
's__new__
chamada, ele executa uma verificação inicial da classe' atributos, verificando se o valor do atributo tem umcontribute_to_class
atributo - oFields
que você define no Django tudo fazer, então eles estão incluídos em um dicionário especial chamadocontributable_attrs
que, por fim, é transmitido durante a migração para gerar o DDL.Um modelo abstrato cria uma tabela com todo o conjunto de colunas para cada subchild, enquanto o uso de herança Python "simples" cria um conjunto de tabelas vinculadas (também conhecido como "herança de múltiplas tabelas"). Considere o caso em que você tem dois modelos:
class Vehicle(models.Model): num_wheels = models.PositiveIntegerField() class Car(Vehicle): make = models.CharField(…) year = models.PositiveIntegerField()
Se
Vehicle
for um modelo abstrato, você terá uma única tabela:No entanto, se você usar a herança simples do Python, terá duas tabelas:
Onde
vehicle_id
está um link para uma linhaapp_vehicle
que também teria o número de rodas do carro.Agora, o Django colocará isso junto na forma de objeto para que você possa acessar
num_wheels
como um atributoCar
, mas a representação subjacente no banco de dados será diferente.Atualizar
Para resolver sua questão atualizada, a diferença entre herdar de uma classe abstrata Django e herdar de Python
object
é que a primeira é tratada como um objeto de banco de dados (portanto, as tabelas são sincronizadas com o banco de dados) e tem o comportamento de aModel
. Herdar de um Python simples nãoobject
dá à classe (e suas subclasses) nenhuma dessas qualidades.fonte
models.OneToOneField(Vehicle)
também seria equivalente a herdar uma classe de modelo, certo? E isso resultaria em duas tabelas separadas, não é?num_wheels
um atributo em acar
, enquanto com aOneToOneField
você terá que fazer isso desreferenciando você mesmo.app_car
tabela de modo que ela também tenha onum_wheels
campo, em vez de ter ovehicle_id
ponteiro?A principal diferença é como as tabelas dos bancos de dados para os modelos são criadas. Se você usar herança sem o
abstract = True
Django, criará uma tabela separada para o modelo pai e filho, que contém os campos definidos em cada modelo.Se você usar
abstract = True
para a classe base, o Django criará apenas uma tabela para as classes que herdam da classe base - não importa se os campos são definidos na classe base ou na classe herdada.Prós e contras dependem da arquitetura do seu aplicativo. Dados os seguintes modelos de exemplo:
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(...)
Se a
Publishable
classe não é abstrato Django irá criar uma tabela para publishables com as colunastitle
edate
e mesas separadas paraBlogEntry
eImage
. A vantagem dessa solução seria que você pode consultar todos os publicáveis para campos definidos no modelo base, não importa se eles são entradas de blog ou imagens. Mas, portanto, o Django terá que fazer joins se você, por exemplo, fizer consultas por imagens ... Se fizerPublishable
abstract = True
Django, não criará uma tabela paraPublishable
, mas apenas para entradas de blog e imagens, contendo todos os campos (também os herdados). Isso seria útil porque nenhuma junção seria necessária para uma operação como get.Veja também a documentação do Django sobre herança de modelos .
fonte
Só queria acrescentar algo que não vi em outras respostas.
Ao contrário das classes Python, a ocultação do nome do campo não é permitida com a herança do modelo.
Por exemplo, experimentei problemas com um caso de uso como segue:
Eu tinha um modelo herdado do auth do django PermissionMixin :
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 # ...
Então eu tinha meu mixin que, entre outras coisas, queria substituir o
related_name
dogroups
campo. Então era mais ou menos assim: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.'))
Eu estava usando estes 2 mixins da seguinte forma:
class Member(PermissionMixin, WithManagedGroupMixin): pass
Sim, esperava que funcionasse, mas não funcionou. Mas o problema era mais sério porque o erro que eu estava recebendo não estava apontando para os modelos, eu não tinha ideia do que estava errado.
Enquanto tentava resolver isso, decidi aleatoriamente mudar meu mixin e convertê-lo em um mixin de modelo abstrato. O erro mudou para este:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
Como você pode ver, esse erro explica o que está acontecendo.
Essa foi uma grande diferença, na minha opinião :)
fonte
A principal diferença é quando você herda a classe User. Uma versão se comportará como uma classe simples, e a outra se comportará como um modelo Django.
Se você herdar a versão base do "objeto", sua classe Employee será apenas uma classe padrão e first_name não se tornará parte de uma tabela de banco de dados. Você não pode criar um formulário ou usar qualquer outro recurso do Django com ele.
Se você herdar a versão models.Model, sua classe Employee terá todos os métodos de um modelo Django e herdará o campo first_name como um campo de banco de dados que pode ser usado em um formulário.
De acordo com a documentação, um modelo abstrato "fornece uma maneira de fatorar informações comuns no nível do Python, enquanto ainda cria apenas uma tabela de banco de dados por modelo filho no nível do banco de dados."
fonte
Vou preferir a classe abstrata na maioria dos casos porque ela não cria uma tabela separada e o ORM não precisa criar junções no banco de dados. E usar a classe abstrata é muito simples no 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(...)
fonte