Ормар
[Pypi version] (https://pypi.org/project/ormar)
[
(https://codecov.io/h/collerek/ormar)CodeFactor (https://www.codefactor.io/repositary/github/collerek/ormar)

Общий обзор
Пакет Ormar является асинхронным ORM для Python с поддержкой Postgres, MySQL и SQLite.
Основные преимущества использования Ormar:
- Получите асинхронный ORM для использования с асинхронными фреймами (fastapi, starlette и т. д.)
- поддерживает только одну модель-вам не нужно поддерживать pydantic и другие орм-модели (sqlchemy, peewe, gino и т. д.)
Цель состоит в том, чтобы создать простой ORM, который может быть использован непосредственно с fastapi на основе pydantic проверки данных (в качестве модели запроса и ответа).
Название Ormar, за исключением явного «ORM» в названии, происходит от шведского слова ormar, что означает змея, и хорватского ормар (e), что означает кабинет.
Что может быть лучше названия для Python ORM, чем Snakes Cabinet:)
Если вам нравится ormar, не забудьте добавить звездочку в репозиторий на github!
Чем больше сообщество, которое мы создаем, тем легче находить ошибки и привлекать участников;)
Документация
Просмотрите документацию для получения дополнительных сведений.
Обратите внимание, что в целях простоты большинство фрагментов документа не предусматривают создание базы данных и планирование выполнения асинхронных функций.
Если вы хотите больше реальных примеров, чем в документе, вы можете просмотреть тестовые папки, так как они фактически должны быть созданы и подключены к базе данных в большинстве тестов.
Но имейте в виду, что это всего лишь тесты, и не все решения подходят для использования в реальной жизни.
Часть экосистемы Fastapi
Как часть экосистемы fastapi, ormar поддерживается в отдельных библиотеках, которые так или иначе работают с базой данных.
Ormar по-прежнему не имеет отношения к диалекту SQL-поэтому реализует только столбцы, работающие во всех поддерживаемых бэкэндах.
В качестве расширения ормар, достижение конкретных столбцов диалектов относительно легко.
Реализация столбцов Postgres: ormar-postgres-extensions
Если вы поддерживаете или используете другую библиотеку и хотите, чтобы она поддерживала ormar, сообщите нам, как мы можем помочь.
Зависимости
В состав Ormar входят:
- Ядро sqlchemy для создания запросов.
- База данных, поддерживаемая асинхронно между базами данных.
- Pydantic используется для проверки данных.
Лицензия
Ormar создан как программное обеспечение с открытым исходным кодом и будет оставаться полностью бесплатным (лицензия MIT).
Когда я пишу открытый код для решения повседневных проблем на работе или для продвижения и создания сильного сообщества Python, вы можете сказать спасибо и попросить меня выпить кофе или спонсировать меня ежемесячно, чтобы помочь гарантировать, что моя работа остается бесплатной и поддерживаемой.
Спонсор
Перенос с sqlchemy и существующих баз данных
Если вы используете sqlchemy и хотите перейти на ormar, ознакомьтесь с инструментом автоматического перевода, который поможет вам перевести существующие модели sqlchemy orm, чтобы вам не пришлось делать это вручную.
Бета-версию можно найти на github: sqlchemy-to-ormar или просто pip install sqlalchemy-to-ormar
sqlalchemy-to-ormar можно использовать с sqlacodegen, чтобы автоматически сопоставлять/генерировать модели ormar из существующих баз данных, даже если ваш проект не использует sqlalchemy.
Миграция и создание баз данных
Поскольку ormar построен на базе SQLALchemy ядра, вы можете использовать alembic для миграции базы данных (что вы должны делать в случае производственного кода).
Для тестирования и базовых приложений sqlchemy достаточно:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | #Примечание this is just a partial snippet full working example below
# 1. Импорты
sqimport alchemy
Import Data abases
Import ormar
# 2. Инициализация
DATABASE_URL = "sqlite://db.sqlite"
«Органа»((
metadata= sqalchemy (),
Database=databases.Database(DATABASE),
engine= sqlalchemy.create_engine (DATABASE),
)
# Define models здесь
3) База данных и возможности создания
base_ormar_config.metadata.create_all (engine)
|
Для получения дополнительной информации о примерах конфигурации alembic, а также о миграции и создании базы данных см. раздел Документация миграции.
Версия посадочного места
ormar все еще находится в разработке: мы рекомендуем фиксировать любые зависимости (т. е. ormar~=0.9.1)
ormar также следует номеру выпуска, т. е. значительные изменения добавляют номер основной версии, а другие изменения и исправления добавляют номер второй версии, поэтому вы должны быть в состоянии безопасно обновлять последнюю версию, но обязательно прочитайте документацию дистрибутива.example: (0.5.2 -> 0.6.0 - breaking, 0.5.2 -> 0.5.3 - non breaking).
Асинхронный Python
Обратите внимание, что ormar является асинхронным ORM, что означает, что вы должны ждать вызова метода, который планируется выполнить в цикле событий. Python имеет встроенный модуль asyncio, который позволяет сделать это.
Обратите внимание, что большинство «обычных» интерпретаторов Python не позволяют выполнять wait вне функции (так как вы фактически запланировали выполнение этой функции с задержкой и не получите сразу результаты).
В современных веб-фреймворках, таких как fastapi, фреймворк будет заниматься этой проблемой для вас, но если вы планируете сделать это самостоятельно, вам нужно сделать это вручную, как описано в разделе Начало работы ниже.
Быстрый старт
Обратите внимание, что тот же сценарий можно найти в папке примеров на github.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 | Из импорта опций
Import Data abases
Import ormar
sqimport alchemy
DATABASE_URL = "sqlite://db.sqlite"
«Органа»((
metadata= sqalchemy (),
Database=databases.Database(DATABASE),
engine = sqlalchemy.create_engine (DATABASE),
)
«All ormar cares is a field with name»(«Все ормар cares is a field with name)
#ormar_config # and proper parameters, but this way you do not have to repeat
# The same parameters if you use only one database
#
# Note that all type hints are optional
# below is a perfectly valid model declaration
# class Author(ormar.Model):
# ormar_config = base_ormar_config.copy (tablename="authors")
#
# id = ormar.Integer(primary_key =True) # <= notice no field types
# name = ormar.String(max_length =100)
Класс автора (ormar.Model):
ormar_config = base_ormar_config.copy (tablename="authors")
id: int = ormar.Integer(primary_key =True)
name: str = ormar.String(max_length =100)
Модель (Ormar):
ormar_config = base_ormar_config.copy (tablename="books")
id: int = ormar.Integer(primary_key =True)
author: Optional[Author] = ormar.ForeignKey(Author)
title: str = ormar.String(max_length =100)
year: int = ormar.Integer(nullable=True)
# create the database
#Примечание That in production you should use migrants
Это не требуется, если вы не хотите переключить базу данных.
«Just to be sure we clear the db before
base_ormar_config.metadata.drop_all (engine)
base_ormar_config.metadata.create_all (engine)
# all functions below are divided into functionality categories
Note How All Functions are defined with async - hence can use await AND needs to
# Be awaited on Their own
async def create():
Create to Work with QuerySet.create method.
# Note that queryset is exposed on each Model's class as objects
tolkien = await Author.objects.create(name="J. R. R. Tolkien")
await Book.objects.create(author=tolkien, title="The Hobbit", год =1937)
await Book.objects.create(author=tolkien, title="The Lord of the Rings", год =1955)
await Book.objects.create(author=tolkien, title="The Silmarillion", год =1977)
#Альтернативная creation of object divided into 2 steps
sapkowski = Author(name="Andrzej Sapkowski")
# Do Some Stuff
sapkowski.save (
# or save() after initialization
Книга (author=sapkowski, title="The Witcher", год =1990).save()
Книга (author=Sapkowski, title="The Tower of Fools", год =2002).save(
# to read more about inserting data into the database
# visit: https://collerek.github.io/ormar/queries/create/
async def read():
# Fetch an instance, without loading a foreign key relationship on it.
# Django Стиль
Book = await Book.objects.get (title="The Hobbit")
#или Python стиль
Book = await Book.objects.get (Book.title = "The Hobbit")
book2 = await Book.objects.first ()
fetch the instance with lower primary key value
Брошюра == book2
Вы можете получить доступ ко всем fields on loaded model
Название: The Hobbit ="The Hobbit"
Год == 1937
When no condition is passed to get()
# it behaves as last() based on primary key column
book3 = await Book.objects.get ()
Название: The Tower of Fools
Когда у вас есть связь, ormar always defines a related model for you
#When All You loaded is a foreign key value like in this example
assert isinstance(book.author, автор)
# primary key is populated from Foreign key stored in books table
assert book.author.pk = 1
# since the related model was not loaded all other fields are None
assert book.author.name is None
# Load the relationship from the database When you already have the related model
# alternatively see joins section below
Скачать книгу author.load ()
assert book.author.name == "J. R. R. Tolkien"
# get all rows for given model
authors = await Author.objects.all()
assert len(authors) = 2
# to read more about reading data from the database
# visit: https://collerek.github.io/ormar/queries/read/
async def update():
# read existing row from db
tolkien = await Author.objects.get (name="J. R. R. Tolkien")
assert tolkien.name == "J. R. R. Tolkien"
tolkien_id = tolkien.id
# change the selected property
tolkien.name = "Джон Рональд Реэль Толкиен"
# call update on a model instance
Tolkien.update()
# confirm that object was updated
tolkien = await Author.objects.get (name="Джон Рональд Реэль Толкиен")
assert tolkien.name == "Джон Рональд Реэль Толкиен"
assert tolkien.id == tolkien_id
# alternatively update data without loading
await Author.objects.filter(name__contains ="Tolkien").update(name="J. R. R. Tolkien")
# to read more about updating data in the database
# visit: https://collerek.github.io/ormar/queries/update/
async def delete():
silmarillion = await Book.objects.get (год =1977)
# call delete(on instance)
Акватика (()
# alternatively delete without loading
await Book.objects.delete(title="The Tower of Fools")
Note That When There is no record ormar raises NoMatch exception
try:
AWAIT Book. Objects.get (год =1977)
Выше ормар.NoMatch:
«No book from 1977!»
# to read more about deleting data from the dabase
# visit: https://collerek.github.io/ormar/queries/delete/
# note that despite the fact that record no longer exists in database
"Объект больше, чем просто нужно, и ты можешь использовать его (and i.e.save ()) again".
tolkien = silmarillion.author
await Book.objects.create(author=tolkien, title="The Silmarillion", год =1977)
async def joins():
# Tho join Two models use select_related
# Django Стиль
book = await Book.objects.select ("author").get(title="The Hobbit")
# Python стиль
В этом случае, если вы хотите, то вы можете Book.objects.select вы можете сделать для этого.
Название книги: The Hobbit
)
# now the author is already prefetched
assert book.author.name == "J. R. R. Tolkien"
By default you also get a second side of the relation
# constructed as lowercase source model name +'s ' (books in this case)
Вы можете также указать имя пользователя с параметром related name.
# Django Стиль
author = await Author.objects.select ("books").all(name="J. R. R. Tolkien")
# Python стиль
((Author.objects.select
Author.name == "J. R. R. Tolkien"
)
assert len(author[0].books) = 3
# for reverse and many to many relations you can also prefetch_related
This executes a separate query for each of related models
# Django стиль
author = await Author.objects. prefetch_related ("books").get(name="J. R. R. Tolkien")
# Python стиль
author = await Author.objects.prefetch_related (Author.books).get
Author.name == "J. R. R. Tolkien"
)
assert len(author.books) = 3
# to read more about relations
# visit: https://collerek.github.io/ormar/relations/
# to read more about joins and subqueries
# visit: https://collerek.github.io/ormar/queries/joins-and-subqueries/
async def filter_and_sort ():
# to filter the Query you can use filter() or pass key-value pars to
# get(), all() etc.
# to use special methods or access related model fields use double
# underscore like to filter by the name of the author use author_name
# Django Стиль
books = await Book.objects.all(author__name ="J. R. R. Tolkien")
# python стиль
books = await Book.objects.all (Book.author.name == "J. R. R. Tolkien")
Assert len(books) = 3
# filter Can accept Special methods and so sparated with Double underscore
# to issue SQL Query ` where authors.name like "%tolkien%"` that is not
Senssitive (hence small t in Tolkien)
# Django Стиль
books = await Book.objects.filter(author__name__icontains ="tolkien").all()
# python стиль
books = await Book.objects.filter(Book. author.name.icontains ("tolkien")).all()
Assert len(books) = 3
# to sort use order_by () function of queryset
# to sort decreasing use hyphen before the field name
# same as with filter you can use double underscores to access related fields
# Django стиль
books = (
await Book.objects.filter(author__name__icontains ="tolkien")
. order_by ("-year")
.all()
)
# python стиль
books = (
await Book.objects.filter(Book. author.name.icontains ("tolkien"))
. order_by (Book. year.desc ())
.all()
)
Assert len(books) = 3
[0].title = "The Silmarillion"
Название: The Hobbit [2]
# to read more about filtering and ordering
# visit: https://collerek.github.io/ormar/queries/filter-and-sort/
async def subset of columns():
# to exclude some columns from loading when querying the database
Вы можете использовать фильмы (метод)
hobbit = await Book.objects.fields (["title"]).get(title="The Hobbit")
# note that fields not included in fields are empty (set to None)
ASET Hobbit.Year Is None
assert hobbit.author is None
Selected Field Is There
title =«The Hobbit»
Alternatively you can providide columns you want to exclude
hobbit = await Book.objects.exclude_fields (["год"]).get(title="The Hobbit")
#Год не сносит
ASET Hobbit.Year Is None
#но author is back
hobbit.author is not None
Также вы не можете игнорировать primary key column - It's Always There
Если ты будешь делать это, то это будет там.
# note that each model have a shortcut for primary_key column which is pk
# and you can filter/set the values by this alias like below
hobbit.pk is not None
Комментарии, которые вы не можете сделать, но не нуллабельные
# (required) in model Definition
try:
await Book.objects.exclude_fields (["title"]).get(title="The Hobbit")
except pydantic.ValidationError:
"Cannot exclude non nullable field title"
# to read more about Selecting subset of columns
#visit: www.example.com
Async def pagination ():
#to limit number of returned rows use limit ((
books = await Book.objects.limit (1). all ()
Assert len (books)= 1
Название: The Hobbit [0]
#to offset number of Returned rows use offset ()
books = await Book.objects.limit (1). offset (1). all ()
Assert len (books)= 1
Название: The Lord of the Rings [10]
#alternatively use paginate that combines both
books = await Book.objects.paginate (page = 2, page_size = 2). all ()
Assert len (books)= 2
#note That We Removed One Book of Sapkowski in delete ((
#and recreated The Silmarillion-by default When no order_by is set
#ordering sorts by Primary key column
[0]. title =«The Witcher»
[1]. title =="The Silmarillion"
#to read more about pagination and number of rows
#visit: www.example.com
async def aggregations ():
#Count:
assert 2 == await Author.objects.count ()
#exists
assert await Book. objects. filter (title ="The Hobbit "). exists ()
# maximum
assert 1990 == await Book. objects.max (columns=["year"])
# minimum
assert 1937 == await Book. objects.min (columns=["year"])
# average
assert 1964.75 = await Book. objects.avg (columns=["year"])
#sume
assert 7859 = await Book. objects.sum (columns=["year"])
# to read more about agregated functions
# visit: https://collerek.github.io/ormar/queries/aggregations/
async def raw_data ():
# extract raw data in a form of dicts or tuples
# note that this skips the validation(!) as models are
# not created from parsed data
# get list of objects as dicts
assert await Book. objects.values () == [
{"id": 1, "author": 1, "title": "The Hobbit", "year": 1937},
{"id": 2, "author": 1, "title": "The Lord of the Rings", "year": 1955},
{"id": 4, "author": 2, "title": "The Witcher", "year": 1990},
{"id": 5, "author": 1, "title": "The Silmarillion", "year": 1977},
]
# get list of objects as tuples
assert await Book. objects.values_list () == [
(1, 1,«The Hobbit», 1937)
(2, 1,«Lord of the Rings», 1955).
(4, 2,«The Witcher», 1990)
(5, 1,«Silmarillion», 1977),
]
filter data - note how you always get a list
assert await Book.objects.filter(title="The Hobbit").values() = [
{"id": 1, "author": 1, "title": "The Hobbit", "year": 1937}
]
# select only wanted fields
assert await Book. objects.filter (title="The Hobbit").values(["id", "title"]) = [
{"id": 1, "title": "The Hobbit"}
]
Если вы выбираете только один column you could flatten it with values_list
assert await Book. objects.values_list ("title", flatten=True) == [
«The Hobbit»,
«The Lord of the Rings»
«The Witcher»,
«Silmarillion».
]
# to read more about extracting raw values
# visit: https://collerek.github.io/ormar/queries/aggregations/
async def with_connect (функция):
Note for Another Backend than sqlite you actually need to
# connect to the dabase to perform db operations
async with database
Функция (()
Если вы используете framework like `fastapi` you shouldn't connect
В твоих подставках, но есть глобальный коннектор пула
# check https://collerek.github.io/ormar/fastapi/ and section with db connection
# gather and execute All functions
normally import should be at the beginning of the file
Import asyncio
Примечание: То, что вы используете отца () функция по выполнению семи функций
#concurrency but we actually modify the data and we rely on the order of functions
Для работы в [
create,
read,
обновление,
delete,
joins,
filter_and_sort
subset of columns,
Пагония,
aggregations,
raw_data,
]:
print(f"Executing:{func.__name__}")
asyncio.run (www.example.com_connect (func))
# drop the datables
metadata.drop_all (engine)
|
Спецификация Омара
Метод набора запросов
Создание* (**kwargs): -> Модель
* get(*args, **kwargs): -> Model
* get_or_none(*args, **kwargs): -> Optional[Model]
* get_or_create(_defaults: Optional[Dict[str, Any]] = None, *args, **kwargs) -> Tuple[Model, bool]
* first(*args, **kwargs): -> Model
* update(each: bool = False, **kwargs) -> int
* update_or_create(**kwargs) -> Model
* bulk_create(objects: List[Model]) -> None
* bulk_update(objects: List[Model], columns: List[str] = None) -> None
* delete(*args, each: bool = False, **kwargs) -> int
* all(*args, **kwargs) -> List[Optional[Model]]
* iterate(*args, **kwargs) -> AsyncGenerator[Model]
* filter(*args, **kwargs) -> QuerySet
* exclude(*args, **kwargs) -> QuerySet
* select_related(related: Union[List, str]) -> QuerySet
* prefetch_related(related: Union[List, str]) -> QuerySet
* limit(limit_count: int) -> QuerySet
* offset(offset: int) -> QuerySet
* count(distinct: bool = True) -> int
* Существо () -> логическое значение
* max(columns: List[str]) -> Any
* min(columns: List[str]) -> Any
* avg(columns: List[str]) -> Any
* sum(columns: List[str]) -> Any
* fields(columns: Union[List, str, set, dict]) -> QuerySet
* exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet
* order_by(columns:Union[List, str]) -> QuerySet
* values(fields: Union[List, str, Set, Dict])
* values_list(fields: Union[List, str, Set, Dict])
Тип отношений
- Один к нескольким-Используйте ForeignKey(to: Model)
- Больше чем-и
ManyToMany(to: Model, Optional[through]: Model)
Тип поля модели
Доступные поля модели (с обязательными параметрами-необязательными параметрами в документе):
- Строка (максимальная длина)
- Текст ()
- Логическое значение ()
- Целое число ()
- Плавание ()
- дата ()
- Время ()
- Дата и время ()
JSON()
- Большое целое ()
- Небольшое целое число ()
- Десятичные (число десятичных знаков, точность)
UUID()
- Большой двоичный (максимальная длина)
- Перечисление (класс перечисления)
- Перечисление, похожее на Field-путем передачи выбора любому другому типу Field
- EncryptedString -передавая encrypt_secret и encrypt_backend
- Внешний ключ (до)
- Больше чем (до)
Доступные параметры поля
Все типы полей поддерживают следующие параметры ключевых слов.
- Первичный ключ: логическое значение
- Пусто: логическое значение
- По умолчанию: любой
- Сервер по умолчанию: любой
- Индекс: Логическое значение
- Единственный: Буль.
- Имя: str
Все поля являются обязательными, если не установлен один из следующих полей:
- nullable -создает столбец, который может быть пустым. Установите значение по умолчанию False. Дополнительные сведения см. в разделе Общие параметры поля.
- sql_nullable-используется для установки различных параметров для pydantic и базы данных. Установите значение по умолчанию для пустых. Подробнее см. Общие параметры поля.
- По умолчанию-Задание значений по умолчанию для полей. Неприменимо к связанным полям
- server_default-устанавливает значения по умолчанию для серверных полей (например, func.now () sqlalchemy). Неприменимо к реляционным полям.
- Первичный ключ с автоматическим приращением-если для столбца установлен первичный ключ, а для этого столбца установлен автоматический приращение. По умолчанию ключ int устанавливает автоматическое приращение.
Доступный сигнал
Сигналы позволяют запускать функцию для данного события в данной модели.
- Предварительное хранение
- После сохранения
- Предварительно обновленное
- После обновления
- Предварительное удаление
- После удаления
- Пресвязывание добавить
- Добавить после связи
- Предварительное удаление
- После удаления связи