如何避免在Django中使用反向外键进行如此多的查询

如何避免在Django中使用反向外键进行如此多的查询,django,django-rest-framework,Django,Django Rest Framework,这些是我的模型(简化): 型号: class Tariff(models.Model): name = models.CharField(max_length=25, blank=True, null=True) service = models.ForeignKey(Service, models.DO_NOTHING, blank=True, null=True) cost = models.DecimalField(max_digits=18, decimal_pl

这些是我的模型(简化):

型号:

class Tariff(models.Model):
    name = models.CharField(max_length=25, blank=True, null=True)
    service = models.ForeignKey(Service, models.DO_NOTHING, blank=True, null=True)
    cost = models.DecimalField(max_digits=18, decimal_places=3, blank=True, null=True)
    price = models.DecimalField(max_digits=18, decimal_places=3, blank=True, null=True)
    datefrom = models.DateField(blank=True, null=True)
    dateto = models.DateField(blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'Tariff'


class Service(models.Model):
    name = models.CharField(max_length=255, blank=True, null=True)
    city = models.ForeignKey(City, models.DO_NOTHING, blank=True, null=True)
    class Meta:
        managed = True
        db_table = 'Service'

class CalendarValue(models.Model):
    calendar = models.ForeignKey(Calendar, models.DO_NOTHING, blank=True, null=True)
    service = models.ForeignKey(Service, models.DO_NOTHING, blank=True, null=True)
    date = models.DateField(blank=True, null=True)
    value = models.CharField(max_length=50, blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'CalendarValue'
class AvailabilitySerializer(serializers.ModelSerializer):
    sum_cost = serializers.DecimalField(18, 3)
    sum_price = serializers.DecimalField(18, 3)
    service__id = serializers.IntegerField()
    service__name = serializers.CharField()
    calendarvalue = serializers.SerializerMethodField()

    class Meta:
        model = Tariff
        fields = ('name', 'sum_cost', 'sum_price', 'service__id', 'service__name', 'calendarvalue')

    def get_calendarvalue(self, tariff):
        start_date = self.context.get('datefrom')
        end_date = self.context.get('dateto')
        service = Service.objects.get(id=tariff['service__id'])
        return CalendarValueSerializer(CalendarValue.objects.filter(service=service,
                                                                    date__range=[start_date, end_date]), read_only=True,
                                       many=True).data  
序列化程序:

class Tariff(models.Model):
    name = models.CharField(max_length=25, blank=True, null=True)
    service = models.ForeignKey(Service, models.DO_NOTHING, blank=True, null=True)
    cost = models.DecimalField(max_digits=18, decimal_places=3, blank=True, null=True)
    price = models.DecimalField(max_digits=18, decimal_places=3, blank=True, null=True)
    datefrom = models.DateField(blank=True, null=True)
    dateto = models.DateField(blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'Tariff'


class Service(models.Model):
    name = models.CharField(max_length=255, blank=True, null=True)
    city = models.ForeignKey(City, models.DO_NOTHING, blank=True, null=True)
    class Meta:
        managed = True
        db_table = 'Service'

class CalendarValue(models.Model):
    calendar = models.ForeignKey(Calendar, models.DO_NOTHING, blank=True, null=True)
    service = models.ForeignKey(Service, models.DO_NOTHING, blank=True, null=True)
    date = models.DateField(blank=True, null=True)
    value = models.CharField(max_length=50, blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'CalendarValue'
class AvailabilitySerializer(serializers.ModelSerializer):
    sum_cost = serializers.DecimalField(18, 3)
    sum_price = serializers.DecimalField(18, 3)
    service__id = serializers.IntegerField()
    service__name = serializers.CharField()
    calendarvalue = serializers.SerializerMethodField()

    class Meta:
        model = Tariff
        fields = ('name', 'sum_cost', 'sum_price', 'service__id', 'service__name', 'calendarvalue')

    def get_calendarvalue(self, tariff):
        start_date = self.context.get('datefrom')
        end_date = self.context.get('dateto')
        service = Service.objects.get(id=tariff['service__id'])
        return CalendarValueSerializer(CalendarValue.objects.filter(service=service,
                                                                    date__range=[start_date, end_date]), read_only=True,
                                       many=True).data  
这非常有效,我得到了所有的关税,包括他们的服务和每个服务的所有日历值。
但我注意到,它会对每个服务进行查询,以获取其Calendarvalue。
有没有办法不用这么多查询就能获取所有数据?
谢谢

我的观点

queryset = Tariff.objects.filter(datefrom__lte=start_date, dateto__gte=end_date)
queryset = queryset.filter(service__city=city_code)

# group by
queryset = queryset.values('name', 'service__id', 'service__name')\
    .annotate(sum_cost=Sum('cost'), sum_price=Sum('price'), min_datefrom=Min('datefrom'), max_dateto=Max('dateto'))\
    .order_by('service__id', 'name')

context = {'datefrom': start_date, 'dateto': end_date}

serializer = AvailabilitySerializer(queryset, many=True, context=context)

return serializer.data
我读过与预取相关的内容,但不知道如何实现。

谢谢。

您可以展示您的视图/如何获取此序列化程序的查询集吗?除了
prefetch\u related
和可能很少的其他调整之外,您肯定可以将
.filter(service=service,…)
更改为
.filter(service\u id=private['service\u id'],…)
跳过一个查询。您能展示您的视图/如何获取此序列化程序的查询集吗?除了
prefetch\u related
和可能很少的其他调整之外,您完全可以将
.filter(service=service,…)
更改为
.filter(service\u id=private['service\u id',…)
跳过一个查询。