Я пишу проект в Django и вижу, что 80% кода находится в файле models.py
. Этот код сбивает с толку, и через некоторое время я перестаю понимать, что на самом деле происходит.
Вот что меня беспокоит:
- Я нахожу уродливым, что мой уровень модели (который должен был отвечать только за работу с данными из базы данных) также отправляет электронную почту, использует API для других служб и т. Д.
- Кроме того, я считаю недопустимым размещать бизнес-логику в представлении, потому что таким образом ее становится трудно контролировать. Например, в моем приложении есть как минимум три способа создания новых экземпляров
User
, но технически оно должно создавать их единообразно. - Я не всегда замечаю, когда методы и свойства моих моделей становятся недетерминированными и когда у них возникают побочные эффекты.
Вот простой пример. Сначала User
модель была такой:
class User(db.Models):
def get_present_name(self):
return self.name or 'Anonymous'
def activate(self):
self.status = 'activated'
self.save()
Со временем это превратилось в это:
class User(db.Models):
def get_present_name(self):
# property became non-deterministic in terms of database
# data is taken from another service by api
return remote_api.request_user_name(self.uid) or 'Anonymous'
def activate(self):
# method now has a side effect (send message to user)
self.status = 'activated'
self.save()
send_mail('Your account is activated!', '…', [self.email])
Я хочу разделить сущности в моем коде:
- Объекты моей базы данных, уровень базы данных: что содержит мое приложение?
- Объекты моего приложения, уровень бизнес-логики: что может сделать мое приложение?
Каковы хорошие практики для реализации такого подхода, который может быть применен в Django?
Ответы:
Похоже , что вы спрашиваете о разнице между моделью данных и моделью домена - последний, где вы можете найти бизнес - логику и объекты , как воспринимаются вашим конечным пользователем, бывшее когда вы на самом деле хранения данных.
Кроме того, я интерпретировал третью часть вашего вопроса следующим образом: как заметить неспособность разделить эти модели.
Это две совершенно разные концепции, и всегда трудно их разделить. Тем не менее, есть несколько общих шаблонов и инструментов, которые можно использовать для этой цели.
О доменной модели
Первое, что вам нужно понять, это то, что ваша модель предметной области не связана с данными; речь идет о действиях и вопросах, таких как «активировать этого пользователя», «деактивировать этого пользователя», «какие пользователи активированы в настоящее время?» и «как зовут этого пользователя?». В классических терминах: речь идет о запросах и командах .
Мышление в командах
Давайте начнем с рассмотрения команд в вашем примере: «активировать этого пользователя» и «деактивировать этого пользователя». Хорошая вещь о командах состоит в том, что они могут быть легко выражены маленьким сценарием «дано, когда тогда»:
Такой сценарий полезен, чтобы увидеть, как одна команда может повлиять на различные части вашей инфраструктуры - в этом случае ваша база данных (своего рода «активный» флаг), ваш почтовый сервер, системный журнал и т. Д.
Такой сценарий также действительно поможет вам в настройке среды разработки через тестирование.
И, наконец, мышление в командах действительно помогает вам создать приложение, ориентированное на задачи. Ваши пользователи оценят это :-)
Выражения команд
Django предоставляет два простых способа выражения команд; оба они являются допустимыми вариантами, и нет ничего необычного в том, чтобы смешать два подхода.
Сервисный уровень
Сервисный модуль уже описан @Hedde . Здесь вы определяете отдельный модуль, и каждая команда представляется как функция.
services.py
Использование форм
Другой способ - использовать форму Django для каждой команды. Я предпочитаю этот подход, потому что он сочетает в себе несколько тесно связанных аспектов:
forms.py
Мышление в запросах
Ваш пример не содержал никаких запросов, поэтому я позволил себе составить несколько полезных запросов. Я предпочитаю использовать термин «вопрос», но запросы - это классическая терминология. Интересные запросы: «Как зовут этого пользователя?», «Может ли этот пользователь войти в систему?», «Показать список деактивированных пользователей» и «Каково географическое распределение деактивированных пользователей?»
Прежде чем отвечать на эти запросы, вы всегда должны задать себе два вопроса: это презентационный запрос только для моих шаблонов и / или запрос бизнес-логики, связанный с выполнением моих команд, и / или отчетный запрос.
Презентационные запросы просто сделаны для улучшения пользовательского интерфейса. Ответы на запросы бизнес-логики напрямую влияют на выполнение ваших команд. Отчеты о запросах предназначены только для аналитических целей и имеют более короткие временные ограничения. Эти категории не являются взаимоисключающими.
Другой вопрос: «Есть ли у меня полный контроль над ответами?» Например, при запросе имени пользователя (в этом контексте) мы не имеем никакого контроля над результатом, потому что мы полагаемся на внешний API.
Создание запросов
Самый простой запрос в Django - это использование объекта Manager:
Конечно, это работает, только если данные фактически представлены в вашей модели данных. Это не всегда так. В этих случаях вы можете рассмотреть варианты ниже.
Пользовательские теги и фильтры
Первый вариант полезен для запросов, которые являются просто презентационными: пользовательские теги и шаблоны фильтров.
template.html
template_tags.py
Методы запросов
Если ваш запрос не просто презентационный, вы можете добавить запросы в ваш services.py (если вы его используете) или ввести модуль query.py :
queries.py
Модели прокси
Прокси-модели очень полезны в контексте бизнес-логики и отчетности. Вы в основном определяете расширенное подмножество вашей модели. Вы можете переопределить базовый QuerySet менеджера, переопределив
Manager.get_queryset()
метод.models.py
Модели запросов
Для запросов, которые по своей сути являются сложными, но выполняются довольно часто, существует возможность моделей запросов. Модель запроса - это форма денормализации, в которой релевантные данные для одного запроса хранятся в отдельной модели. Хитрость, конечно, заключается в том, чтобы поддерживать денормализованную модель в синхронизации с основной моделью. Модели запросов можно использовать только в том случае, если изменения полностью находятся под вашим контролем.
models.py
Первый вариант - обновить эти модели в ваших командах. Это очень полезно, если эти модели меняются только одной или двумя командами.
forms.py
Лучшим вариантом будет использование пользовательских сигналов. Эти сигналы, конечно, испускаются вашими командами. Преимущество сигналов заключается в том, что вы можете синхронизировать несколько моделей запросов с исходной моделью. Кроме того, обработка сигналов может быть загружена в фоновые задачи, используя Celery или аналогичные фреймворки.
signals.py
forms.py
models.py
Держать его в чистоте
При использовании этого подхода становится невероятно легко определить, остается ли ваш код чистым. Просто следуйте этим рекомендациям:
То же самое касается представлений (потому что представления часто страдают от одной и той же проблемы).
Некоторые ссылки
Документация Django: модели прокси
Документация Django: сигналы
Архитектура: доменный дизайн
источник
User.objects.inactive_users()
. Но пример модели прокси здесь IMO приводит к неверной семантике:u = InactiveUser.objects.all()[0]; u.active = True; u.save()
и все жеisinstance(u, InactiveUser) == True
. Также я бы упомянул, что эффективный способ поддерживать модель запросов во многих случаях - это представление db.Я обычно реализую сервисный слой между представлениями и моделями. Это действует как API вашего проекта и дает вам хороший обзор вертолета о том, что происходит. Я унаследовал эту практику от моего коллеги, который часто использует эту технику многоуровневой работы с проектами Java (JSF), например:
models.py
services.py
views.py
источник
Прежде всего, не повторяйся .
Тогда, пожалуйста, будьте осторожны, чтобы не переусердствовать, иногда это просто пустая трата времени и заставляет кого-то терять фокус на том, что важно. Прочтите Дзен питона время от времени.
Посмотрите на активные проекты
хранилище ткань также является хорошим смотреть.
yourapp/models/logicalgroup.py
User
,Group
и связанные модели могут идти подyourapp/models/users.py
Poll
,Question
,Answer
... может пойти подyourapp/models/polls.py
__all__
внутриyourapp/models/__init__.py
Подробнее о MVC
request.GET
/request.POST
etctastypie
илиpiston
Воспользуйтесь преимуществами промежуточного программного обеспечения / тегов-шаблонов
Воспользуйтесь преимуществами модельных менеджеров
User
может идти вUserManager(models.Manager)
.models.Model
.queryset
могли бы пойти вmodels.Manager
.User
одному, поэтому вы можете подумать, что он должен жить на самой модели, но при создании объекта у вас, вероятно, не все детали:Пример:
По возможности используйте формы
Много шаблонного кода может быть устранено, если у вас есть формы, которые сопоставляются с моделью. Это
ModelForm documentation
довольно хорошо. Отделение кода для форм от кода модели может быть полезно, если у вас есть много настроек (или иногда избегать ошибок циклического импорта для более сложного использования).Используйте команды управления, когда это возможно
yourapp/management/commands/createsuperuser.py
yourapp/management/commands/activateinbulk.py
если у вас есть бизнес-логика, вы можете отделить ее
django.contrib.auth
использует бэкэнды , так же, как у db есть бэкэнд ... и т. д.setting
для вашей бизнес-логики (напримерAUTHENTICATION_BACKENDS
)django.contrib.auth.backends.RemoteUserBackend
yourapp.backends.remote_api.RemoteUserBackend
yourapp.backends.memcached.RemoteUserBackend
пример бэкэнда:
мог стать:
больше о шаблонах дизайна
больше о границах интерфейса
yourapp.models
yourapp.vendor
yourapp.libs
yourapp.libs.vendor
илиyourapp.vendor.libs
Короче говоря, вы могли бы иметь
yourapp/core/backends.py
yourapp/core/models/__init__.py
yourapp/core/models/users.py
yourapp/core/models/questions.py
yourapp/core/backends.py
yourapp/core/forms.py
yourapp/core/handlers.py
yourapp/core/management/commands/__init__.py
yourapp/core/management/commands/closepolls.py
yourapp/core/management/commands/removeduplicates.py
yourapp/core/middleware.py
yourapp/core/signals.py
yourapp/core/templatetags/__init__.py
yourapp/core/templatetags/polls_extras.py
yourapp/core/views/__init__.py
yourapp/core/views/users.py
yourapp/core/views/questions.py
yourapp/core/signals.py
yourapp/lib/utils.py
yourapp/lib/textanalysis.py
yourapp/lib/ratings.py
yourapp/vendor/backends.py
yourapp/vendor/morebusinesslogic.py
yourapp/vendor/handlers.py
yourapp/vendor/middleware.py
yourapp/vendor/signals.py
yourapp/tests/test_polls.py
yourapp/tests/test_questions.py
yourapp/tests/test_duplicates.py
yourapp/tests/test_ratings.py
или что-нибудь еще, что поможет вам; найти нужные интерфейсы и границы помогут вам.
источник
Django использует слегка модифицированный вид MVC. В Django нет понятия «контроллер». Ближайший прокси-сервер - это «представление», которое может привести к путанице с конвертированием MVC, потому что в MVC представление больше похоже на «шаблон» Джанго.
В Django «модель» - это не просто абстракция базы данных. В некоторых отношениях он разделяет обязанности с «представлением» Джанго как контролера MVC. Он содержит все поведение, связанное с экземпляром. Если этот экземпляр должен взаимодействовать с внешним API как часть своего поведения, то это все еще код модели. На самом деле, модели вообще не обязаны взаимодействовать с базой данных, поэтому можно предположить, что модели полностью существуют в виде интерактивного слоя для внешнего API. Это гораздо более бесплатное понятие «модель».
источник
В Django структура MVC, как сказал Крис Пратт, отличается от классической модели MVC, используемой в других средах, и я думаю, что основной причиной этого является избегание слишком строгой структуры приложения, как это происходит в других средах MVC, таких как CakePHP.
В Django MVC был реализован следующим образом:
Вид слоя разбит на две части. Представления должны использоваться только для управления HTTP-запросами, они вызываются и отвечают на них. Представления взаимодействуют с остальным приложением (формы, модели, пользовательские классы, в простых случаях напрямую с моделями). Для создания интерфейса мы используем шаблоны. Шаблоны похожи на строки в Django, он отображает в них контекст, и этот контекст был передан представлению приложением (когда запрашивается представление).
Слой модели обеспечивает инкапсуляцию, абстракцию, валидацию, интеллект и делает ваши данные объектно-ориентированными (говорят, что когда-нибудь СУБД тоже будет). Это не означает, что вы должны создавать огромные файлы models.py (на самом деле очень хороший совет - разбить ваши модели на разные файлы, поместить их в папку с именем 'models', сделать в этом файл '__init__.py'). папка, в которую вы импортируете все свои модели и, наконец, используете атрибут 'app_label' моделей models.Model). Модель должна абстрагироваться от работы с данными, она упростит ваше приложение. При необходимости вам также следует создать внешние классы, например «инструменты» для ваших моделей. Вы также можете использовать наследие в моделях, установив для атрибута «abstract» метакласса вашей модели значение «True».
Где отдых? Ну, небольшие веб-приложения, как правило, являются своего рода интерфейсом к данным, в некоторых небольших программных случаях было бы достаточно использовать представления для запроса или вставки данных. В более распространенных случаях используются Forms или ModelForms, которые на самом деле являются «контроллерами». Это не что иное, как практическое решение общей проблемы, и очень быстрое. Это то, что сайт использует.
Если формы вам не подходят, вы должны создать свои собственные классы, чтобы творить чудеса, очень хороший пример этого - приложение администратора: вы можете читать код ModelAmin, он фактически работает как контроллер. Там нет стандартной структуры, я предлагаю вам изучить существующие приложения Django, это зависит от каждого случая. Это то, что разработчики Django намеревались: вы можете добавить класс xml-анализатора, класс API-коннектора, добавить Celery для выполнения задач, использовать Twist для приложения на основе реактора, использовать только ORM, создать веб-сервис, изменить приложение администратора и многое другое. .. Ваша ответственность - создавать код хорошего качества, уважать философию MVC или нет, делать его основанным на модулях и создавать собственные уровни абстракции. Это очень гибкий.
Мой совет: читайте как можно больше кода, вокруг множество приложений django, но не принимайте их всерьез. Каждый случай индивидуален, шаблоны и теория помогают, но не всегда, это неточный совет, django просто предоставляет вам хорошие инструменты, которые вы можете использовать для устранения некоторых проблем (таких как интерфейс администратора, проверка веб-форм, i18n, реализация шаблонов наблюдателей, все ранее упомянутые и другие), но хорошие проекты прибывают из опытных проектировщиков.
PS: используйте класс 'User' из приложения auth (из стандартного django), вы можете создать, например, профили пользователей или хотя бы прочитать его код, это будет полезно для вашего случая.
источник
Старый вопрос, но я все равно хотел бы предложить свое решение. Он основан на признании того, что объектам модели тоже требуются некоторые дополнительные функциональные возможности, хотя неудобно размещать их в models.py . Тяжелая бизнес-логика может быть написана отдельно в зависимости от личного вкуса, но мне, по крайней мере, нравится модель, которая делает все, что связано с самим собой. Это решение также поддерживает тех, кому нравится размещать всю логику внутри самих моделей.
Таким образом, я разработал хак, который позволяет мне отделить логику от определений модели и при этом получить все подсказки от моей IDE.
Преимущества должны быть очевидны, но это перечисляет некоторые, которые я наблюдал:
Я использую это с Python 3.4 и выше и Django 1.8 и выше.
Приложение / models.py
приложение / логика / user.py
Единственное, что я не могу понять, - это как заставить мою среду IDE (в данном случае PyCharm) распознавать, что UserLogic на самом деле является моделью пользователя. Но так как это, очевидно, хак, я очень рад принять небольшую неприятность, всегда указав тип для
self
параметра.источник
Я должен был бы согласиться с вами. В django есть много возможностей, но лучше всего начать с изучения философии дизайна Django .
Вызов API из свойства модели не был бы идеальным, кажется, что было бы более разумно сделать что-то подобное в представлении и, возможно, создать сервисный слой, чтобы все было сухо. Если вызов API является неблокирующим, а вызов дорогим, отправка запроса работнику службы (работнику, потребляющему очередь) может иметь смысл.
В соответствии с философией дизайна Django модели заключают в себе каждый аспект «объекта». Поэтому вся бизнес-логика, связанная с этим объектом, должна там жить:
Побочные эффекты, которые вы описываете, очевидны, логику здесь лучше разбить на Querysets и менеджеров. Вот пример:
models.py
admin.py
источник
Я в основном согласен с выбранным ответом ( https://stackoverflow.com/a/12857584/871392 ), но хочу добавить опцию в разделе «Создание запросов».
Можно определить классы QuerySet для моделей для создания запросов фильтра и так далее. После этого вы можете проксировать этот класс набора запросов для менеджера модели, как это делают встроенные классы Manager и QuerySet.
Хотя, если вам нужно было запросить несколько моделей данных, чтобы получить модель одного домена, мне кажется более разумным поместить это в отдельный модуль, как предлагалось ранее.
источник
Наиболее полная статья о различных вариантах с за и против:
Источник: https://sunscrapers.com/blog/where-to-put-business-logic-django/
источник
Django предназначен для удобной доставки веб-страниц. Если вам это неудобно, возможно, вам следует использовать другое решение.
Я пишу корневые или общие операции на модели (чтобы иметь тот же интерфейс) и другие операции на контроллере модели. Если мне нужна операция из другой модели, я импортирую ее контроллер.
При таком подходе мне достаточно и сложности моих приложений.
Ответ Хедде - это пример, демонстрирующий гибкость самого django и python.
В любом случае, очень интересный вопрос!
источник