Search code examples
pythonregexdjangourlslug

Dynamically check if a slug exists


On a legacy app, I need to check if a URL exists, and if it does not, to redirect it to another location. The problem is that I need to check if that url is present in a set of values, in the urls file and I'm not clear on how best to do that.

For example, both projects and cities are sharing the same url pattern. e.g. /projects/london and /projects/my-project-name.

I want to first check if the slug matches a city, and if it does not to then return the project view (cities cannot match project names).

My urls are currently structured as follows:

url(r'^projects/(?P<project-name>[-\w]+)', get_project, name='project-view'),
url(r'^projects/.*', get_city, name='city-view'),

I know this is very messy, and a bad overall pattern but unfortunately it's not something that can be changed at the moment. So my goal is to figure out if I can first check if the project-name could be a city, and if it is, to redirect onto that view without falling into a redirect loop.

I wondered if I could do something like this:

url(r'^projects/(?P<city>london|paris|new-york)/', get_city, name='city-view'),

where london|paris|new-york are generated dynamically


Solution

  • You can dynamically generate a url with all of the city names, but the url will be cached once django accesses it the first time, so in order to modify the url regex, you'd have to restart the django process. If that's fine for your purposes, you can generate the url like this:

    url(r'^projects/(?P<city>{})/$'.format(city_slugs.join('|')),
        get_city, name='city-view')
    

    But, it would probably be better to create a view routing method that implements the logic to send requests to their appropriate view:

    # urls.py
    # ...
    url(r'^projects/(?P<slug>[-\w]+)/$',
        project_city_router, name='project-city-router'),
    # ...
    
    # views.py
    def is_a_city(slug):
        # If they're in the database, something like:
        # return City.objects.filter(slug=slug).exists()
        return slug in ['london', 'paris', 'new-york', '...']
    
    def project_city_router(request, slug=None):
        if not slug:
            # /projects/
            return render(request, 'my/template.html', {'foo': 'bar'})
        elif is_a_city(slug):
            # /projects/<city>/
            return get_city(request, city=slug)
        else:
            # /projects/<project-name/
            return get_project(request, project_name=slug)
    

    With this router, if the slug argument is a project or city, it returns the result of the get_project or get_city view itself.

    This also allows for your list of cities to be checked dynamically against a database, or file.