使用多个查询参数和外键进行 DRF 过滤

1 python django filtering django-rest-framework

我是 Django 新手,我正在尝试创建一条可以调用的路线,以从数据库中检索车辆数组,但我希望用户能够在 url 中提供多个查询参数(例如http://127.0.0.1:8000/vehicles/?year=2020&make=Toyota:) 。我遇到的问题是我的车辆模型包含对 make 和 v_model 的外键的引用(如此命名是为了避免与 Django“模型”发生冲突)。我有一个看起来不太优雅的解决方案。事实上,每个搜索字段都有三个嵌套条件语句,这让我很怀疑。我尝试使用“filters.SearchFilter”,但我只能提供一个作为搜索基础的值。因此,像这样的请求:http://127.0.0.1:8000/vehicles/?search=2020&search=Toyota只会搜索品牌为“丰田”的车辆,而忽略“年份”参数。

还有其他更干净或更“Django 批准”的方法吗?

这是我的代码:

模型.py:

class Make(models.Model):
    name = models.CharField(max_length=200, unique=True)
    def __str__(self):
        return self.name


class VModel(models.Model):
    name = models.CharField(max_length=200, unique=True)
    make = models.ForeignKey(Make, on_delete=models.CASCADE)
    def __str__(self):
        return self.name


class Vehicle(models.Model):
    make = models.ForeignKey(Make, on_delete=models.CASCADE)
    v_model = models.ForeignKey(VModel, on_delete=models.CASCADE)
    year = models.CharField(max_length=5)
    def __str__(self):
        return self.year + " " + self.v_model.name
Run Code Online (Sandbox Code Playgroud)

视图.py:

这是我对filters.SearchFilter的尝试:

    queryset = Vehicle.objects.all()
    serializer_class = VehicleSerializer
    filter_backends = [filters.SearchFilter]
    search_fields = ['year', 'v_model__name','make__name']
Run Code Online (Sandbox Code Playgroud)

这是我的“工作”解决方案,看起来很老套:(注意:我使用的是name__icontains这样,如果用户输入“Toyot”,它仍然会获得所有带有丰田品牌的汽车)。

class VehicleListViewSet(viewsets.ModelViewSet):
    serializer_class = VehicleSerializer
    queryset = Vehicle.objects.all()
    pagination_class = CustomPagination


    def get_queryset(self):
        qs = super().get_queryset()
        selected_make = self.request.query_params.get('make', None)
        if selected_make:
            try:
                found_make = Make.objects.get(name__icontains=selected_make)
            except:
                return []
            if found_make:
                if found_make.id:
                    qs = qs.filter(make=found_make.id)
        selected_v_model = self.request.query_params.get('v_model', None)
        if selected_v_model:
            try:
                found_v_model = VModel.objects.get(name__icontains=selected_v_model)
            except:
                return []
            if found_v_model:
                if found_v_model.id:
                    qs = qs.filter(v_model=found_v_model.id)
        selected_year = self.request.query_params.get('year', None)
        if selected_year:
            qs = qs.filter(year=selected_year)
        return qs
Run Code Online (Sandbox Code Playgroud)

Mar*_*ino 5

你不应该使用filters.SearchFilter. 相反,请在 ViewSet 中使用属性,如文档本节filterset_fields中的示例。

你的视图集将是这样的:

class VehicleListViewSet(viewsets.ModelViewSet):
    serializer_class = VehicleSerializer
    queryset = Vehicle.objects.all()
    pagination_class = CustomPagination
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['year', 'v_model__name','make__name']
Run Code Online (Sandbox Code Playgroud)

(请注意,没有get_queryset覆盖)您将能够像这样查询您的 API:

http://127.0.0.1:8000/vehicles/?year=2020&make__name=Toyota&v_model__name=Corolla