SlideShare a Scribd company logo
1 of 74
Download to read offline
Разработка	
  	
  
одностраничных	
  веб-­‐приложений	
  	
  
с	
  использованием	
  	
  
PonyORM	
  и	
  ReactJS
Александр	
  Козловский,	
  Алексей	
  Малашкевич
PyCon	
  Russia	
  2015
Задача
Разработать	
  Single	
  Page	
  Application
Приложение	
  -­‐	
  библиотека	
  современного	
  искусства
Задача
Разработать	
  Single	
  Page	
  Application
Приложение	
  -­‐	
  библиотека	
  современного	
  искусства
Особенности	
  приложения:	
  
1. Множество	
  связанных	
  объектов:	
  
художники,	
  картины,	
  история	
  продаж,	
  выставки,	
  
галлереи,	
  каталоги	
  и	
  т.д.
Задача
Разработать	
  Single	
  Page	
  Application
Приложение	
  -­‐	
  библиотека	
  современного	
  искусства
Особенности	
  приложения:	
  
1. Множество	
  связанных	
  объектов:	
  
художники,	
  картины,	
  история	
  продаж,	
  выставки,	
  
галлереи,	
  каталоги	
  и	
  т.д.	
  
2. Нужна	
  мобильная	
  версия	
  приложения	
  (native	
  app)
Задача
Разработать	
  Single	
  Page	
  Application
Приложение	
  -­‐	
  библиотека	
  современного	
  искусства
Особенности	
  приложения:	
  
1. Множество	
  связанных	
  объектов:	
  
художники,	
  картины,	
  история	
  продаж,	
  выставки,	
  
галлереи,	
  каталоги	
  и	
  т.д.	
  
2. Нужна	
  мобильная	
  версия	
  приложения	
  (native	
  app)	
  
3. Разные	
  уровни	
  доступа:	
  	
  
	
   	
   обычные	
  пользователи,	
  платные	
  пользователи,	
  	
  
	
   	
   художники,	
  редакторы,	
  работники	
  галлерей	
  и	
  т.д.
1.	
  Фронтенд.	
  Современные	
  фреймворки:	
  
• Backbone	
  
• AngularJS	
  
• KnockoutJS	
  
• EmberJS	
  
• ReactJS
Выбор	
  технологий
1.	
  Фронтенд.	
  Современные	
  фреймворки:	
  
• Backbone	
  
• AngularJS	
  
• KnockoutJS	
  
• EmberJS	
  
• ReactJS	
  
!
!
2.	
  Способ	
  передачи	
  данных	
  между	
  бэкендом	
  и	
  
фронтендом	
  
• REST	
  
Выбор	
  технологий
1.	
  Фронтенд.	
  Современные	
  фреймворки:	
  
• Backbone	
  
• AngularJS	
  
• KnockoutJS	
  
• EmberJS	
  
• ReactJS	
  
!
!
2.	
  Способ	
  передачи	
  данных	
  между	
  бэкендом	
  и	
  
фронтендом	
  
• REST	
  
!
3.	
  Бэкенд	
  	
  
✓ Язык	
  -­‐	
  Python	
  
✓ База	
  данных	
  -­‐	
  PostgreSQL
Выбор	
  технологий
Проблемы	
  
1. Дублирование	
  моделей	
  на	
  фронтенде	
  (уже	
  есть	
  на	
  
бэкенде)	
  
2. REST	
  может	
  генерировать	
  слишком	
  много	
  запросов	
  
к	
  серверу	
  
3. Нет	
  двунаправленных	
  связей	
  между	
  объектами	
  на	
  
фронтенде	
  (а	
  на	
  бэкенде	
  есть)
Решения	
  
1. Генерировать	
  фронтенд	
  
модели	
  автоматически	
  на	
  
основе	
  моделей	
  на	
  
бэкенде	
  
!
1. Дублирование	
  моделей	
  
на	
  фронтенде	
  (уже	
  есть	
  
на	
  бэкенде)	
  
2. REST	
  может	
  
генерировать	
  слишком	
  
