Как связать несколько многоразовых приложений Django вместе?

17

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

Вот пример того, что я имею в виду: У меня есть приложение для картинок, которое хранит, изменяет размеры и отображает изображения. Также у меня есть приложение weblog, которое хранит, редактирует и отображает тексты. Теперь я хочу объединить эти два, чтобы показывать сообщения в блогах с изображениями.

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

Какова наилучшая практика?

EDIT: Спасибо за ваши очень хорошие ответы, но я все еще ищу более практичный пример того, как решить эту проблему. Чтобы завершить мой пример: иногда было бы неплохо использовать приложение для блога без приложения для картинок. Но если я жестко кодирую зависимость, это уже невозможно. Итак, как насчет 3-го приложения объединить оба?

    
задан JasonTS 20.07.2012 в 14:19
источник

4 ответа

16

Вступительный разговор в нижней части ответа (более прямой ответ). Я предполагаю, что у вас есть одно приложение для обработки текста под названием «Текст» и одно приложение для обработки изображений «Картинки» и третье приложение для ведения блога под названием «Блог».

Большое изображение

Вам нужно будет изучить руководство по языку шаблона для программистов на питоне , Идея состоит в том, что каждая вещь находится в собственном приложении и что у вас есть третье приложение, которое соединяет все. Затем приложения должны предоставлять свои модели и представления, как вам нравится (просто не забудьте сосредоточиться на том, что должно делать приложение), а также предоставить набор templatetags.

Как сделать теги включения

Сделайте теги включения, и это очень просто! Это напомнит вам о написании нормальных представлений.

