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

如何在Django视图中通过QuerySet筛选访问Django模型的@property属性

解决Django中无法用@property属性进行ORM筛选的问题

你遇到的错误Related Field got invalid lookup: patient_number,原因很明确:Django的ORM查询是直接转换成SQL语句执行的,而patient_number是Python层面定义的@property属性,数据库里根本没有这个字段,所以ORM没办法直接对它进行过滤操作。

下面给你三个可行的解决方案,你可以根据自己的业务场景选择:

方案一:解析patient_number格式,直接过滤主键

既然你的patient_number是固定格式PN/D/XXXX(XXXX是4位的Patient主键),那我们可以从用户输入的pn里提取出主键值,然后直接用Patient的主键来筛选,这是性能最优的方案。

修改你的视图函数:

def sort_by_pt(request):
    if request.method=='POST':
        pn=request.POST.get('enter_patient_number')
        report = IpdReport.objects.none()  # 默认返回空结果
        try:
            # 拆分字符串获取主键部分,转成整数
            pk_segment = pn.split('/')[-1]
            patient_pk = int(pk_segment)
            # 直接用主键筛选,高效且可靠
            report = IpdReport.objects.filter(patient__pk=patient_pk)
        except (IndexError, ValueError):
            # 处理输入格式错误的情况,比如用户输入不符合预期格式
            pass
        
        total1=report.aggregate(Sum('realization__amount_received'))
        total2=report.aggregate(Sum('realization__deficit_or_surplus_amount'))
        context={'report': report, 'total1':total1, 'total2':total2}
        return render(request, 'account/ipdreport.html', context)
    else:
        sort=EnterPatientNumber()
        return render(request, 'account/sort.html', {'sort':sort})

优点:不需要修改模型,查询效率极高(主键查询是数据库最快的操作);
缺点:依赖patient_number的固定格式,如果以后格式变更,解析逻辑也要跟着调整。

方案二:把patient_number改成数据库字段

如果希望以后能直接用patient_number做ORM查询,或者担心格式会变化,最好的办法是把这个属性变成真正的数据库字段,在保存Patient的时候自动生成值。

修改你的Patient模型:

from django.db.models.signals import post_save
from django.dispatch import receiver

class Patient(models.Model):
    name=models.CharField(max_length=100)
    mr_uid=models.CharField(max_length=9)
    # 添加数据库存储的patient_number字段,设置唯一约束避免重复
    patient_number = models.CharField(max_length=20, unique=True, blank=True, editable=False)
    
    def __str__(self):
        return self.name+"-"+self.patient_number

# 用信号在Patient保存后生成patient_number(避免save方法被重写时出问题)
@receiver(post_save, sender=Patient)
def generate_patient_number(sender, instance, created, **kwargs):
    if created and not instance.patient_number:
        # 生成格式为PN/D/000X的编号
        instance.patient_number = f"PN/D/{instance.pk:04d}"
        # 再次保存(仅更新patient_number字段,避免触发信号循环)
        instance.save(update_fields=['patient_number'])

然后执行数据库迁移:

python manage.py makemigrations
python manage.py migrate

之后你的视图函数就可以直接用原来的代码了:

report=IpdReport.objects.filter(patient__patient_number=pn)

优点:查询逻辑直观,不依赖格式解析,适合频繁用这个字段查询的场景;
缺点:需要修改模型并迁移数据库,还要处理已有数据的编号生成(可以写个脚本批量给旧Patient生成patient_number)。

方案三:Python层面过滤(不推荐大数据量)

如果只是临时测试或者数据量很小,可以先把所有数据拉到内存里,再用Python的过滤方法筛选。但这个方案性能很差,数据多的时候会严重拖慢速度,不建议在生产环境使用。

示例代码:

def sort_by_pt(request):
    if request.method=='POST':
        pn=request.POST.get('enter_patient_number')
        # 用select_related减少数据库查询次数,避免N+1问题
        all_reports = IpdReport.objects.all().select_related('patient')
        # 在Python层面过滤符合条件的记录
        report = [r for r in all_reports if r.patient.patient_number == pn]
        
        # 手动计算聚合值,因为现在report是列表不是QuerySet了
        total1 = sum(r.realization.amount_received for r in report if hasattr(r, 'realization'))
        total2 = sum(r.realization.deficit_or_surplus_amount for r in report if hasattr(r, 'realization'))
        # 保持context和原来的结构一致,方便模板使用
        context={
            'report': report, 
            'total1': {'realization__amount_received__sum': total1}, 
            'total2': {'realization__deficit_or_surplus_amount__sum': total2}
        }
        return render(request, 'account/ipdreport.html', context)
    else:
        sort=EnterPatientNumber()
        return render(request, 'account/sort.html', {'sort':sort})

优点:不需要修改模型,快速实现;
缺点:数据量大时性能极差,内存占用高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:07:35