Киев, Exception #06, сентябрь 2007
Что круто
Что не круто
Приемы
"Охотничьи рассказы"
Библиотека — вспомогательные функции, которые программист использует в своем workflow.
Фреймворк — workflow, в который программист вставляет свои вспомогательные функции.
На практике менять ключевые компоненты Джанго — ORM, urlconf, шаблоны — либо нереально, либо бесполезно.
Но вы не хотите этого делать!
{% url %}
не работает при слишком слабых связях.
Вы можете распихать свой код в удивительно большое количество мест удивительно большим количеством способов.
Рекомендуемое поведение есть, но вы можете полагаться просто на свой извращенный вкус.
Необходимость дописывать теги к тупым шаблонам — не садизм, а способ заставить программиста создать правильный микроязык для верстки проекта.
Традиционно:
<a href="
{% if item.single %}{% url show %}{% endif %}
{% if item.important %}{% url popup %}{% endif %}
{% if item.many %}{% url list %}{% endif %}">...</a>
Красиво:
<a href="{% item_link item %}">...</a>
Пользоваться newforms правильно и хорошо
Разбиение проекта на приложения часто недооценивают. А зря.
Джанговцы рекомендуют unit-тесты и doc-тесты.
Юнит-тесты можно запускать по отдельности — очень удобно в отладке.
Приятный инструментарий: TestClient
, assertContains
,
assertFormError
, assertTemplateUsed
.
Обобщенный формат начальных данных проекта, без которых он не может работать
Оформление тестовых кусочков
Один из немногих open-source проектов, где использовать разрабатываемый код удобно и довольно безопасно (мы в Яндексе так и делаем)
Код — очень хорошая документация
Следите за django-developers
Любая попытка прикрутить внешнюю авторизацию выливается в какой-нибудь вид хаков
Загрузка данных через память
Админка не умеет ничего кроме CRUD
Ограниченные ACL-права
Джанго умеет общаться только с одним сервером БД
Бранч multi-db поддерживает разделение серверов по моделям, а не по операциям
Разделите настройки
MEDIA_*
,
DATABASE_*
, TEMPLATE_PATH
)Вы наверное делаете по-другому. Но...
proj/ — папка-контейнер media/ — upload, сюда показывает MEDIA_ROOT proj/ — код проекта (под SVN) media/ — скрипты и стили, js/ которые надо отдавать статически css/
MEDIA_URL
, MEDIA_URL/js
и MEDIA_URL/css
алиасятся в свои папки
средствами веб-сервера
globals.py
import threading
globals = threading.local()
middleware.py
class MyMiddleware:
def process_request(self, request):
from globals import globals
globals['user'] = request.user
Когда на объекты запросов нужно повесить дополнительные данные, которые нельзя выбрать в том же запросе
А выбирать их отдельно для каждого отдельного объекта дорого
Article.objects.add_info_from_slow_storage().filter(...)
class MyQuerySet(QuerySet):
def _get_data(self):
result = super(MyQuerySet, self)._get_data()
ext_info = get_ext_info(r.id for r in result)
for r in result:
r.ext_info = ext_info[r.id]
def _clone(self, klass=None, **kwargs):
if klass is None:
klass = MyQuerySet
return super(MyQuerySet, self)._clone(klass, **kwargs)
./manage.py testserver fixture1 fixture2 ...
assertContains
, просто
посмотрев, что же на самом деле там показывается на странице.Известная претензия к Джанго — не умеет автоматически менять схему данных по модели
Но в Яндексе даже создание схемы делается вручную SQL-скриптами
"Мало ли, вдруг по репликам неправильно разойдется"
Репликация — способ размазать нагрузку на чтение по нескольким MySQL-серверам
Большой минус — Джанго этого не умеет.
Размазать нагрузку на чтение с тем же успехом можно и по Memcached-серверам.
Но это не работает для "расчетных" сервисов, где очень много вариантов ответов. Типичный пример — сервер расписаний поездов.
Когда вам говорят, что для кеширования надо использовать именно memcached — верьте!
Главная фишка — единый глобальный кеш на все доступные серверы без выделенного балансировщика.
В Джанго нет профилирующего WSGI-обработчика...
Напишем свой!
from django.core.handlers import wsgi
class ProfileWSGIHandler(object):
def __init__(self):
self.handler = wsgi._WSGIHandler()
def __call__(self, environ, start_response):
profile = hotshot.Profile(...)
return profile.runcall(self.handler, environ, start_response)
def install_profiler(path):
if wsgi.WSGIHandler.__name__ != 'ProfileWSGIHandler':
wsgi._WSGIHandler = wsgi.WSGIHandler
wsgi.WSGIHandler = ProfileWSGIHandler
ProfileWSGIHandler.path = path