定义用于复杂View Generator函数的API(具有许多可配置项)

Wil*_*don 9 python django

我正在为我的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)

而且似乎有点劳累。我主要是在寻找关于可能更好的设计的建议(因为我只是从头开始进行研究)。

gdl*_*lmx 2

看来你正在与 Django 在基于类的视图中构建离散功能/配置的方式作斗争中构建离散功能/配置的方式进行斗争。

\n\n
\n

Django\xe2\x80\x99s 通用的基于类的视图是由提供离散功能的 mixin 构建的。

\n
\n\n

所以,我的建议是:使用 mixins 将tableform类合并到您的 CRUD 操作视图中。在生成器中,所有可配置参数应仅传递给视图

\n\n

背景知识

\n\n

我们来看看django.views.generic.edit.CreateView是如何设计的。它继承了以下位置的方法和属性:\n SingleObjectTemplateResponseMixin、\nBaseCreateView和\nModelFormMixin。\n只需几行代码就可以将它绑定到模型:

\n\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 共享以完成其工作,而fieldsform_valid特定于ModelFormMixin. \n虽然所有可配置的参数/方法都放在 View 类下,但每个 mixin 仅选取它需要的参数/方法。

\n\n

重新设计API

\n\n

记住这一点,让我们开始简化您的视图生成器/工厂。对于此示例,假设您有以下包含通用(默认)设置的基类:

\n\n
from 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\n
BaseViews= {\'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\n
def 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

如何在此 API 中使用 mixin

\n\n

在上面的示例中,我使用挂钩/回调函数form_valid_hook在保存表单数据之前注入电子邮件发送过程。这很丑陋,因为电子邮件的可配置将在模块范围内。最好将其重构为 mixin 类。

\n\n
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\n
AuthorViews = generate_views( Author, \n                  create={ \'message_template\': \'Dear Author {username}, ...\' }, \n                  create_mixins = (FormEmailMixin,) )\n
Run Code Online (Sandbox Code Playgroud)\n