Git Product home page Git Product logo

model_bakery's Introduction

Model Bakery: Smart fixtures for better tests

Build Coverage Latest PyPI version Documentation Status

Model Bakery offers you a smart way to create fixtures for testing in Django. With a simple and powerful API you can create many objects with a single line of code.

Model Bakery is a rename of the legacy Model Mommy project.

Install

pip install model-bakery

Usage and Info

Basic usage

# models.py

from django.db import models

class Customer(models.Model):
    enjoy_jards_macale = models.BooleanField()
    name = models.CharField(max_length=30)
    email = models.EmailField()
    age = models.IntegerField()
    bio = models.TextField()
    days_since_last_login = models.BigIntegerField()
    birthday = models.DateField()
    last_shopping = models.DateTimeField()

# test_models.py

from django.test import TestCase
from model_bakery import baker
from pprint import pprint

class TestCustomerModel(TestCase):
    def setUp(self):
        self.customer = baker.make('shop.Customer')
        pprint(self.customer.__dict__)

"""
{'_state': <django.db.models.base.ModelState object at 0x1129a3240>,
 'age': 3841,
 'bio': 'vUFzMUMyKzlnTyiCxfgODIhrnkjzgQwHtzIbtnVDKflqevczfnaOACkDNqvCHwvtWdLwoiKrCqfppAlogSLECtMmfleeveyqefkGyTGnpbkVQTtviQVDESpXascHAluGHYEotSypSiHvHzFteKIcUebrzUVigiOacfnGdvijEPrZdSCIIBjuXZMaWLrMXyrsUCdKPLRBRYklRdtZhgtxuASXdhNGhDsrnPHrYRClhrSJSVFojMkUHBvSZhoXoCrTfHsAjenCEHvcLeCecsXwXgWJcnJPSFdOmOpiHRnhSgRF',
 'birthday': datetime.date(2019, 12, 3),
 'enjoy_jards_macale': True,
 'id': 1,
 'last_shopping': datetime.datetime(2019, 12, 3, 21, 42, 34, 77019),
 'name': 'qiayYnESvqcYLLBzxpFOcGBIfnQEPx',
 'days_since_last_login': 6016}
"""

Check out documentation for more complete examples.

Contributing

Detailed info here.

Maintainers

Creator

model_bakery's People

Contributors

abbottc avatar adamchainz avatar adrianlc avatar amureki avatar anapaulagomes avatar ashiazed avatar avallbona avatar berinhard avatar bnjmn avatar cb109 avatar cclauss avatar ckrybus avatar cuducos avatar davidpalves avatar dependabot[bot] avatar edald123 avatar giovana-morais avatar honza-m avatar israelst avatar jairhenrique avatar knyghty avatar lassandro avatar maroux avatar michael-k avatar radwon avatar smileychris avatar timgates42 avatar timjk-gp avatar timjklein36 avatar vvvin333 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

model_bakery's Issues

Using make_recipe with _quantity together with related key only applies to last item created

Using make_recipe with _quantity together with related key only applies to last item created

Given this setup code:

mommy_recipes.py:

dog1 = recipe.Recipe(
     Dog
)

dog2 = recipe.Recipe(
    Dog
)

company = recipe.Recipe(
    Company
)

incomplete_person = recipe.Recipe(
    Person,
    dog_set=recipe.related(dog1, dog2)
)

complete_person = incomplete_person.extend(
    company=recipe.foreign_key(company)
)

In a test python file:

people = mommy.make_recipe("complete_person", _quantity=3)

Expected behavior

That each model in people has the 2 dog instances.

Actual behavior

Only the last item in people has the 2 dog instances. The first 2 are empty.

Reproduction Steps

How to reproduce this issue.

See summary above

Versions

Python: 3.6
Django: 1.9
Model Mommy: 1.6.0

Recipe post creation callback

Hi!

I'd like to hear if this is a feature that if not already possible somehow would be something worth considering. My use case is that I have models that are only ever sensible if they have a couple of related models, so creating these by hand after the main model feels cumbersome.

I know I can reference other recipes with recipe.foreign_key() and recipe.related() but these don't always help when for example two relations of a model are also related to each other.

What I'm after is a callback for a recipe that would run just after the recipe created the object, and then the object could be used within this callback to for example create related objects.

I guess what I'm after is something similar to factory boys PostGeneration / PostGenerationMethod but for model mommy recipes.

Document callable and iterable attribute values

It is documented that recipes can have callables and iterators as value here

The source code looks like you can do the same thing when not using custom recipes but rather just mommy.make(Class, **attrs):
https://github.com/berinhard/model_mommy/blob/2f8948ce1d8ee5c29d097d1890bcf2811ef0e80c/model_mommy/mommy.py#L282 and
https://github.com/berinhard/model_mommy/blob/2f8948ce1d8ee5c29d097d1890bcf2811ef0e80c/model_mommy/mommy.py#L284

Also, using it works perfectly fine:

