Архив за [undefined]

Как правильно отдавать команды

Про полезнейшую возможность делать свои команды для для manage.py я уже писал довольно-таки давно. Но поскольку пользуюсь ими регулярно, то с того времени уже сложились некоторые "правила хорошего тона" по написанию этих команд. Одно из них - внутри метода execute команды не стоит рассчитывать на то, что все опции, для которых вы даже прописали default значения, на самом дела присутствуют.

Покажу на примере. Типичная команда:

class Command(BaseCommand):
   option_list = BaseCommand.option_list + [
       make_option("-f", "--foo", default="bar", action="store")
   ]

   def execute(self, *args, **options):
      foo = options["foo"] # Вот тут и может случиться неожиданное!
      # ...

Если запускать это команду через командную строку и без указания опции --foo, то всё будет хорошо - optparse любезно подставит значение по умолчанию и его можно будет спокойно использовать.

Но для вызова команд есть ещё один способ - внутри другого модуля с помощью функции call_command из django.core.management. И в этом случае аргументы и опции передают как параметры функции и при этом не учитываются значения по умолчанию, указанные в option_list. Т.е. если вы вызвали call_command("foobar") без опции foo, то код сломается с KeyError.

Если команду вы же и написали, то решения достаточно простые.

Можно, либо в execute прописывать параметры и значения по умолчанию в аргументах метода. Вот ...

Попрошу обобщать!

Очередная веха в жизни джанги произошла. У ORM в trunk'е появились агрегации! А следовательно уже в следующем релизе 1.1 мы официально их получим. Ура!

Но что нам даст возможность агрегации? То что раньше приходилось делать либо через голые SQL запросы, либо через extra теперь можно совершенно легитимно получить от ORM. Причём всё в том же привычном виде, что и обычные запросы.

По сути добавилось всего два метода к QuerySet'у это aggregate и annotate. Через них и осуществлется вся агрегационная функциональность.

Для того чтобы понять, что эти методы делают, представим себе такую модель данных и немного подергаем запросы:

class Company(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

class Actor(models.Model):
    name = models.CharField(max_length=50)
    num_awards = models.PositiveSmallIntegerField(default=0)

    def __unicode__(self):
        return self.name

class Movie(models.Model):
    title = models.CharField(max_length=100)
    # некий абстрактный рейтинг актера
    rating = models.PositiveSmallIntegerField(default=0) 

    company = models.ForeignKey(Company)

    actors = models.ManyToManyField(Actor)

    def __unicode__(self):
        return self.title

В базе не много данных: всего 3 компании, 6 фильмов и 10 актеров - но нам этого хватит.

В стандартной поставке доступны следующие агрегационные функции: count, avg, max, min, sum, stddev, variance. Так же ...

Принуждение к порядку

В прошлом посте я рассказал, как можно изменять порядок полей в форме (ну и в модели). Там я чуть-чуть затронул описание низкоуровневой реализации механизма выставления по порядку полей (атрибутов), соответствующему тому как эти поля объявлены в классе.

Откуда вообще взялась необходимость в каких-то хитростях для определения этого порядка? Да потому что, после того как класс создан, к его атрибутам можно легко обратиться через __dict__, но как известно обычный питонячий dict - unordered. Т.е. из-за своей реализации в языке, ключи в нем упорядочены не по тому как они в него попадали, а случайно (на самом деле не совсем случайно, но для нас это не интересно). А следовательно информация о порядке объявления атрибутов класса безвозвратно потеряна. Существует распространенный способ обхода этой проблемы, который активно используется в нескольких компонентах джанги - в моделях и формах.

Классический способ

Для заданного класса, который будет классом атрибутов, нуждающихся в упорядочивании (для джанги это models.Field и forms.Field), заводится "статический" счетчик, который, обновляясь при каждом создании объекта данного класса, хранит порядковый номер этого самого объекта. В дальнейшем по этому номеру поля сортируются и заносятся в какой-либо упорядоченный контейнер - например в SortedDict.

Причем не важно количественное значение счетчика. Главное, что поля объявленнные раньше имеют меньший порядковый номер. Поэтому ...

К порядку!

Довольно часто в форумах, либо в рассылках попадаются вопрос типа: "как изменить порядок вывода полей в форме?". Решение на самом деле тривиальное, но нигде явно не описано. Надо этот пробел заполнить:-).

Задача изменения порядка вывода полей формы актуальная для форм, которые создаются на базе моделей (позволю себе такое определение) автоматически, т.е. через ModelForm. В таких случаях иной раз нет возможности редактировать саму модель. Порядок играет роль при итерации по полям или при использовании стандартных хелперов рендеринга (as_p, as_table и т.п.).

А откуда же вообще берется очерёдность полей? Очевидно, что она зависит от того, как объявлены поля в определении модели (кстати, для тех форм, где поля декларируются вручную, это тоже справедливо). Это делается через хитрый механизм со статическим счетчиком (creation_counter'ом) и SortedDict внутри джанги. Но это отдельная тема для обсуждения. Вернемся к порядку :-).

Для форм, которые созданы на базе моделей или вручную, порядок полей можно легко изменить. Поможет в этом сам SortedDict. Для примера возьмем форму для редактирования стандартной модели User c некоторым подмножеством полей:

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ("username", "first_name", "last_name", "email")

Поля класс формы держит в атрибуте base_fields, который и есть SortedDict:

>>> UserForm.base_fields.__class__
<class 'django.utils.datastructures.SortedDict'>
>>> [(name ...

Висит себе такой toolbar

Как у вас выглядит процесс разработки проекта на джанге? Как вы ведете отладку, а как профилирование? Вставляете print и изучаете консоль дев сервера? Вглядываетесь в лог SQL запросов, в котором зачастую трудно понять "а почему же так тормозит?!"? А может быть вы используете что-то другое?

Я вот до недавнего времени примерно все перечисленное и делал. Но в один прекрасный момент мне это надоело, и я решил все-таки потрогать зверька под именем django-debug-toolbar от Роба Хадсона. Отметив его галочкой "интересно" в свое время, у меня так и не доходили руки его поизучать предметно. Наконец-то я созрел.

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

Debug Toolbar

Приложение совсем небольшое и легко устанавливается. Достаточно положить в PYTHON_PATH приложение debug_toolbar, добавить его в INSTALLED_APPS, прописать нужные мидлвари, так же не забыв указать себя в INTERNAL_IPS - вот в принципе и всё. Есть дополнительные настройки отображения панелей и деталей поведения тулбара, но они опциональны.

В основной комплект входят панели:

  • показ версии джанги
  • общее время генерации страницы
  • значения из settings.py
  • HTTP заголовки
  • Параметры ...