Django Polymoprhic

1.3k Views Asked by At

I want to implement models using inheritance and I've found this package django-polymorphic. But I was reading about inheritance in django models and almost on every page I found they recommend using abstract = True in parent model. Which will duplicate fields for subclasses, resultsing in making queries faster.

I've done some testing and found out that this library doesn't use use abstract varaible:

class Parent(PolymorphicModel):
    parent_field = models.TextField()

class Child(Parent):
    child_field = models.TextField()

This results in:

Parent table:

| app_parent| CREATE TABLE `app_parent` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_field` longtext NOT NULL,
  `polymorphic_ctype_id` int(11),
  PRIMARY KEY (`id`),
  KEY `app_polymorphic_ctype_id_a7b8d4c7_fk_django_content_type_id` (`polymorphic_ctype_id`),
  CONSTRAINT `app_polymorphic_ctype_id_a7b8d4c7_fk_django_content_type_id` FOREIGN KEY (`polymorphic_ctype_id`) REFERENCES `django_content_type` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

Child table:

| app_child | CREATE TABLE `app_child` (
  `parent_ptr_id` int(11) NOT NULL,
  `child_field` varchar(20) NOT NULL,
  PRIMARY KEY (`parent_ptr_id`),
  CONSTRAINT `no_parent_ptr_id_079ccc0e_fk_app_parent_id` FOREIGN KEY (`parent_ptr_id`) REFERENCES `app_arent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

Should I use my own classes which uses abstract field or should i stick with this?

1

There are 1 best solutions below

0
On

Do you have a need to be able to query parent table?

Parent.objects.all()

If yes, then most probably you will need to use the multi-table inheritance with abstract=False.

Using model inheritance with abstract=False you get more complicated database schema, with more database relations. Creating a child instance will take 2 inserts instead of 1 (parent and child table). Querying child data will require table join. So this method certainly has got it's shortcomings. But when you want to query for common columns data, it's best supported way in django.

Django polymorphic builds on top of standard django model inheritance, by adding extra column polymorphic_ctype which allows to identify subclass having only a parent object.

There are various ways that you can use to achieve similar results using abstract=True. But often it results in more complicated querying code.

If using abstract=True bellow is 2 examples how you can query common-data of all childs.

  • Chaining multiple queries

    def query_all_childs(**kwargs):
        return chain(
            Child1.objects.filter(**kwargs)
            Child2.objects.filter(**kwargs)
        )
    
  • Using database views

    Manually create a database view that combines several tables (this could be done by attaching sql code to post-migrate signal):

    create database view myapp_commonchild
    select 'child1' as type, a, b from child1
    union all
    select 'child2' as type, a, b from child2
    

    Create a concrete-parent model with managed=False. This flag tells django to ignore the table in database migrations (because we have manually created database view for that).

    class Parent(models.Model):
        a = CharField()
        b = CharField()
    
    class CommonChild(Parent):
        type = models.CharField()
        class Meta:
            managed = False
    
    class Child1(Parent):
        pass
    
    class Child2(Parent):
        pass
    

    Now you can query CommonChild.objects.all() and access common fields of child classes.

Speaking of performance, I don't know how big your tables are or how heavy reads/writes are, but most probably using abstract=False will not impact your performance in a noticeable way.