Friday, August 5, 2022

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

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








--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/8ba00d6b-c7a2-43cf-9f0f-4bbe6293ecf7n%40googlegroups.com.

No comments:

Post a Comment