按 foreignKey 关系中的字段过滤

Posted

技术标签:

【中文标题】按 foreignKey 关系中的字段过滤【英文标题】:Filter by fields from foreignKey relationships 【发布时间】:2021-12-20 08:48:36 【问题描述】:

我有一堆模型,其中一些是连接的(通过外键关系),我编写了一个序列化程序,它允许我打印出我想要的所有连接的字段,并省略我不想要的查看。伟大的。现在我还有一个基本过滤器,它使用包含所有外键的模型 (PmP),但现在我想为来自不同模型的字段(字段名称 e 来自 PmPr 模型)添加另一个过滤器,通过外键连接读取的一个(模型PmP 中的li 连接到模型PmL,包含字段pro 连接到模型PmPr,其中字段e 是)。但我不知道该怎么做,据我所知,我不能在我的viewPmPLListView)中设置两个filter_classes?!而且我不知道如何通过外键关系访问该字段。那么我该怎么做呢?如果我可以通过我现有的过滤器从PmPr 模型访问e 字段 - 那对我来说也很好,我不需要两个过滤器类(如果可能的话)。这只是我的第一个想法。 (顺便说一句。对不起,奇怪的名字,但不幸的是我不允许写真实的名字)

这些是我的模型(至少是相关的):

class PmP(models.Model):
    created_at = models.DateTimeField()
    pr = models.DecimalField(max_digits=6, decimal_places=2)
    li = models.ForeignKey(PmL, models.DO_NOTHING)
    se = models.ForeignKey('PmSe', models.DO_NOTHING)

    class Meta:
        managed = False
        db_table = 'pm_p'



class PmL(models.Model):
    u = models.TextField()
    pro = models.ForeignKey('PmPr', models.DO_NOTHING)
    sh = models.ForeignKey('PmS', models.DO_NOTHING)
    active = models.IntegerField()

    class Meta:
        managed = False
        db_table = 'pm_l'



class PmSe(models.Model):
    name = models.TextField()
    s_i_id = models.TextField(blank=True, null=True)
    sh = models.ForeignKey('PmS',
                             models.DO_NOTHING,
                             blank=True,
                             null=True)

    class Meta:
        managed = False
        db_table = 'pm_se'


class PmPr(models.Model):
    name = models.TextField()
    e = models.CharField(max_length=13)
    created_at = models.DateTimeField()
    cus = models.ForeignKey(PmC, models.DO_NOTHING)
    u_v_p = models.DecimalField(max_digits=10,
                              decimal_places=2,
                              blank=True,
                              null=True)
    cf = models.IntegerField(blank=True, null=True)
    s_k_u = models.IntegerField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'pm_pr'

这就是我的序列化器的样子:

class PmPLSerializer(serializers.ModelSerializer):
    # id = serializers.SerializerMethodField('get_l_id')
    u = serializers.SerializerMethodField('get_l_u')
    sh = serializers.SerializerMethodField('get_sh_name')
    name = serializers.SerializerMethodField('get_pro_name')
    e = serializers.SerializerMethodField('get_pro_e')
    u_v_p = serializers.SerializerMethodField('get_pro_u_v_p')
    s_k_u = serializers.SerializerMethodField('get_pro_s_k_u')
    se = serializers.SerializerMethodField('get_se_name')
    pr = serializers.SerializerMethodField('get_pr')
    created_at = serializers.SerializerMethodField('get_created_at')

    class Meta:
        model = PmP
        # fields = '__all__'
        fields = ('u', 'sh', 'name', 'e', 's_k_u', 'u_v_p', 'pr',
                  'created_at', 'se')
        depth = 2

    def get_l_id(self, obj):
        return obj.li.id

    def get_l_u(self, obj):
        return obj.li.u

    def get_sh_name(self, obj):
        return obj.li.sh.name

    def get_pro_name(self, obj):
        return obj.li.pro.name

    def get_pro_e(self, obj):
        return obj.li.pro.e

    def get_pro_u_v_p(self, obj):
        return obj.li.pro.u_v_p

    def get_pro_s_k_u(self, obj):
        return obj.li.pro.s_k_u

    def get_se_name(self, obj):
        return obj.se.name

    def get_pr(self, obj):
        return obj.pr

    def get_created_at(self, obj):
        return obj.created_at

这是我的过滤器类:

class PmPFilter(rfilters.FilterSet):
    class Meta:
        model = PmP
        fields = [
            "created_at",
            "pr",
        ]

    for field in ["pr"]:
        exec(f'min_field = rfilters.NumberFilter(field, lookup_expr="gte")')
        exec(f'max_field = rfilters.NumberFilter(field, lookup_expr="lte")')


    # filter by date as "is_less_than_or_equal_to"
    written_to = rfilters.CharFilter(method="created_at_to", label="created_at to")

    # filter by date as "is_greater_than_or_equal_to"
    written_from = rfilters.CharFilter(method="created_at_from", label="created_at from")

    # filter by exact date
    written = rfilters.CharFilter(method="created_at_exact", label="created_at exact")

    def created_at_exact(self, queryset, name, value):
        year, month, day, hour, minute, second = self.parse_date(value)
        cdate = datetime(year, month, day, hour, minute, second)
        return queryset.filter(created_at=cdate)

    def created_at_to(self, queryset, name, value):
        year, month, day, hour, minute, second = self.parse_date(value)
        cdate = datetime(year, month, day, hour, minute, second)
        return queryset.filter(created_at__lte=cdate)

    def created_at_from(self, queryset, name, value):
        year, month, day, hour, minute, second = self.parse_date(value)
        cdate = datetime(year, month, day, hour, minute, second)
        return queryset.filter(created_at__gte=cdate)

    def parse_date(self, value):
        return (
            parser.parse(value).year,
            parser.parse(value).month,
            parser.parse(value).day,
            parser.parse(value).hour,
            parser.parse(value).minute,
            parser.parse(value).second,
        )

最后,这是我的观点:

class PmPLListView(generics.ListAPIView):
    queryset = PmP.objects.all()
    serializer_class = PmPLSerializer
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    ordering_fields = ["created_at", "pr"]
    filter_class = PmPFilter
    fields = ("created_at", "pr")
    filter_fields = fields
    search_fields = fields

    def get_queryset(self):
        """
        This view should return a list of all data
        """
        return PmP.objects.filter()

【问题讨论】:

【参考方案1】:

哦,我明白了!我可以用两个下划线访问外来关系。所以我修改了我的过滤器类:

class PmPFilter(rfilters.FilterSet):
    class Meta:
        model = PmPrice
        fields = [
            "created_at",
            "pr",
            "li__pro__e",
        ]
 ...

在我的PmPLListView 视图中,我还添加了双下划线来访问该字段:

class PmPLListView(generics.ListAPIView):
    queryset = PmP.objects.all()
    serializer_class = PmPLSerializer
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    ordering_fields = ["created_at", "pr"]
    filter_class = PmPFilter
    fields = ("created_at", "pr", "li__pro__e")
    filter_fields = fields
    search_fields = fields

现在我可以按字段过滤e

【讨论】:

以上是关于按 foreignKey 关系中的字段过滤的主要内容,如果未能解决你的问题,请参考以下文章

在外键上按字段过滤

按 belongsToMany 关系字段过滤

在Django中按关系字段过滤多对多关系

Laravel 按关系字段过滤项目

Laravel - 按“字段及其关系模型字段”之间的条件过滤模型

组合框过滤数据库中的字段?