or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

field-conversion.mdindex.mdrelationship-fields.mdschemas.md

field-conversion.mddocs/

0

# Field Conversion

1

2

Comprehensive system for converting SQLAlchemy columns, properties, and models into appropriate marshmallow fields, with support for all standard SQLAlchemy types and database-specific extensions.

3

4

**Note:** Type annotations using `Literal` require `from typing import Literal` (Python 3.8+) or `from typing_extensions import Literal` (earlier versions).

5

6

## Capabilities

7

8

### ModelConverter Class

9

10

The core conversion class that translates SQLAlchemy constructs to marshmallow fields with comprehensive type mapping and customization options.

11

12

```python { .api }

13

class ModelConverter:

14

"""Converts SQLAlchemy models into marshmallow fields.

15

16

Provides methods for converting entire models, individual properties,

17

and columns to appropriate marshmallow field types.

18

"""

19

20

def __init__(self, schema_cls: type[ma.Schema] | None = None): ...

21

22

def fields_for_model(

23

self,

24

model: type[DeclarativeMeta],

25

*,

26

include_fk: bool = False,

27

include_relationships: bool = False,

28

fields: Iterable[str] | None = None,

29

exclude: Iterable[str] | None = None,

30

base_fields: dict | None = None,

31

dict_cls: type[dict] = dict,

32

) -> dict[str, fields.Field]:

33

"""Generate dict of field_name: Field pairs for the given model.

34

35

Parameters:

36

- model: SQLAlchemy model class

37

- include_fk: Whether to include foreign key fields

38

- include_relationships: Whether to include relationship fields

39

- fields: Only include these field names (whitelist)

40

- exclude: Exclude these field names (blacklist)

41

- base_fields: Existing fields to merge with

42

- dict_cls: Dict class to use for result

43

44

Returns:

45

Dictionary mapping field names to marshmallow Field instances

46

"""

47

48

def fields_for_table(

49

self,

50

table: sa.Table,

51

*,

52

include_fk: bool = False,

53

fields: Iterable[str] | None = None,

54

exclude: Iterable[str] | None = None,

55

base_fields: dict | None = None,

56

dict_cls: type[dict] = dict,

57

) -> dict[str, fields.Field]:

58

"""Generate dict of field_name: Field pairs for the given table.

59

60

Parameters:

61

- table: SQLAlchemy Table instance

62

- include_fk: Whether to include foreign key fields

63

- fields: Only include these field names (whitelist)

64

- exclude: Exclude these field names (blacklist)

65

- base_fields: Existing fields to merge with

66

- dict_cls: Dict class to use for result

67

68

Returns:

69

Dictionary mapping field names to marshmallow Field instances

70

"""

71

72

@overload

73

def property2field(

74

self,

75

prop: MapperProperty,

76

*,

77

instance: Literal[True] = ...,

78

field_class: type[fields.Field] | None = ...,

79

**kwargs,

80

) -> fields.Field: ...

81

82

@overload

83

def property2field(

84

self,

85

prop: MapperProperty,

86

*,

87

instance: Literal[False] = ...,

88

field_class: type[fields.Field] | None = ...,

89

**kwargs,

90

) -> type[fields.Field]: ...

91

92

def property2field(

93

self,

94

prop: MapperProperty,

95

*,

96

instance: bool = True,

97

field_class: type[fields.Field] | None = None,

98

**kwargs,

99

) -> fields.Field | type[fields.Field]:

100

"""Convert SQLAlchemy Property to marshmallow field.

101

102

Parameters:

103

- prop: SQLAlchemy Property (column property or relationship)

104

- instance: If True, return Field instance; if False, return Field class

105

- field_class: Override field class to use

106

- **kwargs: Additional field constructor arguments

107

108

Returns:

109

Field instance or class depending on instance parameter

110

"""

111

112

@overload

113

def column2field(

114

self,

115

column: sa.Column,

116

*,

117

instance: Literal[True] = ...,

118

**kwargs

119

) -> fields.Field: ...

120

121

@overload

122

def column2field(

123

self,

124

column: sa.Column,

125

*,

126

instance: Literal[False] = ...,

127

**kwargs

128

) -> type[fields.Field]: ...

129

130

def column2field(

131

self,

132

column: sa.Column,

133

*,

134

instance: bool = True,

135

**kwargs

136

) -> fields.Field | type[fields.Field]:

137

"""Convert SQLAlchemy Column to marshmallow field.

138

139

Parameters:

140

- column: SQLAlchemy Column instance

141

- instance: If True, return Field instance; if False, return Field class

142

- **kwargs: Additional field constructor arguments

143

144

Returns:

145

Field instance or class depending on instance parameter

146

"""

147

148

@overload

149

def field_for(

150

self,

151

model: type[DeclarativeMeta],

152

property_name: str,

153

*,

154

instance: Literal[True] = ...,

155

field_class: type[fields.Field] | None = ...,

156

**kwargs,

157

) -> fields.Field: ...

158

159

@overload

160

def field_for(

161

self,

162

model: type[DeclarativeMeta],

163

property_name: str,

164

*,

165

instance: Literal[False] = ...,

166

field_class: type[fields.Field] | None = None,

167

**kwargs,

168

) -> type[fields.Field]: ...

169

170

def field_for(

171

self,

172

model: type[DeclarativeMeta],

173

property_name: str,

174

*,

175

instance: bool = True,

176

field_class: type[fields.Field] | None = None,

177

**kwargs,

178

) -> fields.Field | type[fields.Field]:

179

"""Convert model property to marshmallow field by name.

180

181

Parameters:

182

- model: SQLAlchemy model class

183

- property_name: Name of property to convert

184

- instance: If True, return Field instance; if False, return Field class

185

- field_class: Override field class to use

186

- **kwargs: Additional field constructor arguments

187

188

Returns:

189

Field instance or class depending on instance parameter

190

"""

191

```

