berinhard / model_mommy

No longer maintained, please migrate to model_bakery
http://model-bakery.readthedocs.org/
Other
903 stars 141 forks source link

Using _quantity with random name generator gives always the same value #218

Closed diegopau closed 9 years ago

diegopau commented 9 years ago

I am trying to make a simple test which should create 20 instances of a model called TagModel using Model Mommy recipes.

It seems that the generator I use to fill the tag field is only called once even if I am trying to generate 20 instances, so the test is giving me a "duplicate key value violates unique constraint "core_tagmodel_tag_key".

My recipe in mommy_recipes is:

tag = Recipe(
     TagModel,
     tag=get_random_stuff.get_random_tag(),
)

In test_models.py I create the following simple test:

class TagModelTestModel(TestCase):
    def setUp(self):
        self.tags = mommy.make_recipe('core.tag', _quantity=20)

    def test_tag_creation(self):
        for tag in self.tags:
            print("tag name: ", tag.tag)

        assert len(self.tags) == 25

And this is the generator method I use (get_random_tag()):

SECRET_KEY = 'asd538J878hdfjKEidrfdgf0954lKUJMd03l4mfjejJKkek4AA'

try:
    random = random.SystemRandom()
    using_sysrandom = True
except NotImplementedError:
    import warnings
    warnings.warn('A secure pseudo-random number generator is not available '
                  'on your system. Falling back to Mersenne Twister.')
    using_sysrandom = False

def __string_generator(size=6, chars=string.ascii_uppercase + string.digits):
    """
    Returns a securely generated random string.

    The default length of 12 with the a-z, A-Z, 0-9 character set returns
    a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
    """
    if not using_sysrandom:
        # This is ugly, and a hack, but it makes things better than
        # the alternative of predictability. This re-seeds the PRNG
        # using a value that is hard for an attacker to predict, every
        # time a random string is required. This may change the
        # properties of the chosen random sequence slightly, but this
        # is better than absolute predictability.
        random.seed(
            hashlib.sha256(
                ("%s%s%s" % (
                    random.getstate(),
                    time.time(),
                    SECRET_KEY)).encode('utf-8')
            ).digest())
    return ''.join(random.choice(chars) for _ in range(size))

def get_random_tag():
    print("I've been called!")
    tag = __string_generator(size=random.randint(1, 32),
                             chars=string.ascii_uppercase + string.ascii_lowercase + string.digits + '_')
    print("New Tag!: ", tag)
    return tag
vandersonmota commented 9 years ago

try

tag=get_random_stuff.get_random_tag

instead of

tag=get_random_stuff.get_random_tag()

this will call your generator everytime a recipe is created, instead of only calling during recipe module evaluation.

diegopau commented 9 years ago

@vandersonmota it worked! thanks a lot. I close the issue but because I am still a newbie and couldn't find the right answer on the Internet... could you please explain me why is it important to remove the parenthesis? (i guess this is more Python related than Model Mommy related)

vandersonmota commented 9 years ago

Read: http://model-mommy.readthedocs.org/en/latest/recipes.html#recipes-with-callables

when you did:

tag=get_random_stuff.get_random_tag()

the tag was assigned with the value generated by "get_random_tag", because you already called the function, when you were defining your recipe. When you pass a callable, mommy detects it and calls your function during mommy.make_recipe.

diegopau commented 9 years ago

All clear now, thanks a lot!