Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 3856856

Browse files
committed
update fields.md, index.md and schemas.md
1 parent e8d4967 commit 3856856

5 files changed

Lines changed: 85 additions & 101 deletions

File tree

docs/fields.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
Fields are usually declared as attributes on schema classes:
1+
Fields are passed as a dictionary to the Schema classes:
22

33
```python
4-
class Organisation(typesystem.Schema):
5-
name = typesystem.String(title="Name", max_length=100)
6-
date_created = typesystem.Date(title="Date created", default=datetime.date.today)
7-
owner = typesystem.Reference(to=User, allow_null=True)
4+
import typesystem
5+
6+
user_schema = typesystem.Schema(fields={"name": typesystem.String()})
7+
8+
definitions = typesystem.Definitions()
9+
definitions["User"] = user_schema
10+
11+
organization_schema = typesystem.Schema(
12+
fields={
13+
"name": typesystem.String(title="Name", max_length=100),
14+
"date_created": typesystem.Date(title="Date created", default=datetime.date.today),
15+
"owner": typesystem.Reference(to="User", allow_null=True, definitions=definitions),
16+
}
17+
)
818
```
919

1020
Fields are always *required* in inputs, unless a *default* value is set.
@@ -20,6 +30,7 @@ All fields support the following arguments.
2030
* `description` - A string describing the input. **Default: `None`**
2131
* `default` - A value to be used if no input is provided for this field. May be a callable, such as `datetime.datetime.now`. **Default: `NO_DEFAULT`**
2232
* `allow_null` - A boolean determining if `None` values are valid. **Default: `False`**
33+
* `read_only` - A boolean determining if field should be considered as read-only, this is usually used in form rendering. **Default: `False`**
2334

2435
## Using fields directly
2536

@@ -60,6 +71,7 @@ For example: `username = typesystem.String(max_length=100)`
6071
* `min_length` - A minimum number of characters that valid input stings may contain. **Default: `None`**
6172
* `pattern` - A regular expression that must match. This can be either a string or a compiled regular expression. E.g. `pattern="^[A-Za-z]+$"` **Default: `None`**
6273
* `format` - A string used to indicate a semantic type, such as `"email"`, `"url"`, or `"color"`. **Default: `None`**
74+
* `coerce_types` - A boolean determining if type casting should be done, E.g. changing `None` to `""` if `allow_blank`. **Default: `True`**
6375

6476
### Text
6577

@@ -176,7 +188,7 @@ extra_metadata = typesystem.Object(properties=typesystem.String(max_length=100))
176188
Schema classes implement their validation behaviour by generating an `Object`
177189
field, and automatically determining the `properties` and `required` attributes.
178190

179-
You'll typically want to use `typesystem.Reference(to=SomeSchema)` rather than
191+
You'll typically want to use `typesystem.Reference(to="SomeSchema")` rather than
180192
using the `Object` field directly, but it can be useful if you have a more
181193
complex data structure that you need to validate.
182194

@@ -196,9 +208,9 @@ Used to reference a nested schema.
196208
For example:
197209

198210
```python
199-
owner = typesystem.Reference(to=User, allow_null=True)
211+
owner = typesystem.Reference(to="User", allow_null=True, definitions=definitions)
200212
```
201213

202214
**Arguments**:
203215

204-
* `to` - A schema class or field instance. **Required**
216+
* `to` - Name of schema defined in definitions. **Required**

docs/index.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# TypeSystem
22

33
<p>
4-
<a href="https://travis-ci.org/encode/typesystem">
5-
<img src="https://travis-ci.org/encode/typesystem.svg?branch=master" alt="Build Status">
4+
<a href="https://github.com/encode/typesystem/actions">
5+
<img src="https://github.com/encode/typesystem/workflows/Test%20Suite/badge.svg" alt="Build Status">
66
</a>
77
<a href="https://codecov.io/gh/encode/typesystem">
88
<img src="https://codecov.io/gh/encode/typesystem/branch/master/graph/badge.svg" alt="Coverage">
@@ -44,30 +44,30 @@ $ pip3 install jinja2
4444
```python
4545
import typesystem
4646

47-
class Artist(typesystem.Schema):
48-
name = typesystem.String(max_length=100)
47+
artist_schema = typesystem.Schema(
48+
fields={
49+
"name": typesystem.String(max_length=100)
50+
}
51+
)
4952

50-
class Album(typesystem.Schema):
51-
title = typesystem.String(max_length=100)
52-
release_date = typesystem.Date()
53-
artist = typesystem.Reference(Artist)
53+
definitions = typesystem.Definitions()
54+
definitions["Artist"] = artist_schema
5455

55-
album = Album.validate({
56+
album_schema = typesystem.Schema(
57+
fields={
58+
"title": typesystem.String(max_length=100),
59+
"release_date": typesystem.Date(),
60+
"artist": typesystem.Reference("Artist", definitions=definitions)
61+
}
62+
)
63+
64+
album = album_schema.validate({
5665
"title": "Double Negative",
5766
"release_date": "2018-09-14",
5867
"artist": {"name": "Low"}
5968
})
6069

6170
print(album)
62-
# Album(title='Double Negative', release_date=datetime.date(2018, 9, 14), artist=Artist(name='Low'))
63-
64-
print(album.release_date)
65-
# datetime.date(2018, 9, 14)
66-
67-
print(album['release_date'])
68-
# '2018-09-14'
69-
70-
print(dict(album))
7171
# {'title': 'Double Negative', 'release_date': '2018-09-14', 'artist': {'name': 'Low'}}
7272
```
7373

