Skip to main content

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"

Metadata

Release files for geekie-wafer 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geekie-wafer 3.0.0
File Size Uploaded
geekie_wafer-3.0.0.tar.gz 20.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for geekie-wafer 3.0.0
File Interpreter ABI Platform
geekie_wafer-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.2 kB

Release files / geekie_wafer-3.0.0.tar.gz

Download URL geekie_wafer-3.0.0.tar.gz
Size 20.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3c12998cf3134edf0621af13a0cf195714f51fd578a08bc542fd458bbf39d9cf
BLAKE2b-256 checksum
How to use checksums
420cf316003764de7602f84d426065e36def70edc5c35a0da44b5379c19b0124
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / geekie_wafer-3.0.0-py3-none-any.whl

Download URL geekie_wafer-3.0.0-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c6a7a8bda0334ad8ef46c4a5b1d02da6be3407509e13cee27b8386f973282394
BLAKE2b-256 checksum
How to use checksums
48af07e1d6fd41274f866260c3eed4e4340bedce0979f1e594ef35ed7d73b684
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files

1.2.1

3 release files

1.1.0

3 release files

1.0.1

3 release files

1.0.0

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page