如何减少基于Django类的视图样板

时间:2016-09-29 09:02:19

标签: python django python-3.x boilerplate

我真的很讨厌样板。但是,我不能否认以下代码是一个巨大的好处。所以我的问题是,在Python中做什么来弥补它没有宏(模板)预处理器的事实?

一个想法是写一个工厂功能,但我愿意承认我不知道从哪里开始。 (请注意,这是Django的声明性类和有趣的“魔术”元类物质,我知道足够认识,如果我打破它就不足以理解或调试)

另一种方法是将其转换为模板并通过一个简单的预处理器导入它,该预处理器在Bash中实现${var:-default}之类的东西。 (见What is an alternative to execfile in Python 3?),

with my_preprocessor("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code) # in the current namespace

但是这些年来我见过很多关于exec的警告。引用的SO答案提到调试的行号作为一个问题。然后有这个http://lucumr.pocoo.org/2011/2/1/exec-in-python/警告微妙的问题,包括内存泄漏。我怀疑它们不适用于定义“永不”删除的类的代码,但另一方面,我不希望在生产环境中引入模糊问题的风险最小。

欢迎任何想法或指示。最好的办法是接受切割和粘贴样板吗?任何此类模板都不可能超过二十次修改,通常少于十次。

示例代码。标记为#V的行是唯一通常会自定义的行。前两个类仅使用一次,第三个。

#--- this is boilerplate for a select-view ----
#--- just replace the string "User" by the relevant model and customize 

class UserSelectPopupTable( tables.Table):

     id = SelectorColumn( clickme='<span class="glyphicon glyphicon-unchecked"></span>' ) #V

     class Meta:
        model=User
        attrs={ 'class':'paleblue' }
        empty_text='Sorry, that search did not match anything.'
        fields=( 'name','address', )        #V
        sequence=('id','name','address',)   #V

class UserFilter2(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_expr='icontains')     #V
    address = django_filters.CharFilter(lookup_expr='icontains')  #V
    class Meta:
        model = User
        fields = ('name','address', )   #V (usually same as previous)

class UserSelectPopup( FilterTableView ):
    model=User
    table_class=UserSelectPopupTable
    filterset_class=UserFilter2
    template_name='redacted/select_in_popup.html' 

#--- end boilerplate

1 个答案:

答案 0 :(得分:2)

Python和Django非常棒。

我阅读并重新阅读了用于动态创建类(https://docs.python.org/3/library/functions.html#type)的type的3参数形式的(非常简短的)文档。我编写了一个简单的帮助程序例程Classfactory,以便为type提供更好的接口,并将类结构转换为函数调用,这些调用主要是剪切和粘贴!我到达了以下(我认为也证明你可以用Python编写Javascript ......插入分号的本能很强)

def Classfactory( classname, inheritsfrom=(object,), **kwargs):
    inh = inheritsfrom if isinstance(inheritsfrom, tuple) else (inheritsfrom, )
    return type( classname, inh, kwargs)

ThisPopupFilter = Classfactory( 'ThisPopupFilter', django_filters.FilterSet,

    name = django_filters.CharFilter(lookup_expr='icontains') ,
    address = django_filters.CharFilter(lookup_expr='icontains') ,
    Meta = Classfactory( 'Meta', 
        model = User,
        fields = ('name','address', ),
    ),
)
ThisPopupTable = Classfactory( 'ThisPopupTable', tables.Table, 

    id = SelectorColumn( clickme='<span class="glyphicon glyphicon-unchecked"></span>' ),

    Meta = Classfactory( 'Meta', # default inherit from object
        model=User,
        attrs={ 'class':'paleblue' },
        empty_text='Sorry, that search did not match anything.',
        fields=( 'name','address', ) ,
        sequence=('id','name','address',) ,
    ),
)

UserSelectPopup = Classfactory( 'UserSelectPopup', FilterTableView, 
    model=User,
    table_class=ThisPopupTable,
    filterset_class=ThisPopupFilter,
    template_name='silson/select_in_popup.html', # this template handles any such view
)

现在我突然意识到它不仅仅是可以在其他类中定义的Django Meta类。其他地方不需要的类可以嵌套在需要它的范围内。所以我将前两个类移到了第三个类中,然后重新排列了一下,我能够转移到带参数的工厂函数......

def SelectPopupFactory( Model, fields, sequence=None, 
                clickme='<span class="glyphicon glyphicon-unchecked"></span>' ,
                empty_text='Sorry, that search did not match anything.',):
    return Classfactory( 'UserSelectPopup', FilterTableView, 

    model=Model,
    template_name='silson/select_in_popup.html', # this template handles any such view

    table_class=Classfactory( 'ThisPopupTable', tables.Table,   
        id = SelectorColumn( clickme=clickme ),
        Meta = Classfactory( 'Meta', # default inherit from object
            model=Model,
            attrs={ 'class':'paleblue' },
            empty_text=empty_text,
            fields=fields,
            sequence=sequence,
    )),
    filterset_class=Classfactory( 'ThisPopupFilter', django_filters.FilterSet,
        name = django_filters.CharFilter(lookup_expr='icontains') ,
        address = django_filters.CharFilter(lookup_expr='icontains') ,
        Meta = Classfactory( 'Meta', 
            model = Model,
            fields = ('name','address', ),
    )),
)

UserSelectPopup = SelectPopupFactory( User, 
    fields=('name','address', ), 
    sequence=('id','name','address',) ,
    )

有人可以看到任何根本错误吗? (我感到有点惊讶的是,这一切都在第一次尝试时没有崩溃,模数错别字)

稍后更新一个工作日:我认为这可以作为一个示例/概念证明(它是在没有崩溃的情况下运行的代码)但是对于实际的django_filters和django_tables2用法有几个细节要做。就在这儿。我的工厂功能已经发展并且更有能力,但不太容易与原始的非工厂类定义相关联。