4

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

我有这样的事情:

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 来获取模型名称:

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

下次我有关于继承的问题,我会咨询达尔文!哈哈

4

3 回答 3

5

您可以从 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()'方法。尝试新方法,我认为这会有所帮助。

于 2013-04-24T18:25:18.833 回答
1

?P<my_animal>是的,您的 url 中的任何命名参数(即)将自动作为关键字参数传递给您的视图,

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

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

如果您查看__init__BaseDetailView(from which inherits) 的方法,DetailView您会发现它所做的只是获取kwargs并将它们分配给实例属性,因此您可以轻松地执行以下操作:

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

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

于 2013-04-24T18:18:15.600 回答
1

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

网址.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())),
    ]

视图.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()))
于 2018-10-22T14:06:39.783 回答