models.CalculatedField feature

2,285 views
Skip to first unread message

ilya.ka...@jetbrains.com

unread,
Nov 15, 2017, 11:32:32 AM11/15/17
to Django developers (Contributions to Django itself)
Hello.

I think I just invented very useful feature.
I have the following model

class Customer(models.Model):
    age = models.IntegerField()

I want to show special link in template for customer if she is allowed to drink.

class Customer(models.Model):
    age = models.IntegerField()

    @property
    def allowed_to_drink(self):
        return self.age >= 18

And in template I can do

{% if customer.allowed_to_drink %}.

But in another view I want to show only those customers, who are allowed to drink.
So, in view I write

drinkers = [customer for customer in models.Customer.objects.all() if customer.allowed_to_drink].

But what if I have one million customers? It is insane to check this field on Python side instead of database side.
I use ORM: 
models.Customer.objects.filter(age__gte=18).all()

And DRY principle is now violated. 
See, I have knowledge "allowed_to_drink -> (age >= 18) " and I want to write in once, and then use both in ORM queries and object method.

And I am not alone here: 

Here is what I suggest

class Customer(models.Model):
    age = models.IntegerField()
    allowed_to_drink = models.CalculatedField(Q(age__gte=18))

# Using in ORM
Customer.objects.filter(allowed_to_drink=True)
# It actually converted to
Customer.objects.filter(Q(age__gte=18))

# Using it in code
Customer().allowed_to_drink
# This part a little bit tricky, since you will need to check in on Python side.
# "age__gte" should be converted to
funcs = {
    "gte": lambda instance, field, value: getattr(instance, field) >= value
}
instance_field, func = "age__gte".split("__")
funcs[func](instance, instance_field, 18)

But we already have code to convert Q to SQL, so it should be easy to do it for python.

Some more ideas:
*  "Q" may have lazy evalutation here for cases like "models.CalculatedField(Q(date__gte=now()))". Could be implemented with lambdas, or we can simply allow only literal here.
* With Postrges you can even store calculated field in database (it has calculated column), but it works only for literals.

