Models you create using BaseModel look similar to your Pydantic or Django models. 

The purpose of your models is to validate your data, if you want to normalize it and save it using [dbm](https://docs.python.org/3/library/dbm.html). 
If you wish, you can use your model to perform simple operations on your related data,
for example; `get`, `filter`, `all`, `update` and `save`.

Pydbm can perform type validation, custom validation and custom normalization.

## Basic model usage

```python
from pydbm import BaseModel

__all__ = ["UserModel"]

class UserModel(BaseModel):
    username: str
```

User here is a model with one field; username. Field of username is str type, and it is required field.
Also, It includes a one more field named `id` and `pk`. This field is automatically generated by Pydbm
and these fields are equal.

Now, let's create a user.

```python
user = UserModel(username="hakancelik")
```
user is an instance of UserModel. It has a username field and id field.
If no `ValidationError` raises, it means that the data is valid.

Let's get attributes of user.

```python
assert user.username == "hakancelik"
assert user.id == "20087fb9cf70fb074239826d21c18ea3"
assert user.pk == "20087fb9cf70fb074239826d21c18ea3"

assert user.fields == {'username': 'hakancelik'}
```


## Default values
If you want to set a default value for a field, you can assign a default value to the field directly.

```python
from pydbm import BaseModel

__all__ = ["UserModel"]

class UserModel(BaseModel):
    salt: str = "my-unpredictable-salt"
    username: str
```

While id generates in my new user model, I set a salt value to generate different in my model,
now my id will be different from the one above.

```python
user = UserModel(username="hakancelik")

assert user.username == "hakancelik"
assert user.id == "908cbfedd18749b69a39d3771f6762a2"
assert user.pk == "908cbfedd18749b69a39d3771f6762a2"

assert user.fields == {'username': 'hakancelik'}
```
As you can see with this example, the values of all fields are taken by default during id generation.
But If you want you can change of the fields while generating id.


## Unique Together

```python
from pydbm import BaseModel

__all__ = ["UserModel"]

class UserModel(BaseModel):
    salt: str = "my-unpredictable-salt"
    username: str

    class Config:
        unique_together = ("username",)
```

Now, We create a new user same as above, user's id will be same.

```python
user = UserModel(username="hakancelik")

assert user.username == "hakancelik"
assert user.id == "20087fb9cf70fb074239826d21c18ea3"
assert user.pk == "20087fb9cf70fb074239826d21c18ea3"

assert user.fields == {'username': 'hakancelik'}
```

## Table Name
In Pydbm, default table name is model name. You can change or control it by using `table_name` attribute in `Config` class.

```python
from pydbm import BaseModel

__all__ = ["UserModel"]

class UserModel(BaseModel):
    salt: str = "my-unpredictable-salt"
    username: str

    class Config:
        unique_together = ("username",)
        table_name = "users"
```

Now even if the class name changes, you will not lose your data.

---

## Operations Supported by the Model
So far, we have written a simple model example and got its values, 
but we have not performed any data operations yet, let's examine what's available.


### Save
Save method is used to save the valid data to the database.

```python
user = UserModel(username="hakancelik")
user.save()
```
That's it, now we have saved our user to the database.


### Get
Get method is used to get the data from the database and return it as a model instance.

```python
user = UserModel.get(user.id, default=None)
```

If the user exists in the database, it will return the user, otherwise it raises `DoesNotExists` exception.


### Update
Update method is used to update the data in the database, when you use this method id is not changed.

```python
user = UserModel.get(user.id)
user.update(username="hakan")

assert user.username == "hakan"
assert user.id == "908cbfedd18749b69a39d3771f6762a2"  # id is not changed  
```


### Delete
Delete user from database.
    
```python
user = UserModel.get(user.id)
user.delete()
```


### Create

Create method is used to save the data to the database and return the data as a model instance.

```python
user = UserModel.create(username="hakancelik")
```

It is the same as save, but it returns the user.


### All

All method is used to get all data from the database and iterate model instances.

```python
users = list(UserModel.all())
```


### Filter

Filter method is used to filter data from the database and iterate model instances.
You can filter data by using the fields of the model.

```python
users = list(UserModel.filter(username="hakancelik"))
```


## Model properties

`dict()`

returns a dictionary of the model's fields and values.

`fields`

returns a dictionary of the model's fields and values.


## Field

By using the Field, it is possible to define the default value, default factory, validators and normalizations of the field.
If type of field is int, then it can be validated min_value and max_value.

> Type validation is performed automatically, you do not need to define it.
> Just define the type annotation of the field.


### Default value
When it is used, then the field is a not required field.

```python
from pydbm import BaseModel, Field

__all__ = ["UserModel"]

class UserModel(BaseModel):
    salt: str = Field(default="my-unpredictable-salt")
    username: str
```

### Default factory
When it is used, then the field is a not required field.

```python
from pydbm import BaseModel, Field

__all__ = ["UserModel"]

class UserModel(BaseModel):
    salt: str = Field(default_factory=lambda: "my-unpredictable-salt")
    username: str
```

### Custom Normalization
It can be defined list of normalization functions, to normalize the field value.

```python
from pydbm import BaseModel, Field

__all__ = ["UserModel"]

class UserModel(BaseModel):
    username: str = Field(normalize=[lambda x: x.lower()])
```

In here, username field will be lowered case.

### Custom Validation
We have some built-in validators, but you can also create your own validator.
For more information, see [Validators](/validators).

```python
from pydbm import BaseModel, Field

__all__ = ["UserModel"]

class UserModel(BaseModel):
    username: str = Field(
        validators=[lambda value: value.startswith("@")]
    )
```
