flask-admin: Access row object in LinkRowAction

January 4, 2021

I have an admin interface with tags that can be associated with any number of entries. As a demonstration, flaskadmintagdemo should give you an idea of what my side looks like.

I wanted to have a link next to each tag pointing to a filtered list of all entries tagged with that specific tag.

The logic for that is in flask-admin > model > template.py

class LinkRowAction(BaseListRowAction):
    def render(self, context, row_id, row):
        m = self._resolve_symbol(context, 'row_actions.link')
        # string_types = (str, unicode)
        if isinstance(self.url, string_types):
            url = self.url.format(row_id=row_id)
        else:
            url = self.url(self, row_id, row)
        return m(self, url)

So, we need to amend the column_extra_row_actions to add a LinkRowAction that points to the filtered listing of entries:

class TagView(ModelView):
    class PostURLObj(str):
        """
        Fake str object that fails isinstance(str, unicode) so that we can
        access the row object
        """
        def __new__(self, cls, row_id, row):
            _url = "/admin/?flt0_9=%s" % row.name
            return str.__new__(self, _url)

    column_extra_row_actions = [
        # Cannot use EndpointLinkRowAction since we need access
        # to the row object
        LinkRowAction(
            "glyphicon glyphicon-eye-open fa fa-eye", PostURLObj
        ),
    ]

Instanciating a new str subclassed object - in this case PostURLObj - will call <cls>.__new__(args), not __init__(). Took me a while to figure that out…

But, instead of fixating on working around LinkRowAction, why not just implement a more suitable action by subclassing BaseListRowAction?

class PostRowAction(BaseListRowAction):
    def __init__(self, icon_class):
        super(PostRowAction, self).__init__()
        self.icon_class = icon_class
    def render(self, context, row_id, row):
        m = self._resolve_symbol(context, 'row_actions.link')
        url = "/admin/?flt0_9=%s" % row.name
        return m(self, url)

class TagView(ModelView):
    column_extra_row_actions = [
        PostRowAction(
            "glyphicon glyphicon-eye-open fa fa-eye",
        ),
    ]

Much easier.