brechin / django-computed-property

Computed Property Fields for Django

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

django-filter does not work with ComputedField

SlonSky opened this issue · comments

Suppose we are using django-filter with such FilterSet

class MyFilter(FilterSet):

    class Meta:
        model = MyModel

    order_by = OrderingFilter(
        fields=(
            ('computed_int', 'computed_int'),
        )

For model

class MyModel(models.Model):

    some_field = CharField(max_length=2)
    computed_int = ComputedIntegerField(compute_from='compute_smt')

    @property
    def compute_smt(self):

        # just for example
        return ModelA.objects.filter(my_model=self).count() * 2

When executing a query to model, e.g. MyModel.objects.orderBy('computed_int'), we can observe, that our filter was not apply. The cause is that QuerySet constructs a raw query to DB, but field computed_int is not actual to really state (suppose we have a computed amount of some related objects).

My question is: how we can overcome this issue to provide to QuerySet computed (actual) value of field?
Alternatively, can we make some tool over FilterSet, not QuerySet, to apply filter by executing current QuerySet, and apply to it other filtrations?

Maybe in some variation of such idea:

class MyFilter(FilterSet):

    class Meta:
        model = MyModel

    order_by = OrderingFilter(
        fields=(
            ('computed_int', 'computed_int'),
        ),
        method='order_custom'
    
    def order_custom(self, queryset, name, value):

        if 'computed_int' in value[0]:

            # execute current queryset, apply filter for our custom computed field
            # construct new QuerySet, based on result of previous step
            return our_new_query_set

        return OrderingFilter().filter(queryset, value)

You built a perfect tool, but currently i have to refuse it from my project because of this issue.

@SlonSky I appreciate you taking the time to try out the library and provide feedback. You bring up a good use case that isn't currently handled well. The computed field is re-computed when the model object is instantiated to ensure that the returned value is as accurate as the property (in this case) it is coming from. The extra queries you're seeing are from the serializer loading the object instead of using the database values.

One possibility (somewhat hacky) would be to tell the serializer to use the appropriate values() from the queryset. This avoids doing the count queries again when serializing the filtered data.

views.py - https://gist.github.com/brechin/7a1d27c2cb18b001262f720110adafa0
models.py - https://gist.github.com/brechin/6bab90799d3cb3491de9c19fb8b77cb4
tests.py - https://gist.github.com/brechin/2b7e2d11a4e590369b61d5af07993689

@brechin Thank you for quick reply! Unfortunately, stack of technologies does not allow me to use my own views, so i have to make a compatible for django-filter code (I build a GraphQL API). So if I want to customize models ordering in such way, i need to adapt all the libs by making low-level tools.
Anyway, thank you. Hope this topic would be useful for community!

»The computed field is re-computed when the model object is instantiated« … this sounds expensive. can we make this behaviour optional? my motivation for considering this (or similar) libraries is precisely to keep such computations under control.

my case is different though, so I'll describe it in a test case in my fork.