Могу ли я определить псевдоним для иностранного поля в Django?
Я хочу определить псевдоним для набора связанных внешних ключей, чтобы затем использовать его в общей функции фильтрации. Для более подробного объяснения вот упрощенный пример моего случая использования, демонстрирующий, чего я пытаюсь достичь:
models.py
from django.db import models
class Family(models.Model):
family_name = models.CharField(max_length=100)
pets: models.Manager['Pet'] = ...
members: models.Manager['Person'] = ...
def __str__(self):
return f'{self.id} - {self.family_name}'
class Pet(models.Model):
class PetType(models.TextChoices):
CAT = 'cat'
DOG = 'dog'
LIZARD = 'lizard'
name = models.CharField(max_length=100)
type = models.CharField(max_length=100, choices=PetType)
family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name='pets')
def __str__(self):
return f'{self.id} - {self.name} {self.family.family_name} [{self.type}]'
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name='members')
@property
def pets(self) -> models.Manager[Pet]:
return self.family.pets
def __str__(self):
return f'{self.id} - {self.name} {self.family.family_name}'
Если у меня есть сущность Person
, я могу использовать ее свойство pets
, чтобы получить домашних животных из семьи, то я ищу способ добавить псевдоним/аннотацию/что угодно к набору запросов Person
, чтобы я мог определить:
def has_a_cat(query_set):
return query_set.filter(pets__type='cat')
и затем иметь возможность использовать его для наборов запросов Family
и Person
. Я знаю, что могу отфильтровать набор запросов Person
по типу животного, потому что Person.objects.filter(family__pets__type='cat')
работает отлично, но мне хотелось бы найти способ псевдонима family__pets
для pets
, чтобы я мог использовать общий фильтр.
Я пробовал использовать .annotate(pets=F('family__pets'))
и .alias(pets=F('family__pets))
, но при фильтрации по pets__type
я получаю следующую ошибку:
django.core.exceptions.FieldError: Unsupported lookup 'type' for BigAutoField or join on the field not permitted.
Решение, которое сработало, было предоставлено willem-van-ossem в комментариях под моим вопросом.
Для решения этой проблемы мы можем аннотировать набор запросов с помощью FilteredRelation
, что позволит применить те запросы, которые я хотел применить. Далее пользовательская Manager
реализация выглядит так:
class PersonManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(pets=FilteredRelation('family__pets'))
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name='members')
objects = PersonManager()
@property
def pets(self) -> models.Manager[Pet]:
return self.family.pets
def __str__(self):
return f'{self.id} - {self.name} {self.family.family_name}'
После этого мы можем фильтровать на основе pets
как модели Family
, так и модели Person
и наборы запросов (см. вопрос об остальных классах моделей).