@@ -76,7 +76,7 @@ print(dict(album))
7676
There are plenty of other great validation libraries for Python out there,
7777
including [Marshmallow](https://github.com/marshmallow-code/marshmallow),
7878
[Schematics](https://github.com/schematics/schematics),
79-
[Voluptuous](https://github.com/alecthomas/voluptuous), and many others.
79+
[Voluptuous](https://github.com/alecthomas/voluptuous), [Pydantic](https://github.com/samuelcolvin/pydantic/) and many others.
8080

8181
TypeSystem exists because I want a data validation library that offers
8282
first-class support for:

docs/schemas.md

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
Let's start by defining some schema classes.
1+
Let's start by defining some schemas.
22

33
```python
44
import typesystem
55

6-
class Artist(typesystem.Schema):
7-
name = typesystem.String(max_length=100)
6+
artist_schema = typesystem.Schema(
7+
fields={
8+
"name": typesystem.String(max_length=100)
9+
}
10+
)
811

9-
class Album(typesystem.Schema):
10-
title = typesystem.String(max_length=100)
11-
release_date = typesystem.Date()
12-
artist = typesystem.Reference(Artist)
12+
definitions = typesystem.Definitions()
13+
definitions["Artist"] = artist_schema
14+
15+
album_schema = typesystem.Schema(
16+
fields={
17+
"title": typesystem.String(max_length=100),
18+
"release_date": typesystem.Date(),
19+
"artist": typesystem.Reference("Artist", definitions=definitions)
20+
}
21+
)
1322
```
1423

1524
We've got some incoming user data that we'd like to validate against our schema.
@@ -25,18 +34,18 @@ data = {
2534
We can validate the data against a Schema by using `.validate(data)`.
2635

2736
```python
28-
album = Album.validate(data)
37+
album = album_schema.validate(data)
2938
```
3039

31-
If validation succeeds, this will return an `Album` instance.
40+
If validation succeeds, this will return an `dict`.
3241

3342
If validation fails, a `ValidationError` will be raised.
3443

3544
Alternatively we can use `.validate_or_error(data)`, which will return a
3645
two-tuple of `(value, error)`. Either one of `value` or `error` will be `None`.
3746

3847
```python
39-
album, error = Album.validate_or_error(data)
48+
album, error = album_schema.validate_or_error(data)
4049
if error:
4150
...
4251
else:
@@ -53,7 +62,7 @@ invalid_data = {
5362
'release_date': '2018.09.14',
5463
'artist': {'name': 'x' * 1000}
5564
}
56-
album, error = Album.validate_or_error(invalid_data)
65+
album, error = album_schema.validate_or_error(invalid_data)
5766

5867
print(dict(error))
5968
# {'release_date': 'Must be a valid date format.', 'artist': {'name': 'Must have no more than 100 characters.'}}
@@ -65,7 +74,7 @@ If you want more precise information about exactly what error messages exist,
6574
you can access each individual message with `error.messages()`:
6675

6776
```python
68-
album, error = Album.validate_or_error(invalid_data)
77+
album, error = album_schema.validate_or_error(invalid_data)
6978

7079
for message in error.messages():
7180
print(f'{message.index!r}, {message.code!r}, {message.text!r})')
@@ -75,79 +84,46 @@ for message in error.messages():
7584

7685
## Working with schema instances
7786

78-
Schema instances are returned by calls to `.validate()`.
87+
Python dictionaries are returned by calls to `.validate()`.
7988

8089
```python
8190
data = {
8291
'title': 'Double Negative',
8392
'release_date': '2018-09-14',
8493
'artist': {'name': 'Low'}
8594
}
86-
album = Album.validate(data)
87-
print(album)
88-
# Album(title='Double Negative', release_date=datetime.date(2018, 9, 14), artist=Artist(name='Low'))
89-
```
90-
91-
Attributes on schemas return native python data types.
92-
93-
```python
94-
print(type(album.release_date))
95-
# <class 'datetime.date'>
96-
```
95+
album = album_schema.validate(data)
9796

98-
Schema instances present a dict-like interface, allowing them to be easily serialized.
99-
100-
```python
101-
print(dict(album))
97+
print(album)
10298
# {'title': 'Double Negative', 'release_date': '2018-09-14', 'artist': {'name': 'Low'}}
10399
```
104100

105-
Index lookup on schema instances returns serialized datatypes.
101+
You can also `serialize` data using the schema instance:
106102

107103
```python
108-
print(type(album['release_date']))
109-
# <class 'str'>
104+
artist = artist_schema.serialize({'name': 'Low'})
105+
album = album_schema.serialize({'title': 'Double Negative', 'artist': artist})
110106
```
111107

112-
You can also instantiate schema instances directly.
108+
If `serialize` directly, validation is not done and data returned may be sparsely populated.
109+
Any unused attributes without a default will not be returned.
113110

114111
```python
115-
artist = Artist(name='Low')
116-
album = Album(title='Double Negative', release_date='2018-09-14', artist=artist)
117-
```
118-
119-
When instantiating with keyword arguments, each keyword argument will be validated.
120-
121-
If instantiated directly, schema instances may be sparsely populated. Any unused
122-
attributes without a default will not be set on the instance.
112+
artist = artist_schema.serialize({'name': 'Low'})
113+
album = album_schema.serialize({'title': 'Double Negative', 'artist': artist})
123114

124-
```python
125-
artist = Artist(name='Low')
126-
album = Album(title='Double Negative', artist=artist)
127115
print(album)
128-
# Album(title='Double Negative', artist=Artist(name='Low')) [sparse]
129-
album.release_date
130-
# AttributeError: 'Album' object has no attribute 'release_date'
131-
print(dict(album))
132-
{'title': 'Double Negative', 'artist': {'name': 'Low'}}
116+
# {'title': 'Double Negative', 'artist': {'name': 'Low'}} [sparse]
117+
118+
album['release_date']
119+
# KetError: 'release_date'
133120
```
134121

135-
Sparsely populated instances can be useful for cases of loading data from database,
122+
Sparsely serialized data can be useful for cases of loading data from database,
136123
when you do not need to retrieve all the fields, or for cases of loading nested
137124
data where no database join has been made, and only the primary key of the relationship
138125
is known.
139126

140-
You can also instantiate a schema from an object instance or dictionary.
141-
142-
```python
143-
new_album = Album(album)
144-
```
145-
146-
Note that data validation is not applied when instantiating a schema instance
147-
directly from an instance or dictionary. This should be used when creating
148-
instances against a data source that is already known to be validated, such as
149-
when loading existing instances from a database.
150-
151127
## Using strict validation
152128

153129
By default, additional properties in the incoming user data is ignored.
@@ -162,19 +138,10 @@ data = {
162138
```
163139

164140
After validating against the schema, the `num_tracks` property is not present
165-
on the `album` instance.
141+
in the returned data.
166142

167143
```python
168-
album = Album.validate(data)
169-
album.num_tracks
170-
# AttributeError: 'Album' object has no attribute 'num_tracks'
171-
```
172-
173-
If you use strict validation, additional properties becomes an error instead.
174-
175-
```python
176-
album, error = Album.validate_or_error(data, strict=True)
177-
178-
print(dict(error))
179-
# {'num_tracks': 'Invalid property name.'}
144+
album = album_schema.validate(data)
145+
album['num_tracks]
146+
# KeyError: 'num_tracks'
180147
```

mkdocs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ nav:
2020

2121
markdown_extensions:
2222
- admonition
23+
- pymdownx.highlight
24+
- pymdownx.superfences
25+
- codehilite
2326
- markdown.extensions.codehilite:
2427
guess_lang: false

typesystem/schemas.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ def validate(self, value: typing.Any) -> typing.Any:
7272

7373
return validated
7474

75-
def serialize(self, obj: typing.Any) -> typing.Any:
75+
def serialize(
76+
self, obj: typing.Any
77+
) -> typing.Optional[typing.Dict[str, typing.Any]]:
7678
if obj is None:
7779
return None
7880

0 commit comments

Comments
 (0)