11

I want to use Factory Boy and its support for Faker to generate strings from more than one provider. e.g. combining prefix and name:

# models.py
from django.db import models

class Person(models.Model):
    full_name = models.CharField(max_length=255, blank=False, null=False)


# factories.py
import factory

class PersonFactory(factory.Factory):
    class Meta:
        model = models.Person

    full_name = '{} {}'.format(factory.Faker('prefix'), factory.Faker('name'))

But this doesn't seem to work. e.g.:

>>> person = PersonFactory()
>>> person.full_name
'<factory.faker.Faker object at 0x7f25f4b09e10> <factory.faker.Faker object at 0x7f25f4ab74d0>'

What am I missing?

Phil Gyford
  • 13,432
  • 14
  • 81
  • 143
  • What are the fields on your `models.Person` class? This is relevant info. – wim Sep 07 '17 at 15:47
  • OK, I wasn't sure it would help, but I've added it now. – Phil Gyford Sep 07 '17 at 15:59
  • @PhilGyford: Does [this](https://factoryboy.readthedocs.io/en/latest/index.html#lazy-attributes) work? You'd have to create a `prefix` and a `name` class attribute that don't exist in your model and then combine them to create `full_name`. I'm not sure if this will cause an error, as your original `Person` model doesn't have those fields. – Blender Sep 07 '17 at 16:16
  • @Blender Thanks, but I tried that, and did get an error for the reason you suggest. – Phil Gyford Sep 07 '17 at 16:22

1 Answers1

18

You can use the (essentially undocumented) exclude attribute to make your first approach work:

class PersonFactory(factory.Factory):
    class Meta:
        model = Person
        exclude = ('prefix', 'name')

    # Excluded
    prefix = factory.Faker('prefix')
    name = factory.Faker('name')

    # Shows up
    full_name = factory.LazyAttribute(lambda p: '{} {}'.format(p.prefix, p.name))

You could also just use factory.LazyAttribute and generate it all in one go by directly using faker:

from faker import Faker

fake = Faker()

class PersonFactory(factory.Factory):
    class Meta:
        model = Person

    # Shows up
    full_name = factory.LazyAttribute(lambda p: '{} {}'.format(fake.prefix(), fake.name()))

The downside of this approach is that you you don't have easy access to the person's prefix or name.

Blender
  • 289,723
  • 53
  • 439
  • 496
  • 2
    Thanks! I thought maybe there was a way with `factory.LazyAttribute` but never managed to find quite the right combination of fakes. – Phil Gyford Sep 07 '17 at 17:13
  • 2
    Pretty good. Only quibble is that you will generate transgender (in the literal sense of the word) names this way. – wim Sep 07 '17 at 19:00