访问Django路由/domains/{domain}/{host}时出现DetailView参数错误
解决Django DetailView报错:Generic detail view must be called with either an object pk or a slug
这个报错的原因很明确:Django的DetailView默认会期望从URL中拿到pk或者slug参数,用来自动定位单个模型对象,但你的URL里用的是domain和host两个自定义参数,而且你的get_queryset写法也不符合要求——它应该返回一个QuerySet,但你直接返回了单个对象,这就导致DetailView的内部逻辑混乱了。
下面是具体的修复方案:
1. 重写get_object方法(推荐方式)
因为我们需要通过domain和host两个参数来定位Host对象,直接重写get_object方法来手动查询是最清晰的方式,替换掉你原来的get_queryset方法:
# views.py from django.http import Http404 from django.views.generic import DetailView from .models import Host class HostDetailPageView(DetailView): template_name = 'hosts/hostdetail.html' model = Host def get_object(self, queryset=None): # 从URL的kwargs中获取参数 domain_name = self.kwargs.get('domain') host_name = self.kwargs.get('host') # 检查参数是否存在 if not domain_name or not host_name: raise Http404("Missing required parameters") # 通过Domain的name和Host的name来查询对象 # 这里假设你的Domain模型有一个`name`字段,和URL中的<str:domain>对应 try: return Host.objects.get(domain__name=domain_name, name=host_name) except Host.DoesNotExist: raise Http404("Host does not exist") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 如果需要添加额外的上下文数据,比如你之前提到的启用的域名列表,可以在这里添加 # 示例:context['enabled_domains'] = Domain.objects.filter(is_enabled=True) return context
2. 为什么原来的写法有问题?
- 你的
get_queryset方法返回了filtered.first(),这是单个Host对象,但get_queryset的要求是返回QuerySet实例,DetailView会基于这个查询集再去用pk/slug筛选单个对象,返回单个对象会直接打破这个逻辑。 - DetailView默认的对象查找逻辑只认
pk或slug参数,你的URL里没有这两个参数,所以它不知道怎么定位对象,就抛出了那个错误。
3. 确认URL配置没问题
你的urls.py配置是正确的,参数名domain和host和视图中self.kwargs获取的名称一致,不需要修改:
# urls.py urlpatterns = [ path('domains/', DomainsPageView.as_view()), path('domains/<str:domain>', DomainHostsPageView.as_view()), path('domains/<str:domain>/<str:host>', HostDetailPageView.as_view()), path('', TemplateView.as_view(template_name="hosts/index.html")) ]
这样修改后,当访问/domains/{domain}/{host}时,DetailView会通过你重写的get_object方法,根据传入的域名和主机名找到对应的Host对象,就不会再抛出那个错误了。
内容的提问来源于stack exchange,提问作者steelcityamir
相关产品推荐
相关产品推荐