192

193

### Conversion Functions

194

195

Module-level convenience functions that use a default ModelConverter instance for quick field conversion.

196

197

```python { .api }

198

def fields_for_model(

199

model: type[DeclarativeMeta],

200

*,

201

include_fk: bool = False,

202

include_relationships: bool = False,

203

fields: Iterable[str] | None = None,

204

exclude: Iterable[str] | None = None,

205

base_fields: dict | None = None,

206

dict_cls: type[dict] = dict,

207

) -> dict[str, fields.Field]:

208

"""Generate fields dict from SQLAlchemy model (convenience function).

209

210

Uses default ModelConverter instance.

211

"""

212

213

@overload

214

def property2field(

215

prop: MapperProperty,

216

*,

217

instance: Literal[True] = ...,

218

field_class: type[fields.Field] | None = ...,

219

**kwargs,

220

) -> fields.Field: ...

221

222

@overload

223

def property2field(

224

prop: MapperProperty,

225

*,

226

instance: Literal[False] = ...,

227

field_class: type[fields.Field] | None = ...,

228

**kwargs,

229

) -> type[fields.Field]: ...

230

231

def property2field(

232

prop: MapperProperty,

233

*,

234

instance: bool = True,

235

field_class: type[fields.Field] | None = None,

236

**kwargs,

237

) -> fields.Field | type[fields.Field]:

238

"""Convert SQLAlchemy property to field (convenience function).

239

240

Uses default ModelConverter instance.

241

"""

242

243

@overload

244

def column2field(

245

column: sa.Column,

246

*,

247

instance: Literal[True] = ...,

248

**kwargs

249

) -> fields.Field: ...

250

251

@overload

252

def column2field(

253

column: sa.Column,

254

*,

255

instance: Literal[False] = ...,

256

**kwargs

257

) -> type[fields.Field]: ...

258

259

def column2field(

260

column: sa.Column,

261

*,

262

instance: bool = True,

263

**kwargs

264

) -> fields.Field | type[fields.Field]:

265

"""Convert SQLAlchemy column to field (convenience function).

266

267

Uses default ModelConverter instance.

268

"""

269

270

@overload

271

def field_for(

272

model: type[DeclarativeMeta],

273

property_name: str,

274

*,

275

instance: Literal[True] = ...,

276

field_class: type[fields.Field] | None = ...,

277

**kwargs,

278

) -> fields.Field: ...

279

280

@overload

281

def field_for(

282

model: type[DeclarativeMeta],

283

property_name: str,

284

*,

285

instance: Literal[False] = ...,

286

field_class: type[fields.Field] | None = None,

287

**kwargs,

288

) -> type[fields.Field]: ...

289

290

def field_for(

291

model: type[DeclarativeMeta],

292

property_name: str,

293

*,

294

instance: bool = True,

295

field_class: type[fields.Field] | None = None,

296

**kwargs,

297

) -> fields.Field | type[fields.Field]:

298

"""Get field for model property by name (convenience function).

299

300

Uses default ModelConverter instance.

301

"""

302

```

303

304

### Usage Examples

305

306

#### Converting Entire Models

307

308

```python

309

from marshmallow_sqlalchemy import fields_for_model, ModelConverter

310

from mymodels import User, Post

311

312

# Using convenience function

313

user_fields = fields_for_model(User)

314

# Returns: {"id": Integer(), "name": String(), "email": String(), ...}

315

316

# Include relationships and foreign keys

317

post_fields = fields_for_model(

318

Post,

319

include_fk=True,

320

include_relationships=True

321

)

322

323

# Exclude specific fields

324

user_fields_filtered = fields_for_model(

325

User,

326

exclude=["password_hash", "last_login"]

327

)

328

329

# Only include specific fields

330

user_fields_minimal = fields_for_model(

331

User,

332

fields=["id", "name", "email"]

333

)

334

```

