@@ -18,6 +18,17 @@ core grammars. Besides GINO can be used in a completely `non-ORM way
18
18
<schema.html#gino-core> `__.
19
19
20
20
21
+ Can I use features of SQLAlchemy ORM?
22
+ -------------------------------------
23
+
24
+ SQLAlchemy has `two parts <https://docs.sqlalchemy.org/en/13/ >`__:
25
+
26
+ * SQLAlchemy Core
27
+ * SQLAlchemy ORM
28
+
29
+ GINO is built on top of SQLAlchemy Core, so SQLAlchemy ORM won't work in GINO.
30
+
31
+
21
32
How to join?
22
33
------------
23
34
@@ -58,3 +69,276 @@ If you are using Python 3.7, then ``aiocontextvars`` does nothing at all.
58
69
59
70
This answer is for GINO 0.8 and later, please check earlier versions of
60
71
this documentation if you are using GINO 0.7.
72
+
73
+
74
+ How to define relationships?
75
+ ----------------------------
76
+
77
+ GINO 1.0 or lower doesn't provide relationship definition feature found in typical ORMs.
78
+ However, you could always manually define your tables, design your queries and load the
79
+ results explicitly in GINO. Please see :doc: `loaders ` for more information.
80
+
81
+
82
+ How to define index with multiple columns?
83
+ ------------------------------------------
84
+
85
+ ::
86
+
87
+ class User(db.Model):
88
+ __tablename__ = 'users'
89
+
90
+ first_name = db.Column(db.Unicode())
91
+ last_name = db.Column(db.Unicode())
92
+
93
+ _name_idx = db.Index('index_on_name', 'first_name', 'last_name')
94
+
95
+ The ``_name_idx `` is not used.
96
+
97
+
98
+ Is there a django admin interface for GINO?
99
+ -------------------------------------------
100
+
101
+ Not quite yet, please follow `this discussion
102
+ <https://github.com/python-gino/gino/issues/260> `__.
103
+
104
+
105
+ How to use multiple databases for different users on the fly?
106
+ -------------------------------------------------------------
107
+
108
+ GINO models are linked to a :class: `~gino.api.Gino ` instance, while
109
+ :class: `~gino.api.Gino ` has an optional property ``bind `` to hold a
110
+ :class: `~gino.engine.GinoEngine ` instance. So when you are executing::
111
+
112
+ user = await User.get(request.user_id)
113
+
114
+ The ``bind `` is implicitly used to execute the query.
115
+
116
+ In order to use multiple databases, you would need multiple
117
+ :class: `~gino.engine.GinoEngine ` instances. Here's a full example using FastAPI with
118
+ lazy engine creation::
119
+
120
+ from asyncio import Future
121
+ from contextvars import ContextVar
122
+
123
+ from fastapi import FastAPI, Request
124
+ from gino import create_engine
125
+ from gino.ext.starlette import Gino
126
+
127
+ engines = {}
128
+ dbname = ContextVar("dbname")
129
+
130
+
131
+ class ContextualGino(Gino):
132
+ @property
133
+ def bind(self):
134
+ e = engines.get(dbname.get(""))
135
+ if e and e.done():
136
+ return e.result()
137
+ else:
138
+ return self._bind
139
+
140
+ @bind.setter
141
+ def bind(self, val):
142
+ self._bind = val
143
+
144
+
145
+ app = FastAPI()
146
+ db = ContextualGino(app)
147
+
148
+
149
+ @app.middleware("http")
150
+ async def lazy_engines(request: Request, call_next):
151
+ name = request.query_params.get("db", "postgres")
152
+ fut = engines.get(name)
153
+ if fut is None:
154
+ fut = engines[name] = Future()
155
+ try:
156
+ engine = await create_engine("postgresql://localhost/" + name)
157
+ except Exception as e:
158
+ fut.set_exception(e)
159
+ del engines[name]
160
+ raise
161
+ else:
162
+ fut.set_result(engine)
163
+ await fut
164
+ dbname.set(name)
165
+ return await call_next(request)
166
+
167
+
168
+ @app.get("/")
169
+ async def get():
170
+ return dict(dbname=await db.scalar("SELECT current_database()"))
171
+
172
+
173
+ How to load complex query results?
174
+ ----------------------------------
175
+
176
+ The API doc of :mod: `gino.loader ` explains the available loaders, and there're a few
177
+ examples in :doc: `loaders ` too.
178
+
179
+ Below is an example with a joined result to load both a GINO model and an integer at the
180
+ same time, using a :class: `~gino.loader.TupleLoader ` with two sub-loaders -
181
+ :class: `~gino.loader.ModelLoader ` and :class: `~gino.loader.ColumnLoader `::
182
+
183
+ import asyncio
184
+ import random
185
+ import string
186
+
187
+ import gino
188
+ from gino.loader import ColumnLoader
189
+
190
+ db = gino.Gino()
191
+
192
+
193
+ class User(db.Model):
194
+ __tablename__ = 'users'
195
+
196
+ id = db.Column(db.Integer(), primary_key=True)
197
+ name = db.Column(db.Unicode())
198
+
199
+
200
+ class Visit(db.Model):
201
+ __tablename__ = 'visits'
202
+
203
+ id = db.Column(db.Integer(), primary_key=True)
204
+ time = db.Column(db.DateTime(), server_default='now()')
205
+ user_id = db.Column(db.ForeignKey('users.id'))
206
+
207
+
208
+ async def main():
209
+ async with db.with_bind('postgresql://localhost/gino'):
210
+ await db.gino.create_all()
211
+
212
+ for i in range(random.randint(5, 10)):
213
+ u = await User.create(
214
+ name=''.join(random.choices(string.ascii_letters, k=10)))
215
+ for v in range(random.randint(10, 20)):
216
+ await Visit.create(user_id=u.id)
217
+
218
+ visits = db.func.count(Visit.id)
219
+ q = db.select([
220
+ User,
221
+ visits,
222
+ ]).select_from(
223
+ User.outerjoin(Visit)
224
+ ).group_by(
225
+ *User,
226
+ ).gino.load((User, ColumnLoader(visits)))
227
+ async with db.transaction():
228
+ async for user, visits in q.iterate():
229
+ print(user.name, visits)
230
+
231
+ await db.gino.drop_all()
232
+
233
+
234
+ asyncio.run(main())
235
+
236
+ Be ware of the :class: `tuple ` in ``.gino.load((...)) ``.
237
+
238
+
239
+ How to do bulk insert / update?
240
+ -------------------------------
241
+
242
+ TBD #314
243
+
244
+
245
+ How to print the executed SQL?
246
+ ------------------------------
247
+
248
+ GINO uses the same approach from SQLAlchemy: ``create_engine(..., echo=True) ``.
249
+ (Or ``db.set_bind(..., echo=True) ``) Please see also `here
250
+ <https://docs.sqlalchemy.org/en/13/core/engines.html#sqlalchemy.create_engine.params.echo> `__.
251
+
252
+ If you use any extension, you can also set that in config, by `db_echo ` or `DB_ECHO `.
253
+
254
+
255
+ How to run ``EXISTS `` SQL?
256
+ --------------------------
257
+
258
+ ::
259
+
260
+ await db.scalar(db.exists().where(User.email == email).select())
261
+
262
+
263
+ How to work with Alembic?
264
+ -------------------------
265
+
266
+ The fact that :class: `~gino.api.Gino ` is a :class: `~sqlalchemy.schema.MetaData ` is the
267
+ key to use Alembic. Just import and set ``target_metadata = db `` in Alembic ``env.py ``
268
+ will do. See :doc: `alembic ` for more details.
269
+
270
+
271
+ How to join the same table twice?
272
+ ---------------------------------
273
+
274
+ This is the same pattern as described in SQLAlchemy :ref: `self_referential `, where you
275
+ have a table with "a foreign key reference to itself", or join the same table more than
276
+ once, "to represent hierarchical data in flat tables." We'd need to use
277
+ :func: `~gino.crud.CRUDModel.alias `, for example::
278
+
279
+ class User(db.Model):
280
+ __tablename__ = "users"
281
+
282
+ id = db.Column(db.Integer, primary_key=True)
283
+ parent_id = db.Column(db.ForeignKey("users.id"))
284
+
285
+ Parent = User.alias()
286
+ query = User.outerjoin(Parent, User.parent_id == Parent.id).select()
287
+ users = await query.gino.load(User.load(parent=Parent)).all()
288
+
289
+
290
+ .. _raw-sql :
291
+
292
+ How to execute raw SQL with parameters?
293
+ ---------------------------------------
294
+
295
+ Wrap the SQL with :func: `~sqlalchemy.sql.expression.text `, and use keyword arguments::
296
+
297
+ query = db.text('SELECT * FROM users WHERE id = :id_val')
298
+ row = await db.first(query, id_val=1)
299
+
300
+ You may even load the rows into model instances::
301
+
302
+ query = query.execution_options(loader=User)
303
+ user = await db.first(query, id_val=1)
304
+
305
+
306
+ Gino engine is not initialized?
307
+ -------------------------------
308
+
309
+ GINO models are linked to a :class: `~gino.api.Gino ` instance, while
310
+ :class: `~gino.api.Gino ` has an optional property ``bind `` to hold a
311
+ :class: `~gino.engine.GinoEngine ` instance. So when you are executing::
312
+
313
+ user = await User.get(request.user_id)
314
+
315
+ The ``bind `` is implicitly used to execute the query. If ``bind `` is not set before
316
+ this, you'll see this error:
317
+
318
+ .. code-block :: text
319
+
320
+ gino.exceptions.UninitializedError: Gino engine is not initialized.
321
+
322
+ You could use either:
323
+
324
+ * Call :meth: `~gino.api.Gino.set_bind ` or :meth: `~gino.api.Gino.with_bind ` to set the
325
+ bind on the :class: `~gino.api.Gino ` instance.
326
+ * Use one of the Web framework extensions to set the bind for you in usually the server
327
+ start-up hook.
328
+ * Use explicit ``bind `` for each execution, for example::
329
+
330
+ engine = await create_engine("...")
331
+ # ...
332
+ user = await User.get(request.user_id, bind=engine)
333
+
334
+
335
+ How can I do SQL xxxx in GINO?
336
+ ------------------------------
337
+
338
+ GINO uses `SQLAlchemy Core <https://docs.sqlalchemy.org/en/13/core/ >`__ queries, so
339
+ please check its documentation on how to build queries. The GINO models are simply
340
+ wrappers of SQLAlchemy :class: `~sqlalchemy.schema.Table ` instances, and the column
341
+ attributes on GINO model classes are just SQLAlchemy :class: `~sqlalchemy.schema.Column `
342
+ instances, you can use them in building your SQLAlchemy Core queries.
343
+
344
+ Alternatively, you could always execute the raw SQL directly, see :ref: `raw-sql ` above.
0 commit comments