Skip to content

Commit

Permalink
Support nested serializers
Browse files Browse the repository at this point in the history
Also update README
  • Loading branch information
sloria committed Nov 8, 2013
1 parent a3ce187 commit 93c19d2
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ output/*/index.html
# Sphinx
docs/_build
README.html

sandbox.ipynb
.ipynb_checkpoints
101 changes: 88 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,41 +1,116 @@
===========
***********
marshmallow
===========
***********

Serialization made simple.

.. code:: python
Quickstart
==========

from marshmallow import Serializer, fields
Declaring Serializers
---------------------

Let's start with basic "model".

.. code-block:: python
import datetime as dt
class User(object):
def __init__(self, name, email):
self.name = name
self.email = email
self.created_at = dt.datetime.now()
Create a serializer by defining a class with a ``FIELDS`` variable, which is a dictionary mapping attribute names to a field class that formats the final output of the serializer.

.. code-block:: python
from marshmallow import Serializer, fields
class UserSerializer(Serializer):
FIELDS = {
"name": fields.String,
"email": fields.String
'email': fields.String,
'created_at': fields.DateTime
}
.. code:: python
Serializing Objects
-------------------

Serialize objects by passing them into your serializers. Onced serialized, you can get the dictionary representation via the `data` property and the JSON representation via the `json` property.

.. code-block:: python
user = User(name="Monty", email="monty@python.org")
serialized = UserSerializer(user)
serialized.data
# {'created_at': 'Sun, 10 Nov 2013 15:48:19 -0000',
# 'email': u'monty@python.org',
# 'name': u'Monty'}
serialized.json
# '{"created_at": "Sun, 10 Nov 2013 15:48:19 -0000", "name": "Monty", "email": "monty@python.org"}'
Specifying Attributes
---------------------

By default, serializers will marshal the object attributes that have the same name as the keys in ``FIELDS``. However, you may want to have different field and attribute names. In this case, you can explicitly specify which attribute names to use.

.. code-block:: python
class UserSerializer(Serializer):
FIELDS = {
"name": fields.String,
'email_addr': fields.String(attribute="email"),
'date_created': fields.DateTime(attribute="created_at")
}
Nesting Serializers
-------------------

Serializers can be nested to represent hierarchical structures. For example, a ``Blog`` may have an author represented by a User object.

.. code-block:: python
class Blog(object):
def __init__(self, title, author):
self.title = title
self.author = author # A User object
Use ``fields.Nested``to represent relationship, passing in the ``UserSerializer`` class.

.. code-block:: python
class BlogSerializer(Serializer):
FIELDS = {
'title': fields.String,
'author': fields.Nested(UserSerializer)
}
>>> user = User(name="Monty Python", email="monty@python.org")
>>> serialized = UserSerializer(user)
>>> serialized.data
{"name": "Monty Python", "email": "monty@python.org"}
When you serialize the blog, you will see the nested user representation.

.. code-block:: python
user = User(name="Monty", email="monty@python.org")
blog = Blog(title="Something Completely Different", author=user)
serialized = BlogSerializer(blog)
serialized.data
# {'author': {'created_at': 'Sun, 10 Nov 2013 16:10:57 -0000',
# 'email': u'monty@python.org',
# 'name': u'Monty'},
# 'title': u'Something Completely Different'}
Requirements
------------
============

- Python >= 2.6 or >= 3.3
- Python >= 2.7 or >= 3.3


License
-------
=======

MIT licensed. See the bundled `LICENSE <https://github.com/sloria/marshmallow/blob/master/LICENSE>`_ file for more details.
2 changes: 1 addition & 1 deletion marshmallow/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Serializer(object):
'''
FIELDS = {}

def __init__(self, data):
def __init__(self, data=None):
self._data = data

@property
Expand Down
7 changes: 3 additions & 4 deletions marshmallow/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


from marshmallow import types
from marshmallow.core import marshal
from marshmallow.core import marshal, Serializer
from marshmallow.compat import text_type


Expand Down Expand Up @@ -110,16 +110,15 @@ def output(self, key, obj):


class Nested(Raw):
"""Allows you to nest one set of fields inside another.
See :ref:`nested-field` for more information
"""Allows you to nest a ``Serializer`` or set of fields inside a field.
:param dict nested: The dictionary to nest
:param bool allow_null: Whether to return None instead of a dictionary
with null keys, if a nested dictionary has all-null keys
"""

def __init__(self, nested, allow_null=False, **kwargs):
self.nested = nested
self.nested = nested.FIELDS if issubclass(nested, Serializer) else nested
self.allow_null = allow_null
super(Nested, self).__init__(**kwargs)

Expand Down
31 changes: 30 additions & 1 deletion tests/test_marshmallow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

central = pytz.timezone("US/Central")

##### Models #####

class User(object):
SPECIES = "Homo sapiens"

Expand All @@ -23,16 +25,40 @@ def __init__(self, name, age):
# A TZ-aware datetime
self.updated = central.localize(dt.datetime(2013, 11, 10, 14, 20, 58))

class Blog(object):

def __init__(self, title, user):
self.title = title
self.user = user

###### Serializers #####

class UserSerializer(Serializer):
FIELDS = {
"name": fields.String,
"age": fields.Float,
"created": fields.DateTime,
"updated": fields.DateTime,
"updated_local": fields.LocalDateTime(attribute="updated")
"updated_local": fields.LocalDateTime(attribute="updated"),
'species': fields.String(attribute="SPECIES")
}

class BlogSerializer(Serializer):
FIELDS = {
"title": fields.String,
"user": fields.Nested(UserSerializer)
}

##### The Tests #####

class TestNestedSerializer(unittest.TestCase):
def test_nested(self):
user = User(name="Monty", age=42)
blog = Blog("Monty's blog", user=user)
serialized_blog = BlogSerializer(blog)
serialized_user = UserSerializer(user)
assert_equal(serialized_blog.data['user'], serialized_user.data)


class TestSerializer(unittest.TestCase):

Expand All @@ -59,6 +85,9 @@ def test_tz_datetime_field(self):
def test_local_datetime_field(self):
assert_equal(self.serialized.data['updated_local'], 'Sun, 10 Nov 2013 14:20:58 -0600')

def test_class_variable(self):
assert_equal(self.serialized.data['species'], 'Homo sapiens')

class TestTypes(unittest.TestCase):

def test_rfc822_gmt_naive(self):
Expand Down

0 comments on commit 93c19d2

Please sign in to comment.