فهرست منبع

Merge pull request #1114 from zauberzeug/tortoise-orm

Using Tortoise ORM for database example
Rodja Trappe 1 سال پیش
والد
کامیت
4d559a43de

+ 1 - 0
.gitignore

@@ -9,3 +9,4 @@ tests/media/
 venv
 .idea
 .nicegui/
+*.sqlite*

+ 1 - 1
examples/sqlite_database/.gitignore

@@ -1 +1 @@
-*.db
+*.sqlite*

+ 42 - 70
examples/sqlite_database/main.py

@@ -1,79 +1,51 @@
 #!/usr/bin/env python3
-import sqlite3
-from pathlib import Path
-from typing import Any, Dict
+from typing import List
 
-from nicegui import ui
+import models
+from tortoise.contrib.fastapi import register_tortoise
 
-DB_FILE = Path(__file__).parent / 'users.db'
-DB_FILE.touch()
-conn = sqlite3.connect(DB_FILE, check_same_thread=False)
-cursor = conn.cursor()
-cursor.execute('CREATE TABLE IF NOT EXISTS users (id integer primary key AUTOINCREMENT, name text, age integer)')
-conn.commit()
+from nicegui import app, ui
 
 
-@ui.refreshable
-def users_ui() -> None:
-    cursor.execute('SELECT * FROM users')
-    for row in cursor.fetchall():
-        user = {'id': row[0], 'name': row[1], 'age': row[2]}
-        with ui.card():
-            with ui.row().classes('justify-between w-full'):
-                ui.label(user['id'])
-                ui.label(user['name'])
-                ui.label(user['age'])
-            with ui.row():
-                ui.button('edit', on_click=lambda user=user: open_dialog(user))
-                ui.button('delete', on_click=lambda user=user: delete(user), color='red')
-
-
-def create() -> None:
-    cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', (name.value, age.value))
-    conn.commit()
-    ui.notify(f'Created new user {name.value}')
-    name.value = ''
-    age.value = None
-    users_ui.refresh()
-
-
-def update() -> None:
-    query = 'UPDATE users SET name=?, age=? WHERE id=?'
-    cursor.execute(query, (dialog_name.value, dialog_age.value, dialog_id))
-    conn.commit()
-    ui.notify(f'Updated user {dialog_name.value}')
-    dialog.close()
-    users_ui.refresh()
-
+register_tortoise(
+    app,
+    db_url='sqlite://db.sqlite3',
+    modules={'models': ['models']},  # tortoise will look for models in this main module
+    generate_schemas=True,  # in production you should use version control migrations instead
+)
 
-def delete(user: Dict[str, Any]) -> None:
-    cursor.execute('DELETE from users WHERE id=?', (user['id'],))
-    conn.commit()
-    ui.notify(f'Deleted user {user["name"]}')
-    users_ui.refresh()
 
-
-def open_dialog(user: Dict[str, Any]) -> None:
-    global dialog_id
-    dialog_id = user['id']
-    dialog_name.value = user['name']
-    dialog_age.value = user['age']
-    dialog.open()
-
-
-name = ui.input(label='Name')
-age = ui.number(label='Age', format='%.0f')
-ui.button('Add new user', on_click=create)
-
-users_ui()
-
-with ui.dialog() as dialog:
-    with ui.card():
-        dialog_id = None
-        dialog_name = ui.input('Name')
-        dialog_age = ui.number('Age', format='%.0f')
-        with ui.row():
-            ui.button('Save', on_click=update)
-            ui.button('Close', on_click=dialog.close).props('outline')
+@ui.refreshable
+async def list_of_users() -> None:
+    async def delete(user: models.User) -> None:
+        await user.delete()
+        list_of_users.refresh()
+
+    users: List[models.User] = await models.User.all()
+    for user in reversed(users):
+        with ui.card().classes('w-full'):
+            with ui.row().classes('justify-between w-full items-center'):
+                ui.input('Name', on_change=lambda u=user: u.save()).bind_value(user, 'name') \
+                    .on('blur', list_of_users.refresh)
+                ui.input('Age', on_change=lambda u=user: u.save()).classes('w-20').bind_value(user, 'age') \
+                    .on('blur', list_of_users.refresh)
+                ui.button(on_click=lambda u=user: delete(u), icon='delete') \
+                    .props('flat').classes('ml-auto')
+
+
+@ui.page('/')
+async def index():
+    async def create() -> None:
+        await models.User.create(name=name.value, age=age.value)
+        name.value = ''
+        age.value = None
+        list_of_users.refresh()
+
+    with ui.column().classes('w-96 mx-auto'):
+        with ui.row().classes('w-full items-center px-4'):
+            name = ui.input(label='Name')
+            age = ui.number(label='Age', format='%.0f').classes('w-20')
+            ui.button(on_click=create, icon='add').props('flat').classes('ml-auto')
+        await list_of_users()
 
 ui.run()

+ 7 - 0
examples/sqlite_database/models.py

@@ -0,0 +1,7 @@
+from tortoise import fields, models
+
+
+class User(models.Model):
+    id = fields.IntField(pk=True)
+    name = fields.CharField(max_length=255)
+    age = fields.IntField()

+ 1 - 0
examples/sqlite_database/requirements.txt

@@ -0,0 +1 @@
+tortoise-orm

+ 1 - 1
main.py

@@ -281,7 +281,7 @@ async def index_page(client: Client) -> None:
             example_link('Single Page App', 'navigate without reloading the page')
             example_link('Chat App', 'a simple chat app')
             example_link('Chat with AI', 'a simple chat app with AI')
-            example_link('SQLite Database', 'CRUD operations on a SQLite database')
+            example_link('SQLite Database', 'CRUD operations on a SQLite database with async-support through Tortoise ORM')
             example_link('Pandas DataFrame', 'displays an editable [pandas](https://pandas.pydata.org) DataFrame')
             example_link('Lightbox', 'A thumbnail gallery where each image can be clicked to enlarge')
             example_link('ROS2', 'Using NiceGUI as web interface for a ROS2 robot')