如何在 Django 查询中处理可变数量的参数

how to handle a variable number of arguments in django query

我当前的项目让我构建一个页面,可以在其中按类别过滤输出。这意味着默认情况下您会获得完整的项目列表,但您可以通过单击(多个或单个)复选框来缩小输出范围。这个想法是,如果您只想查看两个类别的结果,您可以选择这两个类别,然后只查看这两个类别的项目。 (想像一个亚马逊页面,您可以在其中搜索商品但过滤结果)

这通过使用 get 方法的表单来实现。所以 url 看起来像:

ip/?family=category_one&family=category_two

我的代码如下:

请注意,固件模型通过 ManyToManyField 与 ProductModel 相关,而 ProductModel 通过 ForeignKey 关系与 Family 相关

*型号

class Family(models.Model):
    family_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    slug = models.SlugField(null=False, unique=True)
    family_name = models.CharField(max_length=50, editable=True, unique=True)

class ProductModel(models.Model):
    model_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    model_number = models.CharField(max_length=25, editable=True, unique=True)
    family_id = models.ForeignKey(Family, on_delete=models.DO_NOTHING, editable=True, null=False, blank=False)

class Firmware(models.Model):
    firmware_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    version = models.CharField(max_length=50, help_text='The \'version number\' of the firmware',
                               editable=True)
    model_id = models.ManyToManyField(ProductModel, editable=True)

*查看:

class FirmwareListView(ListView):
    model = Firmware
    context_object_name = 'firmwares'
    qs = QuerySet()

    def get(self, request, *args, **kwargs):
        requested_families = request.GET.getlist("family")
        if requested_families:
            query = Q(model_id__family_id__family_name__iexact=requested_families[0])
            for f in range(1, len(requested_families)):
                query = query | Q(model_id__family_id__family_name__iexact=requested_families[f])
            self.qs = Firmware.objects.filter(query)
        else:
            self.qs = Firmware.objects.all()
        return super().get(request, *args, **kwargs)

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data()
        context['firmware_list'] = self.qs
        return context

这里最重要的部分是:

        requested_families = request.GET.getlist("family")
        if requested_families:
            query = Q(model_id__family_id__family_name__iexact=requested_families[0])
            for f in range(1, len(requested_families)):
                query = query | Q(model_id__family_id__family_name__iexact=requested_families[f])
            self.qs = Firmware.objects.filter(query)

这个查询会工作(因为它 运行s 和 returns 结果但是结果是错误的。结果似乎忽略了 exact requirement and only 运行 a contains query.换句话说,如果我按类别过滤 foo 我会得到结果对于 foodfoo。如果我 运行 通过执行以下查询:

self.qs = Firmware.objects.filter(Q(model_id__family_id__family_name__iexact=requested_families[0]) | Q(model_id__family_id__family_name__iexact=requested_families[1]))

我选择了两个类别,结果很完美。当然,我不能在生产中使用上述方法,因为我永远不知道用户会选择多少类别。

我看过这个 post ( Django query with variable number of filter arguments ) 并尝试了那里的所有建议,但没有成功。

我认为问题是我正在尝试从一个模型中过滤一个字段,该模型 运行 通过多对多字段然后是外键。没有办法解决这个问题(至少我能想到)

非常感谢您提供的任何帮助。我有点卡在这个上,但必须有办法做到这一点。目前我能想到的唯一方法是对每个term单独查询,然后在最后合并结果。

我不得不做类似的事情。我最终使用了一个将 .exclude() 链接到那些未被选中的查询集。我在表单中使用了一点 javascript 来传递正确的(我不想要的)值。

费了好大劲才弄明白这一点。好吧,我造成了我自己的问题。首先,我通过另一种方法传递我的查询结果(我没有在我原来的问题中提到它,因为我认为它是不相关的)改变了查询。

第二种方法以独特的方式对输出进行排序。当我开始仔细研究该方法时,我注意到查询集在其中突然发生了变化。我发现我在哪里犯了错误,瞧!现在可以正常使用了。

最重要的是使用上述方法(并在下面重新显示)创建可变数量的查询参数的方法非常完美。

要在 Django 中创建可变数量的查询参数,您可以使用:

class ViewName():
    qs = QuerySet()

    def get(self, request, *args, **kwargs):
        get_queries = request.GET.getlist('named_get')
        if get_queries:
            query = Q(query_type=get_queries[0])
                for t in range(1, len(filtered_types)):
                    query = query | Q(query_type=get_queries[t]) #obviously if you need an and here just substitute in & where | currently is
            Model.objects.filter(query)