много	
  запросов	
  к	
  
серверу	
  
3. Нет	
  двунаправленных	
  
связей	
  между	
  
объектами	
  на	
  
фронтенде	
  (а	
  на	
  
бэкенде	
  есть)
Решения	
  
1. Генерировать	
  фронтенд	
  
модели	
  автоматически	
  на	
  
основе	
  моделей	
  на	
  
бэкенде	
  
!
2	
  и	
  3.	
  Передавать	
  связанные	
  
объекты	
  в	
  одном	
  пакете
PonyJS
1. Дублирование	
  моделей	
  
на	
  фронтенде	
  (уже	
  есть	
  
на	
  бэкенде)	
  
2. REST	
  может	
  
генерировать	
  слишком	
  
много	
  запросов	
  к	
  
серверу	
  
3. Нет	
  двунаправленных	
  
связей	
  между	
  
объектами	
  на	
  
фронтенде	
  (а	
  на	
  
бэкенде	
  есть)
PonyJS	
  
PonyJS	
  автоматически	
  генерирует	
  фронтенд	
  модели	
  	
  
на	
  основе	
  моделей	
  PonyORM	
  на	
  бэкенде	
  
!
Почему	
  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
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.	
  Удобный	
  язык	
  запросов	
  
База данных
Место	
  Pony	
  в	
  архитектуре	
  веб-­‐приложения
PonyJS
Data Access
Business Logic
Service API
Приложение на сервере - сервис
Data Structures (ViewModels)
Business Logic
Presentation Layer
Браузер клиента
PonyORM
JSON JSON
Статический
HTML
Общая	
  схема	
  взаимодействия	
  
Браузер Сервер
/artworks
HTML
/api/artworks
JSON
Первый	
  
запрос
AJAX	
  запросы
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)	
  
Описание	
  сущностей
AJAX	
  запрос	
  с	
  фронтенда
Браузер Сервер
AJAX	
  запрос	
  /api/artworks
JSON
pony.load({	
  
	
  	
  	
   url:	
  '/api/artworks',	
  
	
   success:	
  funcDon(data)	
  {	
  
!
!
	
   }	
  
})
Обработчик	
  на	
  бэкенде
Браузер Сервер
JSON
@app.route('/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
Получаем	
  список	
  artworks	
  на	
  фронтенде
Браузер Сервер
AJAX	
  запрос	
  /api/artworks
JSON
pony.load({	
  
	
  	
  	
   url:	
  '/api/artworks',	
  
	
   success:	
  funcDon(artworks)	
  {	
  
	
   	
   console.log(artworks);	
  
	
   }	
  
})
Получили	
  на	
  фронтенде	
  тот	
  же	
  список
Список	
  полноценных	
  объектов	
  сущностей
Атрибуты	
  -­‐	
  это	
  getter&setters
Переход	
  по	
  связям	
  объекта
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
Добавляем	
  объекты	
  Author	
  
Браузер Сервер
JSON
@app.route('/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
Получаем	
  список	
  artworks	
  на	
  фронтенде
Браузер Сервер
AJAX	
  запрос	
  /api/artworks
JSON
pony.load({	
  
	
  	
  	
   url:	
  '/api/artworks',	
  
	
   success:	
  funcDon(artworks)	
  {	
  
	
   	
   console.log(artworks);	
  
	
   }	
  
})
Теперь	
  мы	
  видим	
  автора
Как	
  же	
  он	
  был	
  передан?
user data
identity map
schema
Artwork[1] Artwork[2] Artwork[3]
Author[1] Author[2]
Формат	
  JSON	
  пакета
Оба	
  artwork’а	
  ссылаются	
  	
  
на	
  один	
  и	
  тот	
  же	
  объект	
  Author
user data
identity map
schema
Artwork[1] Artwork[2] Artwork[3]
Author[1] Author[2]
Формат	
  JSON	
  пакета
{	
  
	
  	
  	
  
	
  	
  "data":	
  […],	
  
	
  	
  
!
	
  	
  "objects":	
  {…},	
  
!
!
	
  	
  "schema":	
  [...]	
  
!
}
Пользовательские	
  данные
"data":	
  [	
  
	
   	
  	
  	
  	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "pk":	
  1,	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "class":	
  "Artwork"	
  
	
   	
  	
  	
  	
  },	
  
	
   	
  	
  	
  	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "pk":	
  2,	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "class":	
  "Artwork"	
  
	
   	
  	
  	
  	
  },	
  
	
   	
  	
  	
  	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "pk":	
  3,	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "class":	
  "Artwork"	
  
	
   	
  	
  	
  	
  }	
  
]
Iden~ty	
  Map	
  объектов