>>> def random_name():
...     return random.choice(["Paul Molive", "Anna Mull", "Gail Forcewind"])
...
>>> users = mommy.make(UserProfile, _quantity=5, last_name=random_name)
>>> [u.last_name for u in users]
['Gail Forcewind', 'Anna Mull', 'Gail Forcewind', 'Anna Mull', 'Paul Molive']
>>> 
>>> name_iterable = ("Anna Sthesia", "Paige Turner", "Bob Frapples")
>>> users = mommy.make(UserProfile, _quantity=5, last_name=itertools.cycle(name_iterable))
>>> [u.last_name for u in users]
['Anna Sthesia', 'Paige Turner', 'Bob Frapples', 'Anna Sthesia', 'Paige Turner']

Is this a feature? If so, I think it should be documented here

Related issue: berinhard/model_mommy#81

Versions

Python: 3.7.1
Django: 2.2
Model Mommy: 1.6.0

DecimalRangeField support requested

TypeError: <class 'django.contrib.postgres.fields.ranges.DecimalRangeField'> is not supported by baker.

Expected behavior

Be able to get instance

What you expected.
instance is created

Actual behavior

TypeError: <class 'django.contrib.postgres.fields.ranges.DecimalRangeField'> is not supported by baker.

Reproduction Steps

  1. from django.contrib.postgres.fields import DecimalRangeField as a header
  2. Make a model with DecimalRangeField()
  3. baker.make(MyModel, _quantity=20)

Versions

Python: 3.8.2
Django: django==3.0.5
Model Bakery: model-bakery==1.1.0

More information about the model mommy rename

Today, an user who I'll remain anonymous, created an issue tittle Naming, gender stereotypes etc where he brought up a few questions about the project lib rename from model_mommy to model_bakery.

After my comment, the person asked me to deleted the issue because he thought he had expressed his thoughts in a bad manner which could lead people to think he was against the fighting inequality. Since I don't want this project to become a place where people start to get judged by what they've once said in a hidden Github issue, I'm documenting my replies to his questions because I think they can be helpful if someone brings up the same discussion in the future.

Original content

From @berinhard:

Hi ANON_USER, thanks for your comment. I'd like to reply to your questions making sure that my answers reflect only my personal opinions and they don't say anything about the other maintainers points of view.

How does the renaming of the project help to fight gender stereotypes in tech?

I think that just the existence of this issue is an example of how it can help. This whole discussion won't even exist on this public space if we didn't rename the project and openly address it with the goal of to not reinforce gender stereotypes.

But, even so, I'm maybe not the best person to answer your question properly. Because, first of all, I identify myself as a man, so I don't suffer from any kind of prejudice based on my gender and, second, there are a lot of people who really study and debate this question deeper than I'll ever be able to. In the Python community, for example, Naomi Ceder is doing an incredible work on this direction. I highly recommend you to watch, at least, her talk Antipatterns for Diversity where she touches this gender stereotypes discussion. If you have time and interest, I also recommend you to watch Farewell and Welcome Home: Python in Two Genders at PyCon 2014 where she talks from a more personal point of view about this and other topics.

But I can share with you my personal experience on this process. What I've seen and heard were a a lot of complaints from many people who I admire about the name of the lib. I've even had a very productive discussion about it when I submitted a talk to DjangoCon in 2015, if I'm not wrong about the year. There were also people coming to me and openly saying that they won't use the lib because of clients who were offended by the name and don't want it on their projects. So, even though I couldn't properly understand all the reasons behind it at the time, the reality was a single one: the project name sounds bad one for other people.

I'm a very practical person and when I face the fact that things that I do can potentially harm other people, I just try to change my behavior to prevent this. Simple as that. I wasn't able to maintain the project anymore knowing this and I only had two possible outcomes: drop the project or propose other ways to evolve it. That's when @amureki and @anapaulagomes offered the help to maintain the project and that's when we started to work together on the rename and moved the repo from my personal Github account to this organization.

Are you sure the old project name was based on gender stereotypes in tech rather than biological roles?

I'd be speculating if I answer you that and I'd prefer not to do so. I'm not the project creator and I can't talk about the old name decision. The person who created, a good friend of mine, is no longer maintaining the project and gave me free pass to any kind of project design decision at the time he left, including the project rename.

Anyway, as I strongly refuses the idea of reducing people's genders to biological roles, as many researchers also do, I can only think that the old project name reinforces gender stereotypes.

How come there's other maintained projects of the contributors that look even more stereotypical (grandma)? Are there any plans to rename them as well?

This question does not apply to model bakery or the rename discussion.

Do you think it would be a good idea to contact the creators of FactoryGirl and ObjectDaddy and ask them to rename their projects, too?

I really like this idea, although I won't be the person to do so because maintaining model bakery and my other personal projects already consumes a lot the free time that I devote to free software contributions. If anyone else wants to do so and thinks that model bakery's rename is a good example to use, feel free to do so =)


Now, ANON_USER since you've done a research on the contributors personal projects and profiles, I couldn't avoid doing the same with you. I noticed that this very elaborated issue is practically your only open activity in Github for the last 2 years, so I'm assuming this discussion is very important to you and I'm glad you've started it.

