factory_boy icon indicating copy to clipboard operation
factory_boy copied to clipboard

Allow traits based on a value

Open maxnordlund opened this issue 6 years ago • 4 comments

Hi, first off tip of the hat for a awesome library.

The parameter example uses duration='short'|'long', but Traits only accepts booleans. It would be useful to allow Traits based on some (arbitrary) value rather then just a boolean. Like the duration example.

In my case I have a polymorphic* model, and would like to switch based on a value from an enum. Not quite sure about the interface here, but something like:

class Factory(factroy.Factory):
    class Meta:
        model = Order

    state = 'created'
    created_on = datetime.today()
    paid_on = None
    canceled_on = None
    fulfilled_on = None
    returned_on = None

    class Params:
        status = factroy.Trait(
            created=dict(
                state='created',
                created_on=datetime.today()
            ),
            paid=dict(
                state='paid',
                paid_on=datetime.today()
            ),
            canceled=dict(
                state='canceled',
                canceled_on=datetime.today()
            ),
            fulfilled=dict(
                state='fulfilled',
                fulfilled_on=datetime.today()
            ),
            returned=dict(
                state='returned',
                returned_on=datetime.today()
            )
        )

* It's unfortunately doesn't use the polymorphic support in Django, but it's an homegrown solution.

maxnordlund avatar Nov 27 '17 13:11 maxnordlund

I like the idea :)

We should have thought about it before implementing the Trait in the first place, now we'll need to design a simple migration path though :disappointed:

Regarding the API, I'd like to find a way to protect users from spelling errors; what about the following proposal:

status = factory.Switch({
    OrderStatus.CREATED: factory.Trait(
        state=OrderStatus.CREATED,
        created_on=factory.LazyFunction(datetime.today),
    ),
    OrderStatus.PAID: factory.Trait(
        state=OrderStatus.PAID,
        created_on=factory.LazyFunction(datetime.today),
    ),
})

Pros:

  • Using a dict allows to share constants between the declaration section and using it;
  • Reusing the factory.Trait reuses existing concepts: a switch will simply enable one of several traits

Cons:

  • More verbose than what you proposed :wink:

rbarrois avatar Dec 02 '17 13:12 rbarrois

I like it. I hadn't thought about using something else the strings, which yours solves neatly.

If you want to DRY something up, just add a helper function, that's my philosophy anyways 😀


To be more concrete, I have a model with two foreign keys (for now...), but only one should be set at any given time. I added a Params to select which type, at the point in time it doesn't know if it's building or creating. In the end I always build the subfactory, and in a post_generation hook to conditionally save which ever was built previously. But it's kinda hacky and feels wrong somehow.

maxnordlund avatar Dec 02 '17 14:12 maxnordlund

Hi! Is there any progress on this feature?

maxrothman avatar Apr 24 '20 21:04 maxrothman

+1

ArtemBernatskyy avatar Oct 15 '20 09:10 ArtemBernatskyy