我正在为我的Django项目编写一个视图生成器。我有大量来自旧版应用程序的模型(约150个模型),所有模型都需要相同的基本CRUD操作(显然,仅提供Admin访问权限还不够)。
因此,我正在编写一个生成器,该生成器将为每个模型返回5个视图,当然每个视图都可能具有大量选项,并且我正在尝试为生成器定义合理的API /默认参数格式。
我目前的发电机:
def generate_views(model_class, **kwargs):
"""
For a given model, returns a dict of generic class-based views
"""
###
# Forms
# Optionally generate form classes if not already provided
###
# Append these fields with either "create_" or "update_" to have them only
# apply to that specific type of form
form_override_args = ['fields', 'exclude', 'form_method', 'form_class',
'form_layout', 'widgets', 'media_css', 'media_js']
if 'form_class' not in kwargs and 'create_form_class' not in kwargs:
create_form_kwargs = kwargs.copy()
for arg in form_override_args:
if f'create_{arg}' in kwargs:
create_form_kwargs[arg] = kwargs[f'create_{arg}']
kwargs['create_form_class'] = forms.FormFactory(model_class, **create_form_kwargs).form()
if 'form_class' not in kwargs and 'update_form_class' not in kwargs:
update_form_kwargs = kwargs.copy()
for arg in form_override_args:
if f'update_{arg}' in kwargs:
update_form_kwargs[arg] = kwargs[f'update_{arg}']
kwargs['update_form_class'] = forms.FormFactory(model_class, **update_form_kwargs).form()
if 'form_class' not in kwargs:
kwargs['form_class'] = forms.FormFactory(model_class, **kwargs).form()
###
# Tables
# Optionally generate table classes if not already provided
###
# Append these fields with "table_" to have them only
# apply to the table view
table_override_args = ['fields', 'exclude']
if 'table_class' not in kwargs:
update_table_kwargs = kwargs.copy()
for arg in table_override_args:
if f'table_{arg}' in kwargs:
update_table_kwargs[arg] = kwargs[f'table_{arg}']
kwargs['table_class'] = tables.TableFactory(model_class, **update_table_kwargs).table()
###
# Views
# Generate 5 generic views based on the provided model
###
view_factory = views.ViewFactory(model_class, **kwargs)
return {
'list_view': view_factory.list_view(),
'detail_view': view_factory.detail_view(),
'create_view': view_factory.create_view(),
'update_view': view_factory.update_view(),
'delete_view': view_factory.delete_view()
}
Run Code Online (Sandbox Code Playgroud)
我目前依靠kwargs
,并且我想定义一个完全填写的kwargs
字典的外观。就像是
{
'forms': {
'all': {
},
'create': {
},
'update': {
}
},
'tables': {
'all': {
},
'list': {
}
},
'views': {
'all': {
},
'list': {
},
'detail': {
},
'create': {
},
'update': {
},
'delete': {
}
}
}
Run Code Online (Sandbox Code Playgroud)
而且似乎有点劳累。我主要是在寻找关于可能更好的设计的建议(因为我只是从头开始进行研究)。
看来你正在与 Django 在基于类的视图中构建离散功能/配置的方式作斗争中构建离散功能/配置的方式进行斗争。
\n\n\n\n\nDjango\xe2\x80\x99s 通用的基于类的视图是由提供离散功能的 mixin 构建的。
\n
所以,我的建议是:使用 mixins 将table
和form
类合并到您的 CRUD 操作视图中。在生成器中,所有可配置参数应仅传递给视图。
我们来看看django.views.generic.edit.CreateView
是如何设计的。它继承了以下位置的方法和属性:\n SingleObjectTemplateResponseMixin
、\nBaseCreateView
和\nModelFormMixin
。\n只需几行代码就可以将它绑定到模型:
from myapp.models import Author\nclass AuthorCreateView(CreateView):\n model = Author\n fields = [\'FirstName\',\'FamilyName\',\'BirthDay\']\n def form_valid(self, form):\n # Saves the form instance, sets the current object for the view, and redirects to get_success_url().\n
Run Code Online (Sandbox Code Playgroud)\n\n在这里,该model
属性由所有 mixin 共享以完成其工作,而fields
和form_valid
特定于ModelFormMixin
. \n虽然所有可配置的参数/方法都放在 View 类下,但每个 mixin 仅选取它需要的参数/方法。
记住这一点,让我们开始简化您的视图生成器/工厂。对于此示例,假设您有以下包含通用(默认)设置的基类:
\n\nfrom django.views.generic.edit import CreateView, DeleteView, UpdateView\nfrom django.views.generic import ListView, DetailView\nfrom django_tables2 as SingleTableMixin\n\nclass TableListView(SingleTableMixin, ListView):\n table_pagination = { \'per_page\': 10 }\n # add common configurable parameters here\n\nclass MyOwnCreateView(CreateView):\n success_url = "/yeah"\n # Introduce a configurable method `form_valid_hook`\n def form_valid(self, form):\n if hasattr(self,\'form_valid_hook\'):\n self.form_valid_hook(form)\n return super().form_valid(form)\n
Run Code Online (Sandbox Code Playgroud)\n\n下面是所有 5 个视图的简化生成器函数。
\n\nBaseViews= {\'create\': MyOwnCreateView,\n \'delete\': DeleteView,\n \'update\': UpdateView,\n \'list\' : TableListView,\n \'detail\': DetailView }\n\ndef generate_views(model_class, **kwargs):\n """\n Generate views for `model_class`\n\n Keyword parameters:\n {action}=dict(...)\n {action}_mixins=tuple(...)\n where `action` can be \'list\', \'detail\', \'create\', \'update\', \'delete\'.\n """\n NewViews = {}\n for action, baseView in BaseViews.items():\n viewName = model_class.__name__ + baseView.__name__\n viewAttributes = kwargs.get(action,{})\n viewBaseCls = (baseView,) + kwargs.get(f"{action}_mixins",tuple())\n v = type(viewName, viewBaseCls, viewAttributes) # create a subclass of baseView\n v.model = model_class # bind the view to the model\n NewViews[f\'{action}_view\'] = v\n return NewViews\n
Run Code Online (Sandbox Code Playgroud)\n\n你看,生成器函数被简化为只有 10 行代码。\n而且,API 会变得更加简洁:
\n\ndef validate_author(self, form):\n send_email(form)\n\nAuthorViews = generate_views(Author, \n create=dict(\n success_url=\'/thanks/\',\n form_valid_hook=validate_author), \n ... )\n
Run Code Online (Sandbox Code Playgroud)\n\n在上面的示例中,我使用挂钩/回调函数form_valid_hook
在保存表单数据之前注入电子邮件发送过程。这很丑陋,因为电子邮件的可配置将在模块范围内。最好将其重构为 mixin 类。
from django.core.mail import send_mail\n\nclass FormEmailMixin:\n from_email = \'info@example.com\'\n subject_template = \'We hear you\'\n message_template = \'Hi {username}, ...\'\n\n def form_valid(self, form):\n user_info = dict( username = self.request.user.username\n to_email = ... )\n send_mail(subject_template.format(**user_info),\n message_template.format(**user_info)\n self.from_email , [user_info[\'to_email\'],] )\n return super().form_valid(form)\n
Run Code Online (Sandbox Code Playgroud)\n\n然后你就可以在API调用中使用这个mixin类了。
\n\nAuthorViews = generate_views( Author, \n create={ \'message_template\': \'Dear Author {username}, ...\' }, \n create_mixins = (FormEmailMixin,) )\n
Run Code Online (Sandbox Code Playgroud)\n