Skip to content Skip to sidebar Skip to footer

How To Reduce Django Class Based View Boilerplate

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

Solution 1:

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)

defClassfactory( classname, inheritsfrom=(object,), **kwargs):
    inh = inheritsfrom ifisinstance(inheritsfrom, tuple) else (inheritsfrom, )
    returntype( 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 ...

defSelectPopupFactory( 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.

Post a Comment for "How To Reduce Django Class Based View Boilerplate"