jsonapi.marker

This package contains decorators for attribute and relationship methods or properties. They can be used to create a schema on the fly.

The base Schema find the decorated methods automatic.

Tutorial

Hint

We will use the property decorators, which turn the decorated methods into properties. If you don’t want to turn the methods into properties, you can use the decorators in jsonapi.marker.method instead of jsonapi.marker.property.

Hint

This tutorial is also available in the examples folder: examples/marker/tutorial.py

We will demonstrate the decorators on the following example with a Post model.

class Post(object):

    def __init__(self, id="", text="", author=None, comments=None):
        self._id = id
        self._text = text
        self._author = author
        self._comments = comments or list()
        return None

id attribute

The id_attribute decorator must be used at least once, to tell the serializer how it can find the id of an instance:

    @jsonapi.marker.property.id_attribute()
    def id(self):
        """
        The id attribute must be marked with the *id_attribute* decorator.
        """
        return self._id

attributes

To mark an attribute, you can use the Attribute decorator. If you define no setter, the attribute is read-only and can not be changed by clients.

The attribute marker accept some arguments. E.g.: If you want the attribute to be displayed with a different name in the API, use the name argument.

    @jsonapi.marker.property.attribute(name="headline")
    def title(self):
        """
        Normal attributes can be marked with the *attribute* decorator.

        Because the title is completly derived from the *text* attribute, we
        define no setter. If a client tries to change the value of *title*,
        a Forbidden http error will be thrown.
        """
        # Use the first line of the articles text as title, if not title
        # exists.
        title = self._text[:min(32, self._text.find("\n"))]
        return title


    @jsonapi.marker.property.attribute()
    def text(self):
        """
        In contrary to the *title* attribute, the *text* can be changed.
        To achieve this, we can define a *setter*, just like for every other
        Python property.
        """
        return self._text

    @text.setter
    def text(self, text):
        self._text = text
        return None

to-one relationships

For to-one relationships, which can either be None or a resource, you must use the to_one_relationship decorator.

    @jsonapi.marker.property.to_one_relationship()
    def author(self):
        """
        This property describes a *to-one* relationship. The *author* is turned
        into a Python property.

        Because *py-jsonapi* allows polymorphism, we don't force you to define
        the target type of the returned object. The API will determine the
        type of the related resource automatic.
        """
        return self._author

    @author.setter
    def author(self, author):
        assert isinstance(author, User) or author is None
        self._author = author
        return None

to-many relationships

If you want to mark a to-many relationship, which returns a list of related resources, you must use the to_many_relationship decorator. However, a to-many relationship also requires an adder:

    @jsonapi.marker.property.to_many_relationship()
    def comments(self):
        """
        Defining a *to-many* relationship requires a bit more effort. You must
        define an *adder* in addition to the *setter*.
        """
        return self._comments

    @comments.setter
    def comments(self, comments):
        assert all(isinstance(comment, Comment) for comment in comments)
        self._comments = comments
        return None

    @comments.adder
    def add_comment(self, comment):
        """
        Because we can add new resources to a *to-many* relationship, the
        schema must know how to add a new resource to the relationship and
        not only how to replace.
        """
        assert isinstance(comment, Comment)
        self._comments.append(comment)
        return None

Creating the schema

The Post model is now complete and we want to create the serializer based on our markup:

post_schema = jsonapi.base.schema.Schema(Post)

API

jsonapi.marker.method

This module contains the decorators for methods.

class User(object):

    @attribute()
    def name(self):
        return self._name

    @name.setter
    def set_name(self, name):
        self._name = name.strip()
        return None

