web-dev-qa-db-fra.com

Passer Pk ou Slug à DetailView générique dans Django?

Je suis nouveau sur Django Vues basées sur la classe. J'essaie de faire une vue simple pour obtenir les détails d'un message. Mon views.py:

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

Mon urls.py:

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

Erreur que j'obtiens:

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.

Le pk ou le slug n'est donc pas transmis à la vue de détail générique. Comment passer ça? Je suppose qu'à partir de l'URL, il peut reprendre mais ce n'est pas le cas.

13
Coderaemon

les modèles d'url sont vérifiés dans l'ordre dans lequel vous les définissez

alors voici:

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

... le premier motif est mis en correspondance (car il ne se termine pas par $ donc le segment supplémentaire est juste ignoré)

... et ce modèle ne transmet qu'un seul mot clé arg

En général, c'est une mauvaise idée d'avoir plusieurs modèles d'url pointant vers la même vue. Si possible, vous devriez essayer de créer une seule expression régulière (par exemple en utilisant groupes optionnels ) qui gère les différents cas de l'URL pour une vue particulière. C'est plus explicite de cette façon.

D'un autre côté, inverser simplement l'ordre de vos modèles pour mettre le plus explicite en premier fonctionnerait également et serait correct (c'est la règle Django des urlpatterns!)

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

Comme @ozgur le mentionne, vous devez également indiquer à la vue d'utiliser post_id au lieu de pk en définissant pk_url_kwarg

11
Anentropic

Si vous souhaitez récupérer des détails à l'aide de post_id ou slug, vos URL doivent être comme ceci

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'),

Et votre vue devrait être comme ça

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

Pour plus de détails, veuillez lire le docs .

8
Rajesh Kaushik

Le problème est que vous devez dire à DetailView qu'il doit utiliser post_id mot-clé dans l'URL au lieu de ceux par défaut pk ou slug afin d'obtenir l'objet qui sera affiché.

Cela peut être fait en définissant pk_url_kwarg attribut:

(Votre définition d'URL est également erronée, terminez toujours vos définitions d'URL par $. Ci-dessous la version corrigée)

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

Les URL suivantes correspondront compte tenu des modèles d'URL ci-dessus:

  • / 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"

Alternativement, vous pouvez simplement modifier post_id à pk dans votre URL pour que vous n'ayez rien à toucher dans votre vue:

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

En utilisant chemin :

from Django.urls import path
from . import views

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

Pour slug:

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