Создайте каталог templatetags в папке приложения. Также создайте файл __init__.py в этом templatetags (поэтому каталог становится пакетом 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

Это все, что вам нужно добавить в приложение для картинок. Чтобы использовать это в своем приложении в блоге, вам нужно добавить Картинки на ваш 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>

Обратите внимание, что только у Blog есть зависимости, и это зависимости, которые он должен иметь (без блога без текста и изображений ... но картинки могут жить без текста). Взгляд и размещение должны контролироваться CSS и DIV / SPAN-тегами. Таким образом, вы можете принять приложение «Картинка» и передать его тому, кто понятия не имеет о приложении «Текст», и использовать его, отображая изображения по-разному, возможно, даже не прикасаясь к вашему коду!

Теги включения - это единственное, что я знаю, так как я только что узнал об этом вчера. Я думаю, что это удобство Django, чтобы сделать жизнь простой. На странице документации есть намного больше (в том числе, как сделать «реальные» теги трудным путем без «ярлыков»). Поэтому, если вы обнаружите, что этот метод ограничен, прочитайте документацию ... в нем много примеров. В нем также обсуждается, как создавать фильтры, simple_tags, обсуждения потоков и другие продвинутые материалы.

Введение

У меня была эта именно эта проблема, так как вы и я также хотели что-то другое, чем ответы, которые я читал (я не говорю, что ответы были плохими, они помогли мне многому научиться и дали мне идеи, но я хотел этого, я писать сейчас). Мне удалось понять что-то, что не очень очевидно, благодаря вашему вопросу и, безусловно, благодаря Stack Overflow, так что это мой вклад обратно даже на полтора года вопрос, который, вероятно, заброшен (может помочь googler или два)!

Я также получил много вдохновения из Google Tech Talk многоразовых приложений . В конце (43 минуты) он упоминает несколько хороших примеров, таких как django-tagging , и это то, что он говорит о модели как писать многоразовые приложения. Это дало мне идею для всего этого, потому что это то, как django-tagging решает эту проблему, которую мы имели / имели.

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

ответ дан Aquaplanet 14.01.2013 в 23:01
источник
6

Подумайте об этом так же, как использовать любое стороннее приложение в своем проекте. «Повторно использовать» не означает «без зависимостей». Напротив, вам будет трудно найти приложение, которое не имеет хотя бы одной зависимости, даже если оно просто зависит от Django или основных библиотек Python. (В то время как базовые библиотеки Python обычно считаются «безопасными» зависимостями, то есть каждый будет иметь его, вещи иногда меняются между версиями Python, поэтому вы по-прежнему блокируете свое приложение в определенный момент времени).

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

    
ответ дан Chris Pratt 20.07.2012 в 17:19
источник
5

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

Другое дело, зависимости могут быть хорошими. Приложение для картинок в вашем примере кажется хорошим кандидатом, чтобы быть «многоразовым» приложением. Это просто, делает одно и может использоваться другими приложениями.

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

Это всего лишь немного здравого смысла. Можете ли вы сделать ваши приложения стройными? Если да, попробуйте создать их, чтобы их можно было повторно использовать. Но не бойтесь брать зависимости, когда они имеют смысл. Кроме того, попробуйте разрешить точки расширения, чтобы вы могли поменять зависимости для других. Прямой иностранный ключ здесь не поможет, но возможно что-то вроде сигналов или Restful APIs.

    
ответ дан Josh Smeaton 20.07.2012 в 14:27
источник
4

Я новичок в 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
    

    , а затем используя 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 : этот модуль должен внедрять пробки в конкретные модели этого приложения. «Plug» для модели 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 - это «plug» для модели group . Правила здесь такие же, как «base.py»

  • При определении «пробок» в basemixins.py используйте только функции base.py .
  • Импортировать только из core , django, сторонних приложений и пакетов python.

mixins.py : этот модуль должен использоваться для двух целей

  • Определить сложные «пробки», которые предполагают функциональность сложных конкретных классов, но не взаимоотношения с другими приложениями. Разработанные пробки должны идеально наследовать один из «базовых пробок», определенных в basemixins.py .

  • Определить модели mixin (которые не являются «plug-ыми»), которые могут использоваться конкретными классами этого приложения.

concrete.py : этот модуль должен использоваться для определения (вы догадались) конкретных классов этих приложений и установления отношений с другими приложениями. Короче говоря, этот модуль предполагает ваш проект и все функциональность, которую вы хотите предоставить в ней.

Отношения с другими приложениями должны быть настроены следующим образом:

  • Чтобы установить отношения one to one или many to one с моделью M приложения 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 : Основное приложение - это специальное приложение, в котором хранятся расширенные функции mixin проекта.

  • Эти mixins не должны принимать ничего о каком-либо другом приложении. Единственным исключением из этого правила являются mixins, которые полагаются на базовую функциональность настроек.AUTH_USER_MODEL. Это связано с тем, что вы можете смело предположить, что большинство проектов будут иметь user .

  • Конечно, импорт из пакетов django, сторонних и python по-прежнему разрешен

  • Помните, что всем base.py и basemixins.py модуля разрешено импортировать из core .

Наконец, для того, чтобы все работало по назначению, импортируйте свои модели в models/__init__.py каждого приложения.

Преимущества, которые я нахожу из следующей схемы:

  • Модели повторно используются . Любой может использовать абстрактные базовые модели и микшины для разработки своей собственной конкретной модели. base.py , basemixins.py и связанные с ним заводские методы могут быть объединены вместе с бетонной моделью из голых костей и отправлены в многоразовое приложение.

  • Приложения расширяемые . Все миксины имеют версию и есть четкая схема наследования.

  • Приложения слабо связаны . Доступ к внешним микшинам осуществляется с помощью заводских методов, а внешние модели - с помощью django.conf.settings.

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

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

ответ дан user4013889 25.09.2014 в 22:46
источник
  • Это кажется отличным ответом, поскольку я начинаю изучать Django. Любопытно, почему голосование не было выше, есть ли причина, по которой я не должен использовать это в качестве основы для своего приложения для нескольких приложений? –  neuronet 12.08.2017 в 20:11