Архив за [undefined]

virtualenv: виртуальные окружения

Много у вас разных питонячих проектов работают на одном сервере? У меня вот да. Все ли они использую одни и те же библиотеки или версии библиотек? Нет. Так как сделать, чтобы можно было удобно поддерживать всё это многообразие?

Давным дано я предлагал один вариант - использовать svn:externals и таскать зависимости (код сторонних библиотек и приложений) с собой. Но такой способ очень ограничен в своем применении. Вы должны использовать VCS (subversion или иные поддерживающие аналогичные концепции) для развертывания проекта на сервере и все зависимости тоже должны быть доступны в той же системе контроля версий. Но так случается очень редко. Да и потом далеко не всегда VCS вообще используются для выкладки проектов. Пакетные системы во многих случаях удобней.

Создание изолированных окружений задача довольно давнишняя. И в питонячем мире решается разными способами уже давно. Одним из инструментов является - virtualenv.

Создать окружение просто:

virtualenv myenv

После выполнения этой команды создается директория myenv в который находится некое подмножество unix-like корневой файловой системы. В директории myenv/bin будет лежать бинарник питона, и несколько дополнительных скриптов. В myenv/lib - дерево каталогов, повторяющее оное у текущего установленного питона в системе.

Для того чтобы питонячий код работал в этом окружении, его надо запускать, используя myenv/bin/python, или подключив ...

Разделять надо с умом

Предыдущий мой пост к моему удивлению вызвал бурную реакцию. В нем я рассказывал как некоторые модули джанговского приложения можно разделить на более мелкие.

Общее настроение комментариев - это в большинстве случаев не нужно. Так я полностью с этим согласен. Я и не пропагандирую эти способы как ежедневные практики. Я лишь пытался показать как это можно сделать теоретически.

Да, концептуально необходимость дробления например models.py скорей всего признак каких-то ошибок в проектировании проекта и приложения. Ну а кто не ошибается?

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

Спрос есть - такие вопросы регулярно всплывают в рассылках и форумах. Плохо это или хорошо другой вопрос и не такой однозначный.

Я конечно понимаю, что джангисты перфекционисты по сути (да я и сам такой), но не бывает идеального кода, архитектуры и вообще чего угодно. И как раз проекты, которые "работают" в большинстве своём внутри далеко не пример для подражания. Это баланс. Проектировать всю жизнь и ничего ...

Разделяем толстые модули

Когда ваше джанговские приложение/проект обрастает кодом всё больше и больше, то встает вопрос как максимально удобно организовать файловую структуру. И хотя Джанга не сильно капризна и требовательна к структуре модулей/пакетов, но всё-таки не всегда разработчик может себе позволить вольности. Джанга всё-таки ожидает, что какие-то части приложения будут лежать в определенных местах. Это относится к моделям, тестам, шаблонным тегам, определениям админки и т.п.

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

Но поскольку мы имеем дело с очень гибким инструментов в лице питона и Джанги, то можем в большинстве ситуаций позволить себе обойти эти ограничения.

models.py

Наверно самая частая проблема - это разделение слишком большого файла models.py на более мелкие, атомарные, отвечающий за одну модель модули. Об этом не редко спрашивают в различных форумах и рассылках.

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

Итак представим, что у вас есть большой и толстый файл models.py, в котором есть несколько моделей. Для примера возьмем предметную область блога - модели Post, Comment, Tag. Мы хотим чтобы каждая модель обитала в своем модуле ...

Revisited: Чуть более быстрое удаление объектов в админке

Итак, продолжаю переосмысливать прошлые свои советы и снипеты, которые в нынешних условиях можно реализовать более правильно и изящно.

Сейчас вот посмотрим на проблему добавления к list-view в админке своей "кнопки" с каким-то действием. Возвращаясь к старому посту про "быстрое удаление", можно придумать несколько вариантов реализации подобной концепции, но уже со свежей джангой. Вот они:

  • Для Django 1.0. Поскольку в 1.0 старая админка канула в лету, то reverse('django.contrib.admin.views.main.delete_stage') уже не работает. Но не страшно, код метода модели достаточно чуть-чуть изменить:

    def remove(self):
       from django.core.urlresolvers import reverse
       from django.contrib import admin
       return '<a href="%s" class="deletelink">Delete</a>'\
                   % reverse(admin.site.root,
                              args=("/".join([self.__class__._meta.app_label,
                                       self.__class__._meta.object_name,
                                       self.pk, 'delete']),)))
    

    Это конечно при условии, что вы используете дефолтный админский сайт (объект admin.site), иначе root надо указать объекта используемого кастомного сайта.

    Но лучше поступить по другому. Перенести этот метод в ModelAdmin класс, описывающий админку для данной модели, изменив его например так:

    class EntryAdmin(admin.ModelAdmin):
       #...
       list_display = [..., 'remove_link', ...]
       #...
    
       def remove_link(self, entry):
          from django.core.urlresolvers import reverse
          return '<a href="%s" class="deletelink">Delete</a>'\
                       % reverse(self.admin_site.root,
                                  args=("/".join([self.model._meta ...

Revisited: M2M отношение и post_save сигнал

Давным давно описывал решение проблемы с сигналами и ManyToMany полями. Вопрос остался актуальным и по сей день и частенько всплывает в форумах.

Напомню суть: в save методе (или в обработчике pre_/post_save сигналов) модели родителе (Post) нельзя узнать об изменениях в отношениях с чайлдами (Comment). Это связано с тем, что чайлды присоединяются к родителю после его сохранения.

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

В версии 1.0 нам дали возможность задать для M2M связывания свою модель. Применительно к моему старому примеру это будет выглядеть так:

class Comment(models.Model):
    #...

class Post(models.Model):
    comments = models.ManyToManyField(Comment, through='PostToCommentRelation')
    #...

class PostToCommentRelation(models.Model):
    post = models.ForeignKey(Post)
    comment = models.ForeignKey(Comment)

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

def relation_change_handler(sender, instance, created, **kwargs):
    # работа с instance.post или instance.comment
    pass ...