factory.SelfAttribute not evaluated when passed as list?
Description
I am not sure if this is me using factory_boy wrong, or a bug. When i use a factory.SelfAttribute to pass a attribue to a provider it is evaluated if i pass it directly, but not if passed in a list (alone or with more factory.SelfAttribute's)
Model / Factory code
# Money object generated: ¥2,748
subtotal = factory.Faker("djmoney", currency=_currency)
# money is Money object: ¥2,748 / <class 'djmoney.money.Money'>
tax = factory.Faker(
"djmoney",
money=factory.SelfAttribute("..subtotal"),
multiplier=0.25,
)
# Money is not list of Money objects as expected:
# [<SelfAttribute('subtotal', default=<class 'factory.declarations._UNSPECIFIED'>)>]
# Also, the SelfAttribute says "subtotal" not "..subtotal" anymore?
total = factory.Faker(
"djmoney",
money=[
factory.SelfAttribute("..subtotal"),
# factory.SelfAttribute("..tax"), # Same problem with one or more list items
],
)
Notes
I think the custom provider i am using is irellevant, but its code can be found here: https://gitlab.com/Kagee/homelab-organizer/-/blob/develop/hlo/factories/providers/moneyprovider.py
The same happens if i use factory.LazyAttribute(lambda o: o.factory_parent.subtotal), but then i get a list of LazyAttribute ofc.
Can you show the full method/class? The example you provided is a bit incomplete.
@kingbuzzman The full method/class of what exactly? The whole (subject to change) factory can be seen here https://gitlab.com/Kagee/homelab-organizer/-/blob/develop/hlo/factories/order.py
With help from the Django discord, i rubberducket my way to this code, that worked. If this is how it is supposed to be done, i feel it was not obvious from the documentation.
total = factory.Faker(
"djmoney",
money=factory.List(
[
factory.SelfAttribute("..factory_parent.subtotal"),
factory.SelfAttribute("..factory_parent.tax"),
],
),
)
I guess the reason I'm confused it's because factory.Faker is only ever used inside a Factory and never alone. Example
Yes, the DjangoModelFactory i linked to https://gitlab.com/Kagee/homelab-organizer/-/blob/develop/hlo/factories/order.py
It's hard to reproduce your full setup, but i got "something" working, and I'm getting what looks to be correct. Can you provide a full example of the issue please?
Code (click here to expand!!).
(copy and paste friendly code that can go straight into ipython with no fuss-no muss)
import factory
from decimal import Decimal
import logging
logger = logging.getLogger(__name__)
from django.conf import settings
from faker.providers import BaseProvider
Money = Decimal
class MoneyProvider(BaseProvider):
# settings.CURRENCIES
def djmoney(
self,
text: str | None = None,
currency: str | None = None,
multiplier: Decimal = 1,
money: Money | list[Money] | None = None,
) -> Money:
if money:
# Use the input currency
if isinstance(money, Money):
logger.debug("Money is Money: %s / %s", money, type(money))
new_money = Money(
money.amount * Decimal(multiplier),
)
else:
logger.debug("Money is not Money: %s", money)
new_money = Money(0)
for m in money:
new_money += m
new_money = new_money * Decimal(multiplier)
logger.debug("Money: %s, New money: %s", money, new_money)
return new_money
if not currency:
currency = self.random_choices(settings.CURRENCIES, length=1)[0]
else:
pass
if not text:
# For USD and EUR
text = "@%#,##"
if currency == "NOK":
# 10 NOK ~1 USD, add a digit
text = "@%##,##"
if currency == "JPY":
# 100 NOK ~.7 USD, add two digits and no decimals
text = "@%###"
m = Money(
Decimal(self.numerify(text=text).replace(",", "."))
* Decimal(multiplier),
)
logger.debug("Money returned: %s", m)
return m
factory.Faker.add_provider(MoneyProvider)
class OrderFactory(factory.Factory):
class Meta:
model = dict
subtotal = 5
tax = 5
total = factory.Faker(
"djmoney",
money=factory.List(
[
factory.SelfAttribute("..factory_parent.subtotal"),
factory.SelfAttribute("..factory_parent.tax"),
],
),
)
output
>>> OrderFactory()
{'subtotal': 5, 'tax': 5, 'total': Decimal('10')}
gets summed up and multiplied by 1
Yes, i also got it to work when using factory.List and ..factory_parent.subtotal.
I am still not sure why i had to change the query for subtotal, but i would suggest adding "sending a list of SelfAtrribue/LazyAttribute as argument to a provider" be added to the examples in docs.
PRs welcome! Would this close this issue?
Where in the docs would this make the most sense?
I'm going to be honest; I'm not entirely sure what the issue is..
@rbarrois can you provide some guidance?