But, if you allow me to, I'd also like to invite you to have a more frequent open source activity on Github by not only creating issues, but also collaborating with the code itself. People spend a good amount of their personal time creating and maintaining projects so other people, like you and me, don't have to do so. Although this "business model" is not the ideal one, is the only one we have developed so far as open source development and I'd like to welcome you to help growing free software as well.

I don't know how much time did you take to open this issue or to study our personal profiles, but I'd like to invite you to do the same with our open issues and maybe help us to improve the project. For example, I really liked your critique about the Kid model in our docs. This is something that I didn't notice and can be improved. Feel free to open an issue and help us with that!

If you're not interested on helping with model bakery, please do so with other projects. Code contributions from new people are crucial for a better and more diverse free software development environment.

I'm closing this issue since it won't result in any kind of change in the project's code base.

Add self `model_bakery.SELF` reference for explicit/future attribute set

I think there should be convenient way to reference to the self/future object

Expected behavior

   baker.make(
        'shop.Customer',
         username=model_bakery.FUTURE.first_name
        
    )

After that new object will have the same username as the first_name as an example

Actual behavior

Right now there is no any possible solution AFAIK

P.S. I just wanna migrate to that project but i think it misses that feature! :) I can try to implement/help that if this thing will be approved for sure

GitHub action (or other checker) to validate that CHANGELOG.md was updated

Since we are following https://keepachangelog.com/en/1.0.0/ it would be nice to remind if PR is missing the changelog.

Something like this might work:

.github/workflows/main.yml:

name: Changelog Check

