Paramètres d'URL et logique dans les vues basées sur les classes Django (TemplateView)

94

Je ne sais pas comment il est préférable d'accéder aux paramètres d'URL dans les vues basées sur les classes dans Django 1.5.

Considérer ce qui suit:

Vue:

from django.views.generic.base import TemplateView


class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        return context

URLCONF:

from .views import Yearly


urlpatterns = patterns('',
    url(
        regex=r'^(?P<year>\d+)/$',
        view=Yearly.as_view(),
        name='yearly-view'
    ),
)

Je veux accéder au yearparamètre dans ma vue, donc je peux faire une logique comme:

month_names = [
    "January", "February", "March", "April", 
    "May", "June", "July", "August", 
    "September", "October", "November", "December"
]

for month, month_name in enumerate(month_names, start=1):
    is_current = False
    if year == current_year and month == current_month:
        is_current = True
        months.append({
            'month': month,
            'name': month_name,
            'is_current': is_current
        })

Comment accéder au mieux au paramètre url dans les CBV comme ci-dessus qui est sous-classé TemplateViewet où devrait-on idéalement placer la logique comme celle-ci, par exemple. dans une méthode?

Nayan
la source
Il y a l'option du extra_contextdict simple dans django2, voir ici
Timo

Réponses:

113

Pour accéder aux paramètres d'url dans les vues basées sur les classes, utilisez self.argsou self.kwargsalors vous y accéderiez en faisantself.kwargs['year']

Ngenator
la source
1
Est-il bien compris que je ne suis pas censé créer des variables directement dans la vue comme j'ai ci-dessus? (quelque chose à propos d'eux étant persistants). Aussi, je ne comprends pas où je suis censé placer la logique comme ci-dessus, par exemple. dans quelle méthode? Aussi quand je le fais year = self.kwargs['year']dans la vue que j'obtiens NameError: self not defined.
2
Techniquement, vous ne devriez pas car ils sont au niveau de la classe et sont des variables de classe. Quant à la NameError, où essayez-vous de faire year = self.kwargs['year']? Vous devriez le faire dans une méthode, vous ne pouvez pas le faire au niveau de la classe. Ainsi, par exemple, vous utilisez un TemplateViewqui signifie que vous feriez la logique de votre get_context_dataremplacement.
Ngenator
4
Juste pour le référencement: la documentation sur self.request, self.args etc. peut être trouvée dans docs.djangoproject.com/en/1.10/topics/class-based-views/…
LShi
Vous pouvez également le faire en def __init__(self):fonction dans la classe si vous souhaitez y accéder en dehors d'autres fonctions.
Rahat Zaman
60

Si vous transmettez un paramètre d'URL comme celui-ci:

http://<my_url>/?order_by=created

Vous pouvez y accéder dans une vue basée sur les classes en utilisant self.request.GET(il n'est pas présenté dans self.argsni dans self.kwargs):

class MyClassBasedView(ObjectList):
    ...
    def get_queryset(self):
        order_by = self.request.GET.get('order_by') or '-created'
        qs = super(MyClassBasedView, self).get_queryset()
        return qs.order_by(order_by)
niekas
la source
4
Merci! Cela m'a dérouté ... Je continue à lire des trucs qui impliquent que les paramètres HTTP seront dans les kwargs.
foobaritchen
Pouvez-vous afficher le get_queryset () de la superclasse de MyClassBasedView? Je voudrais juste faire qs=<Object>.objects.<method>
Timo
24

J'ai trouvé cette solution élégante, et pour django 1.5 ou supérieur, comme indiqué ici :

Les vues basées sur les classes génériques de Django incluent désormais automatiquement une variable de vue dans le contexte. Cette variable pointe vers votre objet de vue.

Dans votre views.py:

from django.views.generic.base import TemplateView    

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"
    # Not here 
    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    # dispatch is called when the class instance loads
    def dispatch(self, request, *args, **kwargs):
        self.year = kwargs.get('year', "any_default")

    # other code

    # needed to have an HttpResponse
    return super(Yearly, self).dispatch(request, *args, **kwargs)

La solution d'expédition trouvée dans cette question .
Comme la vue est déjà passée dans le contexte du modèle, vous n'avez pas vraiment besoin de vous en soucier. Dans votre fichier modèle annual.html, il est possible d'accéder à ces attributs de vue simplement en:

{{ view.year }}
{{ view.current_year }}
{{ view.current_month }}

Vous pouvez garder votre urlconf tel quel.

Il vaut la peine de mentionner qu'introduire des informations dans le contexte de votre modèle écrase get_context_data (), donc cela brise en quelque sorte le flux du bean d'action de django .

Evhz
la source
8

Jusqu'à présent, je n'ai pu accéder à ces paramètres d'url qu'à partir de la méthode get_queryset, bien que je ne l'ai essayé qu'avec un ListView et non un TemplateView. J'utiliserai le paramètre url pour créer un attribut sur l'instance d'objet, puis j'utiliserai cet attribut dans get_context_data pour remplir le contexte:

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_queryset(self):
        self.year = self.kwargs['year']
        queryset = super(Yearly, self).get_queryset()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        context['year'] = self.year
        return context
porte de l'enfer
la source
Je trouve cela étrange, y a-t-il une erreur ou quelque chose lorsque vous essayez de faire context['year'] = self.kwargs['year']? Il doit être accessible n'importe où dans la classe.
Ngenator
@Ngenator: Je viens de configurer un projet django propre pour vérifier et il s'avère que vous avez raison. Je ne suis pas sûr de ce qui empêchait cela dans mon code d'origine mais je le découvrirai :). Merci pour le heads-up
hellsgate
7

Que diriez-vous d'utiliser simplement des décorateurs Python pour rendre cela intelligible:

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']
danizen
la source
J'aime celui la. La propriété est réutilisable.
cezar