Parámetros de URL y lógica en vistas basadas en clases de Django (TemplateView)

94

No me queda claro cómo es mejor acceder a los parámetros de URL en vistas basadas en clases en Django 1.5.

Considera lo siguiente:

Ver:

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

Quiero acceder al yearparámetro en mi vista, para poder hacer una lógica como:

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
        })

¿Cuál sería la mejor manera de acceder al parámetro url en CBV como el anterior que está subclasificado TemplateViewy dónde debería uno colocar idealmente la lógica como esta, por ejemplo. en un método?

Nayan
fuente
Existe la opción del extra_contextdict simple en django2, ver aquí
Timo

Respuestas:

113

Para acceder a los parámetros de la URL en vistas basadas en clases, use self.argso self.kwargspara acceder a él haciendoself.kwargs['year']

Ngenator
fuente
1
¿Se entiende correctamente que se supone que no debo crear variables directamente en la vista como lo hice arriba? (algo acerca de que sean persistentes). Además, no entiendo dónde se supone que debo colocar la lógica como la anterior, por ejemplo. en que metodo También cuando lo hago year = self.kwargs['year']en la vista me sale NameError: self not defined.
2
Técnicamente, no debería hacerlo, ya que están a nivel de clase y son variables de clase. En cuanto a NameError, ¿dónde estás intentando hacer year = self.kwargs['year']? Deberías hacerlo en un método, no puedes hacerlo a nivel de clase. Entonces, por ejemplo, está utilizando un, lo TemplateViewque significa que haría la lógica en su get_context_dataanulación.
Ngenator
4
Solo como referencia: la documentación sobre self.request, self.args, etc. se puede encontrar en docs.djangoproject.com/en/1.10/topics/class-based-views/…
LShi
También puedes hacerlo en def __init__(self):función en la clase si quieres acceder a ella fuera de otras funciones.
Rahat Zaman
60

En caso de que pase un parámetro de URL como este:

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

Puede acceder a él en la vista basada en clases usando self.request.GET(no se presenta en self.argsni en 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
fuente
4
¡Gracias! Esto me ha confundido ... sigo leyendo cosas que implican que los parámetros HTTP estarán en los kwargs.
foobarbecue
¿Puede mostrar el get_queryset () de la superclase de MyClassBasedView? Simplemente lo haría qs=<Object>.objects.<method>
Timo
24

Encontré esta elegante solución, y para django 1.5 o superior, como se señala aquí :

Las vistas genéricas basadas en clases de Django ahora incluyen automáticamente una variable de vista en el contexto. Esta variable apunta a su objeto de vista.

En su 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 solución de envío que se encuentra en esta pregunta .
Como la vista ya se pasó dentro del contexto de la plantilla, realmente no necesita preocuparse por eso. En su archivo de plantilla annual.html, es posible acceder a esos atributos de vista simplemente por:

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

Puede mantener su urlconf como está.

Vale la pena mencionar que obtener información en el contexto de su plantilla sobrescribe get_context_data (), por lo que de alguna manera está rompiendo el flujo del bean de acción de django .

Evhz
fuente
8

Hasta ahora, solo he podido acceder a estos parámetros de URL desde el método get_queryset, aunque solo lo probé con ListView, no TemplateView. Usaré el parámetro url para crear un atributo en la instancia del objeto, luego usaré ese atributo en get_context_data para completar el contexto:

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
la puerta del infierno
fuente
Me parece extraño, ¿hay algún error o algo cuando intentas hacer context['year'] = self.kwargs['year']? Debe ser accesible desde cualquier lugar de la clase.
Ngenator
@Ngenator: Acabo de configurar un proyecto django limpio para verificarlo y resulta que estás en lo correcto. No estoy seguro de qué impedía esto en mi código original, pero lo averiguaré :). Gracias por el
aviso
7

¿Qué tal si usamos decoradores de Python para hacer esto inteligible?

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']
danizen
fuente
Me gusta este. La propiedad es reutilizable.
cezar