I think this functionality should be supported by Django core because it affects admin, forms (calculated fields can't be changed), migrations and ORM.


Ilya Kazakevich

PyCharm developer.

 

http://www.jetbrains.com

The Drive to Develop


Matthew Pava

unread,
Nov 15, 2017, 11:49:32 AM11/15/17
to django-d...@googlegroups.com

I handle that situation quite differently.

I have a model manager that annotates different fields to the queryset depending on when I need them.

For your example, I would have something like this:

 

class CustomerQuerySet(models.QuerySet):
  
def with_allowed_to_drink(self):
     
return self.annotate(allowed_to_drink=Case(When(age__gte=18, then=True), default=False))

 

 

Then I actually created a decorator that applies the manager to instances on a model.  It is not as generic as it could be since it depends on function names having the word “with” in them, but I’m sure we could make it more general.

def queryset_on_instance(model: Model):
   
"""Takes the queryset of a model and creates new properties for each instance of the model based on
    the custom queryset class.  Some code is copied from django.models.managers; most of this was inspired by it.
    The queryset methods that are copied must have 'with' in their name.

    Tip: Use this as a decorator on model classes
    """

   
def create_method(method_name):
       
def the_method(self):
           
return getattr(model._default_manager, method_name)().get(pk=self.pk)

       
return the_method

    queryset_class =
getattr(model._default_manager, "_queryset_class", None)
    predicate = inspect.isfunction
   
for name, method in inspect.getmembers(queryset_class, predicate=predicate):
        if 'with' in name:
           
# Only copy missing attributes.
           
if hasattr(model, name):
               
continue
           
# Only copy public methods or methods with the attribute `queryset_only=False`.
           
queryset_only = getattr(method, 'queryset_only', None)
           
if queryset_only or (queryset_only is None and name.startswith('_')):
               
continue
           
# Copy the method onto the model.
           
setattr(model, name, create_method(name))
   
return model

 

 

And then on your Customer model:

@queryset_on_instance
class Customer(models.Model):
   age = models.IntegerField()
   objects = CustomerQuerySet.as_manager()

 

 

In your code or template, you would access the value like so:

customer.with_allowed_to_drink.allowed_to_drink

 

And you can filter by the value like so:

Customers.objects.with_allowed_to_drink().filter(allowed_to_drink=True)

 

I do agree that it would be nice to have something out of the box that could handle custom properties in the QuerySet calls, or, as you put it, calculated fields.

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/58aa034b-99de-4bad-98e4-5c200501b777%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tom Forbes

unread,
Nov 15, 2017, 12:06:23 PM11/15/17
to django-d...@googlegroups.com
SQLAlchemy has this, through a feature called Hybrid Attributes: http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html

Every Django project i've worked on has had to tackle this problem, often poorly. You usually end up with an instance method and a kind of duplicate manager method, or some specific queries spread throughout the code.

The annotate feature is quite cool, but I think Django would benefit from something like this. 


To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscribe@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscribe@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.

Anthony King

unread,
Nov 15, 2017, 1:21:18 PM11/15/17
to django-d...@googlegroups.com
Hey,

this is something I'd like to get involved in.
Last year I began prototyping something (albeit with an awful name) that resembled sqlalchemy's hybrid properties.

I'll spend a bit of time in a few hours sprucing it up to better match their naming, and link it here if anyone is interested.

Josh Smeaton

unread,
Nov 16, 2017, 12:21:38 AM11/16/17
to Django developers (Contributions to Django itself)
I too have seen computed properties poorly implemented as model functions/properties, only some of the time matched with an appropriate database selector. I think there's a lot of merit in the idea.

I don't agree with reimplementing lookups/transforms/funcs to produce the python version of a SQL concept. It's a leaky abstraction. Instead, I'd prefer the model author provides their python calculation:

allowed_to_drink = models.CalculatedField(Q(age__gte=18), lambda c: c.age >= 18)

There may be destructuring/serialisation issues (migrations/pickle) using lambda, unless we can ignore these model fields during deserialisation.

Shai Berger

unread,
Nov 16, 2017, 1:44:21 AM11/16/17
to django-d...@googlegroups.com
On Thursday 16 November 2017 07:21:38 Josh Smeaton wrote:
>
> I don't agree with reimplementing lookups/transforms/funcs to produce the
> python version of a SQL concept. It's a leaky abstraction. Instead, I'd
> prefer the model author provides their python calculation:
>
> is_adult = models.CalculatedField(Q(age__gte=18), lambda c: c.age >= 18)

(name shortened to fit in a 80-char line)

I disagree. The way I see it, these calculated fields should essentially
provide a declarative way to add an annotate() that's always going to be
there. There should be only one source of truth, and that's the database;
parallel implementations tend to disagree on fringe cases with disappointing
results (e.g. consider if age above is nullable -- the database then makes the
calculated field null, the python above makes it raise an exception). Or
consider calculated fields which are aggregates -- a Python version could very
easily become painfully inefficient.

>
> There may be destructuring/serialisation issues (migrations/pickle) using
> lambda, unless we can ignore these model fields during deserialisation.
>

That's another (though minor) argument against lambdas here.

No, the way I see it, the API should just use expressions. Of note, I think it
should also support the output_field keyword. At the hand-waving, brainstorming
level, I think we might, at some point, actually try to implement this by
creating objects in the database (views on Postgres, computed columns on
MySql, etc) -- so that the field is really available in all contexts.

My 2 cents,

Shai.

Marc Tamlyn

unread,
Nov 16, 2017, 5:02:13 AM11/16/17
to django-d...@googlegroups.com
I like the idea, and have a number of concrete use cases I would use it for. In one case I even have a custom manager which always applies a particular `.annotate()` already, so it can be filtered and accessed on the model.

I am inclined to agree with Shai that the fully database version should at least be provided. If that aggregate is expensive, it could be `.defer()`ed (this has its own problems of course). I'm not against the idea of the user being able to provide a python implementation, but I think the database version should be the default as it is more likely to be completely consistent. I definitely think we should not be trying to automagically create python versions of database functions.

Marc

Sjoerd Job Postmus

unread,
Nov 16, 2017, 7:08:48 AM11/16/17
to django-d...@googlegroups.com
I disagree with not being able to calculate it locally (without the database): such a calculation depends on other fields. What if a dependent field is updated, but the object is not yet saved. What should the value be?

>>> c.age, c.is_adult
17, False
>>> c.age = 40
>>> c.age, c.is_adult
40, False

Would not make sense to me.

As a first step, maybe it would make sense to make it easier to support custom filter attribute. Right now you have to create a custom query set subclass implementing .filter where you intercept the value

def filter(self, *args, **kwargs):
    if 'is_adult' in kwargs:
        value = kwargs.pop('is_adult')
        if value:
            kwargs['age__gte'] = 18
        else:
            kwargs['age__lt'] = 18
    return super().filter(*args, **kwargs)

And that's ignoring exclude and Q, and probably a lot of other cases.

Could we make that simpler first? 

Tom Forbes

unread,
Nov 16, 2017, 7:46:13 AM11/16/17
to django-d...@googlegroups.com
I think without client-side Python execution of these functions it greatly limits the use of this feature. In the cases I've seen you have a bunch of `qs.filter(age__gt=18)` scattered around everywhere and a separate `can_drink` function of property. Without the `can_drink` function returning the correct result when the model is updated it's kind of useless, you'd still end up with people still writing their own `can_drink` functions to do so.

If we can make simple cases work well (i.e functions that do arithmetic, and/or, simple comparisons and on simple fields) I think it would be pretty useful. Sure, when you get into quite complex database functions, complex fields with differing support it can get quite hard, but for string/integer/boolean columns with simple operators that different between databases we support?

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscribe@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.

ilya.ka...@jetbrains.com

unread,
Nov 16, 2017, 8:59:17 PM11/16/17
to Django developers (Contributions to Django itself)
Thank you for your intereset this on feature.

I belive this functionality should not delegete everything to database: one of the cooliest Django features is that models do not require database:
they could be just storage of *business* logic (that is why we call them *fat*). I can even fill my models from different source and logic will still exist:

customer = Customer(age=variable_from_csv_file)
if customer.is_allowed_to_drink: ....

But I agree that reflecting *all* database features could be cumbersome. We can limit usage to simple stuff like "eq", "gt(e)", and "contains" and literals as arguments.

I also do not think that we should ask user to use lambdas:
"CalculatedField(Q(age__gte=18), lambda c: c.age >= 18)" literally violates DRY (one of the key Django features after all!)
But we can give user ability to provide custom lambda (for advanced cases)

Actually, my idea about lambdas was about to make calculation "lazy"
"still_actual = Event(Q(end_of_like__lt=date.today())"

I want "today" to be calculated at the moment when method is called or SQL query executed. Something like
"still_actual = Event(lambda expression_api: expression_api.expression(end_of_like__lt=date.today())"
It looks ugly, we can create some sugar for it.

I really like how SQLAlchemy does it. 
They say "It can, in theory, work with any descriptor-based expression system". Can we borrow it?

Ilya.

Mariusz Felisiak

unread,
Nov 17, 2017, 3:51:01 PM11/17/17
to Django developers (Contributions to Django itself)
Some databases such as Oracle [1] or MySQL [2] support virtual (generated) columns based on expressions. IMO it will be great (and should be quite straightforward) to add them to Django. We can take this approach into account.

Shai Berger

unread,
Nov 18, 2017, 8:06:45 AM11/18/17
to django-d...@googlegroups.com
On Thursday 16 November 2017 14:08:20 Sjoerd Job Postmus wrote:
> I disagree with not being able to calculate it locally (without the
> database): such a calculation depends on other fields. What if a dependent
> field is updated, but the object is not yet saved. What should the value
> be?
>
> >>> c.age, c.is_adult
>
> 17, False
>
> >>> c.age = 40
> >>> c.age, c.is_adult
>
> 40, False
>
> Would not make sense to me.
>

While I agree with the gist of this argument, I don't believe in parallel
computations -- that is, having some database-definition of a computed column
emulated in Python. Such computations always end up slightly different in some
edge cases (different precision, different rounding rules, different timezones
between webserver and database server, etc) and this can lead to obscure
heizenbugs and weird, hard-to-track data corruptions.

I see three solutions:

1) Make all computations in Python. This means the feature can be supported
only on databases which can run Python as part of their queries; I'm not quite
sure about the details, but I think PG can. I'm certain none of the other core
databases can, perhaps MSSQL can with IronPython.

