Pull to refresh

Comments 65

Радует что Django в последнее время набирает обороты. Причем вполне заслужено.
Сейчас, правда, все немного замерли в ожидании релиза 1.7.

А статью в закладки и перечитать еще несколько раз.
Я настройки храню в отдельных файлах в подмодуле `settings`. Просто чтобы было чуть больше порядка.

Общие настройки — в settings/base.py, а в деве-стейджинге-продакшне делаю немного некошерно, но для настроек — самое оно:

from .base import *

# отличающиеся настройки
То есть у вас в settings.py хранятся только отличающиеся настройки? Как тогда решается проблема с системой контроля версий? (На продакшне одни settings.py, на деве — другие)
Мы делаем похожее, поэтому отвечу за 404.

В settings.py определены все настройки для боевого сервера.
Затем есть settings_local.py, который импортирует все с settings.py (from settings import *) и переопределяет необходимое, чтобы работало на local.
А сам сервер на локалке, например запускается с дополнительным параметром --settings=settings_local

Тоже самое с settings_dev.py (и другими версиями).

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

Для нас наиболее оптимальный вариант.
У меня вообще нет settings.py (при наличии модуля settings и settings.py у питона просто сорвёт крышу.

При старте сервера (runserver, wsgi — не важно) просто указываются нужное значение в $DJANGO_SETTINGS_MODULE (my_project.settings.productiuon, например).
А в чём профит?
Профит в том, что получается минимум копипасты.
Ну подождите. У вас же всё равно есть эти файлы my_project.settings.production и my_project.settings.development. Какая разница, включать из «запускатора» или из settings.py?
|-project
|--settings
|---__init__.py 
|---base.py
|---ci.py
|---dev.py
|---production.py
|---staging.py


Во-первых, окружений может быть больше чем два, и тогда трюк с ImportError не прокатит, ну или надо будет делать многовложенные try-except.
Во-вторых, разработчиков может быть больше, чем один, и желательно иметь возможность синхронизировать их dev-окружения. Тут .gitignore нам всё поломает.
В-третьих, бОльшая часть настроек (INSTALLED_APPS, менеджеры контекста, мидлвари) — общие для всех окружений. Поэтому имеет смысл держать их в одном месте, коим и является base.py.
Спасибо, осознал, понравилось :)
В two scoops of django читал про другой подход: версионируются абсолютно все настройки (settings/prod.py, settings/dev.py и т.д). А всякие SECRET_KEY, пароли от БД и т.д выносятся в переменные окружения.
Для «add_class»-функциональности (и прочих полезных штук для работы с формами) рекомендую django-widget-tweaks.
Спасибо, отличная вещь!
но при повторном использовании приложений в других проектах приходилось менять пути импорта

можно например использовать относительные импорты
Вы немного не поняли. Создайте django-admin.py startproject one, django-admin startproject two, и попытайтесь импортировать settings.py. Для первого проекта будет from one import settings, для второго from two import settings. Относительные пути не спасут.

P.s. Пример надуманный. Разумеется, по-хорошему from django.conf import settings.
Просто не нужно такие(переносимые) приложения «складывать» в ту же директорию где и settings.py. Кстати емнип «дефолтную» структуру проекта обновили в одной из крайних версий.
Тогда в проекте вместо такого импорта с именем проекта
from project_one.news.models import Model

будет возможен например такой импорт
from news.models import Model


вот еще как пример можно посмотреть github.com/django/djangoproject.com

p.s. по крайней мере у меня это нормально работает
p.p.s. хотя в статье у Вас указана нормальная структура(с приложениями в «корневой» директории, рядом с manage.py), тогда не понятно откуда проблема с импортами
Я вас понял :) У меня эти мысли с унификацией названий проектов появились по нескольким причинам:
1. Я складываю все глобальные утилиты туда же, где и settings.py, в project/project. Например, project/project/templatetags/add_class.py. По мне, так это логично — всё, что применимо ко всему проекту — в основную папку проекта. Вы сейчас предложете вынести всё это в новое приложение utils — и будете тоже правы. Дело вкуса, имхо.
2. Когда на разных проектах одинаковые пути — это сильно облегчает жизнь, проверено. Лично я теперь не зависаю на cd, вспоминая имя проекта.
<irony>У вас слишком расистская статья, слишком много упоминаний рабства.</irony>
Называйте ваш проект project (django-admin.py startproject project) — ну или другим, но одинаковым именем для всех проектов. Раньше я называл проекты соответственно домену, но при повторном использовании приложений в других проектах приходилось менять пути импорта — то from supersite import utils, то from newsite import utils. Это путает и отвлекает.
Это вообще странно. Та структура каталогов, что у вас ниже нарисована вообще не подразумевает, что в импорты попадёт «название проекта». Их туда вообще не нужно включать, т.к. в пути надо включать именно внутренность этой папки проекта (которая вообще неважно как называется), потому непонятно как и зачем оно в импорты попадёт. Названия «основных приложений» как раз, имхо, надо делать разными. Это удобно, например, если несколько таких «основных приложений» рядом надо где-то положить. Для этого к такому виду и было приведено, емнип, в версии 1.4. До этого внутренности были снаружи и там действительно бывали проблемы с повторным использованием и импортами.