335

336

#### Converting Individual Properties

337

338

```python

339

from marshmallow_sqlalchemy import field_for, property2field

340

from mymodels import User

341

342

# Get field for specific property

343

name_field = field_for(User, "name")

344

email_field = field_for(User, "email", dump_only=True)

345

346

# Get field class instead of instance

347

NameFieldClass = field_for(User, "name", instance=False)

348

349

# Convert property directly

350

user_mapper = sa.inspect(User)

351

name_prop = user_mapper.attrs["name"]

352

name_field = property2field(name_prop)

353

```

354

355

#### Converting Columns

356

357

```python

358

from marshmallow_sqlalchemy import column2field

359

from mymodels import User

360

361

# Convert table column

362

user_table = User.__table__

363

name_column = user_table.columns["name"]

364

name_field = column2field(name_column)

365

366

# Add custom validation

367

email_field = column2field(

368

user_table.columns["email"],

369

validate=[validate.Email(), validate.Length(max=255)]

370

)

371

```

372

373

#### Custom Converter

374

375

```python

376

from marshmallow_sqlalchemy import ModelConverter

377

from marshmallow import fields

378

379

class CustomConverter(ModelConverter):

380

"""Custom converter with additional type mappings."""

381

382

SQLA_TYPE_MAPPING = {

383

**ModelConverter.SQLA_TYPE_MAPPING,

384

MyCustomType: fields.String, # Map custom type to String field

385

}

386

387

def _get_field_class_for_data_type(self, data_type):

388

# Custom logic for field type selection

389

if isinstance(data_type, MySpecialType):

390

return fields.Raw

391

return super()._get_field_class_for_data_type(data_type)

392

393

# Use custom converter

394

converter = CustomConverter()

395

fields_dict = converter.fields_for_model(MyModel)

396

397

# Or use with schema

398

class MySchema(SQLAlchemyAutoSchema):

399

class Meta:

400

model = MyModel

401

model_converter = CustomConverter

402

```

403

404

### Type Mappings

405

406

ModelConverter includes comprehensive mappings for SQLAlchemy types:

407

408

#### Standard SQLAlchemy Types

409

410

- `sa.String``fields.String`

411

- `sa.Integer``fields.Integer`

412

- `sa.Float``fields.Float`

413

- `sa.Boolean``fields.Boolean`

414

- `sa.DateTime``fields.DateTime`

415

- `sa.Date``fields.Date`

416

- `sa.Time``fields.Time`

417

- `sa.Numeric``fields.Decimal`

418

- `sa.Text``fields.String`

419

- `sa.JSON``fields.Raw`

420

- `sa.Enum``fields.Enum` (if enum_class) or `fields.Raw`

421

- `sa.PickleType``fields.Raw`

422

423

#### PostgreSQL-Specific Types

424

425

- `postgresql.UUID``fields.UUID`

426

- `postgresql.JSONB``fields.Raw`

427

- `postgresql.ARRAY``fields.List`

428

- `postgresql.HSTORE``fields.Raw`

429

- `postgresql.INET``fields.String`

430

- `postgresql.CIDR``fields.String`

431

- `postgresql.MACADDR``fields.String`

432

- `postgresql.MONEY``fields.Decimal`

433

434

#### MySQL-Specific Types

435

436

- `mysql.BIT``fields.Integer`

437

- `mysql.YEAR``fields.Integer`

438

- `mysql.SET``fields.List`

439

- `mysql.ENUM``fields.Field`

440

441

#### MSSQL-Specific Types

442

443

- `mssql.BIT``fields.Integer`

444

- `mssql.UNIQUEIDENTIFIER``fields.UUID`

445

446

### Field Configuration

447

448

The converter automatically configures fields based on column properties:

449

450

```python

451

# Column with nullable=False becomes required field

452

name = sa.Column(sa.String, nullable=False)

453

# → fields.String(required=True)

454

455

# Column with default value becomes non-required

456

status = sa.Column(sa.String, default="active")

457

# → fields.String(required=False)

458

459

# Column with length constraint gets Length validator

460

username = sa.Column(sa.String(50))

461

# → fields.String(validate=[validate.Length(max=50)])

462

463

# Enum column gets OneOf validator

464

priority = sa.Column(sa.Enum("low", "medium", "high"))

465

# → fields.String(validate=[validate.OneOf(["low", "medium", "high"])])

466

467

# Foreign key columns are excluded by default (unless include_fk=True)

468

user_id = sa.Column(sa.Integer, sa.ForeignKey("users.id"))

469

# → Excluded unless include_fk=True

470

471

# Relationship properties become Related fields

472

posts = relationship("Post", back_populates="author")

473

# → Related() field (unless include_relationships=False)

474

```