Как связать несколько повторно используемых приложений Django вместе?
Я стараюсь писать многократно используемые приложения Django. Теперь я озадачен тем, как собрать их все вместе, чтобы получить окончательный проект.
Вот пример того, что я имею в виду: у меня есть приложение для работы с изображениями, которое хранит, изменяет размеры и отображает изображения. Также у меня есть приложение для ведения блога, которое хранит, редактирует и отображает тексты. Теперь я хочу объединить эти два, чтобы показать сообщения в блоге с изображениями.
Чтобы сделать это, я мог бы поместить поля внешнего ключа в блог, чтобы они указывали на картинки. Но тогда блог не может быть использован без приложения изображения. Также я мог бы создать третье приложение, которое отвечает за соединение обоих.
Каков наилучший способ сделать это?
РЕДАКТИРОВАТЬ: Спасибо за ваши очень хорошие ответы, но я все еще ищу более практический пример, как решить эту проблему. Для завершения моего примера: иногда было бы неплохо использовать приложение для блогов без приложения для работы с изображениями. Но если я жестко закодирую зависимость, это уже невозможно. Так как насчет 3-го приложения для объединения обоих?
4 ответа
Введение в нижней части ответа (более прямо к ответу). Я предполагаю, что у вас есть одно приложение для обработки текста под названием Text и одно приложение для обработки изображений под названием Pictures и третье приложение для ведения блогов под названием Blog.
Большая фотография
Вам нужно будет изучить руководство по языку шаблонов для программистов на Python. Идея состоит в том, что каждая вещь находится в своем собственном приложении, и что у вас есть третье приложение, которое соединяет все. Затем приложения должны предоставлять свои модели и представления так, как вам нравится (помните, что вы должны сосредоточиться на том, что должно делать приложение), а также предоставлять набор тегов-шаблонов.
Как сделать теги включения
Сделайте теги включения, и это действительно легко! Это напомнит вам о написании нормальных просмотров.
Создайте директорию шаблона тегов в папке вашего приложения. Также создайте __init__.py
файл в этом шаблоне тегов (поэтому каталог становится пакетом Python).
Затем создайте файл Python. Имя важно, вы будете использовать это в {% load xyz %}
в шаблонах, которые будут использовать ваше приложение. Например, если вызвать файл picturestags.py
позвонишь{% load picturestags %}
во всех шаблонах, которые будут использовать его.
Сначала в файле добавьте политику, о которой вам не нужно много думать, просто включите это прежде всего:
from django.template import Library
register = Library()
Затем добавьте теги, определив функции с тем же именем, что и ваш тег. Я назову это display_picture в примере, и он будет принимать один аргумент URL. Функция должна создать словарь, который вы будете использовать в шаблоне. Мой пример просто отобразит картинку, на которую указывает URL.
@register.inclusion_tag('pictures/display_picture.html')
def display_picture(url):
return {'picture': url}
Создайте шаблоны путей / рисунки в вашем приложении и создайте файл display_picture.html внутри, содержащий:
<img src="{{ picture }}" />
Как вы, вероятно, понимаете, @register делает это тегом, то, что находится внутри словаря, который возвращает display_picture, - это то, что вы можете использовать в display_picture.html. Очень похоже на ваши обычные функции просмотра.
В итоге вы получите следующие файлы:
pictures/
__init__.py
models.py
views.py
tests.py
templates/
pictures/
display_picture.html
templatetags/
picturetags.py
Это все, что вам нужно добавить в приложение Picture. Чтобы использовать это в своем приложении блога, вам нужно добавить картинки в INSTALLED_APPS. Затем в шаблонах, где вам нужно использовать свой собственный недавно испеченный домашний тег, сначала загрузите его: {% load picturestags %}
затем просто добавьте тег {% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
как это:
{% load picturestags %}
<html>
<body>
{% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
</body>
</html>
Результаты
Это только небольшой пример, но вы можете видеть, что это очень легко расширить. Ваш блог может соединить приложение "Текст и картинки", импортировав их модели и внешний ключ. Существует связь между текстом и рисунками для определенного сообщения в блоге. Ваш blog_post.html-шаблон может выглядеть (упрощенно):
{% load picturestags %}
{% load texttags %}
<html>
<body>
<h1>{{ subject }}</h1>
<div class="article">{% article_to_html articleid %}</div>
<div class="article_picture">{% display_picture %}</div>
</body>
</html>
Обратите внимание, что только у блога есть зависимости, и это должны быть зависимости (нет блога без текста и картинок... но картинки могут жить без текста). Внешний вид и размещение должны контролироваться с помощью CSS и DIV/SPAN-тегов. Таким образом, вы можете взять свое приложение Picture и передать его тому, кто не имеет представления о приложении Text, и использовать его, отображая изображения различными способами, возможно, даже не касаясь вашего кода!
Теги включения - единственное, что я знаю, так как я только что узнал об этом вчера. Я думаю, что Django делает удобство для упрощения жизни. На странице документации есть намного больше (в том числе, как сделать "настоящие" теги сложным способом без "ярлыков"). Так что, если вы найдете этот метод ограниченным, прочитайте документацию... у него есть много примеров. В нем также обсуждается, как создавать фильтры, simple_tags, соображения о потоках и другие дополнительные вещи.
Вступление
У меня была именно эта проблема, так как вы и я также хотели что-то отличное от ответов, которые я прочитал (я не говорю, что ответы были плохими, они помогли мне многому научиться и дали мне понимание, но я хотел, чтобы я писал сейчас), Мне удалось выяснить кое-что, что не очень очевидно, благодаря вашему вопросу и, безусловно, благодаря переполнению стека, так что это мой вклад в ответ даже на полугодовой вопрос, который, вероятно, заброшен (может помочь гуглеру или двум)!
Я также получил много вдохновения от многоразовых приложений Google Tech Talk. В конце (43 минуты) он упоминает несколько хороших примеров, таких как django-tagging, который, по его словам, является моделью для написания приложений многократного использования. Это дало мне идею всего этого, потому что именно так django-tagging решает эту проблему, которая у нас была / есть.
Теперь, после того как я написал все это (заняло час), я впервые чувствую, что могу внести свой вклад вместо того, чтобы просто гуглить и следить за тем, что делают другие, или жаловаться, что другие не пишут, как мне нужно что-то делать. Впервые я беру на себя ответственность за то, чтобы написать свое мнение, чтобы другие могли гуглить это (просто нужно было написать этот абзац:-), потому что это действительно здорово, даже если его можно измельчить на куски или проигнорировать и забыть).
Подумайте об этом так же, как если бы вы использовали любое стороннее приложение в своем проекте. "Повторное использование" не означает "без зависимостей". Напротив, вам будет сложно найти приложение, которое не имеет хотя бы одной зависимости, даже если оно просто зависит от Django или основных библиотек Python. (Хотя основные библиотеки Python обычно рассматриваются как "безопасные" зависимости, т. Е. Все будут иметь их, между версиями Python иногда что-то меняется, так что вы все еще привязываете свое приложение к определенному моменту времени).
Цель повторного использования такая же, как и у DRY: вы не хотите писать один и тот же код снова и снова. В результате, имеет смысл выделить функциональность, например, приложение для работы с изображениями, потому что вы можете использовать его снова и снова в других приложениях и проектах, но ваше приложение для работы с изображениями будет иметь зависимости, и другие пакеты будут зависеть от него, пока круговых зависимостей нет, вы хороши (круговая зависимость означала бы, что вы фактически не разделяли функциональность).
Это хороший вопрос, и мне тоже трудно его решить. Но - вы представляете, что эти приложения публикуются публично, или вы используете их только сами? Если вы не выпускаете, я бы не стал слишком беспокоиться об этом.
Другое дело, что зависимости хорошо иметь. Приложение с фотографиями в вашем примере звучит как хороший кандидат на роль "многоразового" приложения. Это просто, делает одно и может использоваться другими приложениями.
Приложение блога, с другой стороны, обычно должно использовать другие приложения, такие как приложение для работы с изображениями или приложение для тегов. Я нахожу эту зависимость прекрасной. Вы можете попытаться немного абстрагироваться, просто связавшись с медиа-ресурсом, который был добавлен в ваше приложение для работы с изображениями.
Это всего лишь немного здравого смысла. Можете ли вы сделать свои приложения тонкими? Если да, то попробуйте создать их, чтобы их можно было использовать повторно. Но не бойтесь брать зависимости, когда они имеют смысл. Кроме того, попробуйте разрешить точки расширения, чтобы вы могли поменять зависимости для других. Прямой внешний ключ здесь не поможет, но, возможно, что-то вроде сигналов или API Restful может помочь.
Я новичок в Django, и я столкнулся с той же проблемой. Стыдно, что в сообществе Django так много шума по поводу повторно используемых приложений, но нет ни единого авторитетного руководства о том, как соединить их в проекте без жестко закодированных зависимостей.
Я работал над новым проектом Django, и я хотел настроить мои модели, чтобы максимально избежать жесткого кодирования. Это шаблон, который я использовал.
раскладка
project_root/
core/
models/
mixinmodels1.py
mixinmodels2.py
...
utils.py
...
app1/
models/
__init__.py
base.py
basemixins.py
mixins.py
concrete.py
/signals
__init__.py
handlers.py
utils.py
...
app2/
...
...
Модели приложений
base.py
Этот модуль реализует только "причину существования" этого приложения в абстрактных классах. Правила таковы:
Этот модуль обычно импортирует только из
core
приложение. Обычно он ничего не импортирует из других приложений в том же проекте.Исключением из вышеуказанного правила является случай, когда "причина существования" предполагает существование другого приложения. Например,
group
приложение предполагает, что естьuser
приложение где-то. В этом случае способ связать их:# project_root/settings.py AUTH_USER_MODEL = 'app_label.UserModel' # project_root/groups/models/base.py from django.conf import settings
а затем с помощью настроек.AUTH_USER_MODEL, чтобы обратиться к
user
модельИспользуйте этот шаблон для всех приложений, а не только для
user
приложение. Например, вы должны также сделать# project_root/settings.py GROUP_MODEL = 'app_label.GroupModel'
Если вы используете вышеупомянутый шаблон, принимайте только
base.py
другого приложения, на которое вы ссылаетесь. Не предполагайте функциональность разработанных конкретных классов (я буду обсуждать, где поместить конкретные классы в ближайшее время)Конечно, импорт из django, сторонних приложений и пакетов python разрешен.
Убедитесь, что предположения, которые вы делаете в
base.py
любого приложения является надежным и не изменится в будущем. Хороший примерdjango-registration
Джеймсом Беннеттом. Это старое приложение, но его привлекательность не уменьшилась, потому что он сделал твердые предположения. Поскольку хорошие многократно используемые приложения хорошо справляются с задачей, найти такой набор предположений несложно.
basemixins.py
В этом модуле должны быть реализованы заглушки для конкретных моделей этого приложения. "Подключаемым" к модели M является любая модель, которая содержит внешний ключ к модели M. Например:
# project_root/groups/models/basemixins.py
from django.conf import settings
from django.db import models
class BaseOwnedByGroup(models.Model):
"""
This is a plug to the group model. Use this
to implement ownership like relations with
the group model
"""
owner = models.ForeignKey(settings.GROUP_MODEL,
related_name = '%(app_label)s_%(class)s_owner',
verbose_name = 'owner')
# functionality and manager definitions go here.
class Meta:
abstract = True
app_label = 'groups'
BaseOwnedByGroup
это "штепсель" к group
модель. Правила здесь такие же, как 'base.py`
- При определении "пробки" в
basemixins.py
, только предполагайте, что функциональность предоставленаbase.py
, - Импорт только из
core
, Django, сторонние приложения и пакеты Python.
mixins.py
: Этот модуль должен использоваться для двух целей
Определить сложные "пробки", которые предполагают функциональность сложных конкретных классов, но не взаимосвязи с другими приложениями. Сложные разъемы в идеале должны наследовать один из "базовых разъемов", определенных в
basemixins.py
,Определить смешанные модели (которые не являются "пробками"), которые могут использоваться конкретными классами этого приложения.
concrete.py
Этот модуль должен использоваться для определения (как вы уже догадались) конкретных классов этих приложений и для установления отношений с другими приложениями. Короче говоря, этот модуль предполагает ваш проект и все функции, которые вы хотите предоставить в нем.
Отношения с другими приложениями должны быть настроены следующим образом:
Чтобы установить
one to one
или жеmany to one
отношения с моделью М приложенияanother_app
, сделайте следующее:# project_root/another_app/utils.py def plug_to_M_factory(version_label): """ This is a factory method which returns the plug to model M specified by version_label """ if version_label == 'first_version': from another_app.models.basemixins import BasePlugToM return BasePlugToM if version_label == 'second_version': from another_app.models.mixins import PlugToM return PlugToM ... # project_root/groups/models/concrete.py from groups.models.base import BaseGroup from another_app.utils import plug_to_M_factory PlugToMClass = plug_to_M_factory(version_label = 'second_version') class ConcreteGroup(BaseGroup, PlugToMClass): # define your concrete model class Meta: app_label = 'groups'
Чтобы установить
many to many
отношения, рекомендуемый способ заключается в использованииthrough
модель. Унаследуйте правильный плагин вthrough
модель точно так же (как мы делали вConcreteGroup
модель)
signals
: При настройке отношений часто приходится выполнять операции над моделью M приложения app1
, когда модель N приложения app2
изменения. Вы можете использовать сигналы, чтобы справиться с этим. Ваши обработчики могут взять на себя функциональность конкретного отправителя, но часто им это не нужно. Предположение о базовой версии отправителя в base.py
достаточно. Вот почему это хорошая идея всегда использовать settings.ModelName
ссылаться на конкретную модель. Вы можете извлечь класс модели из settings.ModelName
строка либо с помощью ContentType
или используя широкий проект get_model_for_settings
функционировать следующим образом:
# project_root/project/utils.py
from django.db.models import get_model
from django.core.exceptions import ImproperlyConfigured
def get_model_from_settings(model_string):
"""
Takes a string of the form 'app_label.model' as input, returns the
appropriate model class if it can find it.
"""
try:
app_label, model_name = model_string.split('.')
except ValueError:
raise ImproperlyConfigured("function argument must be of the "
"form 'app_label.model_name', got '%s'" % model_string)
model = get_model(app_label, model_name)
if model is None:
raise ImproperlyConfigured("function argument refers to model "
"'%s' that has not been installed" % model_string)
return model
core
: Базовое приложение - это специальное приложение, которое хранит смешанные функции проекта.
Эти миксины не должны предполагать что-либо о любом другом приложении. Единственным исключением из этого правила являются миксины, которые полагаются на базовую функциональность настроек.AUTH_USER_MODEL. Это потому, что вы можете с уверенностью предположить, что большинство проектов будет иметь
user
модель.Конечно, импорт из пакетов django, третьих лиц и python все еще разрешен
Помните, что все
base.py
а такжеbasemixins.py
модули могут импортировать изcore
,
Наконец, чтобы все работало как задумано, импортируйте ваши модели в models/__init__.py
каждого приложения.
Преимущества, которые я нахожу из следующей схемы:
Модели многоразовые. Любой может использовать абстрактные базовые модели и миксины для разработки своей конкретной конкретной модели.
base.py
,basemixins.py
и связанные фабричные методы могут быть объединены с конкретной моделью бетона и отправлены в приложение многократного использования.Приложения являются расширяемыми. Все миксины версионированы и есть четкая схема наследования.
Приложения слабо связаны. Доступ к внешним миксинам осуществляется с помощью заводских методов, а внешние модели указываются с помощью django.conf.settings.
Приложения автономны. Любые изменения в приложении, скорее всего, повредят это приложение и только это приложение. Другие приложения, скорее всего, останутся невредимыми. Даже если внешние приложения ломаются, место, где это может произойти, четко обозначено.
Я использовал эту схему, чтобы уменьшить связь между моими приложениями. Я не опытный программист на Django, и мне есть чему поучиться, поэтому любые отзывы приветствуются.