"objects":	
  {	
  
	
  	
  	
  	
  "Ar~st":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "1":	
  {...},	
  
	
  	
  	
  	
  	
  	
  	
  	
  "2":	
  {...}	
  
	
  	
  	
  	
  },	
  
	
  	
  	
  	
  "Artwork":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "1":	
  {...},	
  
	
  	
  	
  	
  	
  	
  	
  	
  "2":	
  {...},	
  
	
  	
  	
  	
  	
  	
  	
  	
  "3":	
  {...}	
  
	
  	
  	
  	
  }	
  
}
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":	
  {
}	
  
Схема	
  объектов
"schema":	
  [	
  
	
   {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "name":	
  "Author",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "newA•rs":	
  [	
  …	
  ],	
  
	
  	
  	
  	
  	
  	
  	
  	
  "pkA•rs":	
  ["id"]	
  
	
   },	
  
	
   {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "name":	
  "Artwork",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "newA•rs":	
  [	
  …	
  ],	
  
	
  	
  	
  	
  	
  	
  	
  	
  "pkA•rs":	
  ["id"]	
  
	
   }	
  
]	
  
	
  	
  	
  
Схема	
  объектов
!
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "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":	
  [
]
Подгрузка	
  дополнительных	
  объектов
Браузер Сервер
JSON
AJAX	
  запрос	
  /api/artworks?page=2
полученные	
  объекты	
  будут	
  смержены	
  	
  	
  
с	
  Identity	
  Map	
  на	
  фронтенде
Изменим	
  атрибут	
  title
Можно	
  сохранять	
  изменения
Браузер Сервер
JSON
AJAX	
  запрос	
  /api/artworks
изменить	
  
создать	
  
удалить	
  
объекты
JSON
AJAX	
  запрос	
  /api/update
Теперь	
  сохраним	
  изменения
Браузер Сервер
JSON
AJAX	
  запрос	
  /api/artworks
изменить	
  
создать	
  
удалить	
  
объекты
JSON
AJAX	
  запрос	
  /api/update
pony.save({	
  
	
  	
  	
   url:	
  '/api/update',	
  
	
   success:	
  funcDon(data)	
  {	
  
	
   	
   …	
  
	
   }	
  
})
Передаваемый	
  JSON
{	
  
	
  	
  "data":	
  null,	
  	
  
	
  	
  "objects":	
  [	
  
	
   	
  	
  {	
  
	
   	
  	
  	
  	
  "class":	
  "Artwork",	
  
	
   	
  	
  	
  	
  "_cid_":	
  3,	
  
	
   	
  	
  	
  	
  "_status_":	
  "u",	
  
	
   	
  	
  	
  	
  "_pk_":	
  "1",	
  
	
   	
  	
  	
  	
  "~tle":	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "old":	
  "Artwork1",	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  "new":	
  "New	
  Dtle"	
  
	
   	
  	
  	
  	
  }	
  
	
   	
  	
  }	
  
]}
Обработчик	
  на	
  бэкенде
Браузер Сервер
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'})
А	
  как	
  же	
  права	
  доступа?
Права	
  доступа
• Проверяются	
  на	
  сервере	
  при	
  выполнении

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

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

только	
  объекты,	
  которые	
  можно	
  видеть

(row-­‐level	
  permissions)	
  и	
  сериализуются

только	
  атрибуты,	
  которые	
  можно	
  видеть	
  
• При	
  создании	
  и	
  изменении	
  объектов

