Skip to main content

No project description provided

Project description

geekie-wafer

Converts between object graphs represented by a dictionary and by models

Migrating from glibs.wafer

Most things just work by changing the import from glibs.wafer to wafer. The only exception is glibs.wafer.utils was renamed to wafer.fields.

Also the old glibs.utils.alias has been moved to wafer.utils.alias.

Usage

Suppose we have a very simple model serialized as follows:

serialized_presentation = {
    "name": "Rockband Matches",
    "author": "Edward Merryweather",
    "type": "ppt",
}

We can map that to a model using Wafer as follows:

class Presentation(wafer.Model):
    name = wafer.Field()
    author = wafer.Field()
    type = wafer.Field()

Converting the dictionary to a model then becomes very simple

presentation = Presentation(serialized_presentation)
print presentation.name # Outputs Rockband Matches

Going the other way around is also very straightforward

presentation = Presentation()
presentation.name = "Rockband Matches"
presentation.author = "Edward Merryweather"
presentation.serialize() # Outputs {
                         #      "name": "Rockband Matches",
                         #      "author": "Edward Merryweather"
                         # }

Note that fields whose value is None are not written to the dictionary.

Embedded documents

Frequently models contain themselves other models:

friday_fun_event = {
    "presentation": {
        "name": "Rockband Matches",
    },
}

For Wafer to properly serialize and deserialize this kind of object graph, you simply pass the model you want to use to the wafer.Field constructor:

class Event(wafer.Model):
    presentation = wafer.Field(Presentation)

event = Event(friday_fun_event)
event.presentation.name # Outputs "Rockband Matches"

And the other way around:

event = Event()
event.presentatiob # Outputs None
event.presentation = Presentation()
event.presentation.name = "Rockband Matches"
event.serialize() # Outputs the same as the friday_fun_event object

Embedded collections

In case your model contain a list of embedded models, you use the wafer.EmbeddedCollection to declare the field

documents = {
    "presentations": [{
        "name": "Rockband Matches"
    }, {
        "name": "Other"
    }]
}

class DocumentList(wafer.Model):
    presentations = wafer.EmbeddedCollection(Presentation)

list = DocumentList(documents)
list.presentations[1].name # Outputs "Other"

Embedded collection fields are never set to None, and wafer considers None the same as an empty list. Because of that behavior, the following statements are true:

list = DocumentList()
list.presentations # Outputs []
list.serialize()   # Outputs {}, empty list is ignored

Custom serialization/deserialization logic

If you have a field you want to map in some nonstandard way to its serialzed form, override serialize and deserialize methods

Customizing getters/setters

If you have a field whose getter/setter logic you want to customize, you'll find that the following doesn't work:

class Person(wafer.Model):
    weight = wafer.Field()

    @property
    def weight(self): # Erases the old definition for 'weight'
        return self.__weight

    @weight.setter
    def weight(self, value):
        if value < 0:
            raise ValueError()
        self.__weight = value

In this situation, you can declare the field the following way:

class Person(wafer.Model):
    __weight_field = wafer.Field()
    # Rest of the code unaltered

And then everything will work as intended

person = Person({ "weight": -2 }) # raises ValueError

Polymorphism

Your model may have a collection of embedded documents of different types

class Phase(wafer.Model):
    type = wafer.Field()

class VideoPhase(Phase):
    video_url = wafer.Field()

class DrillPhase(Phase):
    exercises = wafer.EmbeddedCollection(Exercise)

class Lecture(wafer.Model):
    phases = wafer.EmbeddedCollection(Phase) # VideoPhase or DrillPhase

In such cases, serialization works out of the box but you'll find that deserialization doesn't as it always tries to convert the dictionary to a Phase object, and the Phase object doesn't know about the different fields each subclass has.

The solution is to use a custom deserialization logic, as follows:

class Phase(wafer.Model):
    @staticmethod
    def deserialize(obj):
        if obj["type"] == "video":
            return VideoPhase(obj)
        else:
            return DrillPhase(obj)

class Lecture(wafer.Model):
    phases = wafer.EmbeddedCollection(Phase, deserializer=Phase.deserialize)

You may want to add logic to each subclass to ensure the type field is correctly stored. Consider the following possibilities:

class VideoPhase(Phase):
    def serialize(self):
        obj = super(VideoPhase, self).serialize()
        obj["type"] = "video"
        return obj

class VideoPhase(Phase):
    def __init__(self, *args, **kwargs):
        super(VideoPhase, self).__init__(*args, **kwargs)
        self.type = "video"

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

geekie_wafer-3.0.0.tar.gz (20.1 kB view hashes)

Uploaded Source

Built Distribution

geekie_wafer-3.0.0-py3-none-any.whl (14.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page