DictObject
==========
Subclassing this type you can declare your object model. `DictObject` itself is
`dict` subclass so you can access object properties as attribute or as an item:
.. code-block:: python
class Foo(dicty.DictObject):
foo = dicty.Field()
obj = Foo(foo='bar')
obj.foo # 'bar'
obj['foo'] # 'bar'
Object constructor accepts properties as keyword arguments, or you can create
instance with `fromjson()` classmethod that takes object dictionary (e.g. result
`json.loads()`) as an argument:
.. code-block:: python
obj = Foo(foo='bar')
obj = Foo.fromjson({'foo': 'bar'})
You can pass `DictObject` instance directly to json library or call `jsonize()`
method first that will return plain dict version of your object with only
declared fields left:
.. code-block:: python
obj = Foo.fromjson({'foo': 123, 'bar': 123})
obj == {'foo': 123, 'bar': 123}
obj.jsonize() == {'foo': 123}
Name aliasing
-------------
If you don't like naming scheme in JSON objects, API and so on. Dicty allows to
choose whatever python name you like while manually specify dictionary key. For
instance map camel-case keys to their underscore counterparts:
.. code-block:: python
class Foo(dicty.DictObject):
prop_foo = dicty.Field('propFoo')
obj = Foo(prop_foo=123)
obj = Foo.fromjson({'propFoo': 123})
obj.prop_foo
obj['propFoo']
Subclassing
-----------
`DictObject` supports subclassing:
.. code-block:: python
class Foo(dicty.DictObject):
foo = dicty.Field()
class Bar(Foo):
bar = dicty.Field()
obj = Bar.fromjson({'foo': 1, 'bar': 2})
print obj.jsonize() # {'foo': 1, 'bar': 2}
Mixins are supported as well:
.. code-block:: python
class FooMixIn(object):
foo = dicty.Field()
class Bar(dicty.DictObject, FooMixIn):
bar = dicty.Field()
obj = Bar.fromjson({'foo': 1, 'bar': 2})
print obj.jsonize() # {'foo': 1, 'bar': 2}
Fields
======
`dicty.Field` is baseclass for all dicty fields. You can use itself directly to
declare a field with no special type info.
Optional fields and default values
----------------------------------
Accessing field that is not set will lead to `AttributeError`:
You can specify default value for your field:
.. code-block:: python
class Foo(dicty.DictObject):
foo = dicty.Field()
obj = Foo()
obj.foo # raises AttributeError
You can mark field as optional, in this case `None` will be returned if it was
not set before:
.. code-block:: python
class Foo(dicty.DictObject):
foo = dicty.Field(optional=True)
obj = Foo()
obj.foo # None
For optional fields you can specify default value other than `None` with
`default` argument:
.. code-block:: python
class Foo(dicty.DictObject):
foo = dicty.Field(optional=True, default=123)
obj = Foo()
obj.foo # 123
obj == {}
Please note that default value does not affect internal dictionary. But if
default value is NOT hashable dict key will be set on `getattr` access.
There is also an option to suply `default_func` it's get default value for
object's field. It takes object instance as an argument. Value returned by
`default_func` is always stored in dict:
.. code-block:: python
class Foo(dicty.DictObject):
id = dicty.Field(optional=True, default_func=lambda obj: uuid.uuid4().hex)
obj = Foo()
obj == {}
obj.id # Would be populated with newly generated UUID
obj == {'id': '07d0af8affaf46c885cc251e17dbc37a'}
Available Fields
----------------
Dicty is shipped with the follwing:
`BooleanField`
`DateField`
`DatetimeField`
`DictField`
`FloatField`
`IntegerField`
`ListField`
`NativeDateField`
`NativeDatetimeField`
`NumberField`
`RegexpStringField`
`StringField`
`TypedDictField`
`TypedListField`
`TypedObjectField`
Sample usage
============
With dicty you can easily describe your data model and then use it to encode/decode JSON objects. It supports
data validataion, optional parameters, default values, nested objects and so on.
.. code-block:: python
import dicty
class MyDoc(dicty.DictObject):
prop1 = dicty.StringField()
prop2 = dicty.IntegerField()
# Regular constructor
doc = MyDoc(prop1='foo', prop2=123)
print doc.prop1 # you can access values as attributes
print doc['prop2'] # as well as dictionary items
print json.dumps(doc)
print json.dumps(doc.jsonify()) # Jsonify will clean and validate output data
# Create instance from dictionary
doc = MyDoc.fromjson({'prop1': 'foo', 'prop2': 123})
# would raise dicty.FieldError here
doc = MyDoc.fromjson({'prop1': 123, 'prop2': 123})
Nested Objects
==============
.. code-block:: python
import dicty
class Foo(dicty.DictObject):
class Bar(dicty.DictObject):
prop = dicty.StringField()
bar = dicty.TypedObjectField(Bar)
obj = Foo()
obj.bar.prop = 123
print obj # {'bar': {'prop': 123}}
.. _CornerApp: https://cornerapp.com/
Mongo-style key pathes
======================
Dicty allows to build key pathes that can be used to create mongo query:
.. code-block:: python
class Foo(dicty.DictObject):
bar = dicty.Field('myBar')
print Foo.bar # 'myBar' full path to the item
print Foo.bar.key # 'myBar' only leaf key
print Foo.bar.attname # 'bar' python attribute name
Nested object:
.. code-block:: python
class Bar(dicty.DictObject):
foo = dicty.TypedObjectField(Foo)
print Bar.foo # 'foo'
print Bar.foo.bar # 'foo.myBar'
List of objects:
.. code-block:: python
class Bar(dicty.DictObject):
items = dicty.TypedListField(Foo)
print Bar.items.foo # 'items.myBar' without index
print Bar.items[0].foo # 'items.0.myBar' indexed path
Dict of objects:
.. code-block:: python
class Bar(dicty.DictObject):
items = dicty.TypedDictField(Foo)
# With index
print Bar.items['maurice'].bar # 'items.maurice.myBar'
# Would raise IndexError
print Bar.items['x.y'].bar