проверяется	
  что	
  пользователь	
  имеет	
  права

на	
  создание/изменение	
  конкретного

экземпляра	
  объекта	
  и	
  конкретных	
  атрибутов
Способы	
  задания	
  прав	
  доступа
• На	
  уровне	
  классов	
  
• На	
  уровне	
  объектов

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

row-­‐level	
  permissions
На	
  основе	
  чего	
  задаются	
  права?
• Группы	
  -­‐	
  характеризуют	
  текущего	
  пользователя.	
  
Примеры:	
  admin,	
  editor,	
  visitor	
  
• Метки	
  -­‐	
  описывают	
  свойства	
  отдельных	
  
объектов.	
  Примеры:	
  public,	
  deleted	
  
• Роли	
  -­‐	
  описывают	
  взаимоотношения	
  
пользователя	
  и	
  конкретного	
  объекта.	
  Примеры:	
  
owner,	
  creator,	
  moderator
Пример	
  задания	
  прав	
  доступа
with db.permissions_for(Artwork):
allow('view’, group='anybody')
allow('create, edit', role='owner')
allow('edit, delete', group='admin')
 Порядок	
  вычисления	
  прав	
  доступа
• Пони	
  запрашивает	
  группы	
  текущего	
  пользователя	
  и	
  
кеширует	
  их	
  
• Код	
  приложения	
  выбирает	
  объекты	
  запросом	
  и	
  
вызывает	
  to_json()	
  
• Пони	
  запрашивает	
  роли	
  текущего	
  пользователя	
  
относительно	
  сериализуемых	
  объектов	
  
• Код	
  приложения	
  вычисляет	
  роли	
  и	
  метки	
  и	
  сообщает	
  
их	
  Пони	
  
• Пони	
  проверяет	
  роли	
  на	
  соответствие	
  декларативно	
  
заданным	
  правам	
  доступа
Использование	
  групп	
  для	
  задания	
  прав
with db.permissions_for(Artwork):
allow('view', group='anybody')
allow('create, edit', role='owner')
allow('edit, delete', group='admin')
 Получение	
  групп	
  текущего	
  пользователя
@groups_getter(Author)
def get_author_groups(author):
return ['author']
!
@groups_getter(User)
def get_user_groups(user):
return [user.type]
Использование	
  ролей	
  для	
  задания	
  прав
with db.permissions_for(Artwork):
allow('view', group='anybody')
allow('create, edit', role='owner')
allow('edit, delete', group='admin')
 Получение	
  ролей	
  текущего	
  пользователя
@roles_getter(Author, Artwork)
def get_author_roles(author, artwork):
if author is artwork.author:
return ['owner']
Проверка	
  роли	
  при	
  создании	
  объекта
with db.permissions_for(Artwork):
allow('view', group='anybody')
allow('create, edit', role='owner')
allow('edit, delete', group=‘admin')
!
Юзер при создании объекта не
сможет указать в поле
artwork.author другого юзера!
 Пример	
  использования	
  меток
Допустим,	
  что	
  некоторые	
  картины	
  
должны	
  быть	
  скрыты	
  от	
  обычных	
  
пользователей	
  при	
  показе	
  на	
  сайте
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)	
  
	
  Пример	
  использования	
  меток
Пример	
  использования	
  меток
with db.permissions_for(Artwork):
allow('view', group='anybody',
label='public')
allow('create, edit', role='owner')
allow('edit, delete', group='admin')
 Получение	
  меток	
  объекта
@labels_getter(Artwork)
def get_artwork_labels(artwork):
if not artwork.hidden:
return ['public']
 Автоматические	
  фильтры	
  запросов
Удобны,	
  если	
  мы	
  хотим	
  автоматически	
  
добавлять	
  условие	
  ко	
  всем	
  запросам	
  для	
  
определенного	
  класса
 Автоматические	
  фильтры	
  запросов
@default_filter(Artwork)
def public(artwork):
return not artwork.hidden
 Автоматические	
  фильтры	
  запросов
artworks = Artwork.select(
lambda a: a.author.name == 'Gerhard Richter'
).order_by(lambda a: a.title)[:3]
 Автоматические	
  фильтры	
  запросов
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
 	
  Отключение	
  автоматических	
  фильтров
with default_filters_disabled:
artworks = Artwork.select(
lambda a: a.author.name == 'Gerhard Richter'
).order_by(lambda a: a.title)[:3]
 	
  Отключение	
  автоматических	
  фильтров
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
Разные	
  права	
  для	
  разных	
  сайтов
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')
ReactJS
• ReactJS	
  позволяет	
  строить	
  страницу	
  из	
  
компонентов	
  
• Компоненты	
  представляют	
  собой	
  слой	
  
View	
  
• Получают	
  свойства	
  (props)

и	
  используют	
  их	
  при	
  рендеринге	
  
• Объекты	
  PonyJS	
  можно	
  использовать

в	
  качестве	
  значений	
  свойств	
  компонентов
Пример	
  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>
}
});
Пример	
  React-­‐компонента
