Search code examples
djangodjango-tables2

How do I add a link to a Custom Boolean Field and pass parameters using Django_Tables2


So I've been having lots of fun with Django_Tables2 and I have it all working really nicely which is great. My table renders with a series of columns from my database which are booleans. These are columns such as 'Completed' etc. Instead of having True and False I have created a custom definition for my boolean fields which renders glyphicons-ok and glyphicons-remove as appropriate. See code below

class BootstrapBooleanColumn(BooleanColumn):
    def __init__(self, null=False, **kwargs):
        if null:
            kwargs["empty_values"]=()
        super(BooleanColumn, self).__init__(**kwargs)

    def render(self, value):
        value = bool(value)
        html = "<span %s></span>"

        class_name = "glyphicon glyphicon-remove"
        if value:
            class_name = "glyphicon glyphicon-ok"
        attrs={'class': class_name}

        attrs.update(self.attrs.get('span', {}))

        return mark_safe(html % (AttributeDict(attrs).as_html()))

My columns are therefore coded accordingly as follows:

completed = BootstrapBooleanColumn(attrs={'th':{'class':'centered nodec'}, 'td':{'data-title':'Completed'}})

However now I'd like to be able to click on one of the icons and have it toggle and update my database accordingly (ie switch from False to True and vice versa.) but I can't seem to be able to pass the parameters and I'm getting myself into a knot.

I tried wrapping the in an anchor

def render(self, value):
    value = bool(value)
    html = "<a href='/tasks/toggle/3'><span %s></span></a>"

which triggers my url with a hard coded id of '3' but I can't work out how to pass any variable parameters. Maybe I'm going about this the wrong way so if anyone can point back in the right, I would really appreciate it.


Solution

  • So after much hair pulling I managed to come up with a workable solution. It may not be the most glamorous kludge but for what I need, it works. I'm posting it here incase it helps anyone else and if anyone comes up with a more elegant solution, that would be great.

    As suspected I was barking up the wrong tree initially. So instead of trying to subclass and modify the built in BooleanColumn, I decided to come at it from another angle by subclassing and modifying the built in LinkColumn.

    Below is the code I added as a class to my tables.py file.

    class BoolLinkColumn(LinkColumn):
        def __init__(self, viewname=None, urlconf=None, args=None, kwargs=None, current_app=None, attrs=None, **extra):
            super(LinkColumn, self).__init__(attrs, **extra)
            self.viewname = viewname
            self.urlconf = urlconf
            self.args = args
            self.kwargs = kwargs
            self.current_app = current_app
    
        def render_link(self, uri, record, value, attrs=None):        
            value = bool(value)  
            column = self.args[1]
            html = "<a href='/tasks/toggle/"+str(record.pk)+"/"+str(column)+"/' %s></a>"
            class_name = "glyphicon glyphicon-remove"
            if value:
                class_name = "glyphicon glyphicon-ok"
            attrs={'class': class_name}       
            attrs.update(self.attrs.get('a', {}))  
            return mark_safe(html % (AttributeDict(attrs).as_html()))
    

    In the def render_link I'm picking up the value of the column and the second argument that I have specified on each column. Effectively this is the name of the column on which I have clicked but I couldn't find any other way of getting the column name dynamically.

    I then use the column name and pk to dynamically modify the precoded html string. I'm passing the variables through my urls.py file to my views.py file. Passing variables back and forth is still a bit of a struggle for me so if anyone can tell me a better way to do this, that would be great.

    I then set the default value of my icons to be false and then test the value of the column on which I have clicked. If the value is true I obviously change the icon as appropriate.

    Finally I update the attrs on my a tag and return the complete html string for rendering.

    Now that I have this in place and working, I can specify my column as below.

    class MyTable(ColumnShiftTable): 
        completed = BoolLinkColumn('my reverse url view', args=[A('pk'),'completed'], text='', empty_values=(), attrs={myattrs}
    

    So at the end of the day what does this allow me to do and why did I go through all this? Simple, I'm using django_tables2 to render my tables and now that I have this in place in my tables.py file, I can specify any boolean column I choose to have a link. As long as I have the appropriate processing code written in my views.py file, I can display a list in my application and I can update (toggle) the Boolean Field directly from the list without having to open an edit window and call the record from the database etc. It is all done in the background and then the screen refreshes and it is very cool.

    As with all things there is probably a better way to achieve this but I couldn't find any documentation anywhere, so this is what I came up with and I'm pretty chuffed with result. :-)

    Happy coding!