Little bit of 'monkey patching'
Tags: python, sanic, example, jinja2
Published: 2020-05-31 22:42:32

**Monkey Patching** - the way to change objects behaviour in runtime. The
way that you can use to customize some third-party logic specially for
your purposes.

This is a pretty small article with an illustration case where Monkey
Patching can be useful. One day I thought about how to avoid sending
common used variables to Jinja2 templates in each route. And in Django,
Flask and aiohttp-jinja2 and many others frameworks/libs for this exists
**context_processor**.

Let’s imagine - you have a Web App with Sanic and Server Side Rendering
Templates with Jinja2.  For example, you have a header with Menu, some
logo, title - any information that is used on each page of your app. You
want to define it in one place.

In my case I need this in the process of work on the Gino-Admin panel.
And I use Sanic & Sanic-Jinja2.

I have something like this on each route:

``` 
return jinja.render(
   "login.html", request, objects=cfg.models, url_prefix=cfg.URL_PREFIX,
)

return jinja.render(
   "db_drop.html",
   request,
   data=await count_elements_in_db(),
   objects=cfg.models,
   url_prefix=cfg.URL_PREFIX,
)

return jinja.render(
   "presets.html",
   request,
   presets_folder=cfg.presets_folder,
   presets=utils.get_presets()['presets'],
   objects=cfg.models,
   url_prefix=cfg.URL_PREFIX,
)
```

And etc.

As you see each time I send  `url_prefix=cfg.URL_PREFIX`, prefix that
used as a base route for admin panel and `objects=cfg.models` - list of
models that we render in Admin panel for add/edit/delete.

I don’t like to duplicate code, also I want to add more customisation,
like set custom title for admin panel in config and other things in the
future. And sending all new common variables inside each route will be
painful. So I need a way to provide these variables only once.

Sanic-Jinja2 also has *context_processor* arg but I tried a lot of things
to send in it, and nothing works for me. I failed. Maybe I have the
wrong hands & head.

But I want this feature, so go on.

## SanicJinja2.render

To render all templates, the Sanic-JInja2 package uses the main method
render(...)

If you never used **Sanic-Jinja2** to add it to Sanic app looks like:

```
from sanic_jinja2 import SanicJinja2

jinja = SanicJinja2()
```

And that's it, after in routes you use it like:

```
return jinja.render(
   "presets.html",
   request,
   presets_folder=cfg.presets_folder,
   presets=utils.get_presets()['presets'],
   objects=cfg.models,
   url_prefix=cfg.URL_PREFIX,
)
```

Okay. Time for monkey patching.

## Main idea

go inside the source code, check that part code you need `re-write` -
and patch with your own implementation that solves your pain.

What I need?  I need to patch each response before page render with
common context variables that are used in each template.

If we jump in  SanicJinja2 source code we will see, that

[https://github.com/lixxu/sanic-jinja2/blob/master/sanic\_jinja2/\_\_init\_\_.py\#L143](https://www.google.com/url?q=https://github.com/lixxu/sanic-jinja2/blob/master/sanic_jinja2/__init__.py%23L143&sa=D&ust=1590967088522000)

I need to change small method:

```
def render(self, template, request, status=200, headers=None, **context):
    return html(
            self.render_string(template, request, **context),
            status=status,
            headers=headers,
        )
```

So I will define my own method. Let’s call it ‘custom\_render’. I will
just copy the paste base method - render and add into context all
variables that I need.

```
def custom_render(
self, template, request, status=200, headers=None, **context
):
   context["admin_panel_title"] = cfg.name
   context["objects"] = cfg.models
   context["url_prefix"] = cfg.route
   context["admin_panel_version"] = __version__
   context["round_number"] = cfg.round_number
   return html(
       self.render_string(template, request, **context),
       status=status,
       headers=headers,
   )
```

And now we need to ‘patch’ our target class **SanicJinja2**:


`SanicJinja2.render = custom_render`

So all the next code that will use this class after the line with
`patch` will call our custom method logic as the `render` method of
SanicJinja2 class.

Beautiful freedom of Python. You have a lot of ways to solve your
issues.

Code samples in project repo:

[https://github.com/xnuinside/gino-admin/blob/master/gino\_admin/config.py\#L20](https://www.google.com/url?q=https://github.com/xnuinside/gino-admin/blob/master/gino_admin/config.py%23L20&sa=D&ust=1590967088525000)