Advice on best way to structure Django models for polymorphic, survey application

Hi everyone!

Just getting started with using Django for a work project, so have been learning the reigns recently! I had a question regarding some best practices for how I would approach designing a survey-style application using Django models. The idea of this app is that it serves a survey (currently only one, may be expanded upon in future), with the contents of this able to be dynamically set via the Django admin panel.

Overall, each survey would have a screens, and each screen can be composed of different components, divided into two main types:

  • simple components - these are basic components that have one parent, which is either a survey screen or a composable component (see below), and contain some data. Think of them as a “leaf node”.
  • composable components - these are components which themselves have data, and in addition, can also store child components themselves.

My main task I’m struggling with figuring out how to best handle is how to best handle the data models for different types of components. As sort of hinted, there would be a lot of different types of components in the app, such as text boxes, multiple choice questions, etc. Some of these are simple, some of these can themselves store other components (see above).

My question is, given that this is somewhat an issue of polymorphism, thinking about database design and database/software architecture, what would be the best way to model these with the Django ORM? I’ve come across 3 ways of modelling this based of research I’ve done online:

  1. use Django’s multi-table inheritance to handle polymorphism. (as an aside, do libraries like django-polymorphic assist with optimisation/efficiencies at all here?)
    • trouble is – how would I be able to satisfy the requirement that the parent of a component can either be another component, or a survey screen?
  2. similar to the above, but use explicit one-to-one fields to handle the hierarchies.
    • same problem as the above, however: how would I be able to satisfy the requirement that the parent of a component can either be another component, or a survey screen?
  3. store the component field data as a JSON blob in an ORM field, then use Django proxy models to represent all the different actual components
    • we still have the same problem as 1 and 2, however.

I’ve also come across GenericForeignKey as well, as well as its potential for pitfalls. But in my case, particularly where a survey screen or a component could be the parent of one component, I’m not sure if there’s really any other good alternative to avoiding it?

In terms of designing such an app, thinking about software/database architecture, and the strengths/weaknesses of Django’s ORM, which one of the above would be the best option with the mentioned considerations? Or, is there a completely different way to approach the problem that would work better for this use case?

Thank you so much for any help!

Use GenericForeignKey to let components have either a survey screen or another component as parent. Combine this with Django’s multi-table inheritance or django-polymorphic to handle different component types. This approach offers flexibility and clean querying. Avoid JSON blobs for structured data.

1 Like

There is another appraoch I’m using in one of the projects I’m working on. There I replaced django-polymorphic against Django models inheriting from one abstract model. As a primary key, I use a UUIDField instead of an an interger-based one. Such a primary key is unique across all of your DB tables. Instead of complicated JOINs, I therefore use UNION. This can all be handled by the Django ORM. That way I can create a queryset over multiple tables without the need of generic foreign keys and a base model.

If you perfer using a JSONField instead, have a look at this library which allows you to do a kind of poor-mens polymorphism: 17. Collection Fieldsdjango-formset

Btw. I’m interested into such an application myself. So if you open source it, please share this with me.

Probably there is no way around GenericForeignKey, given that survey screens and components are different models (and I wouldn’t feel too comfortable having them inherit from a base class since that seems like an OOP inheritance antipattern). Thanks for the help :).