Известно мнение, что Джанго не подходит для написания "наколеночных" проектиков, которым не нужно ничего особенно, кроме вывода HTML'а. Создание скелета проекта на Джанго считается работой, достаточно существенной для того, чтобы не заниматься ею ради пары функций.

Мне всегда было "очевидно", что это не так, но недавно по оброненной коллегой фразе я понял, что это не значит, что это очевидно всем. Многие просто не задумываются об этом. Ну, вы знаете, как это бывает… Поэтому я хочу продемонстрировать создание Джанго-проекта минимальными усилиями.

Упрощение

  1. Нужно отказаться от команды startproject. Она создаёт хороший скелет проекта с аннотированным файлом настроек, но если вы Джанго-проект видите не впервые, этим можно пренебречь.

  2. Файл manage.py — тоже не нужен, это просто локальный хелпер для django-admin.py

  3. Не нужно думать о проекте, как о контейнере для приложений, а следовательно не нужно оформлять свой код, как подключаемое приложение. Приложения — одна из самых сильных архитектурных черт Джанго, но для мелких задач эта гибкость никогда не понадобится.

В итоге мы имеем что-то такое:

Это — полностью рабочий Джанго-проект. Запускается командой:

django-admin.py --settings=settings runserver

Вы получаете джанговский конвейер request-response, шаблоны. Также ничто не должно мешать работать такой хорошей вещи, как библиотека форм, хотя реально я не проверял. Что вы теряете — так это ORM и завязанные на него приложения. Но на то эта задача и "микро".

Экстремальщина

Чтобы нащупать границу того, когда идея дойдёт до абсурда, я решил ещё собрать всё приложение в один самозапускающийся файл. И у меня вышло:

Файл запускается так:

python web.py runserver

Впрочем, особенной радости от такой сборки я не увидел. Чтобы файл читался, его приходится делить на именованные секции, что как бы говорит нам о том, что это таки должны быть разные файлы. Плюс, написать такой файлик из головы я бы всё равно не смог из-за того что много надо делать по памяти: защита от двойной конфиграции настроек, длинные импорты и т.д.

Но как факт — забавно :-).

Побочная идея

Копаясь с инициализацией проекта я обнаружил, что настройка ROOT_URLCONF используется в самом ядре обработки запроса. Это привязывает работу Джанго к наличию реального модуля с определённой в нём переменной urlpatterns. Джанго была бы гибче, если бы конфигурацию URL'ов можно было инициализировать программно простым списком:

settings.configure(
    DEBUG = True,
    urlconf = [
        (r'^$', index),
    ],
)

Это, наверное, было бы даже полезно на практике, когда среду нужно уметь создавать на лету. Например, при тестировании.

