Django基于类的视图:如何向as_view方法传递额外参数?

113

我有一个自定义的基于类的视图。

# myapp/views.py
from django.views.generic import *

class MyView(DetailView):
    template_name = 'detail.html'
    model = MyModel

    def get_object(self, queryset=None):
        return queryset.get(slug=self.slug)
我想像这样传递slug参数(或其他参数)到视图中。
MyView.as_view(slug='hello_world')

我需要覆盖任何方法才能做到这一点吗?

7个回答

125
如果你的urlconf看起来像这样:
url(r'^(?P<slug>[a-zA-Z0-9-]+)/$', MyView.as_view(), name = 'my_named_view')

那么 slug 将在你的视图函数(如 'get_queryset')中可用,像这样:

self.kwargs['slug']

22
为避免在这是可选参数的情况下出现异常,请使用 self.kwargs.get('slug', None) - Risadinha
6
只是好奇,self.kwargs 是在什么时候/哪里被填充的?我正在寻找设置它的基类函数。 - binithb
在 https://github.com/django/django/blob/master/django/views/generic/base.py 中的class View: def as_view(cls, **initkwargs): def view(request, *args, **kwargs): - Apollo Data
不回答问题。 - Kireeti K
这种方法现在已经过时了,现在你可以使用 url('<slug:slug>', MyView.as_view(), name='my_named_view') - Rahat Zaman

105

传递给as_view方法的每个参数都是View类的实例变量。这意味着要将slug作为参数添加,您必须在子类中创建它作为实例变量:

# myapp/views.py
from django.views.generic import DetailView

class MyView(DetailView):
    template_name = 'detail.html'
    model = MyModel
    # additional parameters
    slug = None

    def get_object(self, queryset=None):
        return queryset.get(slug=self.slug)

这样应该可以让 MyView.as_view(slug='hello_world') 正常工作。

如果您通过关键字传递变量,请使用 Erikkson 先生建议的方法:https://dev59.com/imgu5IYBdhLWcg3wMkfF#11494666


3
永远不要使用import *。已编辑您的帖子。 - holms
1
@holms 为了未来读者的启示,PEP8 表示“应避免使用通配符导入(from <module> import )”。应该不如必须强烈,这只是一个例子,但是肯定应该避免*使用通配符导入:https://www.python.org/dev/peps/pep-0008/#imports - user9903
任何地方都没有必须的东西,我们可以以任何方式打破任何东西,但是PEP8只是实践建议,在Python社区中,尽可能使用所有这些实践是一种基本规则,以避免进一步的问题。无论如何,当我提交我的代码时,我的linter总是空的 :)。 - holms
实际变量的slug='hello_world'的值是多少? - Gonzalo Dambra
我猜这里的slug是一个类变量而不是实例变量。 - Aagam Sheth

19
值得注意的是,您无需覆盖get_object()方法就能够根据作为关键字参数传递的slug查找对象 - 您可以使用SingleObjectMixin的属性。 https://docs.djangoproject.com/en/1.5/ref/class-based-views/mixins-single-object/#singleobjectmixin
# views.py
class MyView(DetailView):
    model = MyModel
    slug_field = 'slug_field_name'
    slug_url_kwarg = 'model_slug'
    context_object_name = 'my_model'

# urls.py
url(r'^(?P<model_slug>[\w-]+)/$', MyView.as_view(), name = 'my_named_view')

# mymodel_detail.html
{{ my_model.slug_field_name }}

(slug_fieldslug_url_kwarg默认都是'slug')


1
我应该将我的答案变成维基答案并将您的代码添加到其中吗? - user9903

16

您可以在urls.py中传递参数。 https://docs.djangoproject.com/en/1.7/topics/http/urls/#passing-extra-options-to-view-functions

这也适用于通用视图。例如:

url(r'^$', views.SectionView.as_view(), { 'pk': 'homepage', 'another_param':'?'}, name='main_page'),

在这种情况下,传递给视图的参数不一定是View类的实例变量。使用此方法,您无需将默认页面名称硬编码到YourView模型中,而是可以从urlconf中将其作为参数传递。


1
刚刚更新了Django 3.2的链接: https://docs.djangoproject.com/en/3.2/topics/http/urls/#passing-extra-options-to-view-functions - Ali Rn

15

如果你想要将一个对象添加到模板的上下文中,你可以重写get_context_data方法并将它添加到它的上下文中。如果需要request.user,请求也是self的一部分。

def get_context_data(self, **kwargs):
        context = super(MyTemplateView, self).get_context_data(**kwargs)
        if 'slug' in self.kwargs:
            context['object'] = get_object_or_404(MyObject, slug=self.kwargs['slug'])
            context['objects'] = get_objects_by_user(self.request.user)

        return context

MyObject是什么? - Rob Kwasowski

9

正如Yaroslav Nikitenko所说,如果您不想在View类中硬编码一个新的实例变量,您可以像这样从urls.py向视图函数传递额外选项

url(r'^$', YourView.as_view(), {'slug': 'hello_world'}, name='page_name')

我想补充一下如何在视图中使用它。您可以实现以下方法之一:

# If slug is optional
def the_function(self, request, slug=None):
    # use slug here

# if slug is an optional param among others
def the_function(self, request, **kwargs):
    slug = kwargs.get("slug", None)
    other_param = kwargs.get("other_param", None)

# If slug is required
def the_function(self, request, slug):
    # use slug here

1
我本想在Yaroslav Nikitenko的答案中进行编辑,但被拒绝了,因此我自己写了一个,因为我觉得这是我需要时缺失的信息。 - Emile Bergeron
@YaroslavNikitenko 事后看来,这篇文章太长了,最好以新答案的形式回复。 - Emile Bergeron
@EmileBergeron 最初的问题是关于通用视图,例如DetailView类。您能解释一下如何在那里使用它吗? - bartaelterman
有没有办法让这些额外的参数在基于类的视图中对所有HTTP方法都可用?目前我正在做 get(self, request, model: Model): pass, put(self, request, model: Model): pass, 等等。如果我可以在一个地方分配和类型化'model',那就更整洁了。 - kiloton
@kiloton,如果在文档和 Stack Overflow 上搜索无法解决您的问题,那么您应该提出一个新的问题。 - Emile Bergeron

4

对于 django 3.0 版本,以下是我尝试过的有效方法:

# myapp/views.py
from django.views.generic import DetailView

class MyView(DetailView):
    template_name = 'detail.html'
    slug = None

    def get_object(self, queryset=None):
        self.slug = self.kwargs.get('slug', None)
        return queryset.get(slug=self.slug)

# myapp/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('slug/<slug:slug>/', views.MyView.as_view(), name='myview_by_tag'),
]

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