# Recipes

If you're not comfortable with random data or even if you just want to
improve the semantics of the generated data, there's hope for you.

You can define a **Recipe**, which is a set of rules to generate data
for your models.

It's also possible to store the Recipes in a module called *baker_recipes.py*
at your app's root directory. This recipes can later be used with the
`make_recipe` function:

```
shop/
  migrations/
  __init__.py
  admin.py
  apps.py
  baker_recipes.py   <--- where you should place your Recipes
  models.py
  tests.py
  views.py
```

File: **baker_recipes.py**

```python
from model_bakery.recipe import Recipe
from shop.models import Customer

customer_joe = Recipe(
    Customer,
    name='John Doe',
    nickname='joe',
    age=18,
    birthday=date.today(),
    last_shopping=datetime.now()
)
```

```{note}
You don't have to declare all the fields if you don't want to. Omitted fields will be generated automatically.
```

File: **test_model.py**

```python
from django.test import TestCase

from model_bakery import baker

class CustomerTestModel(TestCase):

    def setUp(self):
        # Load the recipe 'customer_joe' from 'shop/baker_recipes.py'
        self.customer_one = baker.make_recipe(
            'shop.customer_joe'
        )
```

Or if you don't want a persisted instance:

```python
from model_bakery import baker

baker.prepare_recipe('shop.customer_joe')
```

```{note}
You don't have to place necessarily your `baker_recipes.py` file inside your app's root directory.
If you have a tests directory within the app, for example, you can add your recipes inside it and still
use `make_recipe`/`prepare_recipe` by adding the tests module to the string you've passed as an argument.
For example: `baker.make_recipe("shop.tests.customer_joe")`

So, short summary, you can place your `baker_recipes.py` **anywhere** you want to and to use it having in mind
you'll only have to simulate an import but obfuscating the `baker_recipes` module from the import string.
```

```{note}
You can use the \_quantity parameter as well if you want to create more than one object from a single recipe.
```

You can define recipes locally to your module or test case as well. This can be useful for cases where a particular set of values may be unique to a particular test case, but used repeatedly there. For example:

File: **baker_recipes.py**

```python
company_recipe = Recipe(Company, name='WidgetCo')
```

File: **test_model.py**

```python
class EmployeeTest(TestCase):
    def setUp(self):
        self.employee_recipe = Recipe(
            Employee,
            name=seq('Employee '),
            company=baker.make_recipe('app.company_recipe')
        )

    def test_employee_list(self):
        self.employee_recipe.make(_quantity=3)
        # test stuff....

    def test_employee_tasks(self):
        employee1 = self.employee_recipe.make()
        task_recipe = Recipe(Task, employee=employee1)
        task_recipe.make(status='done')
        task_recipe.make(due_date=datetime(2014, 1, 1))
        # test stuff....
```

## Recipes with foreign keys

You can define `foreign_key` relations:

```python
from model_bakery.recipe import Recipe, foreign_key
from shop.models import Customer, PurchaseHistory

customer = Recipe(Customer,
    name='John Doe',
    nickname='joe',
    age=18,
    birthday=date.today(),
    appointment=datetime.now()
)

history = Recipe(PurchaseHistory,
    owner=foreign_key(customer)
)
```

Notice that `customer` is a *recipe*.

You may be thinking: "I can put the Customer model instance directly in the owner field". That's not recommended.

Using the `foreign_key` is important for 2 reasons:

- Semantics. You'll know that attribute is a foreign key when you're reading;
- The associated instance will be created only when you call `make_recipe` and not during recipe definition;

You can also use `related`, when you want two or more models to share the same parent:

```python
from model_bakery.recipe import related, Recipe
from shop.models import Customer, PurchaseHistory

history = Recipe(PurchaseHistory)
customer_with_2_histories = Recipe(Customer,
    name='Albert',
    purchasehistory_set=related('history', 'history'),
)
```

Note this will only work when calling `make_recipe` because the related manager requires the objects in the related_set to be persisted. That said, calling `prepare_recipe` the related_set will be empty.

If you want to set m2m relationship you can use `related` as well:

```python
from model_bakery.recipe import related, Recipe

pencil = Recipe(Product, name='Pencil')
pen = Recipe(Product, name='Pen')
history = Recipe(PurchaseHistory)

history_with_prods = history.extend(
    products=related(pencil, pen)
)
```

When creating models based on a `foreign_key` recipe using the `_quantity` argument, only one related model will be created for all new instances.

```python
from model_baker.recipe import foreign_key, Recipe

person = Recipe(Person, name='Albert')
dog = Recipe(Dog, owner=foreign_key(person))

# All dogs share the same owner
dogs = dog.make_recipe(_quantity=2)
assert dogs[0].owner.id == dogs[1].owner.id
```

