factory_boy
factory_boy copied to clipboard
Extend `SelfAttribute` with some `callback` kwarg, for convenience
The problem
Quite a lot of times I'd like to refine a value that can be accessed with a SelfAttribute
. This can normally be done by combining it with a LazyAttribute
and 1 or 2 param variables, but it'd be quite convenient if that was a shortcut so one didn't have to write that out explicitly every time. And also avoid having to either invent new variable names or prefix them to avoid collisions.
I also think, in general, that it would be a nice extension for SelfAttribute
to expose a way to intercept the picked up value before it's assigned. A bit similar to what LazyAttribute
by itself does (though on the factory instance).
Consider this simple case:
class DataFactory(factory.DictFactory):
coordinates = factory.Dict(
{
"lat": factory.SelfAttribute("..latitude"),
"lng": factory.SelfAttribute("..longitude"),
}
)
class Params:
# Faker outputs latitude and longitude as `Decimal`, I want it as `float`
lat = factory.Faker("latitude")
lng = factory.Faker("longitude")
latitude = factory.LazyAttribute(lambda obj: float(obj.lat))
longitude = factory.LazyAttribute(lambda obj: float(obj.lng))
Which could then become
class DataFactory(factory.DictFactory):
coordinates = factory.DictFactory(
{
"lat": factory.SelfAttribute("..latitude", callback=float),
"lng": factory.SelfAttribute("..longitude", callback=float),
}
)
class Params:
latitude = factory.Faker("latitude")
longitude = factory.Faker("longitude")
Extra notes
- I'm aware that I could place and evaluate the
Faker
instance inside of theLazyAttribute
function, but it feels like this could be supported by the public API, rather than having to resort to a private API. - Better naming suggestions for the "callback" kwarg are welcome (one suggestion I have is
coerce
)
Seems to me, that you want the ability to be able to change the latitude
and longitude
at will AND also have them be cast to float
; all while having a "compressed" / easy to understand package:
class DataFactory(factory.DictFactory):
coordinates = factory.Dict(
{
# Faker outputs latitude and longitude as `Decimal`, I want it as `float`
"lat": factory.LazyAttribute(lambda obj: float(obj.factory_parent.latitude)),
"lng": factory.LazyAttribute(lambda obj: float(obj.factory_parent.longitude)),
}
)
class Params:
latitude = factory.Faker("latitude")
longitude = factory.Faker("longitude")
Thus when calling it DataFactory()
yields random coordinates via Faker
cast-ed into float
. Random example:
>>> DataFactory()
{'coordinates': {'lat': -23.7679435, 'lng': -91.710909}}
And still, have the option to do:
>>> DataFactory(latitude=1, longitude=2)
{'coordinates': {'lat': 1.0, 'lng': 2.0}}
Ah, yeah. I've missed that factory_parent
is part of the API
I guess this addition in the end is a shortcut/convenience method as/if the LazyAttribute
approach becomes a bit verbose
Like I said before, I'm a negative 0 on this. While I don't like more than 1 way to do something, I won't stand in the way of your PR. I accepted it ;) -- let's see what the maintainers say.
I’m not sold on the use case. Why not change the data source to generate the data type of interest? Here, that could mean extending Faker if the use case is general enough, or writing helper for your project that generates float longitude and lattitude (maybe from Faker Decimal values).
class Data(factory.DictFactory):
coordinates = factory.Dict({
"lat": factory.Faker("pyfloat", max_value=90, min_value=-90),
"lng": factory.Faker("pyfloat", max_value=180, min_value=-180),
})
Indeed, composing values from params and other declarations is sometimes cumbersome, but I am not a big fan of this proposal either.
Not much interest has been shown for this proposal, we won’t be moving forward with it in the short term. Thank you for taking the time to submit it, we’ll happily review new proposals in the future!