In test code, it is sometimes useful to monkeypatch a Django method to have stubbed out behavior, so that you can simplify data setup. Even with decent data setup you might want to avoid execution of Django code that is not the target of your test.
The code snippet shown here illustrates a technique to limit the scope of your monkeypatches. It uses the Python "with" statement, which was introduced in 2.5.
[with statement](http://effbot.org/zone/python-with-statement.htm)
The key aspect of the "with" machinery is that you can set up an __exit__ method that gets called even if the code inside the "with" raises an exception. This guarantees that your monkeypatch gets un-monkeyed before any other code gets called.
I don't recommend monkeypatches in production, but if you HAVE to resort to a monkeypatch, I definitely advise using "with" to limit their scope.
The examples on the left illustrate how to suppress versions of reverse() and timesince()--look at the import statements to see which ones I am talking about. Obviously, monkeypatching is not for the faint of the heart, as you need to be able to find the code to monkeypatch in Django source, and you need to be sure there aren't decorators at play.
There are times when you want to hook into the Variable class of django.template to get extra debugging, or even to change its behavior. The code shown is working code that does just that. You can monkeypatch template variable resolution by calling patch_resolver(). I recommend using it for automated tests at first.
My particular version of _resolve_lookup does two things--it provides some simple tracing, and it also simplifies variable resolution. In particular, I do not allow index lookups on lists, since we never use that feature in our codebase, and I do not silently catch so many exceptions as the original version, since we want to err on the side of failure for tests. (If you want to do monkeypatching in production, you obviously want to be confident in your tests and have good error catching.)
As far as tracing is concerned, right now I am doing very basic "print" statements, but once you have these hooks in place, you can do more exotic things like warn when the same object is being dereferenced too many times, or even build up a graph of object interrelationships. (I leave that as an exercise to the reader.)
If you want to see what the core _resolve_lookup method looks like, you can find it in django/templates/__init__.py in your installation, or also here (scroll down to line 701 or so):
[source](http://code.djangoproject.com/browser/django/trunk/django/template/__init__.py)
- template
- variable
- resolve
The code shown allows you, in GeoDjango, to reduce the number of points in your polygons. It helps reduce storage needs and makes queries run faster, at the cost of some precision. It provides a variation on the simplify() method that comes with the GEOS API, allowing you to specify a number of points instead of a distance tolerance.
It is set up as a management command so that you can run it with python manage.py. See the django docs for how to set that up.
The example shown assumes a table called CountyBorders with fields "name" and "mpoly." It should be straightforward to adapt it for your needs. Look at the first three lines of the simplify() method in particular for customization. The rest of the code is pretty generic, and it should run fast enough in a one-time batch process for most needs.
The algorithm tries to keep the 75 points that provide the most definition for the shape. Each point in a polygon defines a triangle with its immediate neighbors. If that triangle has no area (the degenerate case), it is a midpoint on the segment between its neighbors and adds no value whatsoever. This principle is extended to say that the larger the triangle, the more value the point has in defining the shape. (You can find more refined algorithms, but this code seems to work fine by visual inspection.)
The "testdata" tag allows you to inline test data into your templates, similar in spirit to Python doctests. There are two sections--the test data and the actual template to be rendered. In non-test mode your template renders normally from whatever views call it, and there is very little overhead to skip over the test data section (happens at parse time).
Here are the goals:
1. Provide convenient way to test templates without surrounding infrastructure.
2. Make templates be self-documenting in terms of expected data.
3. Allow insertion of test data at arbitrary places in template structure.
Hello-world looks like this:
{% load handytags %}
{% testdata %}
{
'greeting': 'Hello',
'planet': 'World',
}
{% --- %}
{# This is where the actual template begins #}
{{ greeting }} <b>{{ planet }}</b>
{% endtestdata %}
To invoke it, set up urls.py with something like this:
url(r'^testdata/(?P<template_path>.*)', test_template)
def test_template(request, template_path):
context = {'testdata_use': True}
# put request vars into context to help choose
# which test data we want to render
for field in request.GET:
context[field] = request.GET[field]
return render_with_request(template_path, context, request)
Then call:
http://127.0.0.1:8000/testdata/hello_world.html
Features:
1. The testdata tag's rendering will expose missing variables a bit more aggressively than Django normally does.
2. You have the full power of the template language to set the test data (which ultimately gets eval'ed as a Python expression).
3. As mentioned above, the tag is mostly unobtrusive.
Limitations/caveats:
1. Right now the only data format I support is pure Python, but the tag could be modified pretty easily to support JSON or YAML.
2. The VerboseContext class is pretty heavy-handed--I really just want a hook into Django to tell it to render a section with more strictness about variables. Suggestions welcome.
3. You can put the testdata tag pretty much anywhere, but the normal rules apply...for example, if you are in a template that has the extend tag, you'll want
to put the testdata tag in individual blocks.
- templates
- testing
- doctest
- custom-template-tag