Skip to content

Commit 026f782

Browse files
authored
Add model convertors docs (#883)
1 parent 9b08745 commit 026f782

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

docs/model_convertors.md

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
---
2+
3+
# Model Converters
4+
5+
Model Converters are special classes used to convert SQLAlchemy model properties into web interface form fields. They allow you to customize how backend SQLAlchemy models are represented in the admin interface, providing flexibility in handling different data types and validation rules.
6+
7+
This page will guide you through the basics of using and customizing Model Converters. For advanced usage, refer to the [API Reference](./api_reference/model_converter.md).
8+
9+
---
10+
11+
## Overview
12+
13+
The `ModelConverter` class is the base class for converting SQLAlchemy model properties into form fields. It provides default conversions for common SQLAlchemy types (e.g., `String`, `Integer`, `JSON`) and allows you to customize or extend these conversions.
14+
15+
### Base Model Converter
16+
17+
The base `ModelConverter` class looks like this:
18+
19+
```python
20+
class ModelConverter(ModelConverterBase):
21+
@staticmethod
22+
def _string_common(prop: ColumnProperty) -> list[Validator]:
23+
li = []
24+
column: Column = prop.columns[0]
25+
if isinstance(column.type.length, int) and column.type.length:
26+
li.append(validators.Length(max=column.type.length))
27+
return li
28+
29+
@converts("String", "CHAR") # includes Unicode
30+
def conv_string(
31+
self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
32+
) -> UnboundField:
33+
extra_validators = self._string_common(prop)
34+
kwargs.setdefault("validators", [])
35+
kwargs["validators"].extend(extra_validators)
36+
return StringField(**kwargs)
37+
38+
@converts("Text", "LargeBinary", "Binary") # includes UnicodeText
39+
def conv_text(
40+
self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
41+
) -> UnboundField:
42+
kwargs.setdefault("validators", [])
43+
extra_validators = self._string_common(prop)
44+
kwargs["validators"].extend(extra_validators)
45+
return TextAreaField(**kwargs)
46+
```
47+
48+
This class includes methods like `conv_string` and `conv_text` to handle specific SQLAlchemy types. You can extend this class to add custom behavior or override existing conversions.
49+
50+
---
51+
52+
## Customizing Model Converters
53+
54+
You can inherit from `ModelConverter` to create your own converter and customize how specific SQLAlchemy types are handled. For example, you can define a custom converter for `JSON` fields:
55+
56+
```python
57+
from typing import Any
58+
import json
59+
from wtforms import JSONField
60+
from sqladmin import ModelConverter
61+
62+
63+
class CustomJSONField(JSONField):
64+
def _value(self) -> str:
65+
if self.raw_data:
66+
return self.raw_data[0]
67+
elif self.data:
68+
return str(json.dumps(self.data, ensure_ascii=False))
69+
else:
70+
return ""
71+
72+
73+
class CustomModelConverter(ModelConverter):
74+
@converts("JSON", "JSONB")
75+
def conv_json(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField:
76+
return CustomJSONField(**kwargs)
77+
```
78+
79+
In this example:
80+
- `CustomJSONField` is a custom field that formats JSON data for display.
81+
- `CustomModelConverter` overrides the `conv_json` method to use `CustomJSONField` for `JSON` and `JSONB` SQLAlchemy types.
82+
83+
---
84+
85+
## Using Custom Model Converters in Admin Views
86+
87+
To use your custom model converter in the admin interface, specify it in your `ModelView` class:
88+
89+
```python
90+
from sqladmin import ModelView
91+
92+
93+
class BaseAdmin(ModelView):
94+
form_converter: ClassVar[Type[CustomModelConverter]] = CustomModelConverter
95+
```
96+
97+
This ensures that all form fields in the admin interface are generated using your custom converter.
98+
99+
---
100+
101+
## Example: Full Workflow
102+
103+
Here’s a complete example of defining a SQLAlchemy model, creating a custom model converter, and using it in the admin interface:
104+
105+
### Step 1: Define SQLAlchemy Models
106+
107+
```python
108+
from sqlalchemy import Column, Integer, String, JSON, create_engine
109+
from sqlalchemy.orm import declarative_base
110+
111+
112+
Base = declarative_base()
113+
engine = create_engine("sqlite:///example.db", connect_args={"check_same_thread": False})
114+
115+
116+
class User(Base):
117+
__tablename__ = "users"
118+
119+
id = Column(Integer, primary_key=True)
120+
name = Column(String)
121+
preferences = Column(JSON)
122+
123+
124+
Base.metadata.create_all(engine) # Create tables
125+
```
126+
127+
### Step 2: Create Custom Model Converter
128+
129+
```python
130+
from sqladmin import ModelConverter
131+
from wtforms import JSONField
132+
import json
133+
134+
135+
class CustomJSONField(JSONField):
136+
def _value(self) -> str:
137+
if self.raw_data:
138+
return self.raw_data[0]
139+
elif self.data:
140+
return str(json.dumps(self.data, ensure_ascii=False))
141+
else:
142+
return ""
143+
144+
145+
class CustomModelConverter(ModelConverter):
146+
@converts("JSON", "JSONB")
147+
def conv_json(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField:
148+
return CustomJSONField(**kwargs)
149+
```
150+
151+
### Step 3: Use Custom Converter in Admin Interface
152+
153+
```python
154+
from sqladmin import ModelView
155+
156+
157+
class UserAdmin(BaseAdmin):
158+
form_converter = CustomModelConverter
159+
```

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ nav:
2020
- Working with Templates: "working_with_templates.md"
2121
- Working with Custom Views: "writing_custom_views.md"
2222
- Working with Files and Images: "working_with_files.md"
23+
- Model convertors: "model_convertors.md"
2324
- Cookbook:
2425
- Deployment with HTTPS: "cookbook/deployment_with_https.md"
2526
- Optimize relationship loading: "cookbook/optimize_relationship_loading.md"

0 commit comments

Comments
 (0)