2) Just invalidate all calculated columns as soon a change in the model
occurs. So then,

>>> c.age, c.is_adult

17, False

>>> c.age = 40
>>> c.is_adult

StaleCalculatedFieldException

3) If we claim that the calculated field is, essentially, a declarative
shorthand for .annotate(), then treat it as such -- and live with the result
whcih Sjoerd Job described as nonsensical. However, to make it a little less
nonsensical, we'd call the field something like "DBCalculatedField", to point
out that it isn't updated automatically in the model instance.

BTW, with annotate we already have that "nonsensical" behavior today, and
people seem to accept it:

class Teacher(models.Model):
pass

class Student(models.Model):
teacher = models.ForeignKey(Teacher)

...

>>> t = Teacher.objects.annotate(std_count=Count('student')).get(pk=1)
>>> t.std_count
16
>>> Student.objects.create(teacher=t)
>>> t.std_count
16

I understand that the perception may be different for calculated fields which
depend purely on "in-record" fields -- but I don't see a good way to verify
that property on the caclulated field definition, so I suspect the best (or,
rather, least bad) solution is just to change that perception.

Shai

Shai Berger

unread,
Nov 18, 2017, 8:31:49 AM11/18/17
to django-d...@googlegroups.com
On Saturday 18 November 2017 15:06:20 Shai Berger wrote:
>
> 1) Make all computations in Python. This means the feature can be supported
> only on databases which can run Python as part of their queries; I'm not
> quite sure about the details, but I think PG can. I'm certain none of the
> other core databases can

