django-mptt/django-mptt

How do I use order_by?

maksam07 opened this issue · 8 comments

Good afternoon. I want to sort the tree, but I understand that from some level the elements start to be displayed in the wrong level

obj = Model.objects.filter(level__lte=10).annotate(
    children_count=Count('children'),
    descendants_count=Floor((F('rght') - F('lft') - 1) / 2),
    comments_count=Count('comments'),
).order_by('tree_id', 'level', 'children_count', '-slug')

Tell me, can you somehow solve the sorting problem?

You could try running Model._tree_manager.rebuild() once. It could be that the MPTT attributes got out of sync. This can happen, unfortunately.

@matthiask I had such a function when I add new items. But this does not work, in the admin panel (with standard sorting) everything is displayed correctly, but on the site with my sorting it does not. It turns out that some children are not properly "attached" to their parents. And because of this, some parents seem to be empty, although in fact, children are simply not displayed on the desired parent.

Without order_by

111

With order_by

222

I'm not sure what you want to achieve. The ordering doesn't look like either DFS or BFS at all. The "without order_by" example looks like a real tree. the "with order_by" example... I don't know. Is it expected that only the last children per level even have any descendants in turn? I'm confused.

I'm not sure what you want to achieve. The ordering doesn't look like either DFS or BFS at all. The "without order_by" example looks like a real tree. the "with order_by" example... I don't know. Is it expected that only the last children per level even have any descendants in turn? I'm confused.

My point is that with sorting, children "go" to other parents. Parent "49" should have child "50" and that child should have child "52" and so on. But when sorting, it turns out that parent "49" has no children at all, and "50" the child passed to parent "23". This is just an example of connections. I cannot understand how to leave the children with their original parents, but at the same time sort the children according to a certain principle.
Next example: I need to sort comments by "rating". So that first I have the highest rated comments, then fewer, fewer, and even fewer. But if I again use a similar sorting, then already at the second level of comments, they will be displayed for other parents.

Ah, alright. Now I get it.

I'm not sure mptt is especially well suited for this, since the mptt attributes not only encode parent relationships but also ordering within siblings.

I'd try loading the tree into a Python datastructure and sorting it yourself in-memory (if the tree isn't too big) or else investigate whether adjacency lists and recursive common table expressions may be a better fit for what you're trying to achieve. If you're using PostgreSQL you could stuff the children count per node into an array and sort by this array, similar to what I'm doing with the tree_ordering here https://github.com/matthiask/django-tree-queries/blob/b6dd583fad6983533da984caab157096008fd38e/tree_queries/compiler.py#L63 . Of course you'd have to use a calculated value for this, not a model field.

I'm not sure mptt is especially well suited for this, since the mptt attributes not only encode parent relationships but also ordering within siblings.

Perhaps you can suggest which tree building library would be better for me? I want to make comments like reddit. Can you advise something?

Here's a blogpost outlining what they did at Disqus (which is also based on Django):
https://cra.mr/2010/05/30/scaling-threaded-comments-on-django-at-disqus/

I linked my django-tree-queries library above. It cannot be used as-is because you have different ordering requirements but maybe you find something interesting there.

I wouldn't worry about performance too much for the time being; if you only have a few dozen comments per post you can just load everything into memory and to the sorting there (maybe even up to a few hundred comments... I don't know. There's no easy way around profiling/measuring your code.)