4

This isn't really specific to django.

One can model

Place (with location, name, and other common attributes)
 - Restaurant  (menu..)
 - ConcertHall  (hall size..)
  1. in two separate tables and let each one hold all the fields they need. (in django world, this is called abstract inheritance)
  2. in three tables, where one holds the common fields and the other two has their own unique fields. (multi-table inheritance in django)

The authors of book Two scoops of Django 1.8 strongly advise against using multi-table inheritance.

Say you want to query places based on it's location and paginate the results (It doesn't have to be a location, can be any other common attribute we want to filter on)

I can see how I can achieve it using Multi-table inheritance.

select place.id from place LEFT OUTER JOIN "restaurant" on ( restuarant.id=place.id) LEFT OUTER JOIN "concerthall" on ( concerthall.id=place.id) where ... order by distance

Is it feasible to do it with abstract inheritance?

eugene
  • 39,839
  • 68
  • 255
  • 489

2 Answers2

5

According to Django documentation: Model inheritance:

The only decision you have to make is whether you want the parent models to be models in their own right (with their own database tables), or if the parents are just holders of common information that will only be visible through the child models.

I think both possibilities are just tools, equally good tools and it just depends on your use case for their appropriateness. Surely there are specific things to consider for both approaches, and conceptually sometimes multi-table inheritance may be more difficult to comprehend, but other than that this topic just turns to become opinionated.

If you need a single queryset for both models, then it is logical that you consider multi-table inheritance rather than abstract models, because otherwise you would need to get into combining two querysets into one, most probably by using lists as this relevant answer suggests, but you would definitely lose ORM functionality.

Community
  • 1
  • 1
Wtower
  • 18,848
  • 11
  • 103
  • 80
  • Thanks for the answer. I should have been more clean on the last question. How do you achieve the sorting/paginating the combined set when you have no common table? Say you can do it, is it really performance-wise not too much worse than multi-table solution? – eugene May 27 '15 at 08:20
  • 1
    Welcome, I am sorry I didn't get it correctly! Updated. – Wtower May 27 '15 at 08:27
  • 1
    Thanks. Does it imply that is probably not a good idea to use abstract inheritance when you need to query on both models? – eugene May 27 '15 at 08:29
  • Well, yes: it is surely better to maintain ORM functionality, both for performance reasons and for sanity's sake! – Wtower May 27 '15 at 08:30
2

It depends on your usecases, but Django ihave a good Database ORM for Database Normalized table structure.

Keeping the base fields in a model and keeping the specifics on another is the best approach in Database Normalization logic because you may have query on different tables and that is not a desired situation. Django relations and reverse relations offers you what you need at this point.

An Example based on yours considering you are using Multi Table Inheritance:

Place.objects.filter(location=x)
Place.objects.filter(location=x, Q(Q(concerthall__hallsize__gt=y)| Q(restaurant__menu=z)))
Place.objects.filter(location=x, concerthall__id__isnull=True)

First will return you all Restaurants and Concert Halls in x.

Second will return you All places which are Concert Halls with hall sizes greater than y or Restaurants with menu z.

Last one is a super magic query that will return you all places in location x which is not a Concert Hall. That is useful when you have many Models inheriting from Place. You can use <model_name>__id for including/excluding tables according to your needs.

You can built great JOINS including many tables and do stick to Database Normalization rules while doing this. You will keep your related data in one place and avoid possible data integrity problems.

Mp0int
  • 18,172
  • 15
  • 83
  • 114