Но в django структура защищает то, что вы помещаете логику домена в классы моделей (уровень строк) или связанные классы менеджера (уровень таблицы), что делает вещи немного неудобными. Да, прекрасно, что если ваша логика включает только одну таблицу, но в реальном приложении обычно каждый шаг будет включать в себя несколько разных таблиц или даже баз данных, то что мне делать в этом случае?
Поместите логику в View? Я не думаю, что это хорошая практика. или даже перезаписать метод сохранения в классе модели, передав дополнительные данные с помощью ** kwargs? то бэкэнд сломается.
Надеюсь, это иллюстрирует мое замешательство в том, где бизнес-логика должна быть помещена в приложение django.
devharb 11 апр. 2011, в 05:13
Поделиться
Поделиться:
django-models
django-admin
transactions
business-logic
3 ответа
Не уверен, что вы прочитали раздел Managers в Django, он, кажется, решает вашу текущую ситуацию. Предполагая, что у вас установлена следующая модель Account , встроен User .
# accounts/models.py class AccountManager(models.Manager): def create_account(self, account, user_list): . class Account(models.Model): objects = AccountManager()
Не стесняйтесь отделять код менеджера в отдельном файле, если он становится слишком большим. В ваших взглядах:
# views.py from accounts.models import Account Account.objects.create_account(account, user_list)
Бизнес-логика все еще находится в моделях.
ИЗМЕНИТЬ
Ключевое слово здесь переопределяется, а не перезаписывается. Если вы переопределите метод сохранения модели, вы должны иметь в виду, что любые операции создания, обновления из вашего веб-приложения и администратора будут использовать эту новую функциональность. Если вы хотите, чтобы такая бизнес-логика происходила раз в определенном виде, лучше всего оставить ее вне save .
Я думаю, вы можете поместить свою бизнес-логику в свой собственный обычный класс. Вам придется создавать экземпляр этого класса каждый раз, когда вам нужно управлять бизнес-логикой. В качестве альтернативы вы можете поместить свою бизнес-логику в качестве статической функции в этом новом классе, если хотите пропустить подход ООП.
Thierry Lam 11 апр. 2011, в 05:43
Поделиться
Привет Тьерри, как вы думаете, метод сохранения перезаписи это хорошая идея? Также. разве объект менеджера не должен быть более сфокусирован на логике уровня таблицы? (просто любопытно, так как теперь я довольно убежден в вашем подходе)
Источник: overcoder.net
Django: Размещайте логику вне шаблонов (и представлений)
Я действительно не понимал, что это значит, пока не начал страдать от последствий наличия логики в моих файлах .html. После трех лет работы с Django я теперь стараюсь держать бизнес-логику подальше не только от шаблонов, но и от представлений.
В этом посте я постепенно расскажу о наиболее рекомендуемых способах размещения бизнес логики и обрисую преимущества, которые предлагает каждый из них.
Приложение: простой блог
Давайте начнем с извлечения логики из шаблонов. Как и в случае с большинством реальных приложений, пусть наш выдуманный проект начнется с простого блога и позже постепенно начнет усложнятся.
Пусть у нас будет такая простая модель:
Худший вариант: логика в шаблонах
Так же допустим нам нужно видеть наши черновики (не опубликованные статьи), чтобы просмотреть их до публикации и мы не хотим, чтобы они были видны другим посетителям.
# views.py def all_posts(request): context = <> posts = Post.objects.all()[:10] context[‘posts’] = posts return render(request, ‘index.html’, context)
«>> Draft Future Post Date: >
«>>
Date: >
В index.html мы проверяем, является ли request.user администратором, и если так и есть, мы не фильтруем никаких сообщений. В блоке elif, который применяется ко всем остальным посетителям, мы проверяем свойство is_draft, равное False, перед отображением сообщения:
Мы также добавляем некоторую разметку Bootstrap, чтобы администратор мог ясно видеть, является ли определенный пост черновиком или его публикация запланировано на будущее. Нам не нужна эта разметка для обычных посетителей, потому что они не должны видеть эти сообщения.
На самом деле подобный дизайн просто ужасен по нескольким причинам:
- Нет разделения интересов: почему шаблон решает, какие сообщения показывать?
- Нарушает принцип DRY (не повторяй себя): взгляни на тег span, содержащий дату. Мы повторяем его в обоих пунктах нашего оператора if.
- Многословие: наш index.html отображает только ссылки на наши сообщения, но он уже выглядит очень загроможденным.
- Читаемость и удобство обслуживания: шаблонизаторы Jinja/Django очень полезны и функциональны, но так же они известны загроможденным синтаксисом. Если вы вернетесь к этому шаблону через 6 месяцев, сможете ли вы быстро разобраться, что происходит? Вспомните ли вы, что если вы добавите div содержащий имя автора сообщения, вы должны будете сделать это в обоих пунктах if?
Лучший способ
Если вместо этого мы напишем как то так:
# views.py from django.utils import timezone def posts_index(request): context = <> limit = 10 posts = Post.objects.all()[:limit] if not request.user.is_superuser: # filter out drafts and future posts posts = Post.objects.filter(is_draft=False, published_date__lte=timezone.now())[:limit] context[‘posts’] = posts return render(request, ‘index.html’, context)
Тогда наш файл index.html будет выглядеть как то так:
«>> Draft Future Post Date: >
В этом случае мы разместили бизнес-логику вне шаблона. Основной шаблон должен отвечать только за отображение элементов.
Что мы получаем в этом случае:
- DRYness: мы больше не повторяем HTML для рендеринга поста.
- Возможность повторного использования: поскольку index.html больше не принимает решение о том, отображать ли сообщение, мы можем использовать его позже в других представлениях (например, в архиве).
- Удобочитаемость: теперь гораздо понятнее, что происходит в index.html, и его будет легче понять, когда мы вернемся к нему в будущем.
Так что это намного лучше и, вероятно, достаточно, если вы разрабатываете сверхпростое приложение. Но используя такой подход рано или поздно вы все равно начнете повторяться.
Пусть наш блог будет развиваться и допустим нам потребовалось создать новое представление featured_posts, в котором мы хотим отображать только сообщения, помеченные как выделенные, используя поле is_highlighted в модели.
def featured_posts(request): context = <> posts = Post.objects.filter(is_highlighted=True) if not request.user.is_superuser: posts = posts.filter(is_draft=False, published_date__lte=timezone.now()) context[‘posts’] = posts # we’re free to use `index.html` here because our template is now re-usable return render(request, ‘index.html’, context)
Далее создадим еще одно представление, панель мониторинга, где мы отображаем последние 5 обычных сообщений и последние 5 выделенных сообщений (они могут перекрываться):
def dashboard(request): context = <> posts = Post.objects.all() limit = 10 posts_featured = Post.objects.filter(is_highlighted=True) if not request.user.is_superuser: posts = posts.filter(is_draft=False, published_date__lte=timezone.now()) posts_featured = posts_featured.filter(is_draft=False, published_date__lte=timezone.now()) context[‘last_posts’] = posts[:limit] context[‘last_posts_featured’] = posts_featured[:limit] return render(request, ‘dashboard.html’, context)
Мы уже видим тут две проблемы:
- Наш код становится все более детализированным, а в нем всего два поля для фильтрации. Представьте себе 3 или 4 (например, авторы и теги). В реальных приложениях у вас часто будет больше полей для фильтрации.
- Мы переносим детали реализации наших моделей в наши представления: теперь наше представление должно знать, что в наших моделях есть поле is_highlighted.
Что еще хуже, подумайте о том, что произойдет, если мы решим, что сообщения, размещаемые в разделе featured_posts, должны соответствовать еще двум критериям:
Теперь нам нужно обновить код в двух наших представлениях, чтобы включить новые критерии:
Post.objects.filter(is_draft=False, is_highlighted=True, likes__gte=3)
Теперь представьте себе, какую работу вы должны выполнить, когда у вас есть 7 представлений и еще два критерия для фильтрации.
Еще более лучшие способы
Есть два способа сделать это лучше. Мы начнем с первого, который считается менее традиционным и менее естественным, но отлично справляется, если вам нужно что-то быстрое (хотя может быть и более грязное).
Методы класса
Мы добавили в модель два метода, которые мы можем использовать в наших представлениях следующим образом:
# notice: no .objects because it’s model/class method published_posts = Post.published() featured_posts = Post.featured() published_and_featured = Post.published() > posts = Post.objects.all() limit = 10 posts_featured = Post.featured() if not request.user.is_superuser: posts = posts Post.published() context[‘last_posts’] = posts[:limit] context[‘last_posts_featured’] = posts_featured[:limit] return render(request, ‘dashboard.html’, context)
Более того, изменить наши критерии для того, что считается «featured» публикацией, так же просто, как изменить одну строку в Post.featured():
Теперь все представления, которые вызывают этот метод модели, будут обновлены соответствующим образом.
Так что это довольно просто, но, как я писал, считается менее общепринятым в сообществе Django. Главное ограничение модельных методов заключается в том, что они не имеют возможности выстраивать цепочку вызовов:
# пытаемся построить цепочку из двух методов >>> posts_featured_published = Post.featured().published() ‘QuerySet’ object has no attribute ‘published’
Вот почему мы использовали логический оператор AND (‘ для фильтрации queryset posts_featured_published = Post.featured() > posts = Post.objects.all() limit = 10 posts_featured = Post.objects.featured() if not request.user.is_superuser: posts = posts.published() posts_featured = posts_featured.published() context[‘last_posts’] = posts[:limit] context[‘last_posts_featured’] = posts_featured[:limit] return render(request, ‘dashboard.html’, context)
Обратите внимание на то, что эти два метода менеджера теперь связаны друг с другом:
>>> posts_featured_published = Post.objects.featured().published() , ]>
Используя PostQuerySet в файле models.py, мы расширяем имеющиеся у нас методы менеджера, поэтому наряду с get, filter, aggregate и т. д. теперь мы имеем published и featured.
Преимущества использования менеджеров моделей перед методами классов:
- Цепочность и ясность: Post.objects.featured().published() выглядит более питонным и естественным, чем Post.featured() https://webdevblog.ru/django-keeping-logic-out-of-templates-and-views/» target=»_blank»]webdevblog.ru[/mask_link]