@@ -21,29 +21,26 @@ joined-table inheritance.
21
21
22
22
.. code-block :: python
23
23
24
- from flask_sqlalchemy.model import Model
25
- import sqlalchemy as sa
26
- import sqlalchemy.orm
24
+ from sqlalchemy import Integer, String, ForeignKey
25
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
27
26
28
- class IdModel (Model ):
29
- @sa.orm.declared_attr
27
+ class Base (DeclarativeBase ):
28
+ @declared_attr.cascading
29
+ @ classmethod
30
30
def id (cls ):
31
31
for base in cls .__mro__ [1 :- 1 ]:
32
32
if getattr (base, " __table__" , None ) is not None :
33
- type = sa.ForeignKey(base.id)
34
- break
35
- else :
36
- type = sa.Integer
33
+ return mapped_column(ForeignKey(base.id), primary_key = True )
34
+ else :
35
+ return mapped_column(Integer, primary_key = True )
37
36
38
- return sa.Column(type , primary_key = True )
39
-
40
- db = SQLAlchemy(model_class = IdModel)
37
+ db = SQLAlchemy(app, model_class = Base)
41
38
42
39
class User (db .Model ):
43
- name = db.Column(db. String)
40
+ name: Mapped[ str ] = mapped_column( String)
44
41
45
42
class Employee (User ):
46
- title = db.Column(db. String)
43
+ title: Mapped[ str ] = mapped_column( String)
47
44
48
45
49
46
Abstract Models and Mixins
@@ -56,28 +53,49 @@ they are created or updated.
56
53
.. code-block :: python
57
54
58
55
from datetime import datetime
56
+ from sqlalchemy import DateTime, Integer, String
57
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
59
58
60
59
class TimestampModel (db .Model ):
61
60
__abstract__ = True
62
- created = db.Column(db. DateTime, nullable = False , default = datetime.utcnow)
63
- updated = db.Column(db. DateTime, onupdate = datetime.utcnow)
61
+ created: Mapped[datetime] = mapped_column( DateTime, nullable = False , default = datetime.utcnow)
62
+ updated: Mapped[datetime] = mapped_column( DateTime, default = datetime.utcnow , onupdate = datetime.utcnow)
64
63
65
64
class Author (db .Model ):
66
- ...
65
+ id : Mapped[int ] = mapped_column(Integer, primary_key = True )
66
+ username: Mapped[str ] = mapped_column(String, unique = True , nullable = False )
67
67
68
68
class Post (TimestampModel ):
69
- ...
69
+ id : Mapped[int ] = mapped_column(Integer, primary_key = True )
70
+ title: Mapped[str ] = mapped_column(String, nullable = False )
70
71
71
72
This can also be done with a mixin class, inheriting from ``db.Model `` separately.
72
73
73
74
.. code-block :: python
74
75
75
76
class TimestampMixin :
76
- created = db.Column(db. DateTime, nullable = False , default = datetime.utcnow)
77
- updated = db.Column(db. DateTime, onupdate = datetime.utcnow)
77
+ created: Mapped[datetime] = mapped_column( DateTime, nullable = False , default = datetime.utcnow)
78
+ updated: Mapped[datetime] = mapped_column( DateTime, default = datetime.utcnow , onupdate = datetime.utcnow)
78
79
79
80
class Post (TimestampMixin , db .Model ):
80
- ...
81
+ id : Mapped[int ] = mapped_column(Integer, primary_key = True )
82
+ title: Mapped[str ] = mapped_column(String, nullable = False )
83
+
84
+
85
+ Disabling Table Name Generation
86
+ -------------------------------
87
+
88
+ Some projects prefer to set each model's ``__tablename__ `` manually rather than relying
89
+ on Flask-SQLAlchemy's detection and generation. The simple way to achieve that is to
90
+ set each ``__tablename__ `` and not modify the base class. However, the table name
91
+ generation can be disabled by setting `disable_autonaming=True ` in the `SQLAlchemy ` constructor.
92
+
93
+ .. code-block :: python
94
+
95
+ class Base (sa_orm .DeclarativeBase ):
96
+ pass
97
+
98
+ db = SQLAlchemy(app, model_class = Base, disable_autonaming = True )
81
99
82
100
83
101
Session Class
@@ -158,73 +176,3 @@ To customize only ``session.query``, pass the ``query_cls`` key to the
158
176
.. code-block :: python
159
177
160
178
db = SQLAlchemy(session_options = {" query_cls" : GetOrQuery})
161
-
162
-
163
- Model Metaclass
164
- ---------------
165
-
166
- .. warning ::
167
- Metaclasses are an advanced topic, and you probably don't need to customize them to
168
- achieve what you want. It is mainly documented here to show how to disable table
169
- name generation.
170
-
171
- The model metaclass is responsible for setting up the SQLAlchemy internals when defining
172
- model subclasses. Flask-SQLAlchemy adds some extra behaviors through mixins; its default
173
- metaclass, :class: `~.DefaultMeta `, inherits them all.
174
-
175
- - :class: `.BindMetaMixin `: ``__bind_key__ `` sets the bind to use for the model.
176
- - :class: `.NameMetaMixin `: If the model does not specify a ``__tablename__ `` but does
177
- specify a primary key, a name is automatically generated.
178
-
179
- You can add your own behaviors by defining your own metaclass and creating the
180
- declarative base yourself. Be sure to still inherit from the mixins you want (or just
181
- inherit from the default metaclass).
182
-
183
- Passing a declarative base class instead of a simple model base class to ``model_class ``
184
- will cause Flask-SQLAlchemy to use this base instead of constructing one with the
185
- default metaclass.
186
-
187
- .. code-block :: python
188
-
189
- from sqlalchemy.orm import declarative_base
190
- from flask_sqlalchemy import SQLAlchemy
191
- from flask_sqlalchemy.model import DefaultMeta, Model
192
-
193
- class CustomMeta (DefaultMeta ):
194
- def __init__ (cls , name , bases , d ):
195
- # custom class setup could go here
196
-
197
- # be sure to call super
198
- super (CustomMeta, cls ).__init__ (name, bases, d)
199
-
200
- # custom class-only methods could go here
201
-
202
- CustomModel = declarative_base(cls = Model, metaclass = CustomMeta, name = " Model" )
203
- db = SQLAlchemy(model_class = CustomModel)
204
-
205
- You can also pass whatever other arguments you want to
206
- :func: `~sqlalchemy.orm.declarative_base ` to customize the base class.
207
-
208
-
209
- Disabling Table Name Generation
210
- ```````````````````````````````
211
-
212
- Some projects prefer to set each model's ``__tablename__ `` manually rather than relying
213
- on Flask-SQLAlchemy's detection and generation. The simple way to achieve that is to
214
- set each ``__tablename__ `` and not modify the base class. However, the table name
215
- generation can be disabled by defining a custom metaclass with only the
216
- ``BindMetaMixin `` and not the ``NameMetaMixin ``.
217
-
218
- .. code-block :: python
219
-
220
- from sqlalchemy.orm import DeclarativeMeta, declarative_base
221
- from flask_sqlalchemy.model import BindMetaMixin, Model
222
-
223
- class NoNameMeta (BindMetaMixin , DeclarativeMeta ):
224
- pass
225
-
226
- CustomModel = declarative_base(cls = Model, metaclass = NoNameMeta, name = " Model" )
227
- db = SQLAlchemy(model_class = CustomModel)
228
-
229
- This creates a base that still supports the ``__bind_key__ `` feature but does not
230
- generate table names.
0 commit comments