on: [pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Check that CHANGELOG is touched
      run: make check-changelog

bin/check-changelog.sh:

DID_CHANGELOG_CHANGE=$(git diff remotes/origin/master --name-only | grep CHANGELOG.md)
if [ "$DID_CHANGELOG_CHANGE" = "" ]; then
  echo "CHANGELOG.md needs to be updated"
  exit 1
else
  exit 0
fi

Makefile:

check-changelog:
	bin/check-changelog.sh

Can't set the value of a DateTimeField with auto_now or auto_now_add

To reproduce

# models.py
from django.db import models

class MyModel(models.Model):
    some_date = models.DateTimeField(auto_now_add=True)

# test code
from model_mommy import mommy

thing = mommy.make(MyModel, some_date="1985-10-21")
print(thing.some_date)

Expected:

  • The specified date should be printed with time set to midnight

Actual:

  • The current datetime is printed

This is to do with the DateTimeField's pre_save method, which overrides the initial value with the current date if auto_now or auto_now_add are true.

inconsistent behaviour between `gen_from_choices` and `gen_string` (and others)

We're using model-bakery for our testing, and in relation with recipes we stumbled onto an inconsistency in the library. While gen_from_choices returns a callable that is then generates the random value when being called by baker.make_recipe, for example gen_string (and the others) directly return the random value.

Expected behavior

With the example Recipe as written here, I would expect one of the two behaviours:

everything returns a callable

MyModel = Recipe(
    'myapp.MyModel,
    first_name=gen_string(10),
    last_name=gen_string(10),
    user_type=gen_from_choices(USER_TYPES),
    is_staff=True, 
)

and each time I create a model instance with this recipe I get different values for first_name or last_name and also user_type.

everything returns a value

If I know everything returns a value, I would need to define the recipe in this way to achieve my goal of random values per creation:

MyModel = Recipe(
    'myapp.MyModel,
    first_name=lambda: gen_string(10),
    last_name=lambda: gen_string(10),
    user_type=lambda: gen_from_choices(USER_TYPES),
    is_staff=True, 
)

functools.partial also works.

Actual behavior

MyModel = Recipe(
    'myapp.MyModel,
    first_name=gen_string(10),
    last_name=gen_string(10),
    user_type=gen_from_choices(USER_TYPES),
    is_staff=True, 
)

With this recipe, what actually happens is:

  • first_name and last_name always have the same (random) value, since they are called on module-load
  • user_type has a random value generated per instance

Reproduction Steps

use the examples, add the imports, and use baker.make_recipe

Versions

Python: 3.6 / 3.7
Django: 2.2.8
Model Bakery: 1.0.2

Move from travis-ci.org (being deprecated) to travis-ci.com

Description

Currently, we are running travis build on their org service, which is being deprecated.
All public and private projects are supposed to run under travis-ci.com.

To do the switched we need to enable repo access to travis-ci.com and disable it on the old website. This should do the trick.

  • Update the readme badge

After update to 1.1.0 decimal fields is broken

After updating to 1.1.0 tests using models with decimal fields os broken with following trace:

../../.venv/lib/python3.6/site-packages/django/db/backends/base/operations.py:500: in adapt_decimalfield_value
    return utils.format_number(value, max_digits, decimal_places)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

value = Decimal('47109723.57'), max_digits = 7, decimal_places = 2

    def format_number(value, max_digits, decimal_places):
        """
        Format a number into a string with the requisite number of digits and
        decimal places.
        """
        if value is None:
            return None
        if isinstance(value, decimal.Decimal):
            context = decimal.getcontext().copy()
            if max_digits is not None:
                context.prec = max_digits
            if decimal_places is not None:
>               value = value.quantize(decimal.Decimal(".1") ** decimal_places, context=context)
E               decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

../../.venv/lib/python3.6/site-packages/django/db/backends/utils.py:239: InvalidOperation

Reproduction Steps

create model like this

class Account(models.Model):
    balance = models.DecimalField(max_digits=7, decimal_places=2)

baker.make(Account)

Versions

Python: 3.6
Django: 2.0
Model Bakery: 1.1.0

Inconsistent data across tests

I'm running into a weird issue where the data produced does not seem consistent when testing. I have a fairly simple setup for the Recipes where a date value moves one week forward with each iteration. In one test this works as expected, in two others it does not.

Expected behavior

I expect the values to be the same across tests.

Actual behavior

When running the debugger I can see that for the first test (Donations Per Month) there are indeed 36 values, and each one corresponds to the correct date range (Jan-Dec, and 2018-2020). When I look at the data for the second test (Donations Per Week), the data somehow changes and I get the following:

  • correct data for 2018 (1 entry per week for 50 weeks)
  • incorrect data for 2019 (2 entries in December only)
  • correct data for 2020 (1 entry per week for 50 weeks)
  • data for 2021 (48 entries January through November)

It also seems like the third test is using this data set, as instead of 3 entries (2018, 2019, 2020), I get 4 entries (2018, 2019, 2020, 2021).

Reproduction Steps

Here are my Recipes:

this_year = datetime.datetime.now().year
last_year = this_year - 1
two_years = this_year - 2

donation_types = ['credit', 'ach', 'check', 'cash']

donation_two_years = Recipe(
    Donations,
    donation_date=seq(datetime.date(two_years, 1, 1), datetime.timedelta(weeks=1)),
    payment_method=cycle(donation_types),
    net_amount=200,
)

donation_last_year = Recipe(
    Donations,
    donation_date=seq(datetime.date(last_year, 1, 1), datetime.timedelta(weeks=1)),
    payment_method=cycle(donation_types),
    net_amount=450,
)

donation_this_year = Recipe(
    Donations,
    donation_date=seq(datetime.date(this_year, 1, 1), datetime.timedelta(weeks=1)),
    payment_method=cycle(donation_types),
    net_amount=500,
)

And here is the test:

  # Set up the data
        self.two_years = baker.make_recipe('api.donation_two_years', _quantity=50)
        self.last_year = baker.make_recipe('api.donation_last_year', _quantity=50)
        self.this_year = baker.make_recipe('api.donation_this_year', _quantity=50)

    def test_donations_per_month(self):
        self.c.login(username=self.user, password=self.password)
        response = self.c.get(self.donations_month)
        self.assertEqual(len(response.data['labels']), 36)
        self.assertEqual(len(response.data['chart_data']), 36)
        self.assertEqual(response.data['chart_label'], 'Total Donations per Month')

    def test_donations_per_week(self):
        self.c.login(username=self.user, password=self.password)
        response = self.c.get(self.donations_week)
        self.assertEqual(len(response.data['labels']), 150)
        self.assertEqual(len(response.data['chart_data']), 150)
        self.assertEqual(response.data['chart_label'], 'Donations Per Week')

    def test_donations_per_year(self):
        self.c.login(username=self.user, password=self.password)
        response = self.c.get(self.donations_year)
        self.assertEqual(len(response.data['labels']), 3)
        self.assertEqual(len(response.data['chart_data']), 3)
        self.assertEqual(response.data['chart_label'], 'Total Donations Per Year')

Versions

Python: 3.7.7
Django: 3.0.5
Model Bakery: 1.1.0

Generator not being used correctly when doing batch create

Short summary.

Using itertools.cycle doesn't seem to be working correctly when passing a value for _quantity to baker.make.

Expected behavior

I bake up a batch of instances.

Actual behavior

In [12]: baker.make('sgauth.User', username=cycle(['foo', 'bar']), _quantity=2)
IntegrityError: duplicate key value violates unique constraint "sgauth_user_username_key"
DETAIL:  Key (username)=(<itertools.cycle object at 0x11c6ffbd8>) already exists.

Versions

Python: 2.7.16
Django: 1.11.23
Model Bakery: 1.0.2

Ability to load custom fields for Model Mommy to also generate

I'd like to be able to load a custom fields list to model_mommy so that Models with these fields can also be generated.

Maybe this is similar to a Recipe but it would be a one time config load of fields that comply to an interface potentially, and then mommy.make(MyModelWithACustomField) would be able to still generate the Model instance. Thus avoiding the error: <Field> is not supported by mommy

Use a name convention for placeholders for files

The placeholder files for image and file fields have two different naming conventions:

model_bakery/mock_file.txt
model_bakery/mock-img.jpeg

mock-img.jpeg should use underline as well. The MANIFEST.in file should be updated with the new name.

Unable to tell a recipe that it should not generate relation

Short summary

If we have a recipe where we want to override the creation of a ForeignKey, by setting it to None, this does not work as expected. The ForeignKey is created, and then afterwards set to None. This makes it hard if we're interested in doing counts of objects in test, as there'll be "phantom" objects floating around.

Expected behavior

I expected this test to pass:
Example:

#mommy_recipes.py:
order = Recipe(Order, location=foreign_key(location))
offloading = Recipe(
    Offloading, facility=foreign_key(facility), standard_order=foreign_key(order), amount=201.88
)

#test.py
def test_model_mommy():
    service = mommy.make_recipe('facilities.offloading', standard_order=None, template_order=template)

    assert service.standard_order is None # True
    assert Order.objects.count() is 0 # FALSE! One order has been created!

Actual behavior

A phantom object is created, and the test fails. This is really troublesome if you're doing counts in your tests, as sometimes they won't be right and it's very hard to figure out why.

After looking at the code I've determined where the issue is - it's in Recipe::_mapping:

    def _mapping(self, new_attrs):
        _save_related = new_attrs.get('_save_related', True)
        rel_fields_attrs = dict((k, v) for k, v in new_attrs.items() if '__' in k)
        new_attrs = dict((k, v) for k, v in new_attrs.items() if '__' not in k)
        mapping = self.attr_mapping.copy()
        for k, v in self.attr_mapping.items():
            # do not generate values if field value is provided
           # <<<<--- The mistake is on the next line. attrs.get() will return None both if the field
          # does not exist, AND if the field is None. So if I set the field to None, it will *not* skip 
          # model creation
            if new_attrs.get(k):
                continue
            elif mommy.is_iterator(v):
                if isinstance(self._model, string_types):
                    m = finder.get_model(self._model)
                else:
                    m = self._model
                if k not in self._iterator_backups or m.objects.count() == 0:
                    self._iterator_backups[k] = itertools.tee(
                        self._iterator_backups.get(k, [v])[0]
                    )
                mapping[k] = self._iterator_backups[k][1]
            elif isinstance(v, RecipeForeignKey):
                a = {}
                for key, value in list(rel_fields_attrs.items()):
                    if key.startswith('%s__' % k):
                        a[key] = rel_fields_attrs.pop(key)
                recipe_attrs = mommy.filter_rel_attrs(k, **a)
                if _save_related:
                    mapping[k] = v.recipe.make(**recipe_attrs)
                else:
                    mapping[k] = v.recipe.prepare(**recipe_attrs)
            elif isinstance(v, related):
                mapping[k] = v.make()
       # Here we update the mapping with the new_attrs, that also transfers None. This is why
       # the final object has the attribute correctly set to None
        mapping.update(new_attrs)
        mapping.update(rel_fields_attrs)
        return mapping

A fix I've monkeypatched my own recipe is reasonably simple, just use an empty class to distinguish from user-supplied None, and "no-field", DRF does the same:

# empty class
class empty:
    pass

#recipe::_mapping check then looks like this
if new_attrs.get(k, empty) is not empty:
      continue

I've confirmed this fixes the issue. I'm willing to submit a PR if you'll accept it.

Versions

Python: 3.7
Django: 2.2
Model Mommy: 1.6.0

Support postgres date range fields

Currently model bakery does not support postgres' range fields DateRangeField and DatetimeRangeField.

In #80 I've added support to the numeric range fields, but the date/datetime ones are still missing.

Expected behavior

Model bakery should create random values for models with fields as django.contrib.postgres.fields.DateRangeField or django.contrib.postgres.fields.DatetimeRangeField.

Actual behavior

The following error is being raised:

TypeError: <class 'django.contrib.postgres.fields.ranges.DateRangeField'> is not supported by baker.

Reproduction Steps

How to reproduce this issue.

  1. Add DateRangeField and DatetimeRangeField as new fields in our test model;
  2. The tests will start to fail with the given TypeError exception;

Versions

Python: 3.8
Django: 3.0
Model Bakery: 1.1.0

baker.make only creates value for username on Django's User model

Short summary.
I'm not sure if I'm missing something, and please let me know if I am, but whenever I create a user with baker.make it creates a user but only username has value in it. I'd expect .make to generate values for the other fields it supports, like EmailField.

Expected behavior

baker.make(User) would create random values for all fields it supports, including EmailField.

Actual behavior

It doesn't create an email value for the user.

Reproduction Steps

How to reproduce this issue.

In [1]: from model_bakery import baker                                                                                                                                                                                                                                                            

In [2]: from django.contrib.auth.models import User                                                                                                                                                                                                                                               

In [3]: user = baker.make(User)                                                                                                                                                                                                                                                                   

In [4]: user.email                                                                                                                                                                                                                                                                                
Out[4]: ''

In [5]: user.username                                                                                                                                                                                                                                                                             
Out[5]: 'tGIavQwgHZslfbKYWZlhJQOmo [truncated]'

In [6]: user.first_name                                                                                                                                                                                                                                                                           
Out[6]: ''

In [7]: user.last_name                                                                                                                                                                                                                                                                            
Out[7]: ''

Versions

Python: 3.6.9
Django: 1.11.29
Model Bakery: 1.1.0

Also tested on

Versions

Python: 3.8.3
Django: 3.0.6
Model Bakery: 1.1.0

invalid related properties are not reported

With these models:

class Author(models.Model):
    first_name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)