Лично я внутренности того что вы называете «папки с проектом» вытаскиваю в корень внешней вашей «папки сайта». Потому что эта лишняя вложенность там не особо нужна.
Добавлю пару замечаний:
— Приложения достаточно удобно хранить в отдельной директории, например, apps.
— Шаблоны имеет смысл хранить все вместе, но отдельно от приложений (т.е. выделить под все шаблоны отдельную директорию). Такая структура подталкивает хранить шаблоны в структуре app_name/tmpl_name и коллизии сразу видны на глаз. Кроме того шаблоны обычно уникальны для каждого проекта (в отличие от приложений, которые полностью или частично реюзабельны), а если шаблон универсальный то вообще имеет смысл задуматься о создании пакета.

Есть такой замечательный продукт django-cms.
Расписывать все прелести не буду, вкратце — CMS на Django =)
Простите, не удержался
image
Так, господа, я тут не для холивара или для рекламы ссылочку оставил. А для того, чтобы незнающие смогли узнать о наличии таковой CMS.
Недавно смотрел Django-CMS и видяшку на ютубе где аффтар утверждает «Friends don't let friends use Drupal». Но вот как раз в плане CMS-нутости, ИМХО Drupal даст Django-CMS огромную фору, т.к. механизм CCK+Views+Panels это реально мощный конструктор. В Django-CMS ничего такого не нашел. Как унифицированная простейшая CMS-ка пойдет, но не имеет ИМХО большого смысла. Заказчик чаще всего не хочет изучать все эти менюшки и настройки, он хочет зайти в админку и увидеть большую красную кнопку — «PROFIT». А для этого админку требуется сделать под заказчика и Django-CMS тут совсем не помогает +))

P.S: Сам долго юзал Drupal, но последний год использую Django, ибо в работе черезчур уникальные для Drupal-а вещи +))
Drupal — PHP, Django (-cms) — Python, и вообще речь в топике не об этом)
Клиенту, естественно, хочется иметь такую кнопку, кто ж такую не хочет)) как только такая `работающая` появится — закажу себе парочку.
А причем тут язык на котором написана CMS? Я говорю про функционал, что я не видел среди CMS для питона аналогов CCK+Views+Panels в Drupal.
{% include 'slave.html' %}
{% include 'slave.html' with var=var %}

И никакие они не медленные. При частом обращении ФС поместит файл в память и будет все нормально. Вдвойне неактуально во времена ssd vps. У меня основной шаблон так весь в инклудах, потому как редактировать простыни, где нужный код находится в 40-х отступах от левого края совсем неудобно. И в cvs красота — видно, что правили footer.html, а не ничего говорящее base.html.

pre_save

Есть еще Django Dirty Fields — неизменная специя к большинству моих моделей (автор идеи сам Armin!). Умеет так:

class TestModel(DirtyFieldsMixin, models.Model):
    boolean = models.BooleanField(default=True)

>>> tm = TestModel(boolean=True,characters="testing")
>>> tm.save()
>>> tm.boolean = False
>>> tm.is_dirty()
True
>>> tm.get_dirty_fields()
{'boolean': True}
а для чего такое можно использовать?
Чаще всего я его использую в связке с mptt, т.к. предпочитаю хранить урлы категорий в базе:

class TreeModel(MPTTModel, DirtyFieldsMixin):
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
    title = models.CharField(max_length=100)
    slug = models.SlugField(max_length=70, db_index=True, unique=True)
    path = models.CharField(max_length=255, editable=False, unique=True, db_index=True)

Ключевое тут поле path — оно хранит адрес предков: /parent/parent/child/, и при сохранении предка надо это поле обновить у всех детей вниз по иерархии. А чтобы не дергать базу просто так, при сохранении модели я проверяю, изменилось ли поле slug и если да, обновляю его у всех детей.

