Django Abstract Models Versus Regular Inheritance

django abstract models versus regular inheritance

I actually want to know the difference between a model class that
inherits from a django abstract class (Meta: abstract = True) and a
plain Python class that inherits from say, 'object' (and not
models.Model).

Django will only generate tables for subclasses of models.Model, so the former...

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)

...will cause a single table to be generated, along the lines of...

CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);

...whereas the latter...

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)

...won't cause any tables to be generated.

You could use multiple inheritance to do something like this...

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)

...which would create a table, but it will ignore the fields defined in the User class, so you'll end up with a table like this...

CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);

Django absract vs non-abstract model inheritance

You might consider generic relations here.

class Annotation(models.Model):
body = models.TextField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
annotated_object = GenericForeignKey('content_type', 'object_id')

class Book(models.Model):
...
annotations = GenericRelation('Annotation')

class Article(models.Model):
...
annotations = GenericRelation('Annotation')

now you can set annotation.annotated_object to any book or article instance, and in reverse do obj.annotations from both book and article objects.

Why Abstract=True dosen't inherit in Meta class of django model

The Model metaclass resets abstract in a model's Meta class. In this document you can see:

Django does make one adjustment to the Meta class of an abstract base
class: before installing the Meta attribute, it sets abstract=False.
This means that children of abstract base classes don’t automatically
become abstract classes themselves.

Also, you can see the source code of this process in this link:

if abstract:
# Abstract base models can't be instantiated and don't appear in
# the list of models for an app. We do the final setup for them a
# little differently from normal models.
attr_meta.abstract = False
new_class.Meta = attr_meta
return new_class

Django models: Inherit from variable abstract base class

No you can't do this. Django models can't be modified like this during runtime (after django is intialized). Anyway, this is not a good design pattern. You're confusing composition and inheritance. A Transaction is not a type of Fee or a type of Order so it makes no sense to subclass like this.

You can solve your problem without a generic foreign key by just using two separate ForeignKey fields:

class Transactions(models.Model):
transaction_number = models.IntegerField()
order = models.ForeignKey(Orders, null=True, blank=True)
fee = models.ForeignKey(Fees, null=True, blank=True)

You can then query the different transaction types like this:

fee_payments = Transactions.objects.exclude(fee=None)
order_payments = Transaction.objects.exclude(order=None)

Is it OK to use multiple inheritance with Django abstract models?

Sounds like for what you're trying to do, mixins really are the best fit. A simple google search will find lots of articles on implementing mixins in python, such as this one. I'm not sure multiple inheritance is the best way to go about doing it, so you might want to explore all the other options. What else have you thought about?

Django model abstract model with constrains not being inherited: 'constraints' refers to field 'xxx' which is not local to model 'Foo'

1. You can't use GenericForeignKey in UniqueConstraint, because a GenericForeignKey isn’t a normal field object. Instead in UniqueConstraint use content_type and object_id, this is equivalent to content_object:


class Meta:
abstract = True
constraints = [
models.UniqueConstraint(
fields=['content_type', 'object_id', 'user'],
name="%(app_label)s_%(class)s_user_unique"),
]

See Django Docs:

Due to the way GenericForeignKey is implemented, you cannot use
such fields directly with filters (filter() and exclude(), for
example) via the database API. Because a GenericForeignKey
isn’t a normal field object.

Likewise, GenericForeignKey does not appear in ModelForms.

2. According to Django Docs in abstract base classes related_name must contain '%(app_label)s' and '%(class)s'. And you should always specify a unique reverse name and query name for the field.

content_type = models.ForeignKey(
ContentType, on_delete=models.CASCADE,
related_name='%(app_label)s_%(class)s_content_types',
related_query_name='%(app_label)s_%(class)s_content_type',
)

user = models.ForeignKey(
User, blank=False, null=False,
on_delete=models.CASCADE,
related_name='%(app_label)s_%(class)s_users',
related_query_name='%(app_label)s_%(class)s_user',
)

If you are using related_name or related_query_name on a
ForeignKey or ManyToManyField, you must always specify a unique reverse name and query name for the field. This would normally
cause a problem in abstract base classes, since the fields on this
class are included into each of the child classes, with exactly the
same values for the attributes (including related_name and
related_query_name) each time.

To work around this problem, when you are using related_name or
related_query_name in an abstract base class (only), part of the value
should contain '%(app_label)s' and '%(class)s'.

Django, a model class which inherits abstract model class can be also abstract?

Yes!

Of course, you can make an abstract base class that inherits from another abstract base class. You just need to remember to explicitly set abstract=True each time.

Models | Django Documentation

Updated links for 2.2 and 3.0



Related Topics



Leave a reply



Submit