I have this model referencing itself to allow building a tree:
class PartCategory(models.Model):
parent = models.ForeignKey('PartCategory', on_delete=models.DO_NOTHING, null=True, default=None, blank=True)
name = models.TextField()
I now have an SQL query to get one element and all of its child in one select (in this example the element with id=64):
WITH RECURSIVE
under_partcategory(id,name, parent_id,level) AS (
select api_partcategory.id,api_partcategory.name,api_partcategory.parent_id,0 from api_partcategory where api_partcategory.id=64
UNION ALL
SELECT api_partcategory.id,api_partcategory.name,api_partcategory.parent_id, under_partcategory.level+1
FROM api_partcategory JOIN under_partcategory ON api_partcategory.parent_id=under_partcategory.id
ORDER BY 2
)
SELECT * FROM under_partcategory;
I am searching a way to express this query inside a QuerySet to allow adding filtering options and fields construction from my model but I don't quite know if it's kind of possible.
Can I construct a composite QuerySet made from some kind of raw query built from my model, and at the same time allowing to use the filter
and order_by
capabilities?
Edit:
To be more specific I would like to have a QuerySet I can use this whay:
PartCategory.recursive.filter(64)
Internally it would build the request by replacing all embraced parts with elements from PartCategory model:
WITH RECURSIVE
under_{model name}({model fields},level) AS (
select {model fields},0 from {model name} {where {model name}.id=64 #extracted from filter}
UNION ALL
SELECT {model fields}, under_{model_name}.level+1
FROM {model name} JOIN under_{model_name} ON {model_name}.parent_id=under_{model_name}.id
ORDER BY 2
)
SELECT * FROM under_{model_name};
I'm playing with a custom managers to try to build this but for now I don't know how to build {model fields}
from my model and how to return an object eligible for filter
.
Edit 2:
As stated by colwin I made it with django-mptt
but not exactly as I was thinking in first place.
My model with django-mptt
becomes:
from mptt.models import MPTTModel, TreeForeignKey
class PartCategory(MPTTModel):
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
name = models.TextField()
class MPTTMeta:
order_insertion_by = ['name']
And to extract my item 64 with all its childrens I now do:
categories = PartCategory.objects.get(id=64).get_descendants(include_self=True)
This is quite straightforward !
I had the same problem, and wanted to avoid raw SQL. While I'm on PostgreSQL, this is for a package that may be used under other DB contexts. I found this:
https://django-mptt.readthedocs.io/en/latest/overview.html
Shouldn't this work for you?
You can also look at https://github.com/django-mptt/django-mptt