def save(self, *args, **kwargs):
    self.path = self.slug
    if self.parent:
        self.path = '{}/{}'.format(self.parent.path, self.slug)
    # Надо сохранить модель прежде, чем браться за детей, чтобы записать path,
    # но после вызова super.save память о старых полях потрется,
    # поэтому надо запомнить состояние
    is_dirty = 'path' in self.get_dirty_fields()
    super(TreeModel, self).save(*args, **kwargs)
    # Если изменение поля было, пускаем рекурсию — каждый child запустит свою проверку
    if is_dirty:
        for child in self.get_children():
            child.save()
ах вот оно как работает, показывает изменение поля, тогда понятно и полезно :)
Просто из вашего первого примера, не совсем понятно было, что ж оно делает
Спасибо.

Я называю инклюды медленными, потому что в своё время делал
{% for item in items %}
    {% include 'item_description.html' %}
{% endfor %}

Замена include сделала приложение быстрее.

А ещё коварный include не ругнётся, если вы забудете передать какую-либо переменную в его контекст, а inclusion tag ругнётся. Так что лично я не любитель инклюдов, хотя и пользуюсь ими иногда для разбивки полотна html на несколько мелких кусочков, не нуждающихся в контексте, например, {% include 'google_analytics.html' %}.
Я и сейчас так делаю. Это от проекта зависит: если кусочек не маленький и это противоречит DRY (в разумных рамках: большой шаблон, много условий и т.д.)
Вот как это легко решается:

{% for item in items %}
    {% cache 1800 list_item item.id item.modified %}
        {% include 'item_description.html' %}
    {% endcache %}
{% endfor %}

Если item изменится, он тут же обновит кеш, а старый сам протухнет со временем.

{% include 'google_analytics.html' %}

Да прибудет с вами django-chunks!
Но в данном случае переменная var передаётся в шаблон slave.html неявно — var передатся в master.html, а slave.html просто «цепляет» контекст master'а. Таким образом, мы видим, что шаблон внутри {% include %} зависит от контекста основного шаблона. Мы вынуждены следить за контекстом родительского шаблона, иначе в дочерний может попасть что-нибудь не то.

Решается параметром only:

If you want to render the context only with the variables provided (or even no variables at all), use the only option. No other variables are available to the included template:
{% include "name_snippet.html" with greeting="Hi" only %}
Еще можно описывать настройки для всех окружений в settings.py с помощью модуля django-configurations. Очень удобно если больше двух окружений.
Да действительно, надо попробовать
Может не «вместо», а «вместе с»?
Пробовал и специально не упомянул в статье. Из всего функционала пригодилось только workon. На вкус и цвет, как говорится.
Дело привычки конечно. Мне лично удобно когда все «виртуалки» в одном месте и еще некоторые полезные «плюшки».
А меня вот как раз «все виртуалки в одном месте» не устроило :) Плюс стараюсь по минимому ставить софта. Жизнь без virtualenvwrapper возможна! Но он популярен, это да.
Не раз встречал импорт local_settings в начале settings.py, как описано в вашей статье. Честно говоря, не понимаю почему в начале, а не в конце. Суть в том, что вам может понадобится что-либо переопределить. Ну предположим, что в settings.py DEBUG=False и вот как ни крути local_settings, если он подключен в начале, дебаг вы не включите. А вот если делаете импорт в конце, то можете переопределять в local_settings всё, что душе угодно не трогая никак settings.py.
А DEBUG в settings.py не нужен. Не забудете потом, что и где вы переопределили?
Ну про DEBUG, наверное, плохой пример, но, например, можно к INSTALLED_APPS добавлять debug_toolbar или ещё чего там может пригодиться для тестирования. Можно просто в local_settings сделать INSTALLED_APPS += ('debug_toolbar', ) и всё. Мне кажется это удобным.
А можно в settings.py сделать
if DEBUG:
    INSTALLED_APPS += ...

:)
Можно и переопределять, конечно. Но тогда у вас одна опция одновременно хранится в разных файлах… А ведь явное лучше неявного.
Можно, да. Но костыль же. Тем более, если не определить в local_settings INSTALLED_APPS, будет фейл. Но, тут, конечно, можно ещё проверять существование свойства. :) Я на самом деле интересовался не спора ради, а просто, мало ли — есть какие-то причины на то. Но пока я останусь при своём мнении.
Да тут в основном дело вкуса, никакой великой тайны нет :) Что в начале, что в конце — лишь бы нравилось
Старайтесь не писать {% url 'shop/product' id=product.id %}.

