<commit_before># Models

Appier has a data layer used to abstract the user from the underlying data source.
Currently, the data layer only supports [MongoDB](http://www.mongodb.org/), 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:

```python
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:

* `str` - String (this is the default type)
* `unicode` - Unicode (eg: `Eyjafjallajökull`)
* `int` - Integer (eg: `5`)
* `bool` - Boolean (eg: `True`)
* `float` - Float (eg: `1.3`)
* `list` - List of values (eg: `["a"]`)
* `dict` - Key-value dictionary (eg: `{"a": 1}`)
* `appier.File` - Python file object
* `appier.Files` - List of Python file objects
* `appier.ImageFile` - Specialized type file for images (allows resizing, etc.)
* `appier.ImageFiles` - Sequence based data type for the image type
* `appier.reference` - Non relational equivalent of the foreigh reference/key
* `appier.references` - Multiple items (to many) version of the reference type

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

* `index` - Boolean indicating if an index should be created for this attribute in
the data source (faster searches)
* `increment` - Flag indicating if the value should be automatically generated on
persistence by adding 1 to the previously generated value
* `default` - Indicates that the attribute is the `default` representation for the model
(useful for search operations to be able to decide which attribute to search by default).
In case there are multiple defaults in the hierarchy (eg: `Cat` has a default attribute
and it inherits from `Animal`, which also has its own default attribute), then the most
concrete level of the hierarchy has priority (`Cat` in the previous example).
* `safe` - Safe attributes cannot be set automatically with the `apply` operation
* `private` - Private attributes are not retrieved in `get` or `find` operations (useful
to keep passwords safe for example). This behaviour can be bypassed by passing
`rules = False` to these methods
* `immutable` - Immutable attributes cannot be modified, they can only be set at creation time

## Persistence

To create a cat just do:

```python
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:

```python
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 mapping behaviour can
also be performed on a cat that already exists:

```python
cat.apply()
```

If you want to delete the cat then do:

```python
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:

```python
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:

```python
{
    '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`
keyword when defining the model validators:

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

The following validation methods are available in Appier:

* `eq` - equal to specified value (eg: `appier.eq("age", 5)`)
* `gt` - greater than specified value
* `gte` - greater than or equal to specified value
* `lt` - less than specified value
* `lte` - less than or equal to specified value
* `not_null` - not equal to null (eg: `appier.not_null("name")`)
* `not_empty` - not an empty collection like a string or an array
* `not_false` - not equal to `False`
* `is_in` - in the specified list of values (eg: `appier.is_in("name", ("Garfield", "Catbert")`)
* `is_simple` - simple enough to be safely used as part of an URL
* `is_email` - is a valid email
* `is_url` - is a valid URL
* `is_regex` - matches regular expression (eg: `appier.is_regex("name", "cat*")`)
* `field_lt` - less than the value of another attribute (eg: `appier.field_eq("age", "birth_year")
* `field_lte` - less than or equal to the value of another attribute<commit_msg>small syntax fix<commit_after># Models

Appier has a data layer used to abstract the user from the underlying data source.
Currently, the data layer only supports [MongoDB](http://www.mongodb.org/), 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:

```python
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:

* `str` - String (this is the default type)
* `unicode` - Unicode (eg: `Eyjafjallajökull`)
* `int` - Integer (eg: `5`)
* `bool` - Boolean (eg: `True`)
* `float` - Float (eg: `1.3`)
* `list` - List of values (eg: `["a"]`)
* `dict` - Key-value dictionary (eg: `{"a": 1}`)
* `appier.File` - Python file object
* `appier.Files` - List of Python file objects
* `appier.ImageFile` - Specialized type file for images (allows resizing, etc.)
* `appier.ImageFiles` - Sequence based data type for the image type
* `appier.reference` - Non relational equivalent of the foreigh reference/key
* `appier.references` - Multiple items (to many) version of the reference type

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

* `index` - Boolean indicating if an index should be created for this attribute in
the data source (faster searches)
* `increment` - Flag indicating if the value should be automatically generated on
persistence by adding 1 to the previously generated value
* `default` - Indicates that the attribute is the `default` representation for the model
(useful for search operations to be able to decide which attribute to search by default).
In case there are multiple defaults in the hierarchy (eg: `Cat` has a default attribute
and it inherits from `Animal`, which also has its own default attribute), then the most
concrete level of the hierarchy has priority (`Cat` in the previous example).
* `safe` - Safe attributes cannot be set automatically with the `apply` operation
* `private` - Private attributes are not retrieved in `get` or `find` operations (useful
to keep passwords safe for example). This behaviour can be bypassed by passing
`rules = False` to these methods
* `immutable` - Immutable attributes cannot be modified, they can only be set at creation time

## Persistence

To create a cat just do:

```python
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:

```python
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 mapping behaviour can
also be performed on a cat that already exists:

```python
cat.apply()
```

If you want to delete the cat then do:

```python
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:

```python
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:

```python
{
    '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`
keyword when defining the model validators:

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

The following validation methods are available in Appier:

* `eq` - equal to specified value (eg: `appier.eq("age", 5)`)
* `gt` - greater than specified value
* `gte` - greater than or equal to specified value
* `lt` - less than specified value
* `lte` - less than or equal to specified value
* `not_null` - not equal to null (eg: `appier.not_null("name")`)
* `not_empty` - not an empty collection like a string or an array
* `not_false` - not equal to `False`
* `is_in` - in the specified list of values (eg: `appier.is_in("name", ("Garfield", "Catbert")`)
* `is_simple` - simple enough to be safely used as part of an URL
* `is_email` - is a valid email
* `is_url` - is a valid URL
* `is_regex` - matches regular expression (eg: `appier.is_regex("name", "cat*")`)
* `field_lt` - less than the value of another attribute (eg: `appier.field_eq("age", "birth_year"`)
* `field_lte` - less than or equal to the value of another attribute
{'fraction_non_alphanumeric': 0.09620362381363244, 'fraction_numerical': 0.001725625539257981, 'mean_word_length': 3.8763146823727386, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5471075', 'n_tokens_mistral': 3306, 'n_tokens_neox': 3209, 'n_words': 1623}