# The decorated methods must still be called as methods:
user = User()
user.set_name("Homer")
print(user.name())
class jsonapi.marker.method.attribute(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.BaseMarker, jsonapi.base.schema.Attribute

Can be used to mark JSONapi attributes on a class:

class User(object):

    # Way 1: As decorator
    # This will replace the method with a transparent descriptor.

    @Attribute()
    def email(self):
        return self._email

    @email.setter
    def set_email(self, new_addr):
        self._email = new_addr

    # Way 2: As new attribute
    # This will not change the method at all.

    def last_edited(self):
        return self._last_edited

    jlast_edited = Attribute(fget=last_edited)
Seealso:id_attribute
class jsonapi.marker.method.id_attribute(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.BaseMarker, jsonapi.base.schema.IDAttribute

Works like Attribute, but must be used for the id attribute.

class jsonapi.marker.method.to_one_relationship(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.BaseMarker, jsonapi.base.schema.ToOneRelationship

This marker can be used to mark a to-one relationship on a class:

class Post(object):

    @to_one_relationship()
    def author(self):
        '''
        This method may either return a JSONapi identifier, a two tuple
        `(typename, id)` with the id or the actual *author* object.
        '''
        return self._author

    @author.setter
    def set_author(self, new_author):
        '''
        *new_author* is either None or the new author object. **Not**
        just a simple identifier.
        '''
        self._new_author = new_author
        return None
Seealso:to_many_relationship
class jsonapi.marker.method.to_many_relationship(fget=None, fset=None, fdel=None, fadd=None, fextend=None, frem=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.BaseMarker, jsonapi.base.schema.ToManyRelationship

This marker can be used to mark a to-many relationship on a class:

class Post(object):

    @ToManyRelationship()
    def comments(self):
        return self._comments

    @comments.setter
    def set_comments(self, comments):
        '''
        *comments* is a list of comment objects.
        '''
        self._comments = comments
        return None

    @comments.adder
    def add_comment(self, comment):
        self._comments.append(comment)
        return None
Seealso:

ToOneRelationship

Parameters:
  • fget
  • fset
  • fdel
  • fadd – A function used for adding a new object to the relationship.
  • fextend – A funtion used to add a list of objects to the relationship.
  • doc
  • name
adder(f)[source]

Descriptor to change the fadd function.

extender(f)[source]

Descriptor to change the fextend function.

add(resource, relative)[source]
extend(resource, relatives)[source]
class jsonapi.marker.method.constructor[source]

Bases: classmethod, jsonapi.base.schema.Constructor

Can be used, to mark a constructor method. Please note, that a constructor function is always a classmethod.

If you don’t use a special constructor method, the default __init__ constructor is used automatic.

class User(object):

    @constructor()
    def create(cls, name, email, posts):
        '''
        This method receives all attributes and related resources
        sent in the POST request, which is used to create a new
        resource.
        '''
        new_user = cls()
        new_user.set_email(email)
        new_user.set_posts(posts)
        return new_user
create(**kargs)[source]

Calls the decorated method and returns the result.

jsonapi.marker.property

This module contains decorators for properties.

class User(object):

    @attribute()
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name.strip()
        return None

# Using the *property* decorators will turn the methods into properties.
user = User()
user.name = "Homer"
print(user.name)
class jsonapi.marker.property.attribute(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.PropertyMixin, jsonapi.marker.method.attribute

The same as jsonapi.marker.method.attribute, but emulates a Python property().

class jsonapi.marker.property.id_attribute(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.PropertyMixin, jsonapi.marker.method.id_attribute

The same as jsonapi.marker.method.id_attribute, but emulates a Python property().

class jsonapi.marker.property.to_one_relationship(fget=None, fset=None, fdel=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.PropertyMixin, jsonapi.marker.method.to_one_relationship

The same as jsonapi.marker.method.to_one_relationship, but emulates a Python property().

class jsonapi.marker.property.to_many_relationship(fget=None, fset=None, fdel=None, fadd=None, fextend=None, frem=None, doc=None, name=None)[source]

Bases: jsonapi.marker.method.PropertyMixin, jsonapi.marker.method.to_many_relationship

The same as jsonapi.marker.method.to_many_relationship, but emulates a Python property().