If I do:

mommy.make('Author', book__name='foo)

It just fails to make the Book silently, I should get an Error like:

Could not resolve 'book' into field, available fields: ['first_name', 'book_set']

Investigate many to many creation with through option

Someone gave us a hint about this test won't be working:

    def test_create_many_to_many_with_through_option(self):
        """
         This does not works
        """
        # School student's attr is a m2m relationship with a model through
        school = baker.make(models.School, make_m2m=True)
        assert models.School.objects.count() == 1
        assert school.students.count() == baker.MAX_MANY_QUANTITY
        assert models.SchoolEnrollment.objects.count() == baker.MAX_MANY_QUANTITY
        assert models.Person.objects.count() == baker.MAX_MANY_QUANTITY

Expected behavior

We must investigate weather this is working or not.

  • Many to many creation should work via through option
  • The test should reproduce this behavior accordingly

Actual behavior

We have a comment saying it doesn't work. lol

Enable seq to be imported from baker

I think it would be useful to also enable the seq() function to be imported from the baker module like it is in the recipe module so that it can be used as baker.seq() if preferred or to avoid namespace issues.

The current import can be left in place for backwards compatibility or to continue importing seq as before.

Default `MAX_INT` for integer/float/interval generator

We introduced an increased MAX_INT value in recent work: #59

I am not yet sure if this brings any unnecessary overhead to testing systems that are using model_bakery. What I definitely noticed - I had to adapt in several systems code that uses generators, where to be able to still use gen_integer() and gen_float() we need to reimplement those by using identical code (but with different constant - old MAX_INT).

Expected behavior

I believe, in my work environment, I would expect everything to work the way it worked before. :)

