Horrible Things I Regret Doing With Python
Runtime Copy Paste With Global Variable Replacement.
“Could you make InlineAdmin
in Django paginatable Joe?” Guido asked. I googled a while before regurgitating a Stackoverflow answer into Guido's lap. “Oh cool, but we have multiple InlineAdmin
s, can you make it paginate for all of them?” “Sure” I replied, “That'll just be a change to the admin class and some templates”
… Time passes
I had quickly changed the p variable so it was dynamically determined by the model name assigned to the InlineAdmin
class by adding a page_param
property. The only problem was I that needed django's paginator_number
template tag to take into account these new page variables as well.
from django.contrib.admin.views.main import (
ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR,
)
...
@register.simple_tag
def paginator_number(cl, i):
"""
Generates an individual page index link in a paginated list.
"""
if i == DOT:
return '... '
elif i == cl.page_num:
return format_html('<span class="this-page">{}</span> ', i + 1)
else:
return format_html(
'<a href="{}"{}>{}</a> ',
cl.get_query_string({PAGE_VAR: i}),
mark_safe(
' class="end"' if i == cl.paginator.num_pages - 1 else ''
),
i + 1,
)
The main problem being that PAGE_VAR
is a global variable in paginator_number
and I was trying as hard as possible to avoid copy pasting. I pondered for a while before writing out the code vomit in the next sample.
“Can you see what he's doing?” Tom called out, slightly exasperated as he looked at the hideous code I had concocted. I cackled slightly as the other devs gathered around my desk, the faint glow of monitor lighting their faces.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import copy
import types
from django.template import Library
from django.contrib.admin.templatetags.admin_list import (
paginator_number,
pagination
)
register = Library()
@register.simple_tag
def parameterized_paginator_number(cl, i):
'''This ridiculous code makes a copy of paginator_number function
The PAGE_VAR variable is global in paginator number, this
function copies paginator_number and replaces the global PAGE_VAR
with a page_param that we can set.
'''
# copy the globals of paginator_number
func_globals_copy = copy.copy(paginator_number.__globals__)
func_globals_copy['PAGE_VAR'] = cl.page_param
paginator_number_func_copy = types.FunctionType(
code=paginator_number.__code__,
# pass our updated globals
globals=func_globals_copy,
name='parameterized_paginator_number',
argdefs=paginator_number.__defaults__,
closure=paginator_number.__closure__,
)
return paginator_number_func_copy(cl, i)
|
“It's runtime copy pasting, what the hell is wrong with you?”
Here we use copy to make a copy of django's paginator_number's global (line 21). Then, in a horrible twist, we replace the PAGE_VAR
in our new dict (line 22).
In line 24-31, we use the types library to create a new function. With exactly the same code as paginator_number
, but crucially in line 27, we pass in our func_globals_copy
, which contains the replaced PAGE_VAR
global. Then we invoke our new paginator_number
function copy and all is well.
Under the hood, each python function has a reference to a dict containing all the globals that the function uses, all we are doing is badly abusing this. #sorrynotsorry as they say.
“This is your fault django, you made me do this”, I blurted out in some feeble attempt at an excuse following up with “I think it's less brittle than plain copy-pasting though”.
“I actually agree.” Tom said doing his best Patrick Stewart facepalm.
This abomination now lives on in ctxis/django-inline-admin-extensions