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

如何为Django业务目录实现分类+城市/地区层级查询页面

Django业务目录层级查询页面解决方案

问题描述

我开发了一个Django业务目录系统,已完成分类页面(如abc.com/category/manufacturing/)的开发,但在实现分类+城市(如abc.com/category/manufacturing/london/)、分类+城市+地区(如abc.com/category/manufacturing/london/southwark/)的层级查询页面时遇到困难,尝试多种方法均失败。以下是我的代码文件:

现有代码

models.py

from django.db import models
from django.urls import reverse
from tinymce import models as tinymce_models


# Create your models here.

class City(models.Model):
    city = models.CharField(max_length=50)
    slug = models.SlugField()

    def __str__(self):
        return str(self.city)

class District(models.Model):
    district = models.CharField(max_length=50)
    slug = models.SlugField()
    city = models.ForeignKey(City, on_delete=models.CASCADE)    

    def __str__(self):
        return str(self.district)

class FirmaCategory(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField()

    class Meta:
        verbose_name_plural = 'Firma Kategorileri'

    def __str__(self):
        return str(self.title)

class Firma(models.Model):
    category = models.ForeignKey(FirmaCategory, related_name="Firma", on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    slug = models.SlugField()
    adres = tinymce_models.HTMLField()
    tel = tinymce_models.HTMLField()
    website = tinymce_models.HTMLField()
    email = tinymce_models.HTMLField()
    intro = tinymce_models.HTMLField()
    body = tinymce_models.HTMLField()
    city = models.ForeignKey(City,related_name="FirmaCategory" ,on_delete=models.CASCADE)
    district = models.ForeignKey(District,related_name="FirmaCategory", on_delete=models.CASCADE)

    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = 'Firmalar'

    def __str__(self):
        return str(self.title)

    def get_absolute_url(self):
        return reverse('firma-frontpage')   

views.py

from django.db.models import Q
from django.shortcuts import render
from .models import Firma, FirmaCategory, City, District
from django.utils.html import format_html
from django.views.generic import CreateView
from .forms import FirmaForm

# Create your views here.
def firmaana(request):
    firmalar = Firma.objects.all()
    context = {
        'firmalar': firmalar
    }
    return render(request, 'firma/firma-frontpage.html', context)

def firmadetail(request, slug):
    firmax = Firma.objects.get(slug=slug)
    context = {
        'firmax': firmax,
    }  
    return render(request, 'firma/firma-detail.html', context)

def firmacategory(request, slug):
    firmacategory = FirmaCategory.objects.get(slug=slug)
    context = {
        'firmacategory': firmacategory
    }  
    return render(request, 'firma/firma-category.html', context)

urls.py

urlpatterns = [
    path('kategory/city/<city>/', views.list_businesses_by_city, name='firmail'),
    path("", views.firmaana, name="firma-frontpage"),
    path("<slug:slug>/", views.firmadetail, name="firma-detail"),
    path("kategory/<slug:slug>/", views.firmacategory, name="firma-category"),
    path("1/add-firma/", FirmaEkle.as_view(), name="firma-ekle"),
    path('tinymce/', include('tinymce.urls')),
]

解决方案

1. 调整URL配置

修改urls.py,添加层级路由并调整顺序(更具体的路由必须放在前面,避免被通用路由匹配):

urlpatterns = [
    path("", views.firmaana, name="firma-frontpage"),
    # 分类+城市+地区路由
    path("kategory/<slug:category_slug>/<slug:city_slug>/<slug:district_slug>/", views.firma_category_city_district, name="firma-category-city-district"),
    # 分类+城市路由
    path("kategory/<slug:category_slug>/<slug:city_slug>/", views.firma_category_city, name="firma-category-city"),
    # 基础分类路由
    path("kategory/<slug:slug>/", views.firmacategory, name="firma-category"),
    path("<slug:slug>/", views.firmadetail, name="firma-detail"),
    path("1/add-firma/", FirmaEkle.as_view(), name="firma-ekle"),
    path('tinymce/', include('tinymce.urls')),
    # 移除旧的单一城市路由
    # path('kategory/city/<city>/', views.list_businesses_by_city, name='firmail'),
]

2. 编写层级查询视图

在views.py中添加两个新视图,分别处理不同层级的筛选逻辑:

def firma_category_city(request, category_slug, city_slug):
    # 获取分类和城市实例
    category = FirmaCategory.objects.get(slug=category_slug)
    city = City.objects.get(slug=city_slug)
    # 筛选该分类+城市下的企业,用select_related减少数据库查询
    firmalar = Firma.objects.filter(category=category, city=city).select_related('category', 'city', 'district')
    # 获取当前城市的所有地区,用于页面导航
    districts = District.objects.filter(city=city)
    
    context = {
        'category': category,
        'city': city,
        'firmalar': firmalar,
        'districts': districts
    }
    return render(request, 'firma/firma-category-city.html', context)

def firma_category_city_district(request, category_slug, city_slug, district_slug):
    category = FirmaCategory.objects.get(slug=category_slug)
    city = City.objects.get(slug=city_slug)
    district = District.objects.get(slug=district_slug, city=city)
    # 筛选分类+城市+地区下的企业
    firmalar = Firma.objects.filter(category=category, city=city, district=district).select_related('category', 'city', 'district')
    
    context = {
        'category': category,
        'city': city,
        'district': district,
        'firmalar': firmalar
    }
    return render(request, 'firma/firma-category-city-district.html', context)

3. 模板页面适配

创建对应层级的模板文件(如firma-category-city.html和firma-category-city-district.html),展示筛选结果和层级导航。例如在分类页面中添加城市跳转链接:

<!-- firma-category.html 示例片段 -->
<h1>{{ firmacategory.title }}</h1>
<h3>按城市筛选:</h3>
<ul>
{% for city in City.objects.all %}
    <li><a href="{% url 'firma-category-city' firmacategory.slug city.slug %}">{{ city.city }}</a></li>
{% endfor %}
</ul>

<h3>该分类下的所有企业:</h3>
<ul>
{% for firma in firmacategory.Firma.all %}
    <li><a href="{% url 'firma-detail' firma.slug %}">{{ firma.title }}</a></li>
{% endfor %}
</ul>

4. 模型关联优化(可选)

给Firma模型的外键设置更清晰的反向关联名称,方便后续查询:

class Firma(models.Model):
    # ... 其他字段 ...
    city = models.ForeignKey(City, related_name="firms", on_delete=models.CASCADE)
    district = models.ForeignKey(District, related_name="firms_in_district", on_delete=models.CASCADE)

关键注意事项

  • 路由顺序必须严格:层级更细的路由要放在基础路由前面,否则会被提前匹配导致404。
  • 使用select_related优化查询性能,避免N+1查询问题。
  • 给slug字段添加unique=True约束(如slug = models.SlugField(unique=True)),确保每个分类、城市、地区的slug唯一,避免查询时出现多个匹配实例的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 13:46:26