Actual behavior

I cannot use model_bakery's generators everywhere in my tests, as I did before:

  • new MAX_INT blows up tests in places, where we do not have a strict validation and limits for max_length
  • started seeing django.db.utils.DataError: smallint out of range on certain code examples (check out "Reproduction Steps"

Reproduction Steps

How to get django.db.utils.DataError: smallint out of range:

# models.py
class Item(models.Model):
    amount = models.PositiveSmallIntegerField(default=1)

# conftest.py:
baker.make(Item, amount=gen_integer(min_int=1))

Versions

Python: Python 3.5+
Django: 1.11+
Model Bakery: 1.1.0

Possible solutions

  1. Change MAX_INT from constant to a configurable (by library users) setting with a reasonable default (10000? I quickly checked https://github.com/joke2k/faker/blob/master/faker/providers/__init__.py#L95 for inspiration). I am not sure if this is a good option to introduce this setting that might be unrelated to most of the users (but then they just won't use it).
  2. ???

Change docs files from reStructuredText to Markdown

All the files under docs/source/ are .rst files, which is the default format used by Sphinx. Although this is fine, it can be little bit annoying to work with this format when compared to Markdown for example.

Expected behavior

After #44, it would be nice to have a better integrity when it comes to file formats in the project. So the expected output from this issue is to have all the .rst files in our docs in .md. To do so, we have to enable Sphinx to handle Markdown files. All the info you need for this is in this session of Sphinx's docs.

How does model_bakery handle computed properties?

Short summary.

Can/Does model_bakery handle computed properties?

Expected behavior

A model with computed properties is created, and calling that model to retrieve the computed property value returns the calculated data.

What you expected.

IF I have a model named "Exam" and it has a computed property (@Property) 'status', which calculates a status based on values from another table, it should return the correct status.

Actual behavior

I get a message that exam object has no attribute 'status'

Versions

Python: 3.7.5
Django: 2.2
Model Bakery: 1.0.2

Add documentation to _fill_optional kwargs

In issue #74, we realized we're missing documentation on the _fill_optional kwarg. It's enabled in every baker api method (make', prepare, make_recipe, prepare_recipe`) and is used to force values for optiional fields.

Expected behavior

Docs covering the 2 possible usages (populate all fields VS populate specific fields) as described in this comment.

Actual behavior

We don't have docs on it =)

Replace timezone code by Django's built in solution

Model Bakery has a module to deal with timezones that was implemented before Django had a built in support for it. We should replace it by the timezone API from Django 1.11.

https://github.com/model-bakers/model_bakery/blob/c2b609f6b065e587cf4a2e4253b518634d4995b3/model_bakery/timezone.py

Expected behavior

This lib should use Django's built in timezone library instead of bakery's timezone module.

Things I could think of:

  • Replace calls to smart_datetime
  • Replace calls to tz_aware
  • Replace calls to now
  • Replace calls to utc
  • Delete bakery's timezone module

Support for choices parameter in TextField

Short summary.

Expected behavior

If the choices argument is specified on a TextField, Model Bakery should pick from a random valid choice for the field.

Actual behavior

Model Bakery skips TextFields with choices resulting a database IntegrityError:

django.db.utils.IntegrityError: null value in column "status" violates not-null constraint

Reproduction Steps

How to reproduce this issue.

models.py
from django.db import models

class OrderStatus(models.TextChoices):
    CANCELLED = "CANCELLED"
    DELIVERED = "DELIVERED"

class Order(models.Model):
      status = models.TextField(choices=OrderStatus.choices, default=None)
test.py
from model_bakery import baker
from models import Order
order = baker.make(Order)

Versions

Python: 3.8.2
Django: 3.0.7
Model Bakery: 1.1.1

Add examples of pytest usage to the docs

All the code examples in the current documentation relies on Django's test case. Although this is more verbose and I do prefer to use pytest code style when writing my tests, it's good to use only django in the docs to avoid misconceptions on model_bakery dependencies. For example, if people read the docs and notice the test code is more related to pytest than django, they may think that pytest is a dependency, but is not. This can influence them on the decision of adding or not model_bakery to their projects.

Anyway, it would be nice to have a documentation covering how model_bakery should be used with pytest as well. This could be a new section in our documentation explaining:

1 - why we prefer to use pytest-django rather than django's test command (can be a link to one of the millions of blogposts with this discussion);
2 - one example of a unit test using model_bakery;
3 - one example of a pytest fixture with model_bakery;

Use bulk_create when supplying _quantity parameter to baker.make

I noticed that when using the _quantity parameter with baker.make, each model instance is added to the database individually (see this line: https://github.com/model-bakers/model_bakery/blob/master/model_bakery/baker.py#L356).

Would it be reasonable to use django's bulk_create manager method to create them all in one database query in that situation? The difference in efficiency could be significant when running test suites which use that parameter often enough.

SSHR_FREE_DAYS initialization with the workalendar module

To initialize the SSHR_FREE_DAYS you can use the holidays() method from https://github.com/peopledoc/workalendar.

from datetime import date
from workalendar.europe import France
cal = France()
cal.holidays(2012)
[(datetime.date(2012, 1, 1), 'New year'),
(datetime.date(2012, 4, 9), 'Easter Monday'),
(datetime.date(2012, 5, 1), 'Labour Day'),
(datetime.date(2012, 5, 8), 'Victory in Europe Day'),
(datetime.date(2012, 5, 17), 'Ascension Day'),
(datetime.date(2012, 5, 28), 'Whit Monday'),
(datetime.date(2012, 7, 14), 'Bastille Day'),
(datetime.date(2012, 8, 15), 'Assumption of Mary to Heaven'),
(datetime.date(2012, 11, 1), "All Saints' Day"),
(datetime.date(2012, 11, 11), 'Armistice Day'),
(datetime.date(2012, 12, 25), 'Christmas')]

DELETED

The issue deleted by its creator

seq and cycle not working correctly with _quantity parameter when generating foreign_key?

Hi,
when executing the folowing:

fake_puzzle_piece = Recipe(PuzzlePieces, pieces=seq('pieces_'))
fake_puzzle = Recipe(Puzzle, name=seq('puzzle_'),puzzle_pieces=foreign_key(fake_puzzle_piece))
puzzle_set = baker.make_recipe('puzzles.fake_puzzle', _quantity=20)

I would expect:
20 Puzzles to be generated, each linked to a different PuzzlePieces named pieces_1, pieces_2 ...

instead , 20 Puzzles are created, each linked to the same PuzzlePieces named pieces_1

similarly:

when executing the folowing:
pieces=['a','b', ...]
puzzle_piece = Recipe(PuzzlePieces, pieces=cycle(pieces))
puzzle_w_pzl_pieces = Recipe(Puzzle, name=seq('puzzle_'),
                             puzzle_pieces=foreign_key(puzzle_piece))
puzzle_set = baker.make_recipe('puzzles.puzzle_w_pzl_pieces ', _quantity=20)

I would expect:
20 Puzzles to be generated, each linked to a different PuzzlePieces named a,b ...

instead , 20 Puzzles are created, each linked to the same PuzzlePieces named a

Is there something I misunderstood?
How do I manage to have new instances of the foreign_key created each time?

Thanks in advance

Versions

Python:Python 3.7.4
Django: 2.2.7
Model Bakery:1.0.2

Feature: start parameter in recipe.seq() if is passed a string

recipe.seq() already give the possibility of increment_by, and if make something like seq(4) it increment starting in 4. However, would be cool if passed a string to recipe.seq(), it gives the possibility of determinate the start of incrementation.

Something like:

ipv6_set = mommy.make(IPv6Address, _quantity=4, address=seq('2001:12f8:0:28::', start=4))

Returning:

[<IPv6Address: [2001:12f8:0:28::4]>, <IPv6Address: [2001:12f8:0:28::5]>, <IPv6Address: [2001:12f8:0:28::6]>, <IPv6Address: [2001:12f8:0:28::7]>]

Add field lookup syntax to `_fill_optional`

I'll base my feature request using the following models as examples:

class FkModel(models.Model):
    not_optional = models.CharField(max_length=500)
    optional = models.CharField(max_length=500)

class MainModel(models.Model):
    optional_fk = models.ForeignKey(
        FKModel, null=True, blank=True, on_delete=models.SET_NULL
    )

Now, let's imagine we have a test scenario for my MainModel which depends on the optional field from a optional_fk to have some random value so the test can pass.

Model mommy already solves this test setup scenario, but I think we could also be able to do so via _fill_optional with lookup fields. In my opinion, this would increase model mommy's API because we already can use lookup fields to create or prepare instances and recipes.

Expected behavior

My suggestions is to add the lookup feature also to the _fill_optional value. So, the following call would be valid:

obj = mommy.make(MainModel, _fill_optional=['optional_fk__optional'])
assert bool(obj.optional_fk.optional) is True

Actual behavior

Right now, model_mommy force us to manually create an instance of FKModel with the optional fields being populated and then to pass is to mommy.make when creating a new instance of MainModel. Here's how we have to code the same test scenario today:

optional_obj = mommy.make(FKModel, _fill_optional=['optional'])
obj = mommy.make(MainModel, optional_fk=optional_obj)
assert bool(obj.optional_fk.optional) is True

Add a Python code formatter

We should add Black to enforce some code styles to improve legibility and maintenance.

  • Add Black as a development requirement
  • Add it to the pre-commit config

persistant foreignkeys not created

My understanding from the docs is that foreign key fields are supposed to be persistent, but this isn't working for me:

def setUp(self):
        self.submission = mommy.make(QuestSubmission, quest__id=1)
        # Passes if I explicitly provide some foreignkey parameter, like id, or name when making
        assert bool(self.submission.quest.id) is True  

        self.submission = mommy.make(QuestSubmission)  # no explicit foreignkey param
        assert bool(self.submission.quest.id) is True
        # AttributeError: 'NoneType' object has no attribute 'id'

How do I force persistant foreign key fields?

model-mommy==1.6.0
Django==2.0.10

EDIT:
Upon further review, this may be a problem caused by the foreign key's model? Other foreign key are being created as expected.


class XPItem(models.Model):
    name = models.CharField(max_length=50, unique=True)
    xp = models.PositiveIntegerField(default=0)
    # other fields

class Quest(XPItem, IsAPrereqMixin):
     # fields

class QuestSubmission(models.Model):
    quest = models.ForeignKey(Quest, on_delete=models.SET_NULL, null=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related
    # other fields
    semester = models.ForeignKey('courses.Semester', on_delete=models.SET_NULL, null=True)

I can access the user and the semester foreignkeys no problem, it's only the quest foreign key that fails unless I explicitly add a paramter during mommy.make(). I assume this has to do with the inheritance structure of Quest shown above?

Allow string values for `foriegn_key` from other modules

It would be nice if we could treat foreign keys like we would a recipe in baker.make(). Right now it seems like whatever foreign key you use has to be imported into that baker_recipes.py file.

Expected behavior

It would be nice to be able to do something like:

order = Recipe(account=foreign_key('accounts.account'))

It would be even nicer if this would handle things gracefully, if you had for example one recipe that creates an order tied to an account, and a recipe that creates an account with several orders, both in different baker_recipes.py files.

Actual behavior

I get an error in the form of:

AttributeError: module 'order.baker_recipes' has no attribute 'account.account'

Reproduction Steps

Create two baker_recipes.py files in different django apps/whatever and try to use them together without importing recipes from the other.

Versions

Python: 3.7
Django: 2.2
Model Bakery: 1.0.2

_fill_optional doesn't throw any error when using invalid field

When I use an invalid field name to a model through _fill_optional param on make it doesn't throw any error warning about the invalid field that I'm trying to fill. Ex.:

# My model
class Person(models.Model):
  name = fields.Charfield(max_length=100)
  phone = fields.Charfield(max_length=30)

# Trying to fill invalid field

# It doesn't throw any error
person = mommy.make('Person', _fill_optional=['birth_date'])

person.birth_date
> AttributeError: 'Person' object has no attribute 'birth_date'

This should be the expected behavior?

Allow dependencies between fields in recipes

This isn't a bug, more a feature request or query:

Is it possible to have fields within recipes depend on one another. To take a silly example, lets say we want to create a new user:

staff = Recipe(
    "Staff",
    first_name=choice(["bob", "sue"]),
    last_name=choice(["jones", "baker"]),
    username=XXX,
)

In this case it would be nice for the username field to be a callable which returns f"{first_name}{last_name}". This could be put into a save method on Staff, but we might not always want this to work that way.

Perhaps one way to achieve it would be to allow passing a defaults dictionary.. so like make_recipe(model, **kwargs, but where kwargs is a callable that gets called?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.