You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化Django视图以高效处理大数据集的技术咨询

Django视图性能优化:大量客户数据的状态过滤查询优化

问题背景

需要优化处理大量客户数据的Django视图tClients,该视图根据status查询参数(取值-1/0/1)过滤客户,对应客户模型中is_debt属性的状态值。当前实现存在性能瓶颈,且无法在使用values()提升性能的同时完成状态过滤。

当前视图实现

@login_required(login_url='/accounts/login')
def tClients(request):
   
    filter_status = request.GET.get('status', None)

    if filter_status is not None and filter_status in ['-1', '0', '1']:
        clients = Client.objects.select_related()
        # values('id','nni','nom','date','telephone','address','observation')

        clients = [client for client in clients if client.is_debt == int(filter_status)]
    else:
        clients = Client.objects.values('id','nni','nom','date','telephone','address','observation')

    context = {
        'tClients': clients,
    }

    return render(request, 'clients/list_clients.html', context)

Client模型定义

class Client(models.Model):
    nni = models.CharField(max_length=20, verbose_name=_('nni'), unique=True)
    uid = models.CharField(max_length=500)
    nom = models.CharField(max_length=255, verbose_name=_('nom'))
    date = models.DateField(verbose_name=_("date  d'inscription"))
    img = models.ImageField(upload_to='client/qrcodes', blank=True, null=True, verbose_name=_("Image"))
    telephone = models.CharField(max_length=255, null=True, verbose_name=_("Telephone"))
    address = models.CharField(null=True, max_length=255, verbose_name=_("Address"))
    observation = models.TextField(null=True, verbose_name=_("Observation"))
    user = models.ForeignKey(User, blank=True, on_delete=models.SET_NULL, null=True)

    @property
    def is_debt(self):
        current_date = timezone.localdate()
        debts = self.client_detter.all()
        if debts.exists():
            for debt in debts:
                duration = current_date - debt.date_dette
                duration_in_days = duration.days
                if duration_in_days >= debt.dette_duration and debt.amount != debt.dette_montant_payee:
                    return -1
                elif duration_in_days < debt.dette_duration and debt.amount != debt.dette_montant_payee:
                    return 0
            return 1
        else:
            return 1

遇到的问题

  1. 使用values()返回字典时,无法访问模型的@property属性is_debt,无法完成状态过滤。
  2. 尝试用Case/When注解is_debt未生效,因为多债务关联导致逻辑匹配错误。
  3. 当前过滤逻辑需要加载所有客户数据到内存后再筛选,数据量大时性能极差。

优化方案

核心思路

将is_debt的逻辑转移到数据库层面,用Exists子查询判断客户的债务状态,结合annotate和filter完成数据库级过滤,同时通过only()和values()减少数据传输和实例创建开销。

优化后的视图代码

from django.db.models import Case, When, Value, IntegerField, Exists, OuterRef, Q, F
from django.utils import timezone
from django.contrib.auth.decorators import login_required

@login_required(login_url='/accounts/login')
def tClients(request):
    filter_status = request.GET.get('status', None)
    current_date = timezone.localdate()

    # 基础查询:仅选择需要的字段,减少数据加载量
    base_query = Client.objects.only('id', 'nni', 'nom', 'date', 'telephone', 'address', 'observation')

    if filter_status is not None and filter_status in ['-1', '0', '1']:
        # 子查询1:判断客户是否存在逾期未还清的债务
        has_overdue_debt = Exists(
            ClientDetter.objects.filter(
                client=OuterRef('pk'),
                ~Q(amount=F('dette_montant_payee')),
                date_dette__lte=current_date - F('dette_duration')
            )
        )
        # 子查询2:判断客户是否存在未逾期但未还清的债务
        has_active_debt = Exists(
            ClientDetter.objects.filter(
                client=OuterRef('pk'),
                ~Q(amount=F('dette_montant_payee')),
                date_dette__gt=current_date - F('dette_duration')
            )
        )

        # 注解客户的债务状态,完成数据库级过滤
        clients = base_query.annotate(
            is_debt=Case(
                When(has_overdue_debt, then=Value(-1)),
                When(has_active_debt, then=Value(0)),
                default=Value(1),
                output_field=IntegerField()
            )
        ).filter(is_debt=int(filter_status))
        
        # 转换为字典格式,减少模型实例开销
        clients = clients.values('id', 'nni', 'nom', 'date', 'telephone', 'address', 'observation')
    else:
        clients = base_query.values('id', 'nni', 'nom', 'date', 'telephone', 'address', 'observation')

    context = {'tClients': clients}
    return render(request, 'clients/list_clients.html', context)

关键优化点说明

  1. 数据库级过滤:用Exists子查询替代内存遍历,直接在数据库层面判断客户的债务状态,避免加载全量数据到内存。
  2. 精准状态判断:Exists只要匹配到一条符合条件的债务就返回True,完美契合原is_debt属性的优先级逻辑(逾期未还>-1优先于未逾期未还>-0,最后是无债务/已还清>-1)。
  3. 减少数据传输:用only()指定所需字段,避免加载img、uid等不需要的字段;最终用values()返回字典,省去模型实例的创建和初始化开销。
  4. 修复原注解逻辑问题:原Case/When直接关联client_detter会产生多对多匹配问题,Exists子查询确保每个客户只返回一条状态判断结果。

内容的提问来源于stack exchange,提问作者Muhammadoufi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:54:52