15

Jul

Filed in Code, Django, iBegin |

Today I’m going to talk a little bit about one of our hurdles, and a quick solution we came up with to get around it at iBegin. We needed the ability to override URLs per-site, which by itself is fairly easy. You simply change the ROOT_URLCONF in your local or per-site settings file. Let’s take an example:

Let’s say I want /blog/details/<post_slug> on Site A, but on Site B I want that to be /blog/details/<post_id>. This is fairly easy to handle in templates, by simply passing different kwargs to your url resolver, but we needed to be able to handle this in methods like get_absolute_url(). First let’s explain why we needed this instead of the default resolver.

In many cases, your URLs are not going to allow changing variables. This is common in overloading of methods as the arguments accepted can change. At iBegin we had a need for this in several situations, and thus we needed a way to pass varying kwargs to the same view. For example, if you had /blog/details/<post_id> and you just wanted to change it to /blog/view-post/<post_id>/ your resolver is going to pick up the change without any work, and you wont need to change any calls for this URL.

The way the resolver works, is it simply loops over any matching lookup_view and tries to find URL which matches the kwargs or args you passed to it. In our case, we needed to be able to pass it all available kwargs, and have it return a working URL. This allows us to easily change the URL to match any of the accepted arguments in our overloaded view, and not worry about modifying our reverse() lookups.

So back to our original example, using our newly implemented vary_permalink, we can have a dynamic overloaded absolute url by doing the following:

@vary_permalink
def get_absolute_url(self):
    kwarg_map = {
        'city_slug': self.city_slug,
        'state_slug': self.state_slug,
        'country_slug': self.country_slug,
        'business_slug': self.slug,
        'business_id': self.id,
    }
    return ('directory:business', kwarg_map)

In this situation, as long as we have a url named directory:business that accepts any number of those kwargs, in any order, it will be returned. The only limitation presented in this situation currently, is there’s no way of determining which one to return. So for this example, we simply use a first-in-first-out routine. Future plans could possible include extending the url() method and url resolvers to implement a priority flag.

Enough with the explanation, let’s get down to the actual code:

from django.core.urlresolvers import get_resolver, reverse
 
def vary_reverse(lookup_view, kwargs):
    """
    Returns the first matching URL for the ``lookup_view``
    matching any number of ``kwargs`` in any order.
 
    vary_reverse('lookup_view', {'param1': 'value', 'param2': 'value'})
    """
    resolver = get_resolver(None)
    for result in resolver.reverse_dict.getlist(lookup_view):
        pattern, req_kwargs = result[0][0]
        try:
            kwargs = dict([(k, kwargs[k]) for k in req_kwargs])
        except KeyError:
            continue
        else:
            return reverse(lookup_view, kwargs=kwargs)
    raise ValueError('No url matching kwargs.')

The first part is our new reverse() method. We can pass in the same arguments as seen above in the @vary_permalink decorator, which is shown below:

def vary_permalink(func):
    def inner(*args, **kwargs):
        bits = func(*args, **kwargs)
        return vary_reverse(*bits)
    return inner

That’s the quick and dirty rundown of how we’ve solved overloading of views with differing arguments and maintaining callable URL methods on models. Have you come up with a better solution, let me know!

  • http://sos.endofinternet.net/ Skylar

    Nice, I’m going to have to give this a go. Trying to write reusables with this level of abstraction. Seems like a good candidate for core to me right now :)

  • http://www.adventurelifeinpakistan.blogspot.com mujeeb

    This blog is created to the lovers of adventure sports and for those who love to see the world’s most beautiful and challenging mountains.Dare to climb and enjoy the thrills of alpinism.Dare to climb the challenging mountains of pakistan

  • http://www.pariuri-x.ro/ Pariuri sportive

    very interesting post among with the codes… i will try these at my website

blog comments powered by Disqus