Много у вас разных питонячих проектов работают на одном сервере? У меня вот да. Все ли они использую одни и те же библиотеки или версии библиотек? Нет. Так как сделать, чтобы можно было удобно поддерживать всё это многообразие?
Давным дано я предлагал один вариант - использовать 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
. Мы хотим чтобы каждая модель обитала в своем модуле ...
Итак, продолжаю переосмысливать прошлые свои советы и снипеты, которые в нынешних условиях можно реализовать более правильно и изящно.
Сейчас вот посмотрим на проблему добавления к 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 ...
Давным давно описывал решение проблемы с сигналами и 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 ...