Невозможно сформировать ORM queryset в Django

Мне необходимо включить поле, присутствующее в родительской таблице, в таблицу внуков. Я должен сформировать набор запросов для достижения указанной цели, чтобы вернуть список записей для моего мобильного приложения. Чтобы получить ясное представление о моих моделях, смотрите ниже.

#models.py

class Client(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100)
    contact = models.CharField(max_length=10, unique=True)
    email = models.EmailField(null=True)
    address = models.TextField()
    modified_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'clients'
        ordering = ['id']
        verbose_name = 'Client'
        verbose_name_plural = 'Clients'

    def __str__(self):
        return str(self.id) + ". " + self.name

class Rent(models.Model):
    id = models.AutoField(primary_key=True)
    address = models.TextField()
    rent_amount = models.IntegerField()
    deposit_amount = models.IntegerField()
    rent_date = models.DateField()
    document = models.TextField(null=True)
    remarks = models.TextField(null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)
    client_id = models.IntegerField()

    class Meta:
        db_table = 'rent'
        verbose_name = 'Rent'
        verbose_name_plural = 'Rents'

    def __str__(self):
        return self.id
    
    def get_client_name(self):
        client = Client.objects.get(pk=self.client_id)
        return client.name

class RentSchedule(models.Model):
    id = models.AutoField(primary_key=True)
    rent_due_date = models.DateField()
    paid_amount = models.IntegerField(default=0)
    remaining_amount = models.IntegerField()
    payment_status = models.IntegerField(choices=[(0, 'Unpaid'), (1, 'Paid')], default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    rent_id = models.IntegerField()

    class Meta:
        db_table = 'rent_schedule'
        verbose_name = 'Rent Schedule'
        verbose_name_plural = 'Rent Schedule'

    def __str__(self):
        return self.id
    
    def get_client_name(self):
        rent = Rent.objects.get(pk=self.rent_id)
        client = Client.objects.get(pk=rent.client_id)
        return client.name

Ниже представлен мой класс сериализатора.

#serializers.py

class RentListSerializer(serializers.ModelSerializer):
    client_name = serializers.CharField(source='get_client_name', required=False)
    remaining_amount = serializers.IntegerField(read_only=True)
    payment_status = serializers.ChoiceField(choices=[0, 1], default=0, write_only=True, error_messages={'invalid_choice': 'Options are 0 or 1'})
    due_date = serializers.DateField(format="%d-%m-%Y", source='rent_due_date', read_only=True)
    date_filter = serializers.DateField(input_formats=['%m-%Y'], default=datetime.now().strftime('%m-%Y'), write_only=True, required=False)
    sort_by_client = serializers.ChoiceField(choices=["asc", "desc"], write_only=True, required=False, error_messages={'invalid_choice': 'Options are asc or desc'})
    sort_by_due_date = serializers.ChoiceField(choices=["asc", "desc"], write_only=True, required=False, error_messages={'invalid_choice': 'Options are asc or desc'})
    sort_by_remaining_amount = serializers.ChoiceField(choices=["asc", "desc"], write_only=True, required=False, error_messages={'invalid_choice': 'Options are asc or desc'})
    
    class Meta:
        model = RentSchedule
        fields = ['id', 'client_name', 'paid_amount', 'remaining_amount', 'due_date', 'payment_status', 'date_filter', 'sort_by_client', 'sort_by_due_date', 'sort_by_remaining_amount']
    
    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['payment_status'] = "Paid" if instance.remaining_amount == 0 else "Pending"
        representation.pop('due_date') if instance.remaining_amount == 0 else None
        return representation

Теперь, наконец, ниже показано мое представление, основанное на классе, в котором необходимо изменить набор запросов.

Моя задача - включить поле name из модели Client, которая является прародительской моделью, в набор запросов, упомянутый в комментариях. Я хочу, чтобы оно имело псевдоним client_name, чтобы оно появлялось в записях по этому ключу.

queryset = RentSchedule.objects.all()
<<<Поскольку модели

и Rent RentSchedule не имеют типа данных foreignkey из-за ограничений моего проекта, я не смог использовать метод queryset select_related для достижения этой цели. Я могу предложить только метод annonate, который потребует сложного подзапроса.

Как добиться того, чтобы я мог сортировать набор запросов по полю client_name в порядке 'asc' или 'desc'?

Заранее благодарю вас всех.

Если вам нужно, чтобы поле было в наборе запросов, annotate может быть неизбежным. Конструировать подзапросы проще, если их разбить на части.

Я немного заржавел в создании подзапросов, но это должно быть довольно близко. Возможно, вам придется перенести объявления .values() из определения подзапроса и в выражение подзапроса родительского запроса, если они не работают из коробки.

# Extract `name` from the client
client_query = Client.objects.filter(id=OuterRef('client_id')).values('name')


rent_query = Rent.objects
    # Tack on `client_name` from the first subquery
    .annotate(client_name=Subquery(client_query))
    # Select the relevant Rent objects and pull out `client_name`
    .filter(id=OuterRef('rent_id')).values('client_name')

# Tack on `client_name` from the second subquery to the final queryset
queryset = RentSchedule.objects.annotate(client_name=Subquery(rent_query))
Вернуться на верх