Невозможно сформировать 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))