104
Разработка одностраничных вебприложений с использованием PonyORM и ReactJS Александр Козловский, Алексей Малашкевич PyCon Russia 2015

Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Embed Size (px)

Citation preview

Page 1: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Разработка    одностраничных  веб-­‐приложений    

с  использованием    PonyORM  и  ReactJS

Александр  Козловский,  Алексей  МалашкевичPyCon  Russia  2015

Page 2: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

ЗадачаРазработать  Single  Page  ApplicationПриложение  -­‐  библиотека  современного  искусства

Page 3: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

ЗадачаРазработать  Single  Page  ApplicationПриложение  -­‐  библиотека  современного  искусства

Особенности  приложения:  1. Множество  связанных  объектов:  

художники,  картины,  история  продаж,  выставки,  галлереи,  каталоги  и  т.д.

Page 4: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

ЗадачаРазработать  Single  Page  ApplicationПриложение  -­‐  библиотека  современного  искусства

Особенности  приложения:  1. Множество  связанных  объектов:  

художники,  картины,  история  продаж,  выставки,  галлереи,  каталоги  и  т.д.  

2. Нужна  мобильная  версия  приложения  (native  app)

Page 5: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

ЗадачаРазработать  Single  Page  ApplicationПриложение  -­‐  библиотека  современного  искусства

Особенности  приложения:  1. Множество  связанных  объектов:  

художники,  картины,  история  продаж,  выставки,  галлереи,  каталоги  и  т.д.  

2. Нужна  мобильная  версия  приложения  (native  app)  3. Разные  уровни  доступа:         обычные  пользователи,  платные  пользователи,         художники,  редакторы,  работники  галлерей  и  т.д.

Page 6: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

1.  Фронтенд.  Современные  фреймворки:  • Backbone  • AngularJS  • KnockoutJS  • EmberJS  • ReactJS

Выбор  технологий

Page 7: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

1.  Фронтенд.  Современные  фреймворки:  • Backbone  • AngularJS  • KnockoutJS  • EmberJS  • ReactJS  

!!2.  Способ  передачи  данных  между  бэкендом  и  фронтендом  • REST  

Выбор  технологий

Page 8: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

1.  Фронтенд.  Современные  фреймворки:  • Backbone  • AngularJS  • KnockoutJS  • EmberJS  • ReactJS  

!!2.  Способ  передачи  данных  между  бэкендом  и  фронтендом  • REST  

!3.  Бэкенд    ✓ Язык  -­‐  Python  ✓ База  данных  -­‐  PostgreSQL

Выбор  технологий

Page 9: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Проблемы  

1. Дублирование  моделей  на  фронтенде  (уже  есть  на  бэкенде)  

2. REST  может  генерировать  слишком  много  запросов  к  серверу  

3. Нет  двунаправленных  связей  между  объектами  на  фронтенде  (а  на  бэкенде  есть)

Page 10: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Решения  1. Генерировать  фронтенд  

модели  автоматически  на  основе  моделей  на  бэкенде  

!

1. Дублирование  моделей  на  фронтенде  (уже  есть  на  бэкенде)  

2. REST  может  генерировать  слишком  много  запросов  к  серверу  

3. Нет  двунаправленных  связей  между  объектами  на  фронтенде  (а  на  бэкенде  есть)

Page 11: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Решения  1. Генерировать  фронтенд  

модели  автоматически  на  основе  моделей  на  бэкенде  

!2  и  3.  Передавать  связанные  объекты  в  одном  пакете

PonyJS

1. Дублирование  моделей  на  фронтенде  (уже  есть  на  бэкенде)  

2. REST  может  генерировать  слишком  много  запросов  к  серверу  

3. Нет  двунаправленных  связей  между  объектами  на  фронтенде  (а  на  бэкенде  есть)

Page 12: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

PonyJS  

PonyJS  автоматически  генерирует  фронтенд  модели    на  основе  моделей  PonyORM  на  бэкенде  !

Page 13: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Почему  PonyORM?

o1 = Order.objects.get(pk=1) print(o1.total, o1.customer.id) o2 = Order.objects.get(pk=2) print(o2.total, o2.customer.id)

Django  ORM:

1.  Iden~tyMap  Customer  1

Customer  1

Order  1

Order  2

Order  1

Order  2

Customer  1

o1 = Order[1] print(o1.total, o1.customer.id) o2 = Order[2] print(o2.total, o2.customer.id)

