如何在Django中通过关联模型直接按desig_name过滤组内用户?
你完全可以不用循环筛选再用user_id__in,直接利用Django的关联模型双下划线查询特性,就能像过滤contact_no、user_name一样直接通过**user_filter实现职称名称的过滤,而且性能会更好(因为过滤逻辑在数据库层面完成,不用加载额外数据到内存)。
核心思路
Django ORM支持通过双下划线(__)跨关联模型查询字段。你的User模型通过desigs关联到Designation,再通过desig_type关联到DesigType,所以可以直接用desigs__desig_type__desig_name作为查询字段。
具体实现步骤
修正模型中的小错误
先提个小细节:你的模型类继承的是models.model,应该改成首字母大写的models.Model,否则会报错;另外max_length参数需要传整数,不是字符串,顺手一起修正:class Group(models.Model): group_id = models.CharField(max_length=10) name = models.CharField(max_length=20) class User(models.Model): user_id = models.CharField(max_length=10) grp = models.ForeignKey(Group, null=True, blank=True, on_delete=models.SET_NULL) # 建议补充on_delete参数 user_name = models.CharField(max_length=50) contact_no = models.CharField(max_length=20) class DesigType(models.Model): desig_name = models.CharField(max_length=10) class Designation(models.Model): user = models.ForeignKey(User, null=True, blank=True, related_name='desigs', on_delete=models.SET_NULL) desig_type = models.ForeignKey(DesigType, null=True, blank=True, related_name='desigs', on_delete=models.SET_NULL)构造支持职称过滤的user_filter
当UI传入搜索文本时,你只需要把过滤键设为desigs__desig_type__desig_name__icontains(模糊匹配)或者desigs__desig_type__desig_name__exact(精确匹配),值为用户输入的文本,然后直接传入filter(**user_filter)即可。比如视图中的代码可以改成这样:
def your_view(request, grp_id): group = Group.objects.get(pk=grp_id) search_text = request.GET.get('search_text', '') # 构造user_filter user_filter = {} # 按职称名称模糊匹配(如果是精确匹配就换成__exact) if search_text: user_filter['desigs__desig_type__desig_name__icontains'] = search_text # 保留原有其他字段的过滤逻辑,比如用户名、手机号 # contact_search = request.GET.get('contact_search', '') # if contact_search: # user_filter['contact_no__icontains'] = contact_search # 执行查询,用distinct()避免同一个用户因多个职称被重复返回 _queryset = group.user_set.filter(**user_filter).distinct() # 后续处理返回结果...为什么不需要cached_property?
你之前用cached_property生成的desig_types是用户实例的属性,只能在单个用户实例上判断是否包含某个职称,但无法直接用于QuerySet的过滤。而用ORM的关联查询是直接在数据库层面筛选符合条件的用户,效率更高,代码更简洁。
补充说明
- 如果你的搜索是“或”逻辑(比如用户名包含搜索文本或职称包含搜索文本),那需要用
Q对象来组合条件:from django.db.models import Q user_filter = Q(user_name__icontains=search_text) | Q(desigs__desig_type__desig_name__icontains=search_text) _queryset = group.user_set.filter(user_filter).distinct() - 加上
distinct()很重要,因为一个用户可能有多个职称,查询时会返回多条重复的用户记录,distinct()可以帮你去重。
内容的提问来源于stack exchange,提问作者Sidharth Samant