Почему?
Промазал, ответил ниже
Если вы решите потом изменить способ адресации к объекту, то придётся везде заменять теги {% url %}. Пример из моей практики: раньше адреса были вида /shop/product/<id товара>/, но потом пришли сеошники и заставили привести адреса к такому: /shop/product/<slug товара>/. Пришлось во всём проекте заменять {% url 'shop/product' id=product.id %} на {% url 'shop/product' slug=product.slug %}. Если бы я использовал везде get_absolute_url, то было бы достаточно изменить лишь этот метод.
Но ведь это работает только с урлами, однозначно сопоставленными с моделями. Что с остальными делать?
Ничего не делать с ними. Хардкодить {% url 'contacts' %}, по-другому никак.
Подробнее, зачем нужен get_absolute_url, хорошо написано на djangoproject.com.
Ну вот и получается, что в одном месте url, в другом get_absolute_url. Нехорошо. Я за единообразие. Урлы надо менять не так часто, а читается лучше, когда написано везде одинаково.
1) Действительно ли inclusion tags работают быстрее {% include %}?
2) Beautiful soup адово течет, лучше использовать lxml

Дополню пост: есть замечательный апп django-devserver
1) К сожалению, со 100% уверенностью сказать не могу. В моей практике рендериться с inclusion tag быстрее.
2) BS позволяет указать в качестве бэкенда lxml. По умолначию он использет дефолтный html.parser, который до версии питона 2.7.3 был корявым. Если вы имеете в виду, что течёт сам BS, то буду признателен за пруфлинк.
1. Обязательно включать кеширование шаблонов — оно доступно из коробки
2. Для разбития сеттингсов есть хорошая батарейка github.com/2general/django-split-settings
Кэширование — зверь, которого надо уметь готовить. Лучше так: в разработке кэш отключать вообще, но обязательно соблюдать завершающую стадию «а теперь включим кэш», в тестах проверять-проверять-проверять!, на продакте — включать с уверенностью что все оттестировано.
Часто видел трудноуловимые и трудно понимаемые ошибки при наличии кэша, включенного а ля «нам надо ускорить рендеринг! а давай кэширование включим».
Вопрос тем кто уже освоился с джангой, гуру или те кто чувствую себя с джангой вполне комфортно.
Я вот только начал, или даже только начинаю ее изучать.
Меня сбивает столку туториалы для разные версий джанги, одни на 1.5, чуть меньше на 1.6, two scoops которые зацепил тоже 1.6, а тут еще все ждут мега-эпичный релиз 1.7 который позиционируется такимже основательным как 1.0.

Я то понимаю что надо пережовывать все, пахать и пахать. Сталкивался с подобным и в других отрослях.

Но может всетки поделитесь какимито советами, подбдрите, успокоите — вдруг че умного надумали со времен когда сами были новисами :))))
Я начинал с djangobook, которая уже в то время устарела :) Но именно она позволила понять, с чего вообще начать. Думаю, первые 7 глав позволят «въехать» в основные принципы и вполне себе сойдут за туториал, ну а дальше docs.djangoproject.com от начала и до конца.
У джанги есть и собственный туториал, но он в своё время мне не понравился.
даааа, от джангобука многие открещиваются как от нечистой — так что я как бы решил вабще не обращать на нее внимание. но конечно все же просмотрю :)

джанго туториал мне более или менее понравился, сам по себе он далек от полноценного, но через него думаю что я как минимум половину доков прочитал.

меня больше всего напрягает переход между фишками версий...1.5 1.6 1.7, что то добавлено, что то уже похоронено.
Просто начни писать что-нибудь простое. Там блог или чатик, или чонеть для сбора/обработки/отображения какой-неть простой статистики. Или что-то вроде To-Do списка, или сервис который считает сколько ты в день куришь. Главное — четко определи 1 единственную задачу для сервиса, и сделай его как получится. Когда после этого начнешь делать что-то второе, уже будешь ориентироваться в том что ты умеешь, что можно сделать лучше, что для этого надо изучить.

Тут как с английским языком — бессмысленно надеяться что ты походишь на курсы, поучишь словарь и внезапно заговоришь. Чтобы чему-то научиться, нужно это делать) Да, ты будешь косячить, будешь ошибаться, сделаешь кривую херню… а как тут иначе то?))
Sign up to leave a comment.

Articles