As Aymeric has now showed me, actually SQLite can too.

ilya.ka...@jetbrains.com

unread,
Nov 18, 2017, 2:32:48 PM11/18/17
to Django developers (Contributions to Django itself)
Such computations always end up slightly different in some 
edge cases 
I agree.
We can document this fact. Model authors should be aware that they are not allowed to use anything but literals. All other functions are used on their own risc. I believe that comparition of two integers or strings should work stable across all modern platforms.

I still believe database shall not be involved in this process.I do not need database to compare two digits. 

Josh Smeaton

unread,
Nov 18, 2017, 5:59:44 PM11/18/17
to Django developers (Contributions to Django itself)
Until you want to aggregate or filter on that calculation, and perform that filtering in the database rather than pulling back millions of records to do so in python. The database *must* be involved.

Josh Smeaton

unread,
Nov 18, 2017, 6:00:50 PM11/18/17
to Django developers (Contributions to Django itself)
Re-reading your reply, I think I've misunderstood your intent there (I didn't realise who the author was). Please disregard my previous reply.

Shai Berger

unread,
Nov 18, 2017, 6:04:13 PM11/18/17
to django-d...@googlegroups.com
On Saturday 18 November 2017 21:32:47 ilya.ka...@jetbrains.com wrote:
> > Such computations always end up slightly different in some
> > edge cases
>
> I agree.
> We can document this fact. Model authors should be aware that they are not
> allowed to use anything but literals. All other functions are used on their
> own risc.

I believe this suggestion is a footgun -- more likely to be used incorrectly
than it is to be used correctly. People don't really read the docs that
carefully.

For this to be viable, the rule of "nothing but literals and comparisons"
needs to be enforced, not just documented.

As a project, then, we have to decide which we like better:

(1) An API which gives uniform use of the properties whether the object is
saved or not; or,

(2) An API which exposes the full capabilities of the database

I'd go with (2).

Shai.

Anssi Kääriäinen

unread,
Nov 19, 2017, 1:27:44 AM11/19/17
to Django developers (Contributions to Django itself)
Even simple comparisons can be difficult to simulate in Python. As an example, some databases treat empty strings as null. Still, autogenerating the Python side of simple computations is an interesting idea. At least worth a try.

There are some hard problems if trying to push all computations to the db. First problem is that this won't work for unsaved models. Second, if the value isn't fetched on initial select, the database values might change. This is almoust the same problem as when changing values on Python side. Finally, accessing the db just to do a simple computation isn't a good from performance perspective.

For using Python in the DB, on PostgreSQL Python isn't available in hosted environments as Python can access the db machine.

I'd take a look on what it requires to implement this feature as 3rd party addition, and then do the required changes to core.

- Anssi

James Bennett

unread,
Nov 19, 2017, 2:31:50 AM11/19/17
to django-d...@googlegroups.com
To be honest, I'm curious why the SQLAlchemy approach isn't being discussed more here. Implementing a calculated-field property decorator, and supplying both the SQL side (via a method which returns a Q object, most likely) and the Python side wouldn't be too difficult to implement, and avoids the need to try to "magically" generate one from the other.

