We often want to render different HTML templates for phones, tablets, and desktop browsers. Or for AB testing. django-variatmpl
make it easy. By setting request.variant
, you can render the template according to that request.variant
. This library is heavily inspired by Action Pack Variants.
- Install
django-variantmpl
$ pip install django-variantmpl
- Change
django.shortcuts.render
tovariantmpl.shortcuts.render
in your views.
- And set
request.variant
property.
# views.py --
# from django.shortcuts import render
from variantmpl.shortcuts import render # <- add
def sample(request):
# Set variant value
request.variant = 'v2'
return render(request, 'index.html')
- Prepare variant templates.
$ echo 'sample v1' > templates/index.html
$ echo 'sample v2' > templates/index+v2.html
- Confirm
views.sample
display in your browser.
- You can see sample v2.
- It is the result of loading the template(
index+v2.html
) based onrequest.variant
.
Use instead of django.shortcuts.render
.
# views.py --
from variantmpl.shortcuts import render
def sample(request):
request.variant = 'v2'
# Actually "index+v2.html" is rendered
return render(request, 'index.html')
Use instead of django.shortcuts.render_to_response
.
# views.py --
from variantmpl.shortcuts import render_to_response
def sample(request):
# Actually "index+v2.html" is rendered
return render_to_response(request, 'index.html', variant='v2')
You can set variant
as a keyword argument.
Use instead of django.template.loader.render_to_string
.
# views.py --
from django.http import HttpResponse
from variantmpl.template.loader import render_to_string
def sample(request):
request.variant = 'v2'
# Actually "index+v2.html" is rendered
content = render_to_string('index.html', request=request)
return HttpResponse(content)
Use instead of django.template.response.TemplateResponse
.
# views.py --
from django.views.generic import TemplateView
from variantmpl.template.response import TemplateResponse
class SampleView(TemplateView):
template_name = 'sample/index.html'
response_class = TemplateResponse # Replace response class
def get(self, request, **kwargs):
request.variant = 'v2'
# Actually "index+v2.html" is rendered
return super().get(request, **kwargs)
sample = SampleView.as_view()
It is difficult to rewrite all code with large codes already to variantmpl
code. In such a case, you can apply Monkey patch to Django's functions/classes.
Caution : This feature is experimental. This may be deleted in the future if unexpected bad effects occur.
# settings.py --
SECRET_KEY = 'xxxxxx'
# You must write this code below SECRET_KEY.
from variantmpl import monkey
monkey.patch_all()
# views.py --
# You don't need to replace to 'variantmpl'.
from django.shortcuts import render
def sample(request):
request.variant = 'v2'
# Actually "index+v2.html" is rendered
return render(request, 'index.html')
All targets for monkey patching.
django.shortcuts.render
django.shortcuts.render_to_response
django.template.loader.render_to_string
django.template.response.TemplateResponse.resolve_template
They are replaced by the functions/methods of the same name in `variantmpl`.
You can change variant
format. default: +variant
.
# settings.py --
VARIANTMPL_VARIANT_FORMAT = '@{variant}'
# The lookup target template name changes as follows.
"index+variant.html" -> "[email protected]"
You can rename request.variant
property.
# settings.py --
VARIANTMPL_PROPERTY_NAME = 'mutation'
# You can set 'mutation' instead of 'varaiant'
request.mutation = 'v2'
You can change the position of the variant inserted into template path.
# For example, you have this path.
render(request, 'sample1/sample2/index.html')
# variantmpl inserts the variant(v2) as follows.
'sample1/sample2/index+v2.html'
# At this time, VARIANTMPL_TEMPLATE_FORMAT is like this. (default)
VARIANTMPL_TEMPLATE_FORMAT = '{dirpath}{filename}{variant}.{ext}'
dirpath # => 'sample1/sample2/'
filename # => 'index'
variant # => '+v2'
ext # => 'html'
Change this format like this.
VARIANTMPL_TEMPLATE_FORMAT = '{variant}/{dirpath}{filename}.{ext}'
# variantmpl inserts the variant(v2) as follows.
'+v2/sample1/sample2/index.html'
In this case templates layout will change as follows
templates
├── +v2
│ └── sample1
│ └── sample2
│ └── index.html
└── sample1
└── sample2
└── index.html
It can be realized easily using uadetector.
Create Django middleware to set varaint for each device.
class DeviceVariantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.variant = request.ua.device_variant # 'pc' or 'smartphone' or 'mobilephone'
# or 'appliance' or 'crawler'
response = self.get_response(request)
return response
Add the middlewares in settings.py.
# settings.py
MIDDLEWARE = [
'uadetector.django.middleware.UADetectorMiddleware',
'path.to.DeviceVariantMiddleware',
# ... omit ...
]
Prepare templates for device variant.
templates/index.html
templates/index+smartphone.html
templates/index+crawler.html
- Python 3.4 later
- Django 1.10 later
- Support only the latest 3 versions.
MIT Licence. See the LICENSE file for specific terms.
- First release