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

如何结合Django Filter与DAL实现字段链式筛选?

问题

尝试将Django Filters(DF)与Django Autocomplete Light(DAL)结合,实现多字段链式筛选:

  • 选择特定国家;
  • 筛选出所选国家下的所有地区;
  • 筛选出所选地区下的所有城市。

已分别实现DF和DAL功能正常,单独测试DAL转发有效,但结合后仅国家字段自动完成正常,地区和城市字段无法正确筛选,控制台显示转发字典值未设置。

相关代码如下:

视图代码

class CountryAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Country.objects.all()
        
        if self.q:
            qs = qs.filter(name__istartswith=self.q)
        
        return qs

class RegionAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Region.objects.all()
        country = self.forwarded.get("country", None)
        
        if country:
            qs = qs.filter(country=country)
        
        if self.q:
            qs = qs.filter(name__istartwith=self.q)
        
        return qs

class CityAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = City.objects.all()  # Base queryset for cities
        region = self.forwarded.get("region", None)  # Get 'region' value from forwarded data
        
        if region:
            qs = qs.filter(region=region)  # Correctly filter cities by region
        
        if self.q:  # Filter cities based on the search query (q)
            qs = qs.filter(name__istartswith=self.q)
        
        return qs

Django Filter类代码

class RoomFilter(FilterSet):
    
    country = ModelChoiceFilter(lookup_expr="iexact",
                                queryset=Country.objects.all(), 
                                widget=autocomplete.ModelSelect2(url="auth_app:country-autocomplete"),
                                label=_("Land"))
    region = ModelChoiceFilter(lookup_expr="iexact", 
                               queryset=Region.objects.all(),
                               widget=autocomplete.ModelSelect2(url="auth_app:region-autocomplete", forward=["country"]),
                               label=_("Län"))
    city = ModelChoiceFilter(lookup_expr="iexact", 
                             queryset=City.objects.all(), 
                             widget=autocomplete.ModelSelect2(url="auth_app:city-autocomplete", forward=["region"]),
                             label=_("Stad"))

    class Meta():
        model = Room
        fields = ["country", "region", "city"]

Room模型代码

class Room(models.Model):
    member = models.ForeignKey(Member, on_delete=models.CASCADE, null=True, related_name="rooms")
    room_category = models.CharField(_("Kategori"), blank=False, choices=RoomCategory.choices(), max_length=100, default=RoomCategory.choices()[0][0])
    title = models.CharField(_("Rubrik"), max_length=settings.ROOM_TITLE_LENGTH, blank=False, validators=[text_validator])
    country = models.ForeignKey(Country, blank=False, max_length=128, on_delete=models.PROTECT)
    region = models.ForeignKey(Region, blank=False, max_length=128, on_delete=models.PROTECT)
    city = models.ForeignKey(City, blank=False, max_length=128, on_delete=models.PROTECT)
    description = models.TextField(_("Beskrivning"), blank=False, max_length=settings.ROOM_DESCRIPTION_LENGTH)

注:country、region、city字段为django cities light模型的外键。


解决方案

1. 修复视图中的拼写错误

RegionAutoComplete视图里的name__istartwith是拼写错误,应为name__istartswith,该错误会导致地区搜索筛选失效,甚至中断逻辑:

# 修正后的RegionAutoComplete代码
class RegionAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Region.objects.all()
        country = self.forwarded.get("country", None)
        
        if country:
            qs = qs.filter(country=country)
        
        if self.q:
            qs = qs.filter(name__istartswith=self.q)  # 修正拼写
        
        return qs

2. 处理转发字段的类型转换

DAL转发的外键值是字符串类型的ID,直接用于外键筛选可能不匹配,需转换为整数:

# 修正RegionAutoComplete
class RegionAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = Region.objects.all()
        country_id = self.forwarded.get("country")
        if country_id:
            try:
                qs = qs.filter(country_id=int(country_id))
            except ValueError:
                pass  # 忽略无效ID
        
        if self.q:
            qs = qs.filter(name__istartswith=self.q)
        
        return qs

# 修正CityAutoComplete
class CityAutoComplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = City.objects.all()
        region_id = self.forwarded.get("region")
        if region_id:
            try:
                qs = qs.filter(region_id=int(region_id))
            except ValueError:
                pass
        
        if self.q:
            qs = qs.filter(name__istartswith=self.q)
        
        return qs

3. 优化Filter类的查询集

Filter中的ModelChoiceFilter无需手动指定queryset,DAL会自动通过Autocomplete视图获取动态筛选后的结果,手动指定静态查询集会覆盖动态逻辑:

class RoomFilter(FilterSet):
    country = ModelChoiceFilter(
        lookup_expr="iexact",
        widget=autocomplete.ModelSelect2(url="auth_app:country-autocomplete"),
        label=_("Land")
    )
    region = ModelChoiceFilter(
        lookup_expr="iexact",
        widget=autocomplete.ModelSelect2(url="auth_app:region-autocomplete", forward=["country"]),
        label=_("Län")
    )
    city = ModelChoiceFilter(
        lookup_expr="iexact",
        widget=autocomplete.ModelSelect2(url="auth_app:city-autocomplete", forward=["region"]),
        label=_("Stad")
    )

    class Meta():
        model = Room
        fields = ["country", "region", "city"]

4. 确保模板加载DAL资源

在渲染Filter表单的模板中,必须添加{{ filter.form.media }}标签,自动引入DAL的Select2 JS和CSS资源,保证转发联动的JS逻辑正常运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:58:11