如何在Django中对含GenericRelation的GenericForeignKey字段进行筛选排序?
Django ORM 对含GenericForeignKey的模型进行筛选与排序(基于GenericRelation)
我有一个Django模型AssetAssociation,它包含一个GenericForeignKey,关联至Assets、User、Locations等多个模型。
AssetAssociation模型代码:
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class AssetAssociation(models.Model): asset = models.ForeignKey(Assets, on_delete=models.CASCADE, related_name='asset_association') target_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) target_id = models.UUIDField() target_object = GenericForeignKey('target_type', 'target_id') # User / Asset / Location checkout_date = models.DateField(null=True, blank=True) expected_checkin_date = models.DateField(null=True, blank=True) checkin_date = models.DateField(null=True, blank=True) action_type = models.CharField(max_length=50, choices=ASSOCIATION_ACTION_TYPE_CHOICES)
相关模型代码:
from django.contrib.contenttypes.fields import GenericRelation class Assets(models.Model): name = models.CharField(max_length=100, blank=True, null=True) asset_tag = models.CharField(max_length=255) asset_associations = GenericRelation('AssetAssociation', content_type_field='target_type', object_id_field='target_id') class User(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) asset_associations = GenericRelation('AssetAssociation', content_type_field='target_type', object_id_field='target_id') class Locations(models.Model): name = models.CharField(max_length=100) asset_associations = GenericRelation('AssetAssociation', content_type_field='target_type', object_id_field='target_id')
请问如何使用Django的ORM对这些字段进行有效的筛选与排序?具体来说,如何编写一个利用GenericRelation实现字段筛选的函数?
解决方案
1. 基础筛选(基于AssetAssociation自身字段)
直接针对模型本身的字段做筛选,无需关联其他模型:
# 筛选所有已签出但未归还的关联记录 checked_out = AssetAssociation.objects.filter(action_type='checkout', checkin_date__isnull=True) # 筛选逾期未归还的记录 from django.utils import timezone today = timezone.now().date() overdue_records = AssetAssociation.objects.filter( expected_checkin_date__lt=today, checkin_date__isnull=True )
2. 关联目标模型的筛选(通过GenericForeignKey)
结合ContentType筛选关联到特定类型或实例的记录:
from django.contrib.contenttypes.models import ContentType # 筛选所有关联到User的记录 user_ct = ContentType.objects.get_for_model(User) user_associations = AssetAssociation.objects.filter(target_type=user_ct) # 筛选关联到特定用户的记录 specific_user = User.objects.get(id='your-user-uuid') user_specific_records = AssetAssociation.objects.filter( target_type=user_ct, target_id=specific_user.id ) # 筛选关联到名称含"仓库"的Location的记录 location_ct = ContentType.objects.get_for_model(Locations) warehouse_records = AssetAssociation.objects.filter( target_type=location_ct, target_object__name__icontains='仓库' )
3. 利用GenericRelation从反向模型筛选
从关联的目标模型出发,直接获取对应的AssetAssociation记录:
# 获取某用户的所有已签出资产关联 user = User.objects.get(id='your-user-uuid') user_checked_out_assets = user.asset_associations.filter( action_type='checkout', checkin_date__isnull=True ) # 获取某资产所有关联到Location的记录 asset = Assets.objects.get(asset_tag='TAG001') asset_location_links = asset.asset_association.filter( target_type=ContentType.objects.get_for_model(Locations) )
4. 排序操作
使用order_by实现排序,支持自身字段或跨关联字段(跨关联时需明确目标模型类型):
# 按签出日期降序排序 sorted_by_checkout = AssetAssociation.objects.order_by('-checkout_date') # 仅对关联到User的记录,按用户姓氏升序排序 user_ct = ContentType.objects.get_for_model(User) sorted_user_links = AssetAssociation.objects.filter(target_type=user_ct).order_by('target_object__last_name')
5. 封装通用筛选函数
编写可复用的函数,支持按目标模型、实例、额外条件筛选及排序:
from django.contrib.contenttypes.models import ContentType from django.utils import timezone def filter_asset_associations(target_model=None, target_instance=None, filters=None, order_by=None): """ 通用的AssetAssociation筛选函数 :param target_model: 目标模型类(如User、Locations),可选 :param target_instance: 目标模型实例,可选 :param filters: 额外筛选条件字典,如{'action_type': 'checkout'} :param order_by: 排序字段,如'-checkout_date' :return: QuerySet """ queryset = AssetAssociation.objects.all() if target_model: ct = ContentType.objects.get_for_model(target_model) queryset = queryset.filter(target_type=ct) if target_instance: ct = ContentType.objects.get_for_model(type(target_instance)) queryset = queryset.filter(target_type=ct, target_id=target_instance.id) if filters: queryset = queryset.filter(**filters) if order_by: queryset = queryset.order_by(order_by) return queryset # 使用示例 # 1. 获取所有关联到Location且逾期的记录 overdue_locations = filter_asset_associations( target_model=Locations, filters={'expected_checkin_date__lt': timezone.now().date(), 'checkin_date__isnull=True'}, order_by='expected_checkin_date' ) # 2. 获取特定用户的已签出记录 specific_user = User.objects.get(id='your-user-uuid') user_checkouts = filter_asset_associations( target_instance=specific_user, filters={'action_type': 'checkout', 'checkin_date__isnull=True'} )
内容的提问来源于stack exchange,提问作者Hashim khan
相关产品推荐
相关产品推荐

