Models

Appier has a data layer used to abstract the user from the underlying data source. Currently, the data layer only supports MongoDB, so be sure to install it before trying to add models to your app.

A database will be created automatically in MongoDB with name of the app, and collections will be created with the name of the models. Therefore, if a Cat model is defined in a app called HelloApp, a database named HelloApp will be created in MongoDB with a collection named cat inside it.

Model attributes can configured by adding keywords to their declaration:

class Cat(appier.Model):

    id = appier.field(
        type = int,
        index = True,
        increment = True
    )

    name = appier.field(
        type = unicode,
        index = True
    )

An attribute can be one of the following types:

The following keywords can be added to configure the attribute further:

Persistence

To create a cat just do:

cat = Cat()
cat.name = "Garfield"
cat.save()

Once the cat is saved, a value will be set in its id attribute, due to the increment flag being set in the model definition (eg: 1, 2). To update the cat, just make the changes and call save again.

To create the cat and have form data be automatically set in it do this:

cat = Cat.new()
cat.save()

Creating a cat this way will make a form data attribute named name be applied to the name model attribute. The same form of mapping behaviour can also be performed on a cat that already exists:

cat.apply()

If you want to delete the cat then do:

cat.delete()

Validation

When the save method is called on an entity, it will validate the model first. The entity will only be saved in case all validations defined for that model pass. The validate method must be implemented to define which validations should be executed before the entity is saved:

class Cat(appier.Model):

    name = appier.field(
        type = unicode
    )

    age = appier.field(
        type = int
    )

    @classmethod
    def validate(cls):
        return super(Cat, cls).validate() + [
            appier.not_null("name"),
            appier.not_empty("name"),
            appier.gte("age", 5)
        ]

In the previous example, if a Cat entity was saved with the name attribute unset or set as an empty string, then the entity would not be saved and the appier.exceptions.ValidationError exception would be raised. This exception has an errors attribute which includes the failed validations. Upon a validation having failed, an errors map could look like the following:

{
    'name': ['value is null', 'value is empty'],
    'age': ['is not greater than 5']
}

The messages after each failed validation are created using the defaults associated with the validation methods. These messages can be customized by specifying the message the keyword when defining the model validators:

appier.not_empty("name", message = "Please specify the name (mandatory)")

The following validation methods are available in Appier:

In case there is a situation where we want to execute an extra validation method for a specific entity, but not to all entities, we can add that validation method in runtime. For example, if we wanted to run a password strength validator at the time of an account creation, we would first have to add that validator definition method to the hypothetical Account model:

@classmethod
def validate_password_strength(cls):
    return [
        appier.string_gt("_password", 5)
    ]

Afterward, in the place where the signup logic was being executed (eg: a signup handler in a controller), we would need to tell the account instance to execute that validation well, before calling the save method:

account.validate_extra("password_strength")
account.save()

Retrieval

You can retrieve a cat named Garfield by doing the following:

number_cats = Cat.count()
number_garfields = Cat.count(name = "Garfield")

Or retrieve all cats, and find all cats named Garfield:

cats = Cat.find()
garfields = Cat.find(name = "Garfield")

Or count all cats, as well as all named Garfield:

number_cats = Cat.count()
number_garfields = Cat.count(name = "Garfield")

The get, find, and count operations can all use the same kinds of filters. These filters can be simple equality filters, or more advanced:

not_garfield = Cat.get(name = {"$ne" : "Garfield"})
number_not_garfields = Cat.count(name = {"$ne" : "Garfield"})
number_garfields = Cat.find(name = {"$ne" : "Garfield"})

Advanced query operators like $ne are the same as the ones available in MongoDB. For documentation on those please read the MongoDB documentation.

The following is a list of extra-named arguments that can be passed to the find and get methods to configure the query and its results:

Referencing the App

In order to invoke methods that belong to the App object, one can access it through the owner attribute. For example, to resolve the URL for a route within a model:

class Cat(appier.Model):

    id = appier.field(
        type = int,
        index = True,
        increment = True
    )

    def get_show_url(self):
        url = self.owner.url_for("cats.show", id = self.id)
        return url