PonyORM:

Ac~ve  Record

Iden~ty  Map

Page 14: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

session.query(Product).filter(          (Product.name.startswith('A')  &  (Product.image  ==  None))          |  (extract('year',  Product.created_at)  <  2015))

Почему  PonyORM?

select(p  for  p  in  Product          if  p.name.startswith('A')  and  p.image  is  None          or  p.created_at.year  <  2015) Pony

Product.objects.filter(          Q(name__startswith='A',  image__isnull=True)          |  Q(created_at__year__lt=2015)) Django

SQLAlchemy

2.  Удобный  язык  запросов  

Page 15: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

База данных

Место  Pony  в  архитектуре  веб-­‐приложения

PonyJS

Data Access

Business Logic

Service API

Приложение на сервере - сервис

Data Structures (ViewModels)

Business Logic

Presentation Layer

Браузер клиента

PonyORM

JSON JSONСтатический HTML

Page 16: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Общая  схема  взаимодействия  Браузер Сервер

/artworks

HTML

/api/artworks

JSON

Первый  запрос

AJAX  запросы

Page 17: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

class  Author(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          name  =  Required(str)          artworks  =  Set("Artwork")  !

class  Artwork(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          ~tle  =  Required(str)          image  =  Required(str)          author  =  Required(Author)  

Описание  сущностей

Page 18: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

AJAX  запрос  с  фронтендаБраузер Сервер

AJAX  запрос  /api/artworks

JSONpony.load({         url:  '/api/artworks',     success:  funcDon(data)  {  !!   }  })

Page 19: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Обработчик  на  бэкендеБраузер Сервер

[email protected]('/api/artworks')  @db_session  def  get_artworks():     artworks  =  Artwork.select().order_by(Artwork.id)[:3]     print(artworks)    #  [Artwork[1],  Artwork[2],  Artwork[3]]     return  artworks.to_json()

AJAX  запрос  /api/artworks

Page 20: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Получаем  список  artworks  на  фронтенде

Браузер Сервер

AJAX  запрос  /api/artworks

JSON

pony.load({         url:  '/api/artworks',     success:  funcDon(artworks)  {       console.log(artworks);     }  })

Page 21: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Получили  на  фронтенде  тот  же  список

Page 22: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Список  полноценных  объектов  сущностей

Page 23: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Атрибуты  -­‐  это  getter&setters

Page 24: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Переход  по  связям  объекта

Page 25: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

class  Author(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          name  =  Required(str)          artworks  =  Set("Artwork")  !

class  Artwork(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          ~tle  =  Required(str)          image  =  Required(str)          author  =  Required(Author)  

У  объекта  Artwork  есть  связь  с  Author

Page 26: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Добавляем  объекты  Author  Браузер Сервер

[email protected]('/api/artworks')  @db_session  def  get_artworks():     artworks  =  Artwork.select().order_by(Artwork.id)[:3]     return  artworks.to_json(include=[Artwork.author])

AJAX  запрос  /api/artworks

Page 27: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Получаем  список  artworks  на  фронтенде

Браузер Сервер

AJAX  запрос  /api/artworks

JSON

pony.load({         url:  '/api/artworks',     success:  funcDon(artworks)  {       console.log(artworks);     }  })

Page 28: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Теперь  мы  видим  автора

Как  же  он  был  передан?

Page 29: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

user data

identity map

schema

Artwork[1] Artwork[2] Artwork[3]

Author[1] Author[2]

Формат  JSON  пакета

Page 30: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Оба  artwork’а  ссылаются    на  один  и  тот  же  объект  Author

Page 31: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

user data

identity map

schema

Artwork[1] Artwork[2] Artwork[3]

Author[1] Author[2]

Формат  JSON  пакета{            "data":  […],      !

   "objects":  {…},  !

!

   "schema":  [...]  !

}

Page 32: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Пользовательские  данные"data":  [            {                    "pk":  1,                    "class":  "Artwork"            },            {                    "pk":  2,                    "class":  "Artwork"            },            {                    "pk":  3,                    "class":  "Artwork"            }  ]

Page 33: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Iden~ty  Map  объектов"objects":  {          "Ar~st":  {                  "1":  {...},                  "2":  {...}          },          "Artwork":  {                  "1":  {...},                  "2":  {...},                  "3":  {...}          }  }

Page 34: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Iden~ty  Map  объектов!        "Author":  {                  "1":  {                          "id":  1,                          "name":  "Author1"                  },                  "2":  {                          "id":  2,                          "name":  "Author2"                  }          },        

!        "Artwork":  {                  "1":  {                          "id":  1,                          "~tle":  "Artwork1"                          "image":  "/images/1.jpg",                          "author":  1,                  },                  "2":  {                          "id":  2,                          ...                          "author":  1,                  },                  "3":  {                     "id":  3                     ...                          "author":  2,                  }

"objects":  {

}  

Page 35: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Схема  объектов"schema":  [     {                  "name":  "Author",                  "newA�rs":  [  …  ],                  "pkA�rs":  ["id"]     },     {                  "name":  "Artwork",                  "newA�rs":  [  …  ],                  "pkA�rs":  ["id"]     }  ]        

Page 36: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Схема  объектов!        {                  "name":  "Author",                  "newA�rs":  [                          {                                  "auto":  true,                                  "kind":  "PrimaryKey",                                  "name":  "id",                                  "type":  "int"                          },                          {                                  "kind":  "Required",                                  "name":  "name",                                  "type":  "unicode"                          },                          {                                  "kind":  "Set",                                  "name":  "artworks",                                  "reverse":  "author",                                  "type":  "Artwork"                          }                  ],                  "pkA�rs":  ["id"]          },        

{                  "name":  "Artwork",                  "newA�rs":  [                          {                                  "auto":  true,                                  "kind":  "PrimaryKey",                                  "name":  "id",                                  "type":  "int"                          },                          {                                  "kind":  "Required",                                  "name":  "Dtle",                                  "type":  "unicode"                          },                          {                                  "kind":  "Required",                                  "name":  "image",                                  "type":  "unicode"                          },                          {                                  "kind":  "Required",                                  "name":  "author",                                  "reverse":  "artworks",                                  "type":  "Author"                          }                  ],                  "pkA�rs":  ["id"]          }

"schema":  [

]

Page 37: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Подгрузка  дополнительных  объектовБраузер Сервер

JSON

AJAX  запрос  /api/artworks?page=2

полученные  объекты  будут  смержены      с  Identity  Map  на  фронтенде

Page 38: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Изменим  атрибут  title

Page 39: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Можно  сохранять  измененияБраузер Сервер

JSON

AJAX  запрос  /api/artworks

изменить  создать  удалить  объекты

JSON

AJAX  запрос  /api/update

Page 40: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Теперь  сохраним  измененияБраузер Сервер

JSON

AJAX  запрос  /api/artworks

изменить  создать  удалить  объекты

JSON

AJAX  запрос  /api/update

pony.save({         url:  '/api/update',     success:  funcDon(data)  {       …     }  })

Page 41: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Передаваемый  JSON{      "data":  null,        "objects":  [        {            "class":  "Artwork",            "_cid_":  3,            "_status_":  "u",            "_pk_":  "1",            "~tle":  {                    "old":  "Artwork1",                    "new":  "New  Dtle"            }        }  ]}

Page 42: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Обработчик  на  бэкендеБраузер Сервер

JSON

AJAX  запрос  /api/artworks

изменить  создать  удалить  объекты

JSON

AJAX  запрос  /api/update

@app.route('/update',  methods=['POST'])  @db_session  def  update():          diff  =  request.form['diff']          db.from_json(diff)          return  db.to_json({'status':  'ok'})

Page 43: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

А  как  же  права  доступа?

Page 44: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Права  доступа

• Проверяются  на  сервере  при  выполненииto_json()  и  from_json()  

• Позволяют  спрятать  из  метаданных  классы  и  атрибуты  моделей,  которыене  имеет  права  видеть  текущий  пользователь

Page 45: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Права  доступа

• Гарантируют,  что  на  фронтенд  передаютсятолько  объекты,  которые  можно  видеть(row-­‐level  permissions)  и  сериализуютсятолько  атрибуты,  которые  можно  видеть  

• При  создании  и  изменении  объектовпроверяется  что  пользователь  имеет  правана  создание/изменение  конкретногоэкземпляра  объекта  и  конкретных  атрибутов

Page 46: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Способы  задания  прав  доступа

• На  уровне  классов  

• На  уровне  объектов (row-­‐level  permissions)  

Pony  предлагает  очень  удобный  декларативный  способ  задания row-­‐level  permissions

Page 47: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

На  основе  чего  задаются  права?

• Группы  -­‐  характеризуют  текущего  пользователя.  Примеры:  admin,  editor,  visitor  

• Метки  -­‐  описывают  свойства  отдельных  объектов.  Примеры:  public,  deleted  

• Роли  -­‐  описывают  взаимоотношения  пользователя  и  конкретного  объекта.  Примеры:  owner,  creator,  moderator

Page 48: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Пример  задания  прав  доступа

with db.permissions_for(Artwork): allow('view’, group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')

Page 49: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Порядок  вычисления  прав  доступа• Пони  запрашивает  группы  текущего  пользователя  и  кеширует  их  

• Код  приложения  выбирает  объекты  запросом  и  вызывает  to_json()  

• Пони  запрашивает  роли  текущего  пользователя  относительно  сериализуемых  объектов  

• Код  приложения  вычисляет  роли  и  метки  и  сообщает  их  Пони  

• Пони  проверяет  роли  на  соответствие  декларативно  заданным  правам  доступа

Page 50: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Получение  групп  текущего  пользователя

@groups_getter(Author) def get_author_groups(author): return ['author'] !

@groups_getter(User) def get_user_groups(user): return [user.type]

Page 51: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Использование  групп  для  задания  прав

with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')

Page 52: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Получение  ролей  текущего  пользователя

@roles_getter(Author, Artwork) def get_author_roles(author, artwork): if author is artwork.author: return ['owner']

Page 53: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Использование  ролей  для  задания  прав

with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')

Page 54: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Проверка  роли  при  создании  объекта

with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group=‘admin') !

Юзер при создании объекта не сможет указать в поле artwork.author другого юзера!

Page 55: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Пример  использования  меток

Допустим,  что  некоторые  картины  должны  быть  скрыты  от  обычных  пользователей  при  показе  на  сайте

Page 56: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

class  Author(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          name  =  Required(str)          artworks  =  Set("Artwork")  !class  Artwork(db.En~ty):          id  =  PrimaryKey(int,  auto=True)          ~tle  =  Required(str)          image  =  Required(str)          author  =  Required(Author)          hidden  =  Required(bool,  default=False)  

 Пример  использования  меток

Page 57: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Получение  меток  объекта

@labels_getter(Artwork) def get_artwork_labels(artwork): if not artwork.hidden: return ['public']

Page 58: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Пример  использования  меток

with db.permissions_for(Artwork): allow('view', group='anybody', label='public') allow('create, edit', role='owner') allow('edit, delete', group='admin')

Page 59: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Автоматические  фильтры  запросов

Удобны,  если  мы  хотим  автоматически  добавлять  условие  ко  всем  запросам  для  

определенного  класса

Page 60: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Автоматические  фильтры  запросов

@default_filter(Artwork) def public(artwork): return not artwork.hidden

Page 61: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Автоматические  фильтры  запросов

artworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3]

Page 62: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

 Автоматические  фильтры  запросовartworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3] !SELECT "a"."id", "a"."title", "a"."image", "a"."hidden", "a"."author" FROM "Artwork" "a" INNER JOIN "Author" "author-1" ON "a"."author" = "author-1"."id" WHERE "author-1"."name" = 'Gerhard Richter' AND "a"."hidden" = 0 ORDER BY "a"."title" LIMIT 3

Page 63: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

   Отключение  автоматических  фильтров

with default_filters_disabled: artworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3]

Page 64: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

   Отключение  автоматических  фильтровwith default_filters_disabled: artworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3] !SELECT "a"."id", "a"."title", "a"."image", "a"."hidden", "a"."author" FROM "Artwork" "a" INNER JOIN "Author" "author-1" ON "a"."author" = "author-1"."id" WHERE "author-1"."name" = 'Gerhard Richter' ORDER BY "a"."title" LIMIT 3

Page 65: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Разные  права  для  разных  сайтов

with db.permissions_for(Artwork): allow('view', group='anybody', label='public', site='public') allow('create, edit', role='owner', site='admin') allow('edit, delete', group='admin', site='admin')

Page 66: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

ReactJS• ReactJS  позволяет  строить  страницу  из  компонентов  

• Компоненты  представляют  собой  слой  View  

• Получают  свойства  (props) и  используют  их  при  рендеринге  

• Объекты  PonyJS  можно  использоватьв  качестве  значений  свойств  компонентов

Page 67: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Пример  React-­‐компонентаvar ArtworkDescription = React.createClass({ render: function () { var artwork = this.props.artwork; return <div> <h2>{ artwork.title() }</h2> <img src={ artwork.image() } /> <p>{ artwork.author().name() }</p> </div> } });

Page 68: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Пример  React-­‐компонентаvar ArtworksPage = React.createClass({ render: function () { var artworkList = … return <div> <h1>Artwork list</h1> <ul>{ artworkList }</ul> </div> } });

Page 69: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Фрагменты  кода  с  ReactJS

var artworkList = _.map( this.props.artworks, function (item) { return <li key={ item.id() }> <ArtworkDescription artwork={ item } /> </li> });

Page 70: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Фрагменты  кода  с  ReactJSvar ArtworksPage = React.createClass({ render: function () { var artworkList = _.map( this.props.artworks, function (item) { return <li key={ item.id() }> <ArtworkDescription artwork={ item } /> </li> }); return <div> <h1>Artwork list</h1> <ul>{ artworkList }</ul> </div> } });

Page 71: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Фрагменты  кода  с  ReactJSvar ArtworksPage = React.createClass({ render: function () { return <div> <h1>Artwork list</h1> <ul>{ _.map(this.props.artworks, function (item) { return <li key={ item.id() }> <ArtworkDescription artwork={ item } /> </li> }); }</ul> </div> } });

Page 72: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Структура  SPA  на  React

Page 73: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

• Автогенерация  моделей  на  фронтенде  • Сериализация  произвольных  данных,  включая  двусторонние  связи  many-­‐to-­‐many  и  инстансы  сущностей  

• Объекты  с  двунаправленными  связями  на  клиенте  

• Двунаправленный  биндинг  • Передача  изменений  обратно  на  сервер  • Права  доступа

Заключение.  Что  предлагает  PonyJS?

Page 74: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Спасибо!Q&A

Site:  ponyorm.com  Twi�er:  @ponyorm  Github:  github.com/ponyorm/pony

Александр  Козловский,  Алексей  МалашкевичPyCon  Russia  2015

Page 75: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Чем  PonyORM  отличается  от  Django  ORM    

• Pony  использует  паттерн  Iden~ty  Map,  Django  -­‐  Ac~veRecord  • Позволяет  избегать  lost  updates  • Не  растрачивает  память  • В  связанных  объектах  на  других  концах  -­‐  не  копии,  а  единственный  экземляр  объекта  

• Оптимизация  запросов  (подзапрос  в  LEFT  JOIN)  • Можно  работать  с  Пони  в  интерактивном  режиме  • Решение  проблемы  “N+1  запроса”  • Оптимистические  проверки  • Умный  реконнект  • Информативные  сообщения  об  ошибках

Page 76: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Чем  PonyORM  отличается  от  Django  ORM    

• Идентация  SQL  запросов  в  логе  • Атрибут  лифтинг  (multisets)  • Лаконичный  декларативный  язык  запросов  

• Генераторы  и  лямбды  • Агрегирующие  функции  самого  языка  (sum,  min,  max)  • in  используется  для  подзапросов  и  LIKE,  транслируется  в  разный  SQL  в  зависимости  от  аргументов    

• Raw  SQL  • Подстановка  параметров  в  запрос  • Множественное  наследование  сущностей  • Отсутствие  проблемы  “срез  базового  класса”

Page 77: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Чем  PonyORM  отличается  от  Django  ORM    

• Поддержка  пула  соединений    • Кеширование  результата  транслирования  запроса,  результата  запроса  (список  объектов),  и  сами  объекты.  Второй  селект  с  такими  же  параметрами  в  базу  не  пойдет  (если  мы  не  посылали  в  базу  апдейты,  если  посылали  -­‐  этот  кеш  сбросится)  

• PonyJS

Page 78: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

<body>          <table>                  <caption>Products</caption>                  <tbody  data-­‐bind="foreach:  products">                          <tr>                                  <td><input  data-­‐bind="value:  name"></td>                                                                        <ul  data-­‐bind="foreach:  categories">                                                  <li  data-­‐bind="text:  name"></li>                                          </ul>                                  </td>                          </tr>                  </tbody>          </table>  </body>

Двунаправленный биндинг

Page 79: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Двунаправленный биндинг

Page 80: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Other Pony ORM features

• Identity Map • Automatic query optimization • N+1 Query Problem solution • Optimistic transactions • ER Diagram Editor

Page 81: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Django ORM

s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id !

• How many SQL queries will be executed? • How many objects will be created?

Page 82: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Django ORM

s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id

Student 123

Page 83: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Django ORM

s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id

Student 123 Group 1

Page 84: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Django ORM

s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id

Student 123

Student 456

Group 1

Page 85: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Django ORM

s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id

Student 123

Student 456

Group 1

Group 1

Page 86: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Page 87: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM – seeds, IdentityMap

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Student 123

Group 1

Page 88: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM – seeds, IdentityMap

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Student 123

Group 1

seed

Page 89: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM – seeds, IdentityMap

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Student 123

Group 1

seed

Page 90: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM – seeds, IdentityMap

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Student 123

Student 456

Group 1

seed

Page 91: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Pony ORM – seeds, IdentityMap

s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id

Student 123

Student 456

Group 1

seed

Page 92: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Solution for the N+1 Query Problem

orders  =  select(o  for  o  in  Order  if  o.total_price  >  1000)\            .order_by(Order.id)[0:5]  

for  o  in  orders:              print  o.total_price,  o.customer.name

SELECT o.id, o.total_price, o.customer_id,... FROM "Order" o WHERE o.total_price > 1000 ORDER BY o.id LIMIT 5

Page 93: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Order 1

Order 3

Order 4

Order 7

Order 9

Customer 1

Customer 4

Customer 7

Solution for the N+1 Query Problem

Page 94: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

orders = select(o for o in Order if o.price > 1000) for o in orders: print o.total_price, o.customer.name !

SELECT c.id, c.name, … FROM “Customer” c WHERE c.id IN (?, ?, ?)

Solution for the N+1 Query Problem

Page 95: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Order 1

Order 3

Order 4

Order 7

Order 9

Customer 1

Customer 4

Customer 7

Solution for the N+1 Query Problem

Page 96: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Transactions

!

def  transfer_money(id1,  id2,  amount):          account1  =  Account.objects.get(pk=id1)          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account2  =  Account.object.get(pk=id2)          account1.amount  -­‐=  amount          account1.save()          account2.amount  +=  amount          account2.save()

Django ORM

Page 97: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

@transaction.atomic  def  transfer_money(id1,  id2,  amount):          account1  =  Account.objects.get(pk=id1)          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account2  =  Account.object.get(pk=id2)          account1.amount  -­‐=  amount          account1.save()          account2.amount  +=  amount          account2.save()

TransactionsDjango ORM

Page 98: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

@transaction.atomic  def  transfer_money(id1,  id2,  amount):          account1  =  Account.objects.  \       select_for_update.get(pk=id1)          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account2  =  Account.objects.  \       select_for_update.get(pk=id2)          account1.amount  -­‐=  amount          account1.save()          account2.amount  +=  amount          account2.save()

TransactionsDjango ORM

Page 99: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

@db_session  def  transfer_money(id1,  id2,  amount):          account1  =  Account.get_for_update(id=id1)          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account1.amount  -­‐=  amount          account2  =  Account.get_for_update(id=id2)          account2.amount  +=  amount

TransactionsPony ORM

Page 100: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

@db_session  def  transfer_money(id1,  id2,  amount):          account1  =  Account[id1]          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account1.amount  -­‐=  amount          Account[id2].amount  +=  amount

TransactionsPony ORM

Page 101: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

db_session

• Pony tracks which objects where changed • No need to call save() • Pony saves all updated objects in a single

transaction automatically on leaving the db_session scope

Transactions

Page 102: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

@db_session  def  transfer_money(id1,  id2,  amount):          account1  =  Account[id1]          if  account1.amount  <  amount:                  raise  ValueError('Not  enough  funds!')          account1.amount  -­‐=  amount          Account[id2].amount  +=  amount

TransactionsPony ORM

Page 103: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

UPDATE Account SET amount = :new_value WHERE id = :id AND amount = :old_value

Optimistic Locking

Page 104: Разработка одностраничных веб-приложений с использованием PonyORM и ReactJS - Алексей Малашкевич, Александр

Optimistic Locking

• Pony tracks which attributes were read and updated

• If object wasn’t locked using the for_update method, Pony uses the optimistic locking automatically