@@ -18,10 +18,11 @@ It allows you to make queries using the powerful [SQLAlchemy Core][sqlalchemy-co
1818expression language, and provides support for PostgreSQL, MySQL, and SQLite.
1919
2020Databases is suitable for integrating against any async Web framework, such as [ Starlette] [ starlette ] ,
21- [ Sanic] [ sanic ] , [ Responder] [ responder ] , [ Quart] [ quart ] , [ aiohttp] [ aiohttp ] , [ Tornado] [ tornado ] , [ FastAPI] [ fastapi ] ,
22- or [ Bocadillo] [ bocadillo ] .
21+ [ Sanic] [ sanic ] , [ Responder] [ responder ] , [ Quart] [ quart ] , [ aiohttp] [ aiohttp ] , [ Tornado] [ tornado ] , [ FastAPI] [ fastapi ] , or [ Bocadillo] [ bocadillo ] .
2322
24- ** Community** : https://discuss.encode.io/c/databases
23+ ** Documentation** : [ https://www.encode.io/databases/ ] ( https://www.encode.io/databases/ )
24+
25+ ** Community** : [ https://discuss.encode.io/c/databases ] ( https://discuss.encode.io/c/databases )
2526
2627** Requirements** : Python 3.6+
2728
@@ -43,278 +44,51 @@ $ pip install databases[sqlite]
4344
4445Driver support is providing using one of [ asyncpg] [ asyncpg ] , [ aiomysql] [ aiomysql ] , or [ aiosqlite] [ aiosqlite ] .
4546
46- ## Getting started
47-
48- ** Note** : Use ` ipython ` to try these example from the console, since it supports ` await ` .
49-
50- Declare your tables using SQLAlchemy:
51-
52- ``` python
53- import sqlalchemy
47+ ---
5448
49+ ## Quickstart
5550
56- metadata = sqlalchemy.MetaData()
51+ For this example we'll create a very simple SQLite database to run some
52+ queries against.
5753
58- notes = sqlalchemy.Table(
59- " notes" ,
60- metadata,
61- sqlalchemy.Column(" id" , sqlalchemy.Integer, primary_key = True ),
62- sqlalchemy.Column(" text" , sqlalchemy.String(length = 100 )),
63- sqlalchemy.Column(" completed" , sqlalchemy.Boolean),
64- )
54+ ``` shell
55+ $ pip install databases[sqlite]
56+ $ pip install ipython
6557```
6658
59+ We can now run a simple example from the console.
6760
68- You can use any of the sqlalchemy column types such as ` sqlalchemy.JSON ` , or
69- custom column types.
70-
71- ## Queries
72-
73- You can now use any [ SQLAlchemy core] [ sqlalchemy-core ] queries ([ official tutorial] [ sqlalchemy-core-tutorial ] ).
61+ Note that we want to use ` ipython ` here, because it supports using ` await `
62+ expressions directly from the console.
7463
7564``` python
65+ # Create a database instance, and connect to it.
7666from databases import Database
77-
78- database = Database(' postgresql://localhost/example' )
79-
80-
81- # Establish the connection pool
67+ database = Database(' sqlite:///example.db' )
8268await database.connect()
8369
84- # Execute
85- query = notes.insert()
86- values = {" text" : " example1" , " completed" : True }
87- await database.execute(query = query, values = values)
70+ # Create a table.
71+ query = """ CREATE TABLE HighScores (id INTEGER PRIMARY KEY, name VARCHAR(100), score INTEGER)"""
72+ await database.execute(query = query)
8873
89- # Execute many
90- query = notes.insert()
74+ # Insert some data.
75+ query = " INSERT INTO HighScores(name, score) VALUES (:name, :score) "
9176values = [
92- {" text" : " example2" , " completed" : False },
93- {" text" : " example3" , " completed" : True },
77+ {" name" : " Daisy" , " score" : 92 },
78+ {" name" : " Neil" , " score" : 87 },
79+ {" name" : " Carol" , " score" : 43 },
9480]
9581await database.execute_many(query = query, values = values)
9682
97- # Fetch multiple rows
98- query = notes.select()
83+ # Run a database query.
84+ query = " SELECT * FROM HighScores "
9985rows = await database.fetch_all(query = query)
100-
101- # Fetch single row
102- query = notes.select()
103- row = await database.fetch_one(query = query)
104-
105- # Fetch single value, defaults to `column=0`.
106- query = notes.select()
107- value = await database.fetch_val(query = query)
108-
109- # Fetch multiple rows without loading them all into memory at once
110- query = notes.select()
111- async for row in database.iterate(query = query):
112- ...
113-
114- # Close all connection in the connection pool
115- await database.disconnect()
116- ```
117-
118- Connections are managed as task-local state, with driver implementations
119- transparently using connection pooling behind the scenes.
120-
121- ## Raw queries
122-
123- In addition to SQLAlchemy core queries, you can also perform raw SQL queries:
124-
125- ``` python
126- # Execute
127- query = " INSERT INTO notes(text, completed) VALUES (:text, :completed)"
128- values = {" text" : " example1" , " completed" : True }
129- await database.execute(query = query, values = values)
130-
131- # Execute many
132- query = " INSERT INTO notes(text, completed) VALUES (:text, :completed)"
133- values = [
134- {" text" : " example2" , " completed" : False },
135- {" text" : " example3" , " completed" : True },
136- ]
137- await database.execute_many(query = query, values = values)
138-
139- # Fetch multiple rows
140- query = " SELECT * FROM notes WHERE completed = :completed"
141- rows = await database.fetch_all(query = query, values = {" completed" : True })
142-
143- # Fetch single row
144- query = " SELECT * FROM notes WHERE id = :id"
145- result = await database.fetch_one(query = query, values = {" id" : 1 })
146- ```
147-
148- Note that query arguments should follow the ` :query_arg ` style.
149-
150- ## Transactions
151-
152- Transactions are managed by async context blocks:
153-
154- ``` python
155- async with database.transaction():
156- ...
157- ```
158-
159- For a lower-level transaction API:
160-
161- ``` python
162- transaction = await database.transaction()
163- try :
164- ...
165- except :
166- transaction.rollback()
167- else :
168- transaction.commit()
169- ```
170-
171- You can also use ` .transaction() ` as a function decorator on any async function:
172-
173- ``` python
174- @database.transaction ()
175- async def create_users (request ):
176- ...
177- ```
178-
179- Transaction blocks are managed as task-local state. Nested transactions
180- are fully supported, and are implemented using database savepoints.
181-
182- ## Connecting and disconnecting
183-
184- You can control the database connect/disconnect, by using it as a async context manager.
185-
186- ``` python
187- async with Database(DATABASE_URL ) as database:
188- ...
189- ```
190-
191- Or by using explicit connection and disconnection:
192-
193- ``` python
194- database = Database(DATABASE_URL )
195- await database.connect()
196- ...
197- await database.disconnect()
198- ```
199-
200- If you're integrating against a web framework, then you'll probably want
201- to hook into framework startup or shutdown events. For example, with
202- [ Starlette] [ starlette ] you would use the following:
203-
204- ``` python
205- @app.on_event (" startup" )
206- async def startup ():
207- await database.connect()
208-
209- @app.on_event (" shutdown" )
210- async def shutdown ():
211- await database.disconnect()
212- ```
213-
214- ## Connection options
215-
216- The PostgreSQL and MySQL backends provide a few connection options for SSL
217- and for configuring the connection pool.
218-
219- ``` python
220- # Use an SSL connection.
221- database = Database(' postgresql://localhost/example?ssl=true' )
222-
223- # Use a connection pool of between 5-20 connections.
224- database = Database(' mysql://localhost/example?min_size=5&max_size=20' )
225- ```
226-
227- You can also use keyword arguments to pass in any connection options.
228- Available keyword arguments may differ between database backends.
229-
230- ``` python
231- database = Database(' postgresql://localhost/example' , ssl = True , min_size = 5 , max_size = 20 )
232- ```
233-
234- ## Test isolation
235-
236- For strict test isolation you will always want to rollback the test database
237- to a clean state between each test case:
238-
239- ``` python
240- database = Database(DATABASE_URL , force_rollback = True )
241- ```
242-
243- This will ensure that all database connections are run within a transaction
244- that rollbacks once the database is disconnected.
245-
246- If you're integrating against a web framework you'll typically want to
247- use something like the following pattern:
248-
249- ``` python
250- if TESTING :
251- database = Database(TEST_DATABASE_URL , force_rollback = True )
252- else :
253- database = Database(DATABASE_URL )
254- ```
255-
256- This will give you test cases that run against a different database to
257- the development database, with strict test isolation so long as you make sure
258- to connect and disconnect to the database between test cases.
259-
260- For a lower level API you can explicitly create force-rollback transactions:
261-
262- ``` python
263- async with database.transaction(force_rollback = True ):
264- ...
265- ```
266-
267- ## Migrations
268-
269- Because ` databases ` uses SQLAlchemy core, you can integrate with [ Alembic] [ alembic ]
270- for database migration support.
271-
272- ``` shell
273- $ pip install alembic
274- $ alembic init migrations
275- ```
276-
277- You'll want to set things up so that Alembic references the configured
278- ` DATABASE_URL ` , and uses your table metadata.
279-
280- In ` alembic.ini ` remove the following line:
281-
282- ``` shell
283- sqlalchemy.url = driver://user:pass@localhost/dbname
86+ print (' High Scores:' , rows)
28487```
28588
286- In ` migrations/env.py ` , you need to set the `` 'sqlalchemy.url' `` configuration key,
287- and the ` target_metadata ` variable. You'll want something like this:
288-
289- ``` python
290- # The Alembic Config object.
291- config = context.config
292-
293- # Configure Alembic to use our DATABASE_URL and our table definitions.
294- # These are just examples - the exact setup will depend on whatever
295- # framework you're integrating against.
296- from myapp.settings import DATABASE_URL
297- from myapp.tables import metadata
298-
299- config.set_main_option(' sqlalchemy.url' , str (DATABASE_URL ))
300- target_metadata = metadata
301-
302- ...
303- ```
304-
305- Note that migrations will use a standard synchronous database driver,
306- rather than using the async drivers that ` databases ` provides support for.
307-
308- This will also be the case if you're using SQLAlchemy's standard tooling, such
309- as using ` metadata.create_all(engine) ` to setup the database tables.
310-
311- ** Note for MySQL** :
312-
313- For MySQL you'll probably need to explicitly specify the ` pymysql ` dialect when
314- using Alembic since the default MySQL dialect does not support Python 3.
89+ Check out the documentation on [ making database queries] ( database_queries.md )
90+ for examples of how to start using databases together with SQLAlchemy core expressions.
31591
316- If you're using the ` databases.DatabaseURL ` datatype, you can obtain this using
317- ` DATABASE_URL.replace(dialect="pymysql") `
31892
31993<p align =" center " >&mdash ; ⭐️ &mdash ; </p >
32094<p align =" center " ><i >Databases is <a href =" https://github.com/encode/databases/blob/master/LICENSE.md " >BSD licensed</a > code. Designed & built in Brighton, England.</i ></p >
0 commit comments