Skip to content

Commit 2a89cf0

Browse files
Add documentation for asession database feature (#1316)
* Add documentation for asession database feature Co-Authored-By: Alek Petuskey <[email protected]> * Update asession documentation to use @rx.event(background=True) and add state locking Co-Authored-By: Alek Petuskey <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alek Petuskey <[email protected]>
1 parent cb6e640 commit 2a89cf0

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

docs/database/queries.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,156 @@ class State(rx.State):
189189
with rx.session() as session:
190190
return [list(row) for row in session.execute("SELECT * FROM user").all()]
191191
```
192+
193+
## Async Database Operations
194+
195+
Reflex provides an async version of the session function called `rx.asession` for asynchronous database operations. This is useful when you need to perform database operations in an async context, such as within async event handlers.
196+
197+
The `rx.asession` function returns an async SQLAlchemy session that must be used with an async context manager. Most operations against the `asession` must be awaited.
198+
199+
```python
200+
import sqlalchemy.ext.asyncio
201+
import sqlalchemy
202+
203+
import reflex as rx
204+
205+
206+
class AsyncUserState(rx.State):
207+
users: list[User] = []
208+
209+
@rx.event(background=True)
210+
async def get_users_async(self):
211+
async with rx.asession() as asession:
212+
result = await asession.execute(User.select())
213+
async with self:
214+
self.users = (await result.all())
215+
```
216+
217+
### Async Select
218+
219+
The following example shows how to query the database asynchronously:
220+
221+
```python
222+
class AsyncQueryUser(rx.State):
223+
name: str
224+
users: list[User] = []
225+
226+
@rx.event(background=True)
227+
async def get_users(self):
228+
async with rx.asession() as asession:
229+
stmt = User.select().where(User.username.contains(self.name))
230+
result = await asession.execute(stmt)
231+
async with self:
232+
self.users = (await result.all())
233+
```
234+
235+
### Async Insert
236+
237+
To add a new record to the database asynchronously:
238+
239+
```python
240+
class AsyncAddUser(rx.State):
241+
username: str
242+
email: str
243+
244+
@rx.event(background=True)
245+
async def add_user(self):
246+
async with rx.asession() as asession:
247+
asession.add(User(username=self.username, email=self.email))
248+
await asession.commit()
249+
```
250+
251+
### Async Update
252+
253+
To update a user asynchronously:
254+
255+
```python
256+
class AsyncChangeEmail(rx.State):
257+
username: str
258+
email: str
259+
260+
@rx.event(background=True)
261+
async def modify_user(self):
262+
async with rx.asession() as asession:
263+
stmt = User.select().where(User.username == self.username)
264+
result = await asession.execute(stmt)
265+
user = (await result.first())
266+
if user:
267+
user.email = self.email
268+
asession.add(user)
269+
await asession.commit()
270+
```
271+
272+
### Async Delete
273+
274+
To delete a user asynchronously:
275+
276+
```python
277+
class AsyncRemoveUser(rx.State):
278+
username: str
279+
280+
@rx.event(background=True)
281+
async def delete_user(self):
282+
async with rx.asession() as asession:
283+
stmt = User.select().where(User.username == self.username)
284+
result = await asession.execute(stmt)
285+
user = (await result.first())
286+
if user:
287+
await asession.delete(user)
288+
await asession.commit()
289+
```
290+
291+
### Async Refresh
292+
293+
Similar to the regular session, you can refresh an object to ensure all fields are up to date:
294+
295+
```python
296+
class AsyncAddUserForm(rx.State):
297+
user: User | None = None
298+
299+
@rx.event(background=True)
300+
async def add_user(self, form_data: dict[str, str]):
301+
async with rx.asession() as asession:
302+
async with self:
303+
self.user = User(**form_data)
304+
asession.add(self.user)
305+
await asession.commit()
306+
await asession.refresh(self.user)
307+
```
308+
309+
### Async SQL Execution
310+
311+
You can also execute raw SQL asynchronously:
312+
313+
```python
314+
class AsyncRawSQL(rx.State):
315+
users: list[list] = []
316+
317+
@rx.event(background=True)
318+
async def insert_user_raw(self, username, email):
319+
async with rx.asession() as asession:
320+
await asession.execute(
321+
sqlalchemy.text(
322+
"INSERT INTO user (username, email) "
323+
"VALUES (:username, :email)"
324+
),
325+
dict(username=username, email=email),
326+
)
327+
await asession.commit()
328+
329+
@rx.event(background=True)
330+
async def get_raw_users(self):
331+
async with rx.asession() as asession:
332+
result = await asession.execute("SELECT * FROM user")
333+
async with self:
334+
self.users = [list(row) for row in (await result.all())]
335+
```
336+
337+
```md alert info
338+
# Important Notes for Async Database Operations
339+
- Always use the `@rx.event(background=True)` decorator for async event handlers
340+
- Most operations against the `asession` must be awaited, including `commit()`, `execute()`, `refresh()`, and `delete()`
341+
- The `add()` method does not need to be awaited
342+
- Result objects from queries have methods like `all()` and `first()` that must be awaited
343+
- Use `async with self:` when updating state variables in background tasks
344+
```

0 commit comments

Comments
 (0)