假设我有一些继承自基类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()
下次我有关于继承的问题时,我会咨询达尔文!大声笑
答案 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 BaseDetailView
(DetailView
继承),您会看到它正在执行的只是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()))