Large nested hierarchy in Django / Wagtail: parenting or categorizing?

720 views Asked by At

I have a database containing metadata on 50,000 drugs (medications), which are ordered hierarchically (the taxonomy is called ATC). Example with a heart medication follows:

  • A
  • -- A10
  • ---- A10X, Metoprolol

Some drugs have 4 levels.

I need to generate one separate page for each drug. What would be the most effcient way of doing this in wagtail/django, including taking into account that the user desires a simple and intuitive way to search the database.

I've tried django-mptt, defining the following models.

class ATCChapter(MPTTModel): # MAIN CHAPTER
    chapter_letter = models.CharField(max_length=255, null=True, blank=True) # E.g "A"
    chapter_title = models.CharField(max_length=255, null=True, blank=True) # E.g "Cardiac drugs"
    parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')

    class MPTTMeta:
        order_insertion_by = ['chapter_letter']

class ATCSubchapter(MPTTModel): # SUBCHAPTER
    subchapter_letter = models.CharField(max_length=255, null=True, blank=True) # E.g "A10"
    subchapter_title = models.CharField(max_length=255, null=True, blank=True) # E.g "Drugs used in heart failure"
    parent = TreeForeignKey(ATCChapter, on_delete=models.CASCADE, null=True, blank=True, related_name='subchapter')

    class MPTTMeta:
        order_insertion_by = ['subchapter_letter']

class ATCDrug(MPTTModel, Page): # INDIVIDUAL DRUG
    drug_code = models.CharField(max_length=255, null=True, blank=True) # E.g "A10X"
    drug_name = models.CharField(max_length=255, null=True, blank=True) # E.g "Metoprolol"
    parent = TreeForeignKey(ATCSubchapter, on_delete=models.CASCADE, null=True, blank=True, related_name='disease')

    class MPTTMeta:
        order_insertion_by = ['drug_code']

Making migrations provokes the following error: class ATCDrug(MPTTModel, Page): TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

This can be solved by not letting the class inherit from Page, which is bad of course. I removed Page and tried out the models and couldn't verify that the parent-child scheme was correct.

Question: Am I doing something obviously wrong or sub-optimally, given the goal.

System: Wagtail 2.10, Postgresql, plan to use Algolia for search.

Grateful for any advice.

1

There are 1 answers

4
LB Ben Johnston On BEST ANSWER

Based on your requirement that each medication be represented by a single page, and if you do intend to use Wagtail for the medium to long term, I would recommend each medication be a Page model.

This is because you will get all the benefits of being able to show this to users, provide searching, API and UI editing features without any hassles.

However, the next decision relates to whether you put all of these under ONE parent Page model or also store the hierarchy in the Page models.

If you know that the hierarchy is going to be pretty rigid at exactly five levels of depth and each depth has a specific name and agreed definition then it might be best to have these 'levels' codeified into the models also. Be aware though that each medication page's url will default to url.com/level-1/level2-/..../insulin instead of something like url.com/medications/insulin.

However, it would be easy to also make each Medication available at some fixed (non-nested) point in the Url tree via RoutablePageMixin. However, you may find yourself fighting a small amount against Wagtail if you want the canonical URL to be this non-nested variant. The nice thing about this is you will find the Insulin page in the right point in the tree just by navigation via the built in page explorer (admin menu).

Going the other way, it still might be worth storing the hierarchy as Page models but the medication Page models themselves would be children of some other central page. This gives you simpler canonical URLs for each medication, but also means each level Page can use RoutablePageMixin for giving access to its descendant children. The downside of this approach is that the admin editing interface would not directly reflect the hierarchy in regards to the medication page's.

However, at 50,000 entries, the editing interaction will need some tweaking and alternate ways to find the pages anyway.

E.g.

  • home
  • ... levels
  • ...... A1 (etc)
  • ... medications
  • ...... Insulin
  • contact (other misc pages would also be under home)

There may not be a 'right' answer here, and you may find it best to set up some programmatic creation of pages & linking (or something akin to fixtures) and just do it both ways and set up two Sites in your beta Wagtail instance. This way you can see what it feels like to edit, give your team a chance to play and also set up the API. In the end, either way you will end up needing very similar code in terms of page templates and models.