Каталог решений - Использование ORM вместо работы с чистым SQL

Использование ORM вместо работы с чистым SQL

Использование ORM вместо работы с чистым SQL

В наличии

Маленькая заметка про небольшой, но полезный релиз Simple UI. Дополнение к основной статье https://infostart.ru/public/1153616/

Категория:

Описание

В Simple UI оффлайн хранение можно организовать в NoSQL или в SQLite. И несмотря на очевидные плюсы NoSQL, старый добрый SQL рано списывать. Быстрые агрегатные функции, быстрый поиск по сложным фильтрам – все это к SQL. Но есть проблемы – для этого нужно знать собственно сам язык SQL (более того, нужно знать конкретную реализацию под конкретную СУБД, а они существенно отличаются) и конструкции для рутинных CRUD – операций в коде довольно громоздкие, особенно если решение большое и таблиц много – нужно все прописывать вручную. Для того придумали ORM (object-relational mapper) – надстройки над SQL которые позволяют:

•    Работать с таблицами СУБД как с объектами – обращаться к атрибутам объекта, наследовать и т.д.
•    Соответственно не нужно знать синтаксиса – ты работаешь с объектами
•    Не задумываться, с какой СУБД работаешь – NoSQL, PostgreSQL, MySQL – ORM работает со всеми, а разработчик работает с единым интерфейсом ORM. Соответственно опять же не нужно знать особенности и читать доки
•    Писать запросы как попало – а ORM сама сделает оптимизацию
•    За счет простоты повышается читаемость кода и проще поддерживать проекты

В общем во всех отношениях чудесная вещь, призванная упростить жизнь разработчика как на маленьких, так и на больших проектах. Скажем так, после перехода на ORM назад к сырому SQL уже не захочется, по крайней мере в CRUD–операциях точно. С SELECT-ами еще можно поспорить. Но дело в том, что ничего не мешает параллельно ORM работать с таблицами напрямую. Хоть средствами самого ORM, хоть другими способами. Т.е. ORM это просто обертка, которая преобразует код работы с объектами в набор SQL-запросов, которые делают транзакции и ничего не мешает обращаться к таблицам другими способами. Т.е., пожалуй что, не найти ни одного аргумента против ее использования.

На самом деле технология давно пустила корни в различных стеках – она есть в Django (собственный ORM), есть SQLAlchemy который используется в множестве именитых проектах и т.д. Для стека SimpleUI+Python+Android я выбрал Pony ORM https://docs.ponyorm.org/firststeps.html . Она ничем не хуже SQL Alchemy или Peewee, но очень простая (что и нужно для Simple) и там есть, например, такие встроенные штуки как работа с типами JSON – т.е. в таблице хранится JSON, а Pony обращается к объекту таблицы и к JSON объекту как к JSON-объекту. Т.е., грубо говоря, можно обращаться через точку.

Работа через ORM напоминает работу в 1С через менеджер объекта (справочника, документа или регистра) с той лишь разницей, что в 1С нет ООП, нет классов и такие вещи, как, например, табличная часть, подчиненная строке табличной части, изобразить напрямую нельзя. 

Вся документация по Pony с примерами есть в вышеприведенной ссылке, от себя я добавил примеры работы с базой через ORM в конфигурации «Примеры с ORM» — там добавление, редактирование, удаление, вложенные объекты и выборки. А также пример, как можно обращаться к «Документам» через ORM, раз уж там с поддержкой JSON в типах такое можно.

Комплект разработчика для версии 7.75 можно скачать в //sale.itcity.ru/public/1153616/

Вкратце изложу на примерах, как использовать Pony в Simple UI. Тут везде используется только код Python – т.е. только оффлайновое выполение кода. Из 1С онлайн такое не сделаешь

1.    Описываем объекты в общем модуле (в Pony они называются «сущностями»). Тут все просто: Required означает обязательный реквизит, Optional – необязательный, PrimaryKey – первичный ключ (по умолчанию создается id, это не нужно декларировать) и Set – это набор (аналог табличной части) который можно включать в себя другие объекты – те объекты какие то другие и т.д.
Типы значений могут быть простые и могут быть другие объекты. Подробнее тут https://docs.ponyorm.org/entities.html

Например, я объявил 2 объекта «Люди» и «Машины». У каждого человека могут быть несколько машин. 

  from pony.orm import Database,Required,Set,Json,PrimaryKey,Optional
  import datetime
  
  class Person(db.Entity):
          name =  Required(str)
          age = Required(int)
          cars = Set('Car')
  
  class Car(db.Entity):
          make = Required(str)
          model = Required(str)
          owner = Required(Person)
  

2.    Но эти объекты еще не созданы, потому что это было просто описание – в СУБД ничего не произошло. Да и в какой СУБД? Pony о ней ничего не известно. Поэтому тут же, в объем модуле я пишу что СУБД будет SQLite и указываю путь к ней.

  db = Database()
  db.bind(provider='sqlite', 
  filename='//data/data/ru.travelfood.simple_ui/databases/SimpleWMS', create_db=True)

А также объявляю функцию init с вызовом  generate_mapping  которую надо поставить в ПриЗапуске чтобы каждый раз когда программа запускалась она выполнялась она собсвенно и создает таблицы. После запуска приложения можно проверить и убедиться в консоли что наши таблицы созданы

  def init():
      db.generate_mapping(create_tables=True)

 

 
 

3.    Ну и далее работаем уже с объектами Person и Car

Добавить объект (тут и далее они импортируются из общего модуля ui_global)

      with db_session:
          p = ui_global.Person(name=hashMap.get('name'), age=int(hashMap.get('age')))
      Получить объект можно по id:
           with db_session:
          p = ui_global.Person[int(hashMap.get("id"))]
          p.name =hashMap.get('name')
          p.age =int(hashMap.get("age"))

Добавить машину к владельцу

      with db_session:
          ui_global.Car(make=hashMap.get('brand'), model=hashMap.get('model'), owner=ui_global.Person[int(hashMap.get("id"))])
          commit()      

Удалить объект  (предварительно проверив что он есть)
 

      with db_session:
          if ui_global.Person.exists(id=int(hashMap.get("id"))): 
                  ui_global.Person[int(hashMap.get("id"))].delete()

Простой запрос с обходом полей

 

  query = select(c for c in ui_global.Car if c.owner==ui_global.Person[int(hashMap.get("id"))])
  rows=[]
  for car in query:
      rows.append({"make":car.make,"model":car.model,"id":car.id})

 

has been added to your cart:
Оформление заказа