You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Django的ModelViewSet中使用Django Filters,结合City与Country模型实现过滤

实现跨City和Country模型的名称过滤

看起来你想要打造一个能同时搜索城市、国家名称的API接口,用Django Filter结合ModelViewSet来完成这个需求对吧?我来一步步帮你搞定:

1. 先把依赖配置到位

首先确认你已经装了django-filter

pip install django-filter

接着在项目的settings.py里把它加入INSTALLED_APPS:

INSTALLED_APPS = [
    # ... 你的其他应用
    'django_filters',
]

如果你想全局启用过滤后端,可以在REST_FRAMEWORK配置里添上,但你已经在ViewSet里指定了filter_backends,这一步也可以跳过:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
    ]
}

2. 定义关联双模型的FilterSet

你需要写一个自定义FilterSet,让它能同时匹配City的name和关联Country的name字段。假设你的City模型有个外键country关联到Country模型:

import django_filters
from django.db.models import Q
from .models import City

class LocationFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(method='filter_by_location_name', label='Location Name')

    def filter_by_location_name(self, queryset, name, value):
        # 用Q对象实现OR查询:匹配城市名 或 所属国家名包含搜索词
        return queryset.filter(
            Q(name__icontains=value) |
            Q(country__name__icontains=value)
        )

    class Meta:
        model = City
        fields = ['name']

这里用__icontains是做不区分大小写的模糊匹配,要是需要精确匹配,换成__exact就行。

3. 完善你的ModelViewSet

你代码里的get_location_list看起来没写完,其实完全不需要自定义这个方法——Django Filter会自动帮你处理过滤逻辑。把ViewSet调整成这样就好:

from rest_framework import viewsets
from rest_framework.permissions import AllowAny
from django_filters.rest_framework import DjangoFilterBackend
from .models import City
from .serializers import CityListSerializer
from .filters import LocationFilter

class LocationSearchAPI(viewsets.ModelViewSet):
    queryset = City.objects.all().order_by('name')
    permission_classes = [AllowAny]
    serializer_class = CityListSerializer
    filter_backends = (DjangoFilterBackend,)
    # 要是还想用SearchFilter的全局搜索功能,就保留下面这行替换上面的filter_backends
    # filter_backends = (DjangoFilterBackend, SearchFilter)
    filter_class = LocationFilter

    # 除非有额外逻辑要处理,否则不需要重写list方法
    # 真要重写的话可以参考下面的示例:
    # def list(self, request, *args, **kwargs):
    #     queryset = self.filter_queryset(self.get_queryset())
    #     page = self.paginate_queryset(queryset)
    #     if page is not None:
    #         serializer = self.get_serializer(page, many=True)
    #         return self.get_paginated_response(serializer.data)
    #     serializer = self.get_serializer(queryset, many=True)
    #     return Response(serializer.data)

4. 测试你的接口

现在你就可以用想要的URL格式测试了:

http://localhost:8000/api/v1/geo/location-search/?name=Santa+Fe

这个请求会返回所有名称包含"Santa Fe"的城市,以及所属国家名称包含"Santa Fe"的城市。

额外小提示

  • 如果你的City模型关联Country的外键字段不是country,记得把Q(country__name__icontains=value)里的country改成你实际的字段名。
  • 要是想同时支持精确匹配和模糊匹配,可以在FilterSet里加多个过滤字段,比如name_exactname_contains
  • 要是需要返回国家的详细信息,确保你的CityListSerializer里包含了Country的嵌套序列化器:
from rest_framework import serializers
from .models import City, Country

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ['id', 'name']

class CityListSerializer(serializers.ModelSerializer):
    country = CountrySerializer()
    class Meta:
        model = City
        fields = ['id', 'name', 'country']

内容的提问来源于stack exchange,提问作者KitKit

火山引擎 最新活动