var ArtworksPage = React.createClass({
render: function () {
var artworkList = …
return <div>
<h1>Artwork list</h1>
<ul>{ artworkList }</ul>
</div>
}
});
Фрагменты	
  кода	
  с	
  ReactJS
var artworkList = _.map(
this.props.artworks, function (item) {
return <li key={ item.id() }>
<ArtworkDescription artwork={ item } />
</li>
});
Фрагменты	
  кода	
  с	
  ReactJS
var 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>
}
});
Фрагменты	
  кода	
  с	
  ReactJS
var 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>
}
});
Структура	
  SPA	
  на	
  React
• Автогенерация	
  моделей	
  на	
  фронтенде	
  
• Сериализация	
  произвольных	
  данных,	
  включая	
  
двусторонние	
  связи	
  many-­‐to-­‐many	
  
• Объекты	
  с	
  двунаправленными	
  связями	
  на	
  клиенте	
  
• Может	
  использоваться	
  с	
  любым	
  фронтенд-­‐
фреймворком	
  (React,	
  Angular,	
  Knockout	
  и	
  др.)	
  
• Формат	
  передаваемых	
  данных	
  универсален.	
  

Может	
  использоваться	
  с	
  другими	
  бэкендами	
  
• Двунаправленный	
  биндинг	
  
• Передача	
  изменений	
  обратно	
  на	
  сервер	
  
• Декларативные	
  права	
  доступа	
  на	
  уровне	
  объектов
Заключение.	
  Что	
  предлагает	
  PonyJS?
Спасибо!
Q&A
Site:	
  ponyorm.com	
  
Twi•er:	
  @ponyorm	
  
Github:	
  github.com/ponyorm/pony
Александр	
  Козловский,	
  Алексей	
  Малашкевич
PyCon	
  Russia	
  2015

More Related Content

Featured

Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

Featured (20)

Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 

