Изменения модели и кода для получения результатов запроса из БД с несколькими таблицами поиска

Приложение Django, которое я создаю, управляет информацией о клиентах. Короткая версия этого вопроса - как мне построить Django запрос, который будет равен этому sql утверждению...

select cl.id, cl.first, cl.last, ad.zipcode, ph.phone_number, em.email_address
from client.clients as cl
   join client.addresses as ad on cl.id=ad.client_id
   join client.phones as ph on cl.id=ph.client_id
   join client.email_addresses as em on cl.id=em.client_id 
where cl.status_id=1
   and ad.type_id=1
   and ph.type_id=1
   and em.type_id=1;

...даны следующие модели, начиная с сокращенного клиента:

class Client(models.Model):
    id = models.IntegerField(primary_key=True)     
    last = models.CharField(max_length=32)
    first = models.CharField(max_length=32)

Модель адреса:

class Address(models.Model):
    id = models.IntegerField(primary_key=True)
    client = models.ForeignKey(
        'Client',
        on_delete=models.DO_NOTHING,
        blank=False,
        null=False)
    type = models.ForeignKey(
        AddressType,
        on_delete=models.DO_NOTHING,
        blank=False,
        null=False)
    street = models.CharField(max_length=32, blank=True, null=True)
    city = models.CharField(max_length=32, blank=True, null=True)
    state = models.CharField(max_length=2, blank=True, null=True)
    zipcode = models.CharField(max_length=10, blank=True, null=True)

Модель телефона:

class Phone(models.Model):
    id = models.IntegerField(primary_key=True)
    client = models.ForeignKey(
        'Client',
        on_delete=models.DO_NOTHING,
        blank=False,
        null=False)
    type_id = models.ForeignKey(
        PhoneType,        
        on_delete=models.PROTECT,
        blank=False,
        null=False)
    is_primary = models.BooleanField
    country_code = models.CharField(max_length=5)
    phone_number = models.CharField(max_length=16)

Модель адреса электронной почты:

class EmailAddress(models.Model):
    id = models.IntegerField(primary_key=True)
    client = models.ForeignKey(
        'Client',
        on_delete=models.PROTECT,
        blank=False,
        null=False)  
    type_id = models.ForeignKey(
        EmailType,        
        on_delete=models.PROTECT,
        blank=False,
        null=False)
    email_address = models.CharField(max_length=128, blank=False, null=False)

И, наконец, ClientListView, который должен содержать набор запросов:

class ClientListView(ListView):
    model = Client
    template_name = 'client/client_list.html'
    context_object_name = 'clients'

    def get_queryset(self):
        return Client.objects.order_by('-id').filter(status_id=3).select_related(Phone)

Приведенный выше get_queryset и близко не подходит, но в конечном итоге мне нужно получить все связанные данные из всех таблиц поиска, как показано в SQL-запросе выше, и до сих пор ни одна из комбинаций select_related и prefetch_related клаузул, которые я собрал вместе, не сработала.

Помощь будет очень признательна!

Существует много способов получить эту информацию - все зависит от того, как вы хотите получить доступ. Документация Django - отличный источник

Вот пример использования queryset.prefetch_related() (в общем случае самый простой)

clients = Client.objects.filter(status_id=3).prefetch_related(
    "addresses",
    "phones",
    "emails",
)  # will run all queries at once here

print(clients[0].emails[0].email_address)  # will not run additional query here

Вот пример использования queryset.values()

clients = Client.objects.filter(status_id=3).values(
    "id",
    "first",
    "last",
    "addressess__zip_code",
    "phones__phone_number",
    "emails__email_address"
)
print(clients[0]['emails__email_address'])

Вот пример с использованием queryset.annotate() и "F" выражений

clients = Client.objects.filter(status_id=3).annotate(
    zip_code=F("addressess__zip_code"),
    phone_number=F("phones__phone_number"),
    email_address=F("emails__email_address"),
)
print(clients[0].email_address)

(примечание - я не уверен, как здесь будут работать дубликаты - можно использовать что-то вроде специфического для postgres ArrayAgg)


Если ничего не получается, вы всегда можете написать raw SQL:

clients = Client.objects.raw_sql("SELECT my_field FROM clients JOIN ...")
print(clients[0]["my_field"])
Вернуться на верх