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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:53:17