如何减少基于Django类的视图样板 [英] How to reduce Django class based view boilerplate

查看:121
本文介绍了如何减少基于Django类的视图样板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



一个想法要写一个工厂功能,但我会乐意承认,我不知道从哪里开始。 (请注意,这是Django的声明类和有趣的魔术元类的东西正在下面,我知道足够认可,如果我打破它,不够理解或调试)



另一个是将它变成一个模板,并通过一个平凡的预处理器来导入它,该处理器在Bash中实现一些类似 $ {var:-default} 。 (请参阅 Python 3中execfile的替代方法? ),

  with my_preprocessor(somefile.py)as f:
code = compile f.read(),somefile.py,exec)
当前命名空间中的exec(code)#

但是,多年来我看到的 exec 有很多警告。引用的SO答案提到调试行号作为一个问题。那么就是这样, http://lucumr.pocoo.org/ 2011/2/1 / exec-in-python / ,警告微妙的问题,包括内存泄漏。我怀疑他们不会申请定义永不删除的类的代码,但另一方面,我不希望在生产环境中引入模糊的问题。



欢迎任何想法或指针。最好的事情是接受剪切和粘贴样板吗?不太可能有二十种以上的粘贴 - 修改任何此类模板,通常少于十个。



示例代码。标有#V的线是唯一通常被定制的线。前两个课程仅用于第三个。

 #---这是一个选择视图的样板 - --- 
#---只需使用相关模型替换User字符串并自定义

class UserSelectPopupTable(tables.Table):

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

class Meta:
model = User
attrs
fields =('name','address',)#V
序列号=('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(我们

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

#---结束样板


解决方案

Python和Django是非常棒的。



我阅读并重新阅读了3个参数形式的< c>键入,用于动态创建类( https ://docs.python.org/3/library/functions.html#type )。我写了一个简单的帮助程序 Classfactory 类型提供了更好的界面,并将类结构转换为函数调用,这是大多数剪切和粘贴!我到达了以下(我认为也证明你可以在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',#defaul t继承自对象
model = User,
attrs = {'class':'paleblue'},
empty_text ='对不起,搜索不匹配任何东西',
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' ,#此模板处理任何这样的视图

现在我突然意识到这不只是可以在其他类中定义的类Django Meta 任何其他地方不需要的类可以嵌套在需要的范围内。所以我把第二个班的第三个搬到了第二个班,然后再重新排列,我可以转到一个工厂函数参数...

  def SelectPopupFactory(Model,fields,sequence = None,
clickme ='< span class =glyphicon glyphicon-unchecked>< / span>',
empty_text = ',)
return Classfactory('UserSelectPopup',FilterTableView,

model = Model,
template_name ='silson / select_in_popup。 html',#这个模板处理任何这样的视图

table_class = Classfactory('ThisPopupTable',tables.Table,
id = SelectorColumn(clickme = clickme),
Meta = Classfactory ('Meta',#default继承自对象
model = Model,
attrs = {'class':'paleblue'},
empty_text = empty_text,
fields = fields,
sequenc e = 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的实际使用情况并不在这里。我的工厂功能已经演变而且更有能力,但不太容易与原始的非工厂类定义相关。


I really hate boilerplate. However, I can't deny that code such as the following is a huge benefit. So my question, what does one do in Python to make up for the fact that it doesn't come with a macro (template) pre-processor?

One idea would be to write a factory function, but I'll willingly admit that I don't know where to start. (Please note that this is Django with its declarative classes and interesting "magic" metaclassy stuff going on underneath, which I know enough to recognise and not enough to understand or debug if I break it)

The other would be to turn this into a template and import it through a trivial pre-processor that implements something like ${var:-default} in Bash. (see 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

But there are lots of warnings about exec that I've seen over the years. The cited SO answer mentions line numbers for debugging as an issue. Then there is this, http://lucumr.pocoo.org/2011/2/1/exec-in-python/ , warning of subtle problems including memory leaks. I suspect they won't apply to a code defining classes which are "never" deleted, but on the other hand I don't want the slightest risk of introducing obscure problems to a production setting.

Any thoughts or pointers welcome. Is the best thing to do is to accept cut and paste boilerplate? There are unlikely to be more than twenty paste-modifies of any such template, usually less than ten.

Example code. Lines marked #V are the only ones that would commonly be customized. The first two classes are used once only, by the third.

#--- 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

解决方案

Python and Django are awesome.

I read and re-read the (quite short) documentation of the 3-argument form of type that you use to dynamically create classes (https://docs.python.org/3/library/functions.html#type). I wrote a trivial helper routine Classfactory to provide a better interface to type, and translated the class structure into function calls, which was mostly cut and paste! I arrived at the following (which I think also proves that you can write Javascript in Python ... the instinct to insert semicolons was strong)

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
)

Now I suddenly realized that it's not just Django Meta classes that can be defined inside other classes. Any class that is not needed elsewhere can be nested in the scope where it is needed. So I moved the first two classes inside the third, and then with a bit more rearranging I was able to move to a factory function with arguments ...

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',) ,
    )

Can anybody see anything fundamentally wrong with this? (I'm feeling slightly amazed that it all ran and did not crash at the first attempt, modulo typos)

UPDATE a workday later: I think this is OK as an example / proof of concept (it is code that ran without crashing) but there are several fine points to do with the actual django_filters and django_tables2 usage that aren't right here. My factory function has evolved and is more capable, but less easy to relate to the original non-factory class definitions.

这篇关于如何减少基于Django类的视图样板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