Комментарии: 30

  1. Google user

    I have a feeling there should be a comma: "In tests, for example".

  2. astur.net.ru

    Кончится всё конкурсом в стиле "во сколько байт можно запихнуть работающую программу на джанго?" :)

  3. Дмитрий

    Что вы теряете — так это ORM и завязанные на него приложения.

    Что мешает подключить? :) Я всегда рассматривал Django не как монструозный швейцарский нож, а как конструктор, по умолчанию собранный в самой удобной конфигурации. Да, некоторые его части не совместимы со сторонними кубиками, но это не проблема. Этакий Lego от веб-разработки.

  4. Ivan Sagalaev

    Что вы теряете — так это ORM и завязанные на него приложения.

    Что мешает подключить? :)

    Для этого уже нужно приложение с собственным именем и models.py. Не то чтобы это было плохо, но это уже не "простенькое" решение.

  5. Andrey

    При всем уважении к django, я думаю, что для таких мелочей приятнее использовать webpy.org, так как ~50k кода фреймворка дают возможность делать небольшие и гибкие штуковины и не подталкивают к написанию еще чего-то, потому что фреймворк дает возможность это сделать. Конечно никаких сомнений нету, что при помощи django все это тоже можно реализовать, но имея кучу инструментов тяжело остановится на только действительно необходимых и сделать задачу не "приукрасив" ее.

  6. Алекс

    Легким движением руки Джанго превращается... превращается... Джанго превращается... в Синатру! http://www.sinatrarb.com/

    Ну это на правах холивара, разумеется :)

  7. Mighty

    Всё равно получается несколько громоздко. Как мне кажется, для подобных задач лучше всё-таки использовать что-нибудь вроде Flask или webpy. И с wsgi вопрос сразу решается :)

  8. igorekk

    Я бы предпочёл Bottle.py для таких задач.

  9. Руслан Кеба

    Спасибо! Действительно получаются довольно красивые микро-решения.

    Хотя я в последних двух подходящих случаях пользовал tornado.

  10. Google user

    Про manage.py и startproject - точно, нафиг не нужны.

    А вот заголовок у поста обманчивый. Это микроприложение, а не микрофреймворк. Точно так же как никто не обязывает писать огроменные энтерпрайзы там, где достаточно

    #!/usr/bin/env python
    import sys
    
    map(sys.stdout.write, ( line for line in sys.stdin if 'test' in line ))
    
  11. hardtop

    Иван, спасибо за эксперементирование. Я считаю, что уж если на сервере стоит Django, то смысла ставить Flask или webpy немного. В них надо копаться, натыкаться на ограничения и тонкости. А тут - всё же привычная среда.

  12. Дмитрий

    Или можно использовать пирамиду[1], в отличии от этого урезанного комбайна.

    1. http://docs.pylonsproject.org/projects/pyramid/dev/
  13. Homer

    Николай Яремко упоминал на субботнике подобный (свой) проект: http://www.github.com/makiwara/protosome Правда он чуть-чуть больше, ну и сложнее соответственно.

    А запускать лучше указав каталог проекта: django-admin.py runserver --pythonpath=/myprojects/mini --settings=settings

  14. Сеня Сумкин

    а еще можно нафиг удалить файл views.py и использовать 'direct_ to_template' в urls.py:

    from django.conf.urls.defaults import *
    
    urlpatterns = patterns('',
        (r'^$', 'django.views.generic.simple.direct_to_template', {'template':'index.hml'}),
        (r'^test/(?P<id>\d+)/$', 'django.views.generic.simple.direct_to_template', {'template':'test.html'}),
    )
    # для доступа к переменной <id> в шаблоне 'test.hml' используем: {{params.id}}
    # подробнее: http://docs.djangoproject.com/en/dev/ref/generic-views/
    

    оффтоп: оказывается если слово начинается и заканчивается нижним подчеркивание, в нотации markdown оно превращается в слово. Что еще хуже — 'direct(нижнее подчеркивание)to(нижнее подчеркивание)template' превращается в 'directtotemplate'

  15. sh4d0w

    Very cool, I hadn't realized django could be so compact. Coming from a rails standpoint, this reduces the number of files I'd expect in a micro-project by a ridiculous amount

  16. Денис

    Отличный пост! Сделал один проект на Bottle, компактно, конечно, но концепция DRY там не работает ( Написал кучу всего, что обычно в джанге не делал. И не нравится в Bottle, что нарушается концепция "Явное лучше, чем неявное". Там дюжина способов извлечь данные запроса! Не хорошо ( Вывод: если знаешь Django, то используй его. В микро, макро, и любом другом масштабе. Это реально хороший фреймворк.

  17. ashish.gd@gmail.com

    This seems super cool and pretty much exactly something I was looking out for. As a novice beginner in Python and learning Django, I often wondered about the heavy boot-strapping Django did for small learning projects. This definitely helps my concern. Though, am still grasping many of the entities used in this post. It would be great help if you could wrap all this in a script and create a utility out of it for someone like me. Many thanks again for this post and explanation.

  18. Eli Bendersky

    Thanks for showing this. I always had trouble understanding what Micro-frameworks are for if everything is doable with a stripped Django.

  19. Luka Zakrajsek

    You can use models too.

    from django.conf import settings
    
    settings.configure(
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': 'dev.db',
            }
        }
    )
    
    from django.db import models
    import sys
    
    sys.modules['web.web'] = sys.modules[__name__]
    sys.modules[__name__].__name__ = 'web.web'
    
    class MyModel(models.Model):
        name = models.CharField(max_length=255)
    
  20. Good

    Ah! long needed. Many thanks

  21. softwaremaniacs.org - Today, 7:21 PM

  22. What you get is a Django request-response pipeline, template system. Most nice things like form library should also work though I didn't check. What you lose is the ORM and all applications depending on it. Software Maniacs blog » Django as a micro-framework

  23. Andrew Pendleton

    Just for fun, I decided to see how easy it would be to factor what was left of the boilerplate in your single-file example into its own reusable chunk. It turned out to be pretty straightforward, and yielded something like this:

    import djmicro
    djmicro.configure()
    
    from django.shortcuts import render
    
    @djmicro.route(r'^$')
    def hello(request):
        return render(request, 'index.html', {})
    
    @djmicro.route(r'^test/(\d+)/$')
    def test(request, id):
        return render(request, 'test.html', {'id': id})
    
    if __name__ == '__main__':
        djmicro.run()
    

    The source of the reusable chunk is here.

  24. Ivan Sagalaev

    Andrew, this is totally cool! May be it's even getting close to be actually useful :-).

  25. Andrew Pendleton

    Yeah... I'm curious to see if it's possible to encapsulate the application state in an object, like Flask does, rather than having everything be global and having to hang all sorts of stuff like urlpatterns off of the main file. I think Simon Willison experimented with some of this a couple of years ago, so I might look into how he did it. I think getting models working would also be pretty cool (and I see that there are some tips on that in Luka's comment), because I don't think it would be that big of a stretch to be able to build single-file projects that leverage existing reusable apps, like the admin.

  26. В комментариях к посту Django as a Micro-Framework в блоге Ивана Сагалаева некий Andrew Pendleton выложил отличное решение для использования Джанго для микропроектов — djmicro.

  27. Josh

    Nice post. I wish I could speak another language as well as you appear to speak English!

  28. ricky.clarkson@gmail.com

    Some of this seems out of date, e.g., at least in Ubuntu it's now just django-admin instead of django-admin.py, and the 3-file version doesn't run out of the box (apologies, I saw the problem on someone else's machine and don't have the errors to hand).

    It'd be great if you could fix it up somehow for the next gullible fool who says "just search for 'minimal django example' and run what you see"

  29. Ivan Sagalaev

    This post is more than three years old, of course it's out of date :-)

  30. garej

    oh, amazing! what a long time ago this was obvious for some clever people :)))

    @Luka Zakrajsek suggested model connection. A little less hacky solution could be as follows:

    settings.configure(
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': 'dev.db',
        }
    },
    
    INSTALLED_APPS=[__name__, ]       # added
    
    )
    
    from django.db import models
    
    class MyModel(models.Model):
        name = models.CharField(max_length=255)
    
        class Meta:
            db_table = 'table_name'   # added
    

Добавить комментарий