将 Pk 或 Slug 传递给 Django 中的通用 DetailView?

Posted

技术标签:

【中文标题】将 Pk 或 Slug 传递给 Django 中的通用 DetailView?【英文标题】:Passing Pk or Slug to Generic DetailView in Django? 【发布时间】:2015-11-04 00:46:44 【问题描述】:

我是基于 Django 类的视图的新手。我正在尝试制作一个简单的视图来获取帖子的详细信息。 我的意见.py:

from django.views.generic import ListView, View, DetailView 
class GenreDetail(DetailView):
            model = Post
            template_name = "post.html"

我的 urls.py:

urlpatterns = [
        url(r'(?P<post_id>[^/]+)', GenreDetail.as_view(), name = 'post'),
        url(r'(?P<post_id>[^/]+)/(?P<slug>[-\w]+)$', GenreDetail.as_view()),
    ] 

我得到的错误:

AttributeError at /2/memoirs-of-a-geisha-by-arthur-golden
Generic detail view GenreDetail must be called with either an object pk or a slug.

所以 pk 或 slug 不会传递到 Generic Detailview。我该如何通过?我假设它可以从 url 中获取,但事实并非如此。

【问题讨论】:

【参考方案1】:

使用path:

from django.urls import path
from . import views

urlpatterns = [
    path('<pk>/', views.GenreDetail.as_view(), name="post")]

对于slug

path('<slug:slug>/', views.GenreDetail.as_view(), name="post")

【讨论】:

那么生成的url是什么?【参考方案2】:

问题是你必须告诉DetailView 它应该在URL 中使用post_id 关键字而不是默认的pkslug 才能获得将显示的对象。

这可以通过设置pk_url_kwarg属性来完成:

(你的 url 定义也是错误的,总是以 $ 结束你的 url 定义。下面是更正的版本)

url(r'(?P<post_id>\d+)$', GenreDetail.as_view(), name = 'post'),
url(r'(?P<post_id>\d+)/(?P<slug>[-\w]+)$', GenreDetail.as_view()),

根据上面的 url 模式,以下 url 将匹配:

/2 /2/memoirs-of-a-geisha-by-arthur-golden
from django.views.generic import DetailView 

class GenreDetail(DetailView):
    model = Post
    template_name = "post.html"
    pk_url_kwarg = "post_id"

或者,您可以在您的 url 中将 post_id 更改为 pk,这样您就不必触摸视图中的任何内容:

url(r'(?P<pk>\d+)$', GenreDetail.as_view(), name = 'post'),
url(r'(?P<pk>\d+)/(?P<slug>[-\w]+)$', GenreDetail.as_view()),

【讨论】:

在 url 中将查询参数的名称更改为 pk 也可以。谢谢。【参考方案3】:

如果您想使用 post_id 或 slug 获取详细信息,那么您的网址应该是这样的

url(r'post/(?P<post_id>\d+)/$', GenreDetail.as_view(), name = 'post_detail'),
url(r'post/(?P<slug>[-\w]+)/$', GenreDetail.as_view(), name = 'post_detail_slug'),

而你的观点应该是这样的

from django.views.generic import DetailView 

class GenreDetail(DetailView):
    model = Post
    template_name = "post.html"
    pk_url_kwarg = "post_id"
    slug_url_kwarg = 'slug'
    query_pk_and_slug = True

更多详情请阅读docs。

【讨论】:

请注意 query_pk_and_slug 是在 Django 1.8 中引入的 pk_url_kwarg = "post_id" slug_url_kwarg = 'slug' query_pk_and_slug = True 这里非常优雅的解决方案。太棒了。【参考方案4】:

按照您定义的顺序检查网址模式

所以这里:

urlpatterns = [
        url(r'(?P<post_id>[^/]+)', GenreDetail.as_view(), name = 'post'),
        url(r'(?P<post_id>[^/]+)/(?P<slug>[-\w]+)$', GenreDetail.as_view()),
    ] 

...第一个模式正在匹配(因为它不以$ 结尾,所以额外的段被忽略了)

...而且该模式只传递一个关键字 arg

通常,让多个 url 模式指向同一个视图是个坏主意。如果可能的话,您应该尝试制作一个单一的正则表达式(例如使用optional groups)来处理特定视图的url 的各种情况。这样更明确。

另一方面,简单地颠倒你的模式的顺序,把更明确的模式放在第一位也可以工作并且是正确的(这是 Django 的 urlpatterns 规则!)

urlpatterns = [
        url(r'(?P<post_id>[^/]+)/(?P<slug>[-\w]+)$', GenreDetail.as_view()),
        url(r'(?P<post_id>[^/]+)', GenreDetail.as_view(), name = 'post'),
    ] 

正如@ozgur 提到的,您还需要通过设置pk_url_kwarg 来告诉视图使用post_id 而不是pk

【讨论】:

@Tjorriemorrie 哪个部分不起作用?你有错误吗?这是一个旧答案,因此在较新版本的 Django 中界面可能已更改,但基本原则仍然适用 127.0.0.1:8000/players/2818 与 players/(?P&lt;pk&gt;\d+)/(?P&lt;nick&gt;[-\w])$ [name='player_detail'] 不匹配。我已经在 DetailView 中设置了 url_kwargs @Tjorriemorrie 认为 url 不应该与那个 urlpattern 匹配......你的 urlconf 中是否还有更通用的正则表达式,比如 players/(?P&lt;pk&gt;\d+)

以上是关于将 Pk 或 Slug 传递给 Django 中的通用 DetailView?的主要内容,如果未能解决你的问题,请参考以下文章

难以将 slug 添加到 Django 中的通用详细信息视图

python 没有PK或Slug的Django Generic DetailView

Django:“如何在将 url() 传递给 MyClass.as_view() 之前从 url() 获取 slug?”或“如何检查注册是不是开放?”

如何为 DetailView 指定 pk 或 slug 以外的其他内容

与 Django 通用视图中的 pk 不同的 url 路径

必须使用对象 pk 或 slug 调用通用详细视图 ProfileView