Annotated model fields – consistent access in model meta, reverse foreign keys

24 views
Skip to first unread message

jools

unread,
Aug 5, 2022, 5:11:30 AM8/5/22
to Django users
Dear community,

here’s a question/thought about how you handle annotated fields.

Here’s my use case:

```
from django import models

class Person(models.Model):
    given_name = models.CharField(max_length=100)
    family_name = models.CharField(max_length=100)
   
    # Some people prefer a different name
    preferred_name = models.CharField(max_length=200, blank=True, null=True)    
```

I’d like to have a field `full_name` that is annotated whenever I fetch data from the database (`@property def full_name(self): …` doesn’t work for me).

It should return the `preferred_name` (if present) or join `given_name` and `family_name`.

I was happy to accomplish this by using a custom manager:

```
from django.db.models.functions import Coalesce, Concat
from django.db.models import CharField, Value

class PersonManager(models.Manager):
    def get_queryset(self):
        qs = super().get_queryset()
       
        # The annotation details don’t matter, actually.
        return qs.annotate(
            full_name=Coalesce(
                F('preferred_name'),
                Concat(F('given_name'), Value(' '), F('family_name')),
                output_field=CharField()
            )
        )
       
# … and add `objects = PersonManager()` to person model.
```

When querying directly, this works nicely: `Person.objects.first().full_name` returns the full name.

Though, when following a reverse foreign key relation, things go wrong:

```
class Restaurant(models.Model):
    name = models.CharField(max_length=100)
    owner = models.ForeignKey(Person, on_delete=models.SET_NULL)
```

```
Restaurant.objects.first().owner.full_name
# gives an exception, because the custom manager’s code isn't executed.
```
I read that you can set the `base_manager_name` on the `Person`’s meta class, but I didn’t have luck with that either.
Moreover, you can’t use the `full_name` for ordering on the Person’s meta class.

Long story short: What’s your preferred way that allows access to annotated properties consistently?
(I was thinking about custom `AnnotatedCharField`s, `AnnotatedTextField`s etc. that allow defining annotated fields on the model itself. Tempting, huh?)

Looking forward to your comments!

Best regards
Julian








jools

unread,
Aug 5, 2022, 5:20:27 AM8/5/22
to Django users
Reply all
Reply to author
Forward
0 new messages