ilya.ka...@jetbrains.com

unread,
Nov 19, 2017, 6:14:46 AM11/19/17
to Django developers (Contributions to Django itself)
Ok, I see not everyone are happy with idea of ORM expression calculated on Python side:)
We can:

* Provide two different backends (Database-based and Python-based or  "run python in database" backend) for calculated field
* Or provide python-based backend as third party lib
* Or provide whole solution as third party lib (if Django API is flexible enough for that)

I still do not like database approach.
* If I have list of objects, do you want to have O(n) queries?
* If calculated field is function of only model fields (no foreign keys etc) it should work locally: some projects may have no database at all.



>>Even simple comparisons can be difficult to simulate in Python. As an example, some databases treat empty strings as null
But ORM should handle it somehow, should not it?
I want to belive that "filter(alias__isnull=True)" works identically across all databases.
So, we need to do same on Python side.

>>To be honest, I'm curious why the SQLAlchemy approach isn't being discussed more here.
I think SQLAlchemy approach is good, but we should use Django ORM expressions language.
See: we need some high-level expression language to describe functions of models that could be 
calculated both on SQL side and Python side.
And we already have it: it is Django ORM expressions language:)
We have some kind of AST for all those Q, F expressions etc, so we only need to create evaluator for this language. 

No need to use external language, based on Python, as SQLAlchemy does.

Why do we need
from sqlalchemy import func
func.concat(cls.first_name, ' ', cls.last_name)

if we already have
F('first_name') + F('last_name')
?


>>and supplying both the SQL side (via a method which returns a Q object, most likely) and the Python side 
But it ruin DRY idea by making author to write same logic twice (for Python and SQL), is not it?

Still, user may have ability to provide custom expression, 
but for simple cases like "age__gt=18" we can do it "automagically".

Django generates SQL code from its expressions. Sometimes this code is sophisticated.
Why can't we do that for Python, if we do it for SQL?

Josh Smeaton

unread,
Nov 19, 2017, 6:05:44 PM11/19/17
to Django developers (Contributions to Django itself)
Introducing an `as_python` to the expression API would probably work. It'd involve a lot of work to implement for all supportable expression types. Considering there are many 3rd party / user code expressions in the wild without as_python, we'd need to find a way to fail gracefully.

However, for our core types, we can mimic SQL behaviour for nulls/empty strings, and other weird backend behaviour.

It might be interesting to see a proof of concept implementing support for some of the more complex expressions like Case/When and datetime truncation with timezones. Then there are Exists() subqueries and Window functions that can't work without consulting the database, and that should probably not be implemented to fetch row by row on demand (like foreign keys without select related).

If some kind of rough consensus can be reached on an implementation I think we'd probably want to see a DEP considering the tradeoffs and different opinions raised in this thread.

Shai Berger

unread,
Nov 19, 2017, 6:22:45 PM11/19/17
to django-d...@googlegroups.com
On Sunday 19 November 2017 13:14:46 ilya.ka...@jetbrains.com wrote:
> Ok, I see not everyone are happy with idea of ORM expression calculated on
> Python side:)

As the main "not happy" (I think), I should say, it's not the calculation-on-
Python-side per se that bugs me; it's the claim that the two ways of
calculation will always give the same results. As has been pointed out, on all
sorts of edge cases they won't, and if the (programmer) user is unaware of
that, this can cause all sorts of issues.

If an API is set up, that makes this point clear enough in the naming, I will
be satisfied. For this, the SQLAlchemy syntax could be used, with appropriate
names; e.g. we'd declare

