Pull to refresh

Автоматическая привязка URL к view

Reading time2 min
Views2K
Я недавно «подсел» на Django и мне очень нравится этот фреймворк. Однако есть деталь, которая доставляет беспокойство. Это одновременное редактирование файла с моими views и файла urls.py при создании нового view. Я понимаю, что это связано с тем, что URL-ы и views вещи достаточно разные и для большей модульности лучше держать их раздельно, однако для небольших проектов было бы очень удобно иметь возможность править view и его настройки в одном месте.

Здесь я предлагаю такое решение, конечно же, завязанное на декораторах.

Итак, что в конечном итоге мы хотим получить? Допустим, нам нужно нарисовать новый view. Хочется дописать к этой функции декоратор with_url:

@with_url(r'^my/cool/url/')
def my_cool_view(request):
    ...


чтобы он сам всё подвязал, а нам не пришлось лазать руками в urls.py.

Для этой цели служит небольшой модуль easyurls из пакета, назовём его, djangoutils:

urlpatterns = []

def with_url(url, context={}):
    def decorator(fun):
        global urlpatterns
        urlpatterns.append((url, fun, context))

        return fun

    return decorator

def load_patterns(module_name):
    from django.conf.urls.defaults import patterns
    __import__(module_name)

    return patterns(module_name, *urlpatterns)


Что он делает? Декоратор with_url добавляет в глобальную переменную urlpatterns привязку URL к нашему view. А функция load_patterns просто импортирует модуль views, чтобы все декораторы сработали, после чего возвращает заполненный urlpatterns.

Используется это следующим образом. В файле urls.py мы пишем

from djangoutils.easyurls import load_patterns

urlpatterns = load_patterns('app.views') + patterns(...)


где app.views — модуль views нашего приложения. Больше мы в этот файл не заглядываем.

В модуль app.views импортируем декоратор:

from djangoutils.easyurls import with_url


и теперь новые view можно писать примерно таким образом:

# простой пример
@with_url(r'^json/list/items/')
@json_http
@in_list
def json_list_items(request, list):
    ...

# чуть сложнее :-)
@with_url(r'^$')
@with_url(r'^start/', {'is_root': False})
def start(request, is_root=True):
    ...


Отступление: вообще, я активно пользуюсь декораторами для облегчения написания однотипного кода. Например, здесь декоратор json_http преобразует результат функции в JSON строку и кидает её в response.

Недостаток предложенного решения, который я вижу сразу — это использование только одного load_patterns в URL. Решить его можно, перенеся urlpatterns из easyurls в модуль views. Думаю, сделать это несложно, как дополнительное преимущество отпадёт необходимость в использовании load_patterns и будет достаточно стандартного include. Как недостаток — придётся выписывать urlpatterns = [] в каждом модуле views, который мы вяжем таким образом. Возможно и не придётся, если python может из одного модуля создавать глобальную переменную в другом и проверять есть ли она там уже.

Это не велосипед, кстати?

Спасибо тем, кто дочитал до этого места :-)
Tags:
Hubs:
Total votes 24: ↑18 and ↓6+12
Comments12

Articles