Простое использование¶
Класс Action и его использование¶
Пример экшена, который читает даннные из POST’а. Проверяет значение C и сохраняет в БД значения A и B. Код:
from m3.ui.actions import Action
from m3.ui.actions.results import OperationResult
...
class MyFirstAction(Action):
url = 'save'
verbose_name = u'Сохранение данных'
short_name = 'my-first-action-alias'
def pre_run(self, request, context):
""" pre_run удобно использовать для начальных проверок """
value = request.POST.get('C')
if value < 0:
# Обработка запроса на этом прерывается
return OperationResult(success=False, message=u'Недопустимое значение')
def run(self, request, context):
""" Основная работа экшена производится здесь """
a = request.POST.get('A')
b = request.POST.get('B')
SomeModel.objects.create(a=a, b=b)
return OperationResult(message=u'Данные сохранены')
OperationResult это один из возможных результатов работы экшена, который автоматически преобразуется в Django HttpResponse внутри контроллера. В нашем случае класс OperationResult указывает успешно ли завершилась операция success и несет соответствующее сообщение message.
Класс ActionPack и его использование¶
Экшены и паки входящие внутрь нашего пака задаются с помощью атрибутов actions и subpacks. Пример:
from m3.ui.actions import ActionPack
class MyFormActionPack(ActionPack):
def __init__(self):
super(MyFormActionPack, self).__init__()
self.actions.extend([
GetWindowAction, GetDataAction(), SaveAction, DeleteAction()
])
self.subpacks.append( InnerActionPack )
class InnerActionPack(ActionPack):
url = 'inner'
def __init__(self):
super(InnerActionPack, self).__init__()
self.actions.extend([
SubAction1, SubAction2, ...
])
Обратите внимание, что в список actions можно добавлять как классы, так и экземпляры экшенов. На самом деле это не имеет значения, т.к. контроллер при инициализации автоматически создает экземпляры экшенов и паков, а также дополняет их специальными атрибутами. Об этом в главе Класс ActionController и его использование
Часто бывает необходимо получить прямой доступ к экшенам внутри пака через атрибуты. Живой пример из М3:
class BaseDictionaryActions(ActionPack):
def __init__(self):
super(BaseDictionaryActions, self).__init__()
self.list_window_action = DictListWindowAction()
self.select_window_action = DictSelectWindowAction()
self.edit_window_action = DictEditWindowAction()
...
self.actions = [
self.list_window_action,
self.select_window_action,
self.edit_window_action,
...
]
Класс ActionController и его использование¶
Определение контроллера состоит из нескольких этапов:
- Экземпляр контроллера как правило создается в файле app_meta.py внутри приложения.
- Чтобы передавать в него запросы Django нужно создать вьюшку контроллера dict_view и зарегистрировать url pattern для неё в методе register_urlpatterns. Он вызывается автоматически для всех приложений.
- Регистрация паков в контроллере производится методом register_actions.
Пример файла app_meta.py внутри приложения dicts:
# Именованный экземпляр контроллера
dict_controller = ActionController(url='/core-dicts', name=u'Справочники')
def register_urlpatterns():
""" Регистрация вьюшки контроллера """
return urls.defaults.patterns('',
(r'^core-dicts/', 'mis.core.dicts.app_meta.dict_view'),
)
def dict_view(request):
""" Вьюшка контроллера """
return dict_controller.process_request(request)
def register_actions():
""" Регистрация паков в контроллере """
dict_controller.packs.extend([
MyFormActionPack,
MyDictionaryActions
])
При добавлении пака в контроллер вызывается метод _build_pack_node, который создает экземпляр пака и всех вложенных в него экшенов и паков. Заполняются служебные атрибуты, с помощью которых можно обходить дерево:
- parent - ссылка на родительский пак
- controller - ссылка на родительский контроллер
Чаще всего бывает нужно найти экшен или пак в уже сформированной иерархии. Для этого есть несколько методов:
- По известному адресу экшен можно найти методом get_action_by_url
- Пак по имени или классу можно найти методом find_pack
- Рекомендуется искать экшены и паки по короткому имени (псевдониму). Для него есть атрибут short_name.
Пример:
from m3.helpers import urls
my_action = urls.get_action('my-action-alias')
my_pack = urls.get_pack('my-pack-alias')
Контекст запросов в М3 (m3.actions.context)¶
В Django параметры запросов передаются с помощью класса HttpRequest в трех словарях POST, GET и REQUEST. Извлекаемые из них значения не проверяются и не конвертируются в сложные типы Python. Ещё один минус, что извлечение, как правило, делают внутри кода вьюшки, таким образом загромождается место под бизнес логику.
В М3 был разработан механизм контекста, который позволяет:
- Автоматически извлекать значения из запроса по заранее заданным правилам.
- Преобразовывать сырое значение в указанный в правилах тип.
- Передавать контекст в ответе к визуальным компонентам.
Определение контекста в экшенах¶
Правила извлечения контекста задаются внутри метода context_declaration экшена. Представляют собой список экземпляров класса ActionContextDeclaration или его упрощенное написание ACD. Так же можно использовать декларативное описание по определенной схеме
- Action.context_declaration()¶
Метод объявления необходимости наличия определенных параметров в контексте.
Должен возвращать список из экземпляров ActionContextDeclaration либо словарь описания контекста для DeclarativeActionContext
Результат: описание необходимости наличия определенных параметров в запросе Тип результата: list of m3_core.actions.context.ActionContextDeclaration либо m3_core.actions.context.DeclarativeActionContext
- class m3.actions.context.ActionContextDeclaration(name='', default=None, type=None, required=False, verbose_name='', *args, **kwargs)¶
Класс, который определяет правило извлечения параметра из запроса и необходимость его наличия в объекте контекста ActionContext.
Параметры: - name (str) – имя параметра
- type – тип извлекаемого значения
- required (bool) – указывает что параметр обязательный
- default – значение параметра по умолчанию, используется если его нет в запросе, но наличие обязательно
- verbose_name (unicode) – человеческое имя параметра, необходимо для сообщений об ошибках
- human_name()¶
Возвращает человеческое название параметра verbose_name
Пример определения правил:
from m3.actions.context import ActionContextDeclaration, ACD
...
class GetRowsAction(Action):
def context_declaration(self):
return [
ActionContextDeclaration(name='id', type=int, required=True, verbose_name=u'Идентификатор модели'),
ActionContextDeclaration(name='start', type=int, required=True),
ActionContextDeclaration(name='limit', type=int, required=True)
]
...
class GetRowsAction(Action):
url = 'save'
def context_declaration(self):
# декларативное опимание контекста
return {
# параметр запроса -> параметры разбора
'ids': {
# значение по умолчанию,
# используется при отсутствии параметра в запросе
# если не указано - параметр считается обязательным
'default': '',
# тип парсера, может быть:
# - строкой - именем одного из предопределенных парсеров
# - callable-объектом, выполняющим парсинг. Такой объект
# может возбуждать ValueError/TypeError/KeyError/IndexError
# в случае неправильного формата данных, что позволяет
# использовать в качестве парсера что-то вроде:
# 'type': ['on', 'yes'].__contains__
# 'type': {1: 'Male', 2: 'Female'}.get
# 'type': float
# 'type': json.loads
'type': int
# наименование параметра, понятное пользователю
# используется в сообщениях об ошибках
'verbose_name': u'Идентификатор объекта'
},
#
'date': {
'type': datetime.date,
'verbose_name': u'Дата начала действия изменений'
},
'comment': {
'type': str
}
}
@transaction.commit_on_success
def run(self, request, context):
if not hasattr(context, 'comment'):
context.comment = u'Без комментариев'
for id in context.ids:
SomeModel.objects.create(pid=id, comment=context.comment)
msg = u'Изменения внесены на дату %s' % context.date
return OperationResult(message=msg)
Разберем правило:
ACD(name='date', required=True, type=datetime.date, verbose_name=u'Дата начала действия изменений')
Из словаря REQUEST запроса HttpRequest будет извлечено значение с именем “date” и приведено к типу datetime.date. Если его нет в REQUEST, то до run() управление не дойдет и будет сгенерировано исключение RequiredFailed.
Разберем правило:
ACD(name='comment', type=str)
Из словаря REQUEST запроса HttpRequest будет извлечено значение с именем “comment”. Если его нет в REQUEST, то соответствующий атрибут не будет добавлен в context, т.к. required=False по умолчанию. Управление будет передано в run().
Возможные результаты работы экшенов (m3.actions.results)¶
В Django в качестве конечного ответа вьюшек используется класс HttpResponse. В М3, при работе с экшенами и паками, используются более высокие абстракции - класс производные от ActionResult.
Главные отличия ActionResult от HttpResponse:
- ActionResult используется для хранения и трансформации ответа приложения на запрос. HttpResponse же напротив является готовым ответом и содержит в себе данные специфичные для протокола http, например status_code и cookie. Которые не нужны при написании бизнес-логики.
- Тип ответа в ActionResult определяется классом и интерфейсом им предоставляемым, а в HttpResponse только mimetype.
- ActionResult поддерживает передачу контекста
- ActionResult преобразуется в HttpResponse после обработки запроса в контроллере с помощью метода get_http_response.
Благодаря этим особенностям в процессе обработки запроса можно изменять и даже подменять ответы. Например, подключенный плагин может модифицировать форму, передаваемую через ExtUIScriptResult, добавив в нее новые контролы.
- class m3.actions.results.ActionResult(data=None, http_params={})¶
- get_http_response()¶
Результат: соответствующий данному результату выполнения действия ответ Тип результата: django.http.HttpResponse
- process_http_params(response)¶
Добавляет параметры http в ответ
Параметры: response (наследник m3_core.actions.results.ActionResult) – ответ, в который добавляются параметры Результат: http-ответ с добавленными параметрами Тип результата: наследник m3_core.actions.results.ActionResult
От него наследуется более сложный базовый класс BaseContextedResult, который может передавать контекст в визуальные компоненты и формы.
- class m3.actions.results.BaseContextedResult(data=None, context=None, http_params={})¶
Абстрактный базовый класс, который оперирует понятием результата выполнения операции, ‘отягощенного некоторым контектом’
Простые обёртки над HttpResponse¶
- class m3.actions.results.HttpReadyResult(data=None, http_params={})¶
Результат выполнения операции в виде готового HttpResponse. Для данного класса в data храниться объект класса HttpResponse.
- class m3.actions.results.TextResult(data=None, http_params={})¶
Результат, данные data которого напрямую передаются в HttpResponse
- class m3.actions.results.XMLResult(data=None, http_params={})¶
Результат в формате xml, данные которого напрямую передаются в HttpResponse
Ответы передающие JSON¶
Предназначены для работы с JSON и готовыми к JSON-сериализации данными
- class m3.actions.results.JsonResult(data=None, http_params={})¶
Результат выполнения операции в виде готового JSON объекта для возврата в response. Для данного класса в data храниться строка с данными JSON объекта.
- class m3.actions.results.PreJsonResult(data=None, secret_values=False, dict_list=None)¶
Результат выполнения операции в виде, например, списка объектов, готовых к сериализации в JSON формат и отправке в HttpResponse. В data передается объект для сериализации. В dict_list указывается список объектов и/или атрибутов вложенных объектов для более глубокой сериализации. Смотри класс т3.core.json.M3JSONEncoder. Параметр специфичный для проекта secret_values - используется чтобы указать, что передаются персональные обезличенные данные и их расшифровать перед отправкой клиенту.
Результат выполнения операции¶
- class m3.actions.results.OperationResult(success=True, code='', message='', *args, **kwargs)¶
Результат выполнения операции, описанный в виде Ajax результата ExtJS: success или failure. В случае если операция выполнена успешно, параметр success должен быть True, иначе False.
Параметры: - success (boolean) – флаг успеха операции
- message (unicode) – сообщение, поясняющее результат выполнения операции.
- code (unicode) – текст javascript, который будет выполнен на клиенте в результате обработки результата операции.
- static by_message(message)¶
Возвращает экземпляр OperationResult построенный исходя из сообщения message. Если сообщение не пустое, то операция считается проваленной и success=False, иначе операция считается успешной success=True.
Параметры: message (unicode) – текст сообщения об ошибке, или не указан
- get_http_response()¶
Возвращает объект HttpResponse, соответствующий данному результату выполнения операции
Результат: http-ответ, соответствующий данному результату Тип результата: django.http.HttpResponse