feincms / django-tree-queries

Adjacency-list trees for Django using recursive common table expressions. Supports PostgreSQL, sqlite, MySQL and MariaDB.
https://django-tree-queries.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
433 stars 27 forks source link

tree_ordering: returns int instead of string values after upgrade from 0.16.1 to 0.18 #65

Closed nuarhu closed 7 months ago

nuarhu commented 7 months ago

Hi,

after upgrading to 0.18 my tests are failing at the usages of tree_ordering. I tried different versions, and the failures do not show up with 0.16.1 but with 0.17 and 0.18.

Model:

class TranslationKey(TreeNode):
    label = CharField(
        null=False,
        blank=False,
        max_length=255,
    )
    objects = TreeQuerySet.as_manager(with_tree_fields=True)

    class Meta:
        ordering = ['label']
        unique_together = ['parent', 'label']

    def __str__(self):
        return self.label

    @property
    def full_label(self):
        tree_ordering = getattr(self, 'tree_ordering', None)
        if tree_ordering:
            # this is only present for objects fetched via TranslationKey.objects
            return '.'.join(tree_ordering)
        return '.'.join([tk.label for tk in self.ancestors(include_self=True)])

full_label returns a translation label like global.button.save where global, button, and save are 3 instances of TranslationKey.

This code is running fine up until 0.16.1 but fails after upgrading to 0.17 when running as part of a Django test - which means that the DB is setup with migrations from scratch before each test. In this case, my tests run on SQLite (the actual app runs on Postgres).

I have not found any release notes that indicate I need to change something (which would be completely fine), and I have not found any issues in this github repo about this. Am I the only one facing this issue?

Thanks for this library!

matthiask commented 7 months ago

Hi @nuarhu

Thanks! I'm sorry to hear that recent changes which were added to support more ways of ordering siblings broke your code. The tree_ordering and tree_path field contents are not supposed to be a part of the public interface of the package since its early days (https://github.com/feincms/django-tree-queries/commit/56004f4ba45f8720426608b7ad42e065060caed3), but I now see that this is only mentioned in the changelog and the code, and not in the more obvious places. So, you should always fall back to the self.ancestors() case. If that's too slow, either denormalize the full label and save it on each node or build the node tree in the Python code and traverse the ancestry yourself? I know that those sound like worse choices than what you had. I have been thinking on and off about allowing additional CTE fields but haven't gotten around to doing that just yet.

nuarhu commented 7 months ago

Thank you for the speedy answer!

Ok, I'll remove the usage then. Thanks for your suggestion of denormalizing the label. I just need to think through what is necessary to keep them up to date.

jmbarbier commented 7 months ago

Just to mention that @nuarhu is not alone being hit by this change, i was heavily using those 2 fields, so, for now, sticking to 0.16 is my only workaround in a reasonable amount of work.

matthiask commented 7 months ago

Yeah, I hear you. By the way, tree_path hasn't changed, only tree_ordering.

I think the additional functionality provided by recent changes is a good thing, but I can see that it's breaking other use cases. Would it help if there was a way to include additional fields in the recursive CTE? I don't know how something like this could be implemented on sqlite3 and mysql/mariadb without the same ugly hacks as the current tree_ordering and tree_path uses, but that's how it is.

matthiask commented 7 months ago

Here's a terrible hack which works for a very basic test case on PostgreSQL: https://github.com/feincms/django-tree-queries/compare/mk/65-recursive-fields-hack

Something like this could work, but it would obviously have to be cleaned up a lot.

jmbarbier commented 7 months ago

@matthiask that was exactly what i planned to do (i'm on pg-only projects), and it is really a great addition when you need to retrieve other fields (permissions, status, ...) for all ancestors, without having to loop with get_ancestors. You did it faster... thanks a lot :clap: :heart:

matthiask commented 7 months ago

@jmbarbier Great! It's not finished yet btw, so if you want to contribute there's still work to do :-)

I won't be able to finish this in the next ~10 days.

matthiask commented 7 months ago

I've committed the extra_fields() hack to main (see #67), if you have a chance to test it out I'd appreciate some feedback. It's postgres-only at the moment, but I will probably get around cleaning it up.

matthiask commented 7 months ago

Check this out: https://github.com/feincms/django-tree-queries/blob/42e797c1645a9b936f36390ea2aedff1be3feaab/tests/testapp/test_queries.py#L928-L952