Разработка Single Page Applications с использованием Pony ORM и React JS

  • 1. Разработка     одностраничных  веб-­‐приложений     с  использованием     PonyORM  и  ReactJS Александр  Козловский,  Алексей  Малашкевич PyCon  Russia  2015
  • 2. Задача Разработать  Single  Page  Application Приложение  -­‐  библиотека  современного  искусства
  • 3. Задача Разработать  Single  Page  Application Приложение  -­‐  библиотека  современного  искусства Особенности  приложения:   1. Множество  связанных  объектов:   художники,  картины,  история  продаж,  выставки,   галлереи,  каталоги  и  т.д.
  • 4. Задача Разработать  Single  Page  Application Приложение  -­‐  библиотека  современного  искусства Особенности  приложения:   1. Множество  связанных  объектов:   художники,  картины,  история  продаж,  выставки,   галлереи,  каталоги  и  т.д.   2. Нужна  мобильная  версия  приложения  (native  app)
  • 5. Задача Разработать  Single  Page  Application Приложение  -­‐  библиотека  современного  искусства Особенности  приложения:   1. Множество  связанных  объектов:   художники,  картины,  история  продаж,  выставки,   галлереи,  каталоги  и  т.д.   2. Нужна  мобильная  версия  приложения  (native  app)   3. Разные  уровни  доступа:         обычные  пользователи,  платные  пользователи,         художники,  редакторы,  работники  галлерей  и  т.д.
  • 6. 1.  Фронтенд.  Современные  фреймворки:   • Backbone   • AngularJS   • KnockoutJS   • EmberJS   • ReactJS Выбор  технологий
  • 7. 1.  Фронтенд.  Современные  фреймворки:   • Backbone   • AngularJS   • KnockoutJS   • EmberJS   • ReactJS   ! ! 2.  Способ  передачи  данных  между  бэкендом  и   фронтендом   • REST   Выбор  технологий
  • 8. 1.  Фронтенд.  Современные  фреймворки:   • Backbone   • AngularJS   • KnockoutJS   • EmberJS   • ReactJS   ! ! 2.  Способ  передачи  данных  между  бэкендом  и   фронтендом   • REST   ! 3.  Бэкенд     ✓ Язык  -­‐  Python   ✓ База  данных  -­‐  PostgreSQL Выбор  технологий
  • 9. Проблемы   1. Дублирование  моделей  на  фронтенде  (уже  есть  на   бэкенде)   2. REST  может  генерировать  слишком  много  запросов   к  серверу   3. Нет  двунаправленных  связей  между  объектами  на   фронтенде  (а  на  бэкенде  есть)
  • 10. Решения   1. Генерировать  фронтенд   модели  автоматически  на   основе  моделей  на   бэкенде   ! 1. Дублирование  моделей   на  фронтенде  (уже  есть   на  бэкенде)   2. REST  может   генерировать  слишком   много  запросов  к   серверу   3. Нет  двунаправленных   связей  между   объектами  на   фронтенде  (а  на   бэкенде  есть)
  • 11. Решения   1. Генерировать  фронтенд   модели  автоматически  на   основе  моделей  на   бэкенде   ! 2  и  3.  Передавать  связанные   объекты  в  одном  пакете PonyJS 1. Дублирование  моделей   на  фронтенде  (уже  есть   на  бэкенде)   2. REST  может   генерировать  слишком   много  запросов  к   серверу   3. Нет  двунаправленных   связей  между   объектами  на   фронтенде  (а  на   бэкенде  есть)
  • 12. PonyJS   PonyJS  автоматически  генерирует  фронтенд  модели     на  основе  моделей  PonyORM  на  бэкенде   !
  • 13. Почему  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
  • 14. 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.  Удобный  язык  запросов  
  • 15. База данных Место  Pony  в  архитектуре  веб-­‐приложения PonyJS Data Access Business Logic Service API Приложение на сервере - сервис Data Structures (ViewModels) Business Logic Presentation Layer Браузер клиента PonyORM JSON JSON Статический HTML
  • 16. Общая  схема  взаимодействия   Браузер Сервер /artworks HTML /api/artworks JSON Первый   запрос AJAX  запросы
  • 17. 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)   Описание  сущностей
  • 18. AJAX  запрос  с  фронтенда Браузер Сервер AJAX  запрос  /api/artworks JSON pony.load({         url:  '/api/artworks',     success:  funcDon(data)  {   ! !   }   })
  • 19. Обработчик  на  бэкенде Браузер Сервер JSON @app.route('/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
  • 20. Получаем  список  artworks  на  фронтенде Браузер Сервер AJAX  запрос  /api/artworks JSON pony.load({         url:  '/api/artworks',     success:  funcDon(artworks)  {       console.log(artworks);     }   })
  • 21. Получили  на  фронтенде  тот  же  список
  • 25. 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
  • 26. Добавляем  объекты  Author   Браузер Сервер JSON @app.route('/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
  • 27. Получаем  список  artworks  на  фронтенде Браузер Сервер AJAX  запрос  /api/artworks JSON pony.load({         url:  '/api/artworks',     success:  funcDon(artworks)  {       console.log(artworks);     }   })
  • 28. Теперь  мы  видим  автора Как  же  он  был  передан?
  • 29. user data identity map schema Artwork[1] Artwork[2] Artwork[3] Author[1] Author[2] Формат  JSON  пакета
  • 30. Оба  artwork’а  ссылаются     на  один  и  тот  же  объект  Author
  • 31. user data identity map schema Artwork[1] Artwork[2] Artwork[3] Author[1] Author[2] Формат  JSON  пакета {            "data":  […],       !    "objects":  {…},   ! !    "schema":  [...]   ! }
  • 32. Пользовательские  данные "data":  [            {                    "pk":  1,                    "class":  "Artwork"            },            {                    "pk":  2,                    "class":  "Artwork"            },            {                    "pk":  3,                    "class":  "Artwork"            }   ]
  • 33. Iden~ty  Map  объектов "objects":  {          "Ar~st":  {                  "1":  {...},                  "2":  {...}          },          "Artwork":  {                  "1":  {...},                  "2":  {...},                  "3":  {...}          }   }
  • 34. 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":  { }  
  • 35. Схема  объектов "schema":  [     {                  "name":  "Author",                  "newA•rs":  [  …  ],                  "pkA•rs":  ["id"]     },     {                  "name":  "Artwork",                  "newA•rs":  [  …  ],                  "pkA•rs":  ["id"]     }   ]        
  • 36. Схема  объектов !        {                  "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":  [ ]
  • 37. Подгрузка  дополнительных  объектов Браузер Сервер JSON AJAX  запрос  /api/artworks?page=2 полученные  объекты  будут  смержены       с  Identity  Map  на  фронтенде
  • 39. Можно  сохранять  изменения Браузер Сервер JSON AJAX  запрос  /api/artworks изменить   создать   удалить   объекты JSON AJAX  запрос  /api/update
  • 40. Теперь  сохраним  изменения Браузер Сервер JSON AJAX  запрос  /api/artworks изменить   создать   удалить   объекты JSON AJAX  запрос  /api/update pony.save({         url:  '/api/update',     success:  funcDon(data)  {       …     }   })
  • 41. Передаваемый  JSON {      "data":  null,        "objects":  [        {            "class":  "Artwork",            "_cid_":  3,            "_status_":  "u",            "_pk_":  "1",            "~tle":  {                    "old":  "Artwork1",                    "new":  "New  Dtle"            }        }   ]}
  • 42. Обработчик  на  бэкенде Браузер Сервер 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'})
  • 43. А  как  же  права  доступа?
  • 44. Права  доступа • Проверяются  на  сервере  при  выполнении
 to_json()  и  from_json()   • Позволяют  спрятать  из  метаданных  классы  и   атрибуты  моделей,  которые
 не  имеет  права  видеть  текущий  пользователь
  • 45. Права  доступа • Гарантируют,  что  на  фронтенд  передаются
 только  объекты,  которые  можно  видеть
 (row-­‐level  permissions)  и  сериализуются
 только  атрибуты,  которые  можно  видеть   • При  создании  и  изменении  объектов
 проверяется  что  пользователь  имеет  права
 на  создание/изменение  конкретного
 экземпляра  объекта  и  конкретных  атрибутов
  • 46. Способы  задания  прав  доступа • На  уровне  классов   • На  уровне  объектов
 (row-­‐level  permissions)   Pony  предлагает  очень  удобный  декларативный   способ  задания
 row-­‐level  permissions
  • 47. На  основе  чего  задаются  права? • Группы  -­‐  характеризуют  текущего  пользователя.   Примеры:  admin,  editor,  visitor   • Метки  -­‐  описывают  свойства  отдельных   объектов.  Примеры:  public,  deleted   • Роли  -­‐  описывают  взаимоотношения   пользователя  и  конкретного  объекта.  Примеры:   owner,  creator,  moderator
  • 48. Пример  задания  прав  доступа with db.permissions_for(Artwork): allow('view’, group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')
  • 49.  Порядок  вычисления  прав  доступа • Пони  запрашивает  группы  текущего  пользователя  и   кеширует  их   • Код  приложения  выбирает  объекты  запросом  и   вызывает  to_json()   • Пони  запрашивает  роли  текущего  пользователя   относительно  сериализуемых  объектов   • Код  приложения  вычисляет  роли  и  метки  и  сообщает   их  Пони   • Пони  проверяет  роли  на  соответствие  декларативно   заданным  правам  доступа
  • 50. Использование  групп  для  задания  прав with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')
  • 51.  Получение  групп  текущего  пользователя @groups_getter(Author) def get_author_groups(author): return ['author'] ! @groups_getter(User) def get_user_groups(user): return [user.type]
  • 52. Использование  ролей  для  задания  прав with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group='admin')
  • 53.  Получение  ролей  текущего  пользователя @roles_getter(Author, Artwork) def get_author_roles(author, artwork): if author is artwork.author: return ['owner']
  • 54. Проверка  роли  при  создании  объекта with db.permissions_for(Artwork): allow('view', group='anybody') allow('create, edit', role='owner') allow('edit, delete', group=‘admin') ! Юзер при создании объекта не сможет указать в поле artwork.author другого юзера!
  • 55.  Пример  использования  меток Допустим,  что  некоторые  картины   должны  быть  скрыты  от  обычных   пользователей  при  показе  на  сайте
  • 56. 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)    Пример  использования  меток
  • 57. Пример  использования  меток with db.permissions_for(Artwork): allow('view', group='anybody', label='public') allow('create, edit', role='owner') allow('edit, delete', group='admin')
  • 58.  Получение  меток  объекта @labels_getter(Artwork) def get_artwork_labels(artwork): if not artwork.hidden: return ['public']
  • 59.  Автоматические  фильтры  запросов Удобны,  если  мы  хотим  автоматически   добавлять  условие  ко  всем  запросам  для   определенного  класса
  • 61.  Автоматические  фильтры  запросов artworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3]
  • 62.  Автоматические  фильтры  запросов 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
  • 63.    Отключение  автоматических  фильтров with default_filters_disabled: artworks = Artwork.select( lambda a: a.author.name == 'Gerhard Richter' ).order_by(lambda a: a.title)[:3]
  • 64.    Отключение  автоматических  фильтров 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
  • 65. Разные  права  для  разных  сайтов 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')
  • 66. ReactJS • ReactJS  позволяет  строить  страницу  из   компонентов   • Компоненты  представляют  собой  слой   View   • Получают  свойства  (props)
 и  используют  их  при  рендеринге   • Объекты  PonyJS  можно  использовать
 в  качестве  значений  свойств  компонентов
  • 67. Пример  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> } });
  • 68. Пример  React-­‐компонента var ArtworksPage = React.createClass({ render: function () { var artworkList = … return <div> <h1>Artwork list</h1> <ul>{ artworkList }</ul> </div> } });
  • 69. Фрагменты  кода  с  ReactJS var artworkList = _.map( this.props.artworks, function (item) { return <li key={ item.id() }> <ArtworkDescription artwork={ item } /> </li> });
  • 70. Фрагменты  кода  с  ReactJS var 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> } });
  • 71. Фрагменты  кода  с  ReactJS var 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> } });
  • 73. • Автогенерация  моделей  на  фронтенде   • Сериализация  произвольных  данных,  включая   двусторонние  связи  many-­‐to-­‐many   • Объекты  с  двунаправленными  связями  на  клиенте   • Может  использоваться  с  любым  фронтенд-­‐ фреймворком  (React,  Angular,  Knockout  и  др.)   • Формат  передаваемых  данных  универсален.  
 Может  использоваться  с  другими  бэкендами   • Двунаправленный  биндинг   • Передача  изменений  обратно  на  сервер   • Декларативные  права  доступа  на  уровне  объектов Заключение.  Что  предлагает  PonyJS?
  • 74. Спасибо! Q&A Site:  ponyorm.com   Twi•er:  @ponyorm   Github:  github.com/ponyorm/pony Александр  Козловский,  Алексей  Малашкевич PyCon  Russia  2015