is_adult = models.DBCalculatedField(
Case(When(age__gt=18, then=True), default=False
)
# (I'm pretty sure we want an Expr there, not a Q object)

and later add

@is_adult.local_estimate
def is_adult(self):
return (self.age > 18)

and allow the local estimate to be used whenever we think the object is not
fresh from the database. Such names would at least give people pause for
thought, when they provide the Python almost-equivalent, that it might
actually not be equivalent.

For this to provide the value I'm after, though, the range of
"local_estimate"s provided automatically must be extremely limited, if any at
all.

> But it ruin DRY idea by making author to write same logic twice (for Python
> and SQL), is not it?

I'll give it a slogan:

It isn't really DRY if it isn't really R

That is: Telling people "what you're doing here is only *almost* equivalent to
what you'd be doing there", *in the code* and not just in the docs, is far
more important than what seems like DRY if you don't take this "almost" in
account.

The rest of this mail may be a little argumentative, and not essential. Feel
free to skip.

>
> I still do not like database approach.
> * If I have list of objects, do you want to have O(n) queries?

Why would you need O(n) queries? If the calculated field is added as if with
annotate(), you get it all in one query. You may need O(n) queries if you want
to update n objects -- but then, at least with Django core, you need O(n)
queries anyway.

> * If calculated field is function of only model fields (no foreign keys
> etc) it should work locally: some projects may have no database at all.
>

If a project has no database at all, why would it use anything more elaborate
than simple Python properties?

> >>Even simple comparisons can be difficult to simulate in Python. As an
>
> example, some databases treat empty strings as null
> But ORM should handle it somehow, should not it?
> I want to belive that "filter(alias__isnull=True)" works identically across
> all databases.
> So, we need to do same on Python side.
>

You want to believe it, but it isn't true... exactly because, as Anssi said,
some databases (namely Oracle) store empty strings as null, while others (more
correctly) think an empty string is a valid string value.

Shai.

ilya.ka...@jetbrains.com

unread,
Nov 19, 2017, 7:49:09 PM11/19/17
to Django developers (Contributions to Django itself)
Ok, can we have

 is_adult = models.DBCalculatedField(ExpressionWrapper(Q(age__gt=18), output_field=BooleanField()))

Any query for model with this field will be automatically annotated with 
.annotate(is_adult =ExpressionWrapper(Q(age__gt=18), output_field=BooleanField()))

We may use F fields also, something like 
full_name = models.DBCalculatedField(ExpressionWrapper(F('first_name') + F('last_name'), output_field=CharField()))

We may simplify it to
 is_adult = models.DBCalculatedField(expr=Q(age__gt=18), output_field=BooleanField()))

It will work for any object, created from database.

For local computation we may give user ability to provide lambda or decorate method (as in your example).

After it, I can create my own mixin as *third party lib* to patch model with DBCalculatedField and calcuate its expression locally.


class Customer(models.Model, UnsafeCalculateDbLocallyMixin):
    age = models.IntegerField()
    is_adult = models.DBCalculatedField(expr=Q(age__gt=18), output_field=BooleanField()))


Ilya.

Robert Roskam

unread,
Nov 19, 2017, 11:31:23 PM11/19/17
to Django developers (Contributions to Django itself)
+1 for this approach.

Shai Berger

unread,
Nov 20, 2017, 2:29:29 AM11/20/17
to django-d...@googlegroups.com
I will be perfectly happy with that.

ilya.ka...@jetbrains.com

unread,
Nov 20, 2017, 10:31:31 PM11/20/17
to Django developers (Contributions to Django itself)
Thank you all.

I've created a feature request https://code.djangoproject.com/ticket/28822 and will try to implement in my Django fork next week. I am not Django source code guru, so any help is welcome:)

Silvio

unread,
Jan 30, 2018, 10:38:48 PM1/30/18
to Django developers (Contributions to Django itself)
I implemented this feature by having a default manager that overrides get_queryset. it loops over any calculated field declared on the model and annotates that model. Always.

The main issue I ran into is that these fields were not query-able when used for related lookups.

So:

Customer.objects.filter(is_adult=True) worked fine.

But Orders.objects.filter(customer__is_adult=True) did not, because these related queries did *not* go through the objects.get_queryset we know and love.

So if this addition could handle these cases, it'd be great.

Otherwise, it can already be done by just providing a "CalculatedFieldManager" and assigning that to your model.

Robert Roskam

unread,
Feb 3, 2018, 7:06:08 PM2/3/18
to Django developers (Contributions to Django itself)
Hey, 

So has this stalled? Because this looked really great, and I was looking forward to seeing it!


On Monday, November 20, 2017 at 10:31:31 PM UTC-5, ilya.ka...@jetbrains.com wrote:

Jeremy Nauta

unread,
Jan 2, 2023, 3:21:52 PM1/2/23
to Django developers (Contributions to Django itself)
FYI I have posted a tentative first draft of this feature, feedback is greatly appreciated!

https://github.com/django/django/pull/16417
Reply all
Reply to author
Forward
0 new messages