0
# Models and Tables
1
2
Declarative model base classes with automatic table naming, bind key support, and Flask integration features. Includes table creation utilities and metadata management.
3
4
## Capabilities
5
6
### Model Base Class
7
8
The declarative base class for defining database models with Flask-SQLAlchemy enhancements.
9
10
```python { .api }
11
class Model:
12
"""
13
Base class for declarative database models.
14
15
Provides Flask-SQLAlchemy specific functionality including
16
automatic table naming and query property integration.
17
"""
18
19
__fsa__: SQLAlchemy # Internal reference to extension
20
query_class: type[Query] # Query class for model queries
21
query: Query # Query property for this model
22
23
def __repr__(self) -> str:
24
"""String representation showing model name and primary key."""
25
```
26
27
### Query Property Implementation
28
29
Internal class that implements the `query` property on model classes.
30
31
```python { .api }
32
class _QueryProperty:
33
"""
34
A class property that creates a query object for a model.
35
36
This is the internal implementation behind Model.query. It creates
37
a Query instance bound to the model class and current session.
38
39
Note: This is an internal class but is documented for completeness
40
since users interact with it through Model.query.
41
"""
42
43
def __get__(self, obj: Model | None, cls: type[Model]) -> Query:
44
"""
45
Create and return a Query instance for the model class.
46
47
Parameters:
48
- obj: Model instance (unused, queries are class-level)
49
- cls: Model class to create query for
50
51
Returns:
52
Query instance bound to the model class and current session
53
"""
54
```
55
56
### Table Class
57
58
Enhanced Table class that automatically selects metadata based on bind keys.
59
60
```python { .api }
61
class Table(sa.Table):
62
def __init__(
63
self,
64
name: str,
65
*args: sa_sql_schema.SchemaItem,
66
bind_key: str | None = None,
67
**kwargs: Any,
68
) -> None:
69
"""
70
Create a table with automatic metadata selection.
71
72
Parameters:
73
- name: Table name
74
- args: Columns, constraints, and other schema items
75
- bind_key: Bind key to select appropriate metadata
76
- kwargs: Additional table arguments
77
"""
78
```
79
80
### Bind Key Mixins
81
82
Mixins that provide automatic metadata selection based on model bind keys.
83
84
```python { .api }
85
class BindMixin:
86
"""
87
DeclarativeBase mixin to set model metadata based on __bind_key__.
88
89
For SQLAlchemy 2.x declarative base classes.
90
"""
91
92
__fsa__: SQLAlchemy
93
metadata: sa.MetaData
94
95
@classmethod
96
def __init_subclass__(cls, **kwargs: dict[str, Any]) -> None:
97
"""Set metadata based on __bind_key__ if present."""
98
99
class BindMetaMixin(type):
100
"""
101
Metaclass mixin for automatic bind key metadata assignment.
102
103
For SQLAlchemy 1.x declarative metaclass usage.
104
"""
105
106
def __init__(
107
cls,
108
name: str,
109
bases: tuple[type, ...],
110
d: dict[str, Any],
111
**kwargs: Any,
112
) -> None:
113
"""Set metadata based on __bind_key__ during class creation."""
114
```
115
116
### Table Naming Mixins
117
118
Mixins that provide automatic table name generation from class names.
119
120
```python { .api }
121
class NameMixin:
122
"""
123
DeclarativeBase mixin for automatic __tablename__ generation.
124
125
Converts CamelCase model names to snake_case table names.
126
For SQLAlchemy 2.x declarative base classes.
127
"""
128
129
@classmethod
130
def __init_subclass__(cls, **kwargs: dict[str, Any]) -> None:
131
"""Generate __tablename__ if not explicitly set."""
132
133
@classmethod
134
def __table_cls__(cls, *args: Any, **kwargs: Any) -> sa.Table | None:
135
"""Determine final table object for the model."""
136
137
class NameMetaMixin(type):
138
"""
139
Metaclass mixin for automatic table naming.
140
141
For SQLAlchemy 1.x declarative metaclass usage.
142
"""
143
144
def __init__(
145
cls,
146
name: str,
147
bases: tuple[type, ...],
148
d: dict[str, Any],
149
**kwargs: Any,
150
) -> None:
151
"""Generate __tablename__ during class creation."""
152
153
def __table_cls__(cls, *args: Any, **kwargs: Any) -> sa.Table | None:
154
"""Determine final table object for the model."""
155
```
156
157
### Default Metaclasses
158
159
Combined metaclasses that provide both bind key and naming functionality.
160
161
```python { .api }
162
class DefaultMeta(BindMetaMixin, NameMetaMixin, sa_orm.DeclarativeMeta):
163
"""
164
SQLAlchemy declarative metaclass with __bind_key__ and __tablename__ support.
165
166
Combines bind key metadata selection and automatic table naming.
167
"""
168
169
class DefaultMetaNoName(BindMetaMixin, sa_orm.DeclarativeMeta):
170
"""
171
SQLAlchemy declarative metaclass with __bind_key__ support only.
172
173
Provides bind key functionality without automatic table naming.
174
"""
175
```
176
177
### Utility Functions
178
179
Helper functions for table naming and model configuration.
180
181
```python { .api }
182
def should_set_tablename(cls: type) -> bool:
183
"""
184
Determine whether __tablename__ should be generated for a model.
185
186
Parameters:
187
- cls: Model class to check
188
189
Returns:
190
True if __tablename__ should be auto-generated
191
"""
192
193
def camel_to_snake_case(name: str) -> str:
194
"""
195
Convert CamelCase name to snake_case.
196
197
Parameters:
198
- name: CamelCase string to convert
199
200
Returns:
201
snake_case version of the name
202
"""
203
```
204
205
## Usage Examples
206
207
### Basic Model Definition
208
209
```python
210
class User(db.Model):
211
id = db.Column(db.Integer, primary_key=True)
212
username = db.Column(db.String(80), unique=True, nullable=False)
213
email = db.Column(db.String(120), unique=True, nullable=False)
214
215
# Table name automatically generated as 'user'
216
```
217
218
### SQLAlchemy 2.x Style Models
219
220
```python
221
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
222
223
class Base(DeclarativeBase):
224
pass
225
226
db = SQLAlchemy(app, model_class=Base)
227
228
class User(db.Model):
229
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
230
username: Mapped[str] = mapped_column(db.String(80), unique=True)
231
```
232
233
### Multiple Database Models
234
235
```python
236
class User(db.Model):
237
__bind_key__ = 'users'
238
id = db.Column(db.Integer, primary_key=True)
239
username = db.Column(db.String(80), unique=True)
240
241
class Log(db.Model):
242
__bind_key__ = 'logs'
243
id = db.Column(db.Integer, primary_key=True)
244
message = db.Column(db.Text)
245
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
246
```
247
248
### Custom Table Names
249
250
```python
251
class UserAccount(db.Model):
252
__tablename__ = 'accounts' # Override automatic naming
253
id = db.Column(db.Integer, primary_key=True)
254
name = db.Column(db.String(100))
255
```
256
257
### Direct Table Creation
258
259
```python
260
users_table = db.Table(
261
'users',
262
db.Column('id', db.Integer, primary_key=True),
263
db.Column('name', db.String(50)),
264
bind_key='users' # Specify bind key for metadata selection
265
)
266
```
267
268
### Disabling Automatic Naming
269
270
```python
271
# Disable automatic table naming globally
272
db = SQLAlchemy(app, disable_autonaming=True)
273
274
class User(db.Model):
275
__tablename__ = 'users' # Must specify explicitly
276
id = db.Column(db.Integer, primary_key=True)
277
```
278
279
## Model Querying
280
281
All models automatically get a `query` property for database queries:
282
283
```python
284
# Query using the model's query property
285
user = User.query.get(1)
286
users = User.query.filter_by(active=True).all()
287
288
# The query property uses the configured query_class
289
users = User.query.filter(User.age > 18).paginate(page=1, per_page=10)
290
```