Django基于类的视图(TemplateView)中的URL参数和逻辑

121

我不清楚在Django 1.5的基于类的视图中最佳方法是如何访问URL参数。

考虑以下内容:

视图:

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

我想在我的视图中访问year参数,以便可以执行以下逻辑:

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

如何在像上面这样的继承了TemplateView的类中访问最佳的url参数,以及在哪里理想地放置这样的逻辑,例如在一个方法中?


1
django2中有一个简单的extra_context字典选项,详情请见这里 - Timo
5个回答

151
为了在基于类的视图中访问URL参数,请使用self.argsself.kwargs,因此您可以通过self.kwargs['year']进行访问。

1
请问我理解得对吗,不应该像上面那样直接在视图中创建变量吗?(好像是因为它们是持久的)。另外,我不明白我应该把上面那样的逻辑放在哪个方法中?还有,当我在视图中执行year = self.kwargs['year']时,会出现NameError: self not defined的错误。 - user1319936
4
从技术上讲,你不应该这样做,因为它们是类级别的变量。至于NameError,你在哪里尝试执行year = self.kwargs['year']?你应该在方法中执行它,而不能在类级别上执行。例如,你正在使用TemplateView,这意味着你应该在get_context_data覆盖中执行逻辑。 - Ngenator
6
仅供参考:关于self.request、self.args等的文档可以在https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-display/中找到。 - LShi
如果你想在其他函数之外访问它,也可以在类的def __init__(self):函数中完成。 - Rahat Zaman

82

如果你像这样传递URL参数:

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

使用 self.request.GET 可以在类视图中访问它(它不会出现在self.args或者self.kwargs中):

from django.views.generic.list import ListView

class MyClassBasedView(ListView):
    ...
    def get_queryset(self):
        order_by = self.request.GET.get('order_by') or '-created'
        qs = super().get_queryset()
        return qs.order_by(order_by)

6
谢谢!这让我很困惑...我看到的一些内容都暗示了HTTP参数将在kwargs中。 - foobarbecue
你能展示一下MyClassBasedView的超类的get_queryset()吗?我只需要做 qs=<Object>.objects.<method> - Timo

28

我发现了这个优雅的解决方案,对于django 1.5或更高版本,正如这里所指出的:

Django的通用类视图现在自动在上下文中包含一个view变量。此变量指向您的视图对象。

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

这个问题中找到的派遣解决方案。
由于视图已经在模板上下文中传递,您不需要太担心它。在您的模板文件yearly.html中,可以通过以下方式简单地访问那些视图属性:

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

您可以保留您的 urlconf 不变。

值得提到的是,将信息传递到模板上下文中会覆盖 get_context_data(),因此它在某种程度上会破坏 Django 的动作流。


15

那么,使用Python装饰器来使其更易理解如何?

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']

2
我喜欢这个。这个属性是可重复使用的。 - cezar

8

到目前为止,我只能从get_queryset方法内部访问这些URL参数,虽然我只尝试过ListView而不是TemplateView。我将使用url参数在对象实例上创建一个属性,然后在get_context_data中使用该属性来填充上下文:

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

1
我觉得这很奇怪,当你尝试执行 context['year'] = self.kwargs['year'] 时是否出现了错误或其他问题?它应该在类的任何地方都可以访问。 - Ngenator
@Ngenator:我刚刚建立了一个干净的Django项目来进行双重检查,结果证明你是正确的。我不确定原始代码中是什么阻止了这一点,但我会找出来的 :)。感谢你的提醒。 - hellsgate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接