Django - 将模型名称作为参数传递给通用视图

时间:2013-04-24 18:10:04

标签: django django-generic-views

假设我有一些继承自基类Animal的模型。我可以使用通用视图并将Cat / 12路由到详细视图,将Dod / 10路由到具有不同上下文的相同详细视图。 但我想从网址获取模型名称,以便我不必定义路由。

我有这样的事情:

url(r'^cat/(?P<slug>[-\w]+)/$',
    DetailView.as_view(
        queryset=Cat.objects.filter(),
        model=Cat,
        context_object_name='animal',
        template_name='animal/detail.html'),
    name='detail'),
url(r'^dog/(?P<slug>[-\w]+)/$',
    DetailView.as_view(
        queryset=Dog.objects.filter(),
        model=Dog,
        context_object_name='animal',
        template_name='animal/detail.html'),
    name='detail'),
...

显然,这是重复的代码。我宁愿做这样的事情:

url(r'^?P<my_animal>\w+/(?P<slug>[-\w]+)/$',
    DetailView.as_view(
        queryset=my_animal.objects.filter(),
        model=my_animal,
        context_object_name='animal',
        template_name='animal/detail.html'),
    name='detail'),
...

我可以这样做吗?

修改

这是我最终得到的结果,感谢达尔文的帮助。它避免了if / else获取Model名称:

class AnimalDetailView(DetailView):
    context_object_name='animal'
    template_name='animals/detail.html'

    def dispatch(self, request, *args, **kwargs):
        my_animal = kwargs.get('my_animal', None)
        self.model = get_model('animals',my_animal.capitalize())
        try:
            ret = super(AnimalDetailView, self).dispatch(request, *args, **kwargs)
        except AttributeError:
            raise Http404
        return ret

    def get_queryset(self):
        return self.model.objects.filter()

下次我有关于继承的问题时,我会咨询达尔文!大声笑

3 个答案:

答案 0 :(得分:4)

您可以从DetailView继承并覆盖 dispatch 方法,以使用以下内容构建您自己的规则:

class AnimalDetailView(DetailView):
    context_object_name='animal'
    template_name='animal/detail.html'

    def dispatch(self, request, *args, **kwargs):
        my_animal = kwargs.get('my_animal', None)
        if my_animal == 'dog':
            self.model = Dog
        elif my_animal == 'cat':
            self.model = Cat

        return super(AnimalDetailView, self).dispatch(request, *args, **kwargs)

    def get_queryset(self):
        return self.model.objects.filter()

并使用这样的urlpattern:

url(r'^?P<my_animal>\w+/(?P<slug>[-\w]+)/$', AnimalDetailView.as_view())

修改: 我上次犯了一个错误,因为我们无法实例化一个视图类,只使用'as_view()'方法。尝试新方法,我认为这可以提供帮助。

答案 1 :(得分:1)

是,您网址will automatically be passed as keyword arguments to your views,中的任何已命名参数(即?P<my_animal>):

  

使[基于类的视图]工作的关键部分是,当调用基于类的视图时,各种有用的东西存储在自身上;以及请求(self.request),这包括根据URLconf捕获的位置(self.args)和基于名称的(self.kwargs)参数。

因此您可以在视图中以self.kwargs['my_animal']访问它们。

如果您look at the __init__ method of BaseDetailViewDetailView继承),您会看到它正在执行的只是kwargs并将它们分配给实例属性,因此您可以轻松地执行以下:

url(r'^?P<model>\w+/(?P<slug>[-\w]+)/$',...

并且视图应自动将URL中传递的值分配给self.model。当然你需要小心并确保在这里验证输入,但这是一种从用户指定的模型中动态抓取对象的好方法

答案 2 :(得分:0)

另一种方法是直接传递模型,而不是通过变量传递

url.py

from django.urls import path
from . import views
# Common url
urlpatterns = [
    path('', views.MoneyIndex.as_view(), name='money_index'),
    path('cat', views.CatListView.as_view(), name='cat_list'),
    path('dog', views.DogListView.as_view(), name='dog_list'),
    ]

# Dynamic url
MYMODELS = ['Cat','Dog',]
for modelX in MYMODELS:
    urlpatterns = urlpatterns + [
        path('{0}/add'.format(modelX.lower()), views.MyCreateView.as_view(model=modelX), name='{0}_create'.format(modelX.lower())),
        path('{0}/<pk>'.format(modelX.lower()), views.MyDetailView.as_view(model=modelX), name='{0}_detail'.format(modelX.lower())),
        path('{0}/<pk>/upd'.format(modelX.lower()), views.MyUpdateView.as_view(model=modelX), name='{0}_update'.format(modelX.lower())),
        path('{0}/<pk>/del'.format(modelX.lower()), views.MyDeleteView.as_view(model=modelX), name='{0}_delete'.format(modelX.lower())),
    ]

view.py

from django.views import generic
from .models import Cat, Dog
from .forms import CatForm, DogForm

class MyDetailView(generic.DetailView):     
        def __init__(self, *args, **kwargs):
            super(MyDetailView, self).__init__(*args, **kwargs)
            modeltxt = self.model
            self.model = eval(modeltxt)


 class MyCreateView(generic.CreateView):
    def __init__(self, *args, **kwargs):
        super(MyCreateView, self).__init__(*args, **kwargs)
        modeltxt = self.model
        self.model = eval(modeltxt)
        self.form_class = eval('{0}Form'.format(modeltxt))
        self.success_url = reverse_lazy('{0}_list'.format(modeltxt.lower()))    


class MyUpdateView(generic.UpdateView):
    def __init__(self, *args, **kwargs):
        super(MyUpdateView, self).__init__(*args, **kwargs)
        modeltxt = self.model
        self.model = eval(modeltxt)
        self.form_class = eval('{0}Form'.format(modeltxt))
        self.success_url = reverse_lazy('{0}_list'.format(modeltxt.lower())) 


class MyDeleteView(generic.DeleteView):
    def __init__(self, *args, **kwargs):
        super(MyDeleteView, self).__init__(*args, **kwargs)
        modeltxt = self.model
        self.model = eval(modeltxt)
        # Must be done because default points to modelname_confirm_delete.html
        self.template_name = 'appname/{0}_detail.html'.format(modeltxt.lower())
        self.success_url = reverse_lazy('{0}_list'.format(modeltxt.lower()))