Skip to content

Commit c44df78

Browse files
committed
Update python app docs
1 parent 5ccb818 commit c44df78

File tree

1 file changed

+192
-135
lines changed
  • ydb/docs/ru/core/dev/example-app/python

1 file changed

+192
-135
lines changed
Lines changed: 192 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Приложение на Python
22

3-
На этой странице подробно разбирается код [тестового приложения](https://github.com/ydb-platform/ydb-python-sdk/tree/master/examples/basic_example_v1), доступного в составе [Python SDK](https://github.com/ydb-platform/ydb-python-sdk) {{ ydb-short-name }}.
3+
На этой странице подробно разбирается код [тестового приложения](https://github.com/ydb-platform/ydb-python-sdk/tree/master/examples/basic_example_v2), доступного в составе [Python SDK](https://github.com/ydb-platform/ydb-python-sdk) {{ ydb-short-name }}.
44

55
## Скачивание и запуск {#download}
66

@@ -37,27 +37,62 @@ def run(endpoint, database, path):
3737
exit(1)
3838
```
3939

40-
Фрагмент кода приложения для создания сессии:
41-
42-
```python
43-
session = driver.table_client.session().create()
44-
```
45-
4640
{% include [create_table.md](../_includes/steps/02_create_table.md) %}
4741

48-
Для создания таблиц используется метод `session.create_table()`:
42+
Для создания таблиц используется метод `pool.execute_with_retries()`:
4943

5044
```python
51-
def create_tables(session, path):
52-
session.create_table(
53-
os.path.join(path, 'series'),
54-
ydb.TableDescription()
55-
.with_column(ydb.Column('series_id', ydb.PrimitiveType.Uint64)) # not null column
56-
.with_column(ydb.Column('title', ydb.OptionalType(ydb.PrimitiveType.Utf8)))
57-
.with_column(ydb.Column('series_info', ydb.OptionalType(ydb.PrimitiveType.Utf8)))
58-
.with_column(ydb.Column('release_date', ydb.OptionalType(ydb.PrimitiveType.Uint64)))
59-
.with_primary_key('series_id')
45+
def create_tables(pool, path):
46+
print("\nCreating table series...")
47+
pool.execute_with_retries(
48+
"""
49+
PRAGMA TablePathPrefix("{}");
50+
CREATE table `series` (
51+
`series_id` Uint64,
52+
`title` Utf8,
53+
`series_info` Utf8,
54+
`release_date` Uint64,
55+
PRIMARY KEY (`series_id`)
56+
)
57+
""".format(
58+
path
59+
)
60+
)
61+
62+
print("\nCreating table seasons...")
63+
pool.execute_with_retries(
64+
"""
65+
PRAGMA TablePathPrefix("{}");
66+
CREATE table `seasons` (
67+
`series_id` Uint64,
68+
`season_id` Uint64,
69+
`title` Utf8,
70+
`first_aired` Uint64,
71+
`last_aired` Uint64,
72+
PRIMARY KEY (`series_id`, `season_id`)
73+
)
74+
""".format(
75+
path
76+
)
77+
)
78+
79+
print("\nCreating table episodes...")
80+
pool.execute_with_retries(
81+
"""
82+
PRAGMA TablePathPrefix("{}");
83+
CREATE table `episodes` (
84+
`series_id` Uint64,
85+
`season_id` Uint64,
86+
`episode_id` Uint64,
87+
`title` Utf8,
88+
`air_date` Uint64,
89+
PRIMARY KEY (`series_id`, `season_id`, `episode_id`)
90+
)
91+
""".format(
92+
path
93+
)
6094
)
95+
6196
```
6297

6398
В параметр path передаётся абсолютный путь от корня:
@@ -66,39 +101,41 @@ def create_tables(session, path):
66101
full_path = os.path.join(database, path)
67102
```
68103

69-
С помощью метода `session.describe_table()` можно вывести информацию о структуре таблицы и убедиться, что она успешно создалась:
104+
Функция `pool.execute_with_retries(query)`, в отличие от `tx.execute()`, загружает в память результат запроса до его возвращения клиенту.
105+
Благодаря этому нет необходимости использовать специальные контрукции для контроля над стримом, однако нужно с осторожностью пользоваться данным методом с большими `SELECT` запросами.
106+
Подробнее про стримы будет сказано ниже.
70107

71-
```python
72-
def describe_table(session, path, name):
73-
result = session.describe_table(os.path.join(path, name))
74-
print("\n> describe table: series")
75-
for column in result.columns:
76-
print("column, name:", column.name, ",", str(column.type.item).strip())
77-
```
108+
## Работа со стримами {#work-with-streams}
78109

79-
Приведенный фрагмент кода при запуске выводит на консоль текст:
110+
Результатом выполнения `tx.execute()` является стрим. Стрим позволяет считать неограниченное количество строк и объем данных, не загружая в память весь результат. Однако, для корректного сохранения состояния транзакции на стороне `ydb`
111+
стрим необходимо прочитывать до конца после каждого запроса. Для удобства результат функции `tx.execute()` представлен в виде контекстного менеджера, который долистывает стрим до конца после выхода.
80112

81-
```bash
82-
> describe table: series
83-
('column, name:', 'series_id', ',', 'type_id: UINT64')
84-
('column, name:', 'title', ',', 'type_id: UTF8')
85-
('column, name:', 'series_info', ',', 'type_id: UTF8')
86-
('column, name:', 'release_date', ',', 'type_id: UINT64')
113+
```python
114+
with tx.execute(query) as _:
115+
pass
87116
```
117+
88118
{% include [steps/03_write_queries.md](../_includes/steps/03_write_queries.md) %}
89119

90120
Фрагмент кода, демонстрирующий выполнение запроса на запись/изменение данных:
91121

92122
```python
93-
def upsert_simple(session, path):
94-
session.transaction().execute(
95-
"""
96-
PRAGMA TablePathPrefix("{}");
97-
UPSERT INTO episodes (series_id, season_id, episode_id, title) VALUES
98-
(2, 6, 1, "TBD");
99-
""".format(path),
100-
commit_tx=True,
101-
)
123+
def upsert_simple(pool, path):
124+
print("\nPerforming UPSERT into episodes...")
125+
126+
def callee(session):
127+
with session.transaction().execute(
128+
"""
129+
PRAGMA TablePathPrefix("{}");
130+
UPSERT INTO episodes (series_id, season_id, episode_id, title) VALUES (2, 6, 1, "TBD");
131+
""".format(
132+
path
133+
),
134+
commit_tx=True,
135+
) as _:
136+
pass
137+
138+
return pool.retry_operation_sync(callee)
102139
```
103140

104141
{% include [pragmatablepathprefix.md](../_includes/auxilary/pragmatablepathprefix.md) %}
@@ -108,28 +145,45 @@ def upsert_simple(session, path):
108145
Для выполнения YQL-запросов используется метод `session.transaction().execute()`.
109146
SDK позволяет в явном виде контролировать выполнение транзакций и настраивать необходимый режим выполнения транзакций с помощью класса `TxControl`.
110147

111-
В фрагменте кода, приведенном ниже, транзакция выполняется с помощью метода `transaction().execute()`. Устанавливается режим выполнения транзакции `ydb.SerializableReadWrite()`. После завершения всех запросов транзакции она будет автоматически завершена с помощью явного указания флага: `commit_tx=True`. Тело запроса описано с помощью синтаксиса YQL и как параметр передается методу `execute`.
148+
В фрагменте кода, приведенном ниже, транзакция выполняется с помощью метода `transaction().execute()`. Устанавливается режим выполнения транзакции `ydb.QuerySerializableReadWrite()`. После завершения всех запросов транзакции она будет автоматически завершена с помощью явного указания флага: `commit_tx=True`. Тело запроса описано с помощью синтаксиса YQL и как параметр передается методу `execute`.
112149

113150
```python
114-
def select_simple(session, path):
115-
result_sets = session.transaction(ydb.SerializableReadWrite()).execute(
116-
"""
117-
PRAGMA TablePathPrefix("{}");
118-
$format = DateTime::Format("%Y-%m-%d");
119-
SELECT
120-
series_id,
121-
title,
122-
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(release_date AS Int16))) AS Uint32))) AS release_date
123-
FROM series
124-
WHERE series_id = 1;
125-
""".format(path),
126-
commit_tx=True,
127-
)
128-
print("\n> select_simple_transaction:")
129-
for row in result_sets[0].rows:
130-
print("series, id: ", row.series_id, ", title: ", row.title, ", release date: ", row.release_date)
131-
132-
return result_sets[0]
151+
def select_simple(pool, path):
152+
print("\nCheck series table...")
153+
154+
def callee(session):
155+
# new transaction in serializable read write mode
156+
# if query successfully completed you will get result sets.
157+
# otherwise exception will be raised
158+
with session.transaction(ydb.QuerySerializableReadWrite()).execute(
159+
"""
160+
PRAGMA TablePathPrefix("{}");
161+
$format = DateTime::Format("%Y-%m-%d");
162+
SELECT
163+
series_id,
164+
title,
165+
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(release_date AS Int16))) AS Uint32))) AS release_date
166+
FROM series
167+
WHERE series_id = 1;
168+
""".format(
169+
path
170+
),
171+
commit_tx=True,
172+
) as result_sets:
173+
first_set = next(result_sets)
174+
for row in first_set.rows:
175+
print(
176+
"series, id: ",
177+
row.series_id,
178+
", title: ",
179+
row.title,
180+
", release date: ",
181+
row.release_date,
182+
)
183+
184+
return first_set
185+
186+
return pool.retry_operation_sync(callee)
133187
```
134188

135189
В качестве результата выполнения запроса возвращается `result_set`, итерирование по которому выводит на консоль текст:
@@ -140,37 +194,54 @@ series, Id: 1, title: IT Crowd, Release date: 2006-02-03
140194
```
141195

142196

143-
{% include [param_prep_queries.md](../_includes/steps/07_param_prep_queries.md) %}
197+
## Параметризованные запросы {#param-queries}
198+
199+
Для выполнения параметризованных запросов в метод `tx.execute()` необходимо передать словарь с параметрами специального вида, где ключом служит имя параметра, а значение может быть одним из следующих:
200+
1. Обычное значение (без указывания типов допустимо использовать только int, str, bool);
201+
2. Кортеж со значением и типом;
202+
3. Специальный тип ydb.TypedValue(value=value, value_type=value_type).
203+
204+
Фрагмент кода, демонстрирующий возможность использования параметризованных запросов:
144205

145206
```python
146-
def select_prepared(session, path, series_id, season_id, episode_id):
147-
query = """
148-
PRAGMA TablePathPrefix("{}");
149-
DECLARE $seriesId AS Uint64;
150-
DECLARE $seasonId AS Uint64;
151-
DECLARE $episodeId AS Uint64;
152-
$format = DateTime::Format("%Y-%m-%d");
153-
SELECT
154-
title,
155-
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(air_date AS Int16))) AS Uint32))) AS air_date
156-
FROM episodes
157-
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
158-
""".format(path)
159-
160-
prepared_query = session.prepare(query)
161-
result_sets = session.transaction(ydb.SerializableReadWrite()).execute(
162-
prepared_query, {
163-
'$seriesId': series_id,
164-
'$seasonId': season_id,
165-
'$episodeId': episode_id,
166-
},
167-
commit_tx=True
168-
)
169-
print("\n> select_prepared_transaction:")
170-
for row in result_sets[0].rows:
171-
print("episode title:", row.title, ", air date:", row.air_date)
207+
def select_with_parameters(pool, path, series_id, season_id, episode_id):
208+
def callee(session):
209+
query = """
210+
PRAGMA TablePathPrefix("{}");
172211
173-
return result_sets[0]
212+
DECLARE $seriesId AS Uint64;
213+
DECLARE $seasonId AS Uint64;
214+
DECLARE $episodeId AS Uint64;
215+
216+
$format = DateTime::Format("%Y-%m-%d");
217+
SELECT
218+
title,
219+
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(air_date AS Int16))) AS Uint32))) AS air_date
220+
FROM episodes
221+
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
222+
""".format(
223+
path
224+
)
225+
226+
with session.transaction(ydb.QuerySerializableReadWrite()).execute(
227+
query,
228+
{
229+
"$seriesId": series_id, # could be defined implicit in case of int, str, bool
230+
"$seasonId": (season_id, ydb.PrimitiveType.Uint64), # could be defined via tuple
231+
"$episodeId": ydb.TypedValue(
232+
episode_id, ydb.PrimitiveType.Uint64
233+
), # could be defined via special class
234+
},
235+
commit_tx=True,
236+
) as result_sets:
237+
print("\n> select_prepared_transaction:")
238+
first_set = next(result_sets)
239+
for row in first_set.rows:
240+
print("episode title:", row.title, ", air date:", row.air_date)
241+
242+
return first_set
243+
244+
return pool.retry_operation_sync(callee)
174245
```
175246

176247
Приведенный фрагмент кода при запуске выводит на консоль текст:
@@ -180,58 +251,44 @@ def select_prepared(session, path, series_id, season_id, episode_id):
180251
('episode title:', u'To Build a Better Beta', ', air date:', '2016-06-05')
181252
```
182253

183-
{% include [scan_query.md](../_includes/steps/08_scan_query.md) %}
184-
185-
```python
186-
def executeScanQuery(driver):
187-
query = ydb.ScanQuery("""
188-
SELECT series_id, season_id, COUNT(*) AS episodes_count
189-
FROM episodes
190-
GROUP BY series_id, season_id
191-
ORDER BY series_id, season_id
192-
""", {})
193-
194-
it = driver.table_client.scan_query(query)
195-
196-
while True:
197-
try:
198-
result = next(it)
199-
print result.result_set.rows
200-
except StopIteration:
201-
break
202-
```
203254

204255
{% include [transaction_control.md](../_includes/steps/10_transaction_control.md) %}
205256

206-
Фрагмент кода, демонстрирующий явное использование вызовов `transaction().begin()` и `tx.Commit()`:
257+
Фрагмент кода, демонстрирующий явное использование вызовов `transaction().begin()` и `tx.commit()`:
207258

208259
```python
209-
def explicit_tcl(session, path, series_id, season_id, episode_id):
210-
query = """
211-
PRAGMA TablePathPrefix("{}");
212-
213-
DECLARE $seriesId AS Uint64;
214-
DECLARE $seasonId AS Uint64;
215-
DECLARE $episodeId AS Uint64;
216-
217-
UPDATE episodes
218-
SET air_date = CAST(CurrentUtcDate() AS Uint64)
219-
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
220-
""".format(path)
221-
prepared_query = session.prepare(query)
222-
223-
tx = session.transaction(ydb.SerializableReadWrite()).begin()
224-
225-
tx.execute(
226-
prepared_query, {
227-
'$seriesId': series_id,
228-
'$seasonId': season_id,
229-
'$episodeId': episode_id
230-
}
231-
)
260+
def explicit_tcl(pool, path, series_id, season_id, episode_id):
261+
def callee(session):
262+
query = """
263+
PRAGMA TablePathPrefix("{}");
264+
265+
DECLARE $seriesId AS Uint64;
266+
DECLARE $seasonId AS Uint64;
267+
DECLARE $episodeId AS Uint64;
268+
269+
UPDATE episodes
270+
SET air_date = CAST(CurrentUtcDate() AS Uint64)
271+
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
272+
""".format(
273+
path
274+
)
275+
276+
# Get newly created transaction id
277+
tx = session.transaction(ydb.QuerySerializableReadWrite()).begin()
278+
279+
# Execute data query.
280+
# Transaction control settings continues active transaction (tx)
281+
with tx.execute(
282+
query,
283+
{"$seriesId": series_id, "$seasonId": season_id, "$episodeId": episode_id},
284+
) as _:
285+
pass
286+
287+
print("\n> explicit TCL call")
232288

233-
print("\n> explicit TCL call")
289+
# Commit active transaction(tx)
290+
tx.commit()
234291

235-
tx.commit()
292+
return pool.retry_operation_sync(callee)
236293
```
237294

0 commit comments

Comments
 (0)