This will cause an issue if your models use `OneToOneField`. In that case, you can provide `one_to_one=True` to the recipe to make sure every instance created by `_quantity` has a unique id.

```python
from model_baker.recipe import foreign_key, Recipe

person = Recipe(Person, name='Albert')
dog = Recipe(Dog, owner=foreign_key(person, one_to_one=True))

# Each dog has a unique owner
dogs = dog.make_recipe(_quantity=2)
assert dogs[0].owner.id != dogs[1].owner.id
```

## Recipes with callables

It's possible to use `callables` as recipe's attribute value.

```python
from datetime import date
from model_bakery.recipe import Recipe
from shop.models import Customer

customer = Recipe(
    Customer,
    birthday=date.today,
)
```

When you call `make_recipe`, Model Bakery will set the attribute to the value returned by the callable.

## Recipes with iterators

You can also use *iterators* (including *generators*) to provide multiple values to a recipe.

```python
from itertools import cycle

names = ['Ada Lovelace', 'Grace Hopper', 'Ida Rhodes', 'Barbara Liskov']
customer = Recipe(Customer,
    name=cycle(names)
)
```

Model Bakery will use the next value in the *iterator* every time you create a model from the recipe.

## Sequences in recipes

Sometimes, you have a field with an unique value and using `make` can cause random errors. Also, passing an attribute value just to avoid uniqueness validation problems can be tedious. To solve this you can define a sequence with `seq`

```python
from model_bakery.recipe import Recipe, seq
from shop.models import Customer

CustomerRecipe = Recipe(Customer,
    name=seq('Joe'),
    age=seq(15)
)

customer = baker.make_recipe('shop.CustomerRecipe')
customer.name  # 'Joe1'
customer.age  # 16

new_customer = baker.make_recipe('shop.CustomerRecipe')
new_customer.name  # 'Joe2'
new_customer.age  # 17
```

This will append a counter to strings to avoid uniqueness problems, and it will sum the counter with numerical values.

An optional `suffix` parameter can be supplied to augment the value for cases like generating emails
or other strings with common suffixes.

```python
from model_bakery import.recipe import Recipe, seq
from shop.models import Customer

CustomerRecipe = Recipe(Customer, email=seq('user', suffix='@example.com'))

customer = baker.make_recipe('shop.CustomerRecipe')
customer.email  # 'user1@example.com'

customer = baker.make_recipe('shop.CustomerRecipe')
customer.email  # 'user2@example.com'
```

Sequences and iterables can be used not only for recipes, but with `baker` as well:

```python
from model_bakery import baker

customer = baker.make('Customer', name=baker.seq('Joe'))
customer.name  # 'Joe1'

customers = baker.make('Customer', name=baker.seq('Chad'), _quantity=3)
for customer in customers:
     print(customer.name)
#  'Chad1'
#  'Chad2'
#  'Chad3'
```

You can also provide an optional `increment_by` argument which will modify incrementing behaviour. This can be an integer, float, Decimal or timedelta. If you want to start your increment differently, you can use the `start` argument, only if it's not a sequence for `date`, `datetime` or `time` objects.

```python
from datetime import date, timedelta
from model_bakery.recipe import Recipe, seq
from shop.models import Customer


CustomerRecipe = Recipe(Customer,
    age=seq(15, increment_by=3)
    height_ft=seq(5.5, increment_by=.25)
    # assume today's date is 21/07/2014
    appointment=seq(date(2014, 7, 21), timedelta(days=1)),
    name=seq('Custom num: ', increment_by=2, start=5),
)

customer = baker.make_recipe('shop.CustomerRecipe')
customer.age  # 18
customer.height_ft  # 5.75
customer.appointment  # datetime.date(2014, 7, 22)
customer.name  # 'Custom num: 5'

new_customer = baker.make_recipe('shop.CustomerRecipe')
new_customer.age  # 21
new_customer.height_ft  # 6.0
new_customer.appointment  # datetime.date(2014, 7, 23)
customer.name  # 'Custom num: 7'
```

Be aware that `seq` may query the database to determine when to reset. Therefore, a `SimpleTestCase` test method (which disallows database access) can call `prepare_recipe` on a Recipe with a `seq` once, but not not more than once within a test, even though the record itself is never saved to the database.

## Overriding recipe definitions

Passing values when calling `make_recipe` or `prepare_recipe` will override the recipe rule.

```python
from model_bakery import baker

baker.make_recipe('shop.customer', name='Ada Lovelace')
```

This is useful when you have to create multiple objects and you have some unique field, for instance.

## Recipe inheritance

If you need to reuse and override existent recipe call extend method:

```python
customer = Recipe(
    Customer,
    bio='Some customer bio',
    age=30,
    enjoy_jards_macale=True,
)
sad_customer = customer.extend(
    enjoy_jards_macale=False,
)
```
