-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit includes markdown and sample files related to fields support in Protean.
- Loading branch information
Showing
36 changed files
with
1,049 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
101 changes: 101 additions & 0 deletions
101
docs/guides/domain-definition/fields/association-fields.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# Association Fields | ||
|
||
## `HasOne` | ||
|
||
Represents an one-to-one association between an aggregate and its entities. | ||
This field is used to define a relationship where an aggregate is associated | ||
with at most one instance of a child entity. | ||
|
||
```python hl_lines="10 13" | ||
{! docs_src/guides/domain-definition/fields/association-fields/001.py !} | ||
``` | ||
|
||
!!!note | ||
If you carefully observe the `HasOne` field declaration, the child entity's | ||
name is a string value! This is usually the way to avoid circular references. | ||
It applies to all aspects of Protean that link two entities - the string | ||
value will be resolved to the class at runtime. | ||
|
||
The `Author` entity can now be persisted along with the `Book` aggregate: | ||
|
||
```shell hl_lines="3 12-13" | ||
In [1]: book = Book( | ||
...: title="The Great Gatsby", | ||
...: author=Author(name="F. Scott Fitzgerald") | ||
...: ) | ||
|
||
In [2]: domain.repository_for(Book).add(book) | ||
Out[2]: <Book: Book object (id: a4a642d9-87ed-44de-9889-c687466f171b)> | ||
|
||
In [3]: domain.repository_for(Book)._dao.query.all().items[0].to_dict() | ||
Out[3]: | ||
{'title': 'The Great Gatsby', | ||
'author': {'name': 'F. Scott Fitzgerald', | ||
'id': '1f275e92-9872-4d96-b999-4ef0fbe61013'}, | ||
'id': 'a4a642d9-87ed-44de-9889-c687466f171b'} | ||
``` | ||
|
||
!!!note | ||
Protean adds a `Reference` field to child entities to preserve the inverse | ||
relationship - from child entity to aggregate - when persisted. This is | ||
visible if you introspect the fields of the Child Entity. | ||
|
||
```shell hl_lines="7 13" | ||
In [1]: from protean.reflection import declared_fields, attributes | ||
|
||
In [2]: declared_fields(Author) | ||
Out[2]: | ||
{'name': String(required=True, max_length=50), | ||
'id': Auto(identifier=True), | ||
'book': Reference()} | ||
|
||
In [3]: attributes(Author) | ||
Out[3]: | ||
{'name': String(required=True, max_length=50), | ||
'id': Auto(identifier=True), | ||
'book_id': _ReferenceField()} | ||
``` | ||
|
||
We will further review persistence related aspects around associations in the | ||
Repository section. | ||
<!-- FIXME Link Repository section --> | ||
|
||
## `HasMany` | ||
|
||
Represents a one-to-many association between two entities. This field is used | ||
to define a relationship where an aggregate has multiple instances of a child | ||
entity. | ||
|
||
```python hl_lines="11" | ||
{! docs_src/guides/domain-definition/fields/association-fields/002.py !} | ||
``` | ||
|
||
Protean provides helper methods that begin with `add_` and `remove_` to add | ||
and remove child entities from the `HasMany` relationship. | ||
|
||
```shell hl_lines="4-5 12-13 16 23" | ||
In [1]: post = Post( | ||
...: title="Foo", | ||
...: comments=[ | ||
...: Comment(content="Bar"), | ||
...: Comment(content="Baz") | ||
...: ] | ||
...: ) | ||
|
||
In [2]: post.to_dict() | ||
Out[2]: | ||
{'title': 'Foo', | ||
'comments': [{'content': 'Bar', 'id': '085ed011-15b3-48e3-9363-99a53bc9362a'}, | ||
{'content': 'Baz', 'id': '4790cf87-c234-42b6-bb03-1e0599bd6c0f'}], | ||
'id': '29943ac9-a9eb-497b-b6d2-466b30ecd5f5'} | ||
|
||
In [3]: post.add_comments(Comment(content="Qux")) | ||
|
||
In [4]: post.to_dict() | ||
Out[4]: | ||
{'title': 'Foo', | ||
'comments': [{'content': 'Bar', 'id': '085ed011-15b3-48e3-9363-99a53bc9362a'}, | ||
{'content': 'Baz', 'id': '4790cf87-c234-42b6-bb03-1e0599bd6c0f'}, | ||
{'content': 'Qux', 'id': 'b1a7aeda-81ca-4d0b-9d7e-6fe0c000b8af'}], | ||
'id': '29943ac9-a9eb-497b-b6d2-466b30ecd5f5'} | ||
``` |
105 changes: 105 additions & 0 deletions
105
docs/guides/domain-definition/fields/container-fields.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# Container Fields | ||
|
||
## `List` | ||
|
||
A field that represents a list of values. | ||
|
||
**Optional Arguments** | ||
|
||
- **`content_type`**: The type of items in the list. Defaults to `String`. | ||
Accepted field types are `Boolean`, `Date`, `DateTime`, `Float`, `Identifier`, | ||
`Integer`, `String`, and `Text`. | ||
- **`pickled`**: Whether the list should be pickled when stored. Defaults to | ||
`False`. | ||
|
||
!!!note | ||
Some database implementations (like Postgresql) can store lists by default. | ||
You can force it to store the pickled value as a Python object by | ||
specifying `pickled=True`. Databases that don’t support lists simply store | ||
the field as a python object. | ||
|
||
```python hl_lines="9" | ||
{! docs_src/guides/domain-definition/fields/simple-fields/001.py !} | ||
``` | ||
|
||
The value is provided as a `list`, and the values in the `list` are validated | ||
to be of the right type. | ||
|
||
```shell hl_lines="6 12" | ||
In [1]: user = User(email="[email protected]", roles=['ADMIN', 'EDITOR']) | ||
|
||
In [2]: user.to_dict() | ||
Out[2]: | ||
{'email': '[email protected]', | ||
'roles': ['ADMIN', 'EDITOR'], | ||
'id': '582d946b-409b-4b15-b3be-6a90284264b3'} | ||
|
||
In [3]: user2 = User(email="[email protected]", roles=[1, 2]) | ||
ERROR: Error during initialization: {'roles': ['Invalid value [1, 2]']} | ||
... | ||
ValidationError: {'roles': ['Invalid value [1, 2]']} | ||
``` | ||
|
||
## `Dict` | ||
|
||
A field that represents a dictionary. | ||
|
||
**Optional Arguments** | ||
|
||
- **`pickled`**: Whether the dict should be pickled when stored. Defaults to | ||
`False`. | ||
|
||
```python hl_lines="10" | ||
{! docs_src/guides/domain-definition/fields/container-fields/002.py !} | ||
``` | ||
|
||
A regular dictionary can be supplied as value to payload: | ||
|
||
|
||
```shell hl_lines="3 9" | ||
In [1]: event=UserEvent( | ||
...: name="UserRegistered", | ||
...: payload={'name': 'John Doe', 'email': '[email protected]'} | ||
...: ) | ||
|
||
In [2]: event.to_dict() | ||
Out[2]: | ||
{'name': 'UserRegistered', | ||
'payload': {'name': 'John Doe', 'email': '[email protected]'}, | ||
'id': '44e9143f-f4a6-40da-9128-4b6c013420d4'} | ||
``` | ||
|
||
!!!note | ||
Some database implementations (like Postgresql) can store dicts as JSON | ||
by default. You can force it to store the pickled value as a Python object | ||
by specifying pickled=True. Databases that don’t support lists simply store | ||
the field as a python object. | ||
|
||
## `ValueObject` | ||
|
||
Represents a field that holds a value object. This field is used to embed a | ||
Value Object within an entity. | ||
|
||
**Arguments** | ||
|
||
- **`value_object_cls`**: The class of the value object to be embedded. | ||
|
||
```python hl_lines="7-15 20" | ||
{! docs_src/guides/domain-definition/fields/container-fields/003.py !} | ||
``` | ||
|
||
You can provide an instance of the Value Object as input to the value object | ||
field: | ||
|
||
```shell hl_lines="2 8" | ||
In [1]: account = Account( | ||
...: balance=Balance(currency="USD", amount=100.0), | ||
...: name="Checking" | ||
...: ) | ||
|
||
In [2]: account.to_dict() | ||
Out[2]: | ||
{'balance': {'currency': 'USD', 'amount': 100.0}, | ||
'name': 'Checking', | ||
'id': '513b8a78-e00f-45ce-bb6f-11ef0cccbec6'} | ||
``` |
Oops, something went wrong.