class ModelWithDefaultAsOldMoney(models.Model): money = MoneyField(default=OldMoney('0.01', 'RUB'), max_digits=10, decimal_places=2)
class TestFExpressions: parametrize_f_objects = pytest.mark.parametrize('f_obj, expected', ( (F('money') + Money(100, 'USD'), Money(200, 'USD')), (F('money') + OldMoney(100, 'USD'), Money(200, 'USD')), (Money(100, 'USD') + F('money'), Money(200, 'USD')), (F('money') - Money(100, 'USD'), Money(0, 'USD')), (Money(100, 'USD') - F('money'), Money(0, 'USD')), (F('money') * 2, Money(200, 'USD')), (F('money') * F('integer'), Money(200, 'USD')), (Money(50, 'USD') * F('integer'), Money(100, 'USD')), (F('integer') * Money(50, 'USD'), Money(100, 'USD')), (Money(50, 'USD') / F('integer'), Money(25, 'USD')), (Money(51, 'USD') % F('integer'), Money(1, 'USD')), (F('money') / 2, Money(50, 'USD')), (F('money') % 98, Money(2, 'USD')), (F('money') / F('integer'), Money(50, 'USD')), (F('money') + F('money'), Money(200, 'USD')), (F('money') - F('money'), Money(0, 'USD')), )) @parametrize_f_objects def test_save(self, f_obj, expected): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, 'USD'), integer=2) instance.money = f_obj instance.save() instance.refresh_from_db() assert instance.money == expected @parametrize_f_objects def test_f_update(self, f_obj, expected): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, 'USD'), integer=2) ModelWithVanillaMoneyField.objects.update(money=f_obj) instance.refresh_from_db() assert instance.money == expected def test_default_update(self): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, 'USD'), integer=2) second_money = Money(100, 'USD') ModelWithVanillaMoneyField.objects.update(second_money=second_money) instance.refresh_from_db() assert instance.second_money == second_money @pytest.mark.parametrize('create_kwargs, filter_value, in_result', ( ({ 'money': Money(100, 'USD'), 'second_money': Money(100, 'USD') }, { 'money': F('money') }, True), ({ 'money': Money(100, 'USD'), 'second_money': Money(100, 'USD') }, { 'money': F('second_money') }, True), ({ 'money': Money(100, 'USD'), 'second_money': Money(100, 'EUR') }, { 'money': F('second_money') }, False), ({ 'money': Money(50, 'USD'), 'second_money': Money(100, 'USD') }, { 'second_money': F('money') * 2 }, True), ({ 'money': Money(50, 'USD'), 'second_money': Money(100, 'USD') }, { 'second_money': F('money') + Money(50, 'USD') }, True), ({ 'money': Money(50, 'USD'), 'second_money': Money(100, 'EUR') }, { 'second_money': F('money') * 2 }, False), ({ 'money': Money(50, 'USD'), 'second_money': Money(100, 'EUR') }, { 'second_money': F('money') + Money(50, 'USD') }, False), )) def test_filtration(self, create_kwargs, filter_value, in_result): instance = ModelWithVanillaMoneyField.objects.create(**create_kwargs) assert (instance in ModelWithVanillaMoneyField.objects.filter( **filter_value)) is in_result def test_update_fields_save(self): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, 'USD'), integer=2) instance.money = F('money') + Money(100, 'USD') instance.save(update_fields=['money']) instance.refresh_from_db() assert instance.money == Money(200, 'USD') INVALID_EXPRESSIONS = [ F('money') + Money(100, 'EUR'), F('money') * F('money'), F('money') / F('money'), F('money') % F('money'), F('money') + F('integer'), F('money') + F('second_money'), F('money')**F('money'), F('money')**F('integer'), F('money')**2, ] @pytest.mark.parametrize('f_obj', INVALID_EXPRESSIONS) def test_invalid_expressions_access(self, f_obj): instance = ModelWithVanillaMoneyField.objects.create( money=Money(100, 'USD')) with pytest.raises(ValidationError): instance.money = f_obj
class TestVanillaMoneyField: @pytest.mark.parametrize( "model_class, kwargs, expected", ( (ModelWithVanillaMoneyField, { "money": Money("100.0") }, Money("100.0")), (ModelWithVanillaMoneyField, { "money": OldMoney("100.0") }, Money("100.0")), (BaseModel, {}, Money(0, "USD")), (BaseModel, { "money": "111.2" }, Money("111.2", "USD")), (BaseModel, { "money": Money("123", "PLN") }, Money("123", "PLN")), (BaseModel, { "money": OldMoney("123", "PLN") }, Money("123", "PLN")), (BaseModel, { "money": ("123", "PLN") }, Money("123", "PLN")), (BaseModel, { "money": (123.0, "PLN") }, Money("123", "PLN")), (ModelWithDefaultAsMoney, {}, Money("0.01", "RUB")), (ModelWithDefaultAsFloat, {}, Money("12.05", "PLN")), (ModelWithDefaultAsStringWithCurrency, {}, Money("123", "USD")), (ModelWithDefaultAsString, {}, Money("123", "PLN")), (ModelWithDefaultAsInt, {}, Money("123", "GHS")), (ModelWithDefaultAsDecimal, {}, Money("0.01", "CHF")), (CryptoModel, { "money": Money(10, "USDT") }, Money(10, "USDT")), ), ) def test_create_defaults(self, model_class, kwargs, expected): instance = model_class.objects.create(**kwargs) assert instance.money == expected retrieved = model_class.objects.get(pk=instance.pk) assert retrieved.money == expected def test_old_money_defaults(self): instance = ModelWithDefaultAsOldMoney.objects.create() assert instance.money == Money(".01", "RUB") @pytest.mark.parametrize( "model_class, other_value", ( (ModelWithVanillaMoneyField, Money("100.0")), (BaseModel, Money(0, "USD")), (ModelWithDefaultAsMoney, Money("0.01", "RUB")), (ModelWithDefaultAsFloat, OldMoney("12.05", "PLN")), (ModelWithDefaultAsFloat, Money("12.05", "PLN")), ), ) def test_revert_to_default(self, model_class, other_value): if hasattr(model_class._meta, "get_field"): default_instance = model_class._meta.get_field( "money").get_default() else: default_instance = model_class._meta.get_field_by_name( "money").default instance1 = model_class.objects.create() pk = instance1.pk # Grab a fresh instance, change the currency to something non-default # and unexpected instance2 = model_class.objects.get(id=pk) instance2.money = Money(other_value.amount, "DKK") instance2.save() instance3 = model_class.objects.get(id=pk) assert instance3.money == Money(other_value.amount, "DKK") # Now change the field back to the default currency instance3.money = copy(default_instance) instance3.save() instance4 = model_class.objects.get(id=pk) assert instance4.money == default_instance @pytest.mark.parametrize("value", ((1, "USD", "extra_string"), (1, None), (1, ))) def test_invalid_values(self, value): with pytest.raises(ValidationError): BaseModel.objects.create(money=value) @pytest.mark.parametrize("money_class", (Money, OldMoney)) @pytest.mark.parametrize("field_name", ("money", "second_money")) def test_save_new_value(self, field_name, money_class): ModelWithVanillaMoneyField.objects.create( **{field_name: money_class("100.0")}) # Try setting the value directly retrieved = ModelWithVanillaMoneyField.objects.get() setattr(retrieved, field_name, Money(1, "DKK")) retrieved.save() retrieved = ModelWithVanillaMoneyField.objects.get() assert getattr(retrieved, field_name) == Money(1, "DKK") def test_rounding(self): money = Money("100.0623456781123219") instance = ModelWithVanillaMoneyField.objects.create(money=money) # TODO. Should instance.money be rounded too? retrieved = ModelWithVanillaMoneyField.objects.get(pk=instance.pk) assert retrieved.money == Money("100.06") @pytest.fixture(params=[Money, OldMoney]) def objects_setup(self, request): Money = request.param ModelWithTwoMoneyFields.objects.bulk_create(( ModelWithTwoMoneyFields(amount1=Money(1, "USD"), amount2=Money(2, "USD")), ModelWithTwoMoneyFields(amount1=Money(2, "USD"), amount2=Money(0, "USD")), ModelWithTwoMoneyFields(amount1=Money(3, "USD"), amount2=Money(0, "USD")), ModelWithTwoMoneyFields(amount1=Money(4, "USD"), amount2=Money(0, "GHS")), ModelWithTwoMoneyFields(amount1=Money(5, "USD"), amount2=Money(5, "USD")), ModelWithTwoMoneyFields(amount1=Money(5, "EUR"), amount2=Money(5, "USD")), )) @pytest.mark.parametrize( "filters, expected_count", ( (Q(amount1=F("amount2")), 1), (Q(amount1__gt=F("amount2")), 2), (Q(amount1__in=(Money(1, "USD"), Money(5, "EUR"))), 2), (Q(id__in=(-1, -2)), 0), (Q(amount1=Money(1, "USD")) | Q(amount2=Money(0, "USD")), 3), (Q(amount1=Money(1, "USD")) | Q(amount1=Money(4, "USD")) | Q(amount2=Money(0, "GHS")), 2), (Q(amount1=OldMoney(1, "USD")) | Q(amount1=OldMoney(4, "USD")) | Q(amount2=OldMoney(0, "GHS")), 2), (Q(amount1=Money(1, "USD")) | Q(amount1=Money(5, "USD")) | Q(amount2=Money(0, "GHS")), 3), (Q(amount1=Money(1, "USD")) | Q(amount1=Money(4, "USD"), amount2=Money(0, "GHS")), 2), (Q(amount1=Money(1, "USD")) | Q(amount1__gt=Money(4, "USD"), amount2=Money(0, "GHS")), 1), (Q(amount1=Money(1, "USD")) | Q(amount1__gte=Money(4, "USD"), amount2=Money(0, "GHS")), 2), ), ) @pytest.mark.usefixtures("objects_setup") def test_comparison_lookup(self, filters, expected_count): assert ModelWithTwoMoneyFields.objects.filter( filters).count() == expected_count def test_date_lookup(self): DateTimeModel.objects.create(field=Money(1, "USD"), created="2016-12-05") assert DateTimeModel.objects.filter( created__date="2016-12-01").count() == 0 assert DateTimeModel.objects.filter( created__date="2016-12-05").count() == 1 @pytest.mark.parametrize( "lookup, rhs, expected", ( ("startswith", 2, 1), ("regex", "^[134]", 3), ("iregex", "^[134]", 3), ("istartswith", 2, 1), ("contains", 5, 2), ("lt", 5, 4), ("endswith", 5, 2), ("iendswith", 5, 2), ("gte", 4, 3), ("iexact", 3, 1), ("exact", 3, 1), ("isnull", True, 0), ("range", (3, 5), 4), ("lte", 2, 2), ("gt", 3, 3), ("icontains", 5, 2), ("in", (1, 0), 1), ), ) @pytest.mark.usefixtures("objects_setup") def test_all_lookups(self, lookup, rhs, expected): kwargs = {"amount1__" + lookup: rhs} assert ModelWithTwoMoneyFields.objects.filter( **kwargs).count() == expected def test_exact_match(self): money = Money("100.0") instance = ModelWithVanillaMoneyField.objects.create(money=money) retrieved = ModelWithVanillaMoneyField.objects.get(money=money) assert instance.pk == retrieved.pk def test_issue_300_regression(self): date = datetime.datetime(year=2017, month=2, day=1) ModelIssue300.objects.filter(money__created=date) ModelIssue300.objects.filter(money__created__gt=date) def test_range_search(self): money = Money("3") instance = ModelWithVanillaMoneyField.objects.create( money=Money("100.0")) retrieved = ModelWithVanillaMoneyField.objects.get(money__gt=money) assert instance.pk == retrieved.pk assert ModelWithVanillaMoneyField.objects.filter( money__lt=money).count() == 0 def test_filter_chaining(self): usd_instance = ModelWithVanillaMoneyField.objects.create( money=Money(100, "USD")) ModelWithVanillaMoneyField.objects.create(money=Money(100, "EUR")) query = ModelWithVanillaMoneyField.objects.filter().filter( money=Money(100, "USD")) assert usd_instance in query assert query.count() == 1 @pytest.mark.parametrize( "model_class", (ModelWithVanillaMoneyField, ModelWithChoicesMoneyField)) def test_currency_querying(self, model_class): model_class.objects.create(money=Money("100.0", "ZWN")) assert model_class.objects.filter( money__lt=Money("1000", "USD")).count() == 0 assert model_class.objects.filter( money__lt=Money("1000", "ZWN")).count() == 1 @pytest.mark.usefixtures("objects_setup") def test_in_lookup(self): assert ModelWithTwoMoneyFields.objects.filter( amount1__in=(Money(1, "USD"), Money(5, "EUR"))).count() == 2 assert (ModelWithTwoMoneyFields.objects.filter( Q(amount1__lte=Money(2, "USD")), amount1__in=(Money(1, "USD"), Money(3, "USD"))).count() == 1) assert ModelWithTwoMoneyFields.objects.exclude( amount1__in=(Money(1, "USD"), Money(5, "EUR"))).count() == 4 assert ModelWithTwoMoneyFields.objects.filter( amount1__in=(1, Money(5, "EUR"))).count() == 2 assert ModelWithTwoMoneyFields.objects.filter( amount1__in=(1, 5)).count() == 3 @pytest.mark.usefixtures("objects_setup") def test_in_lookup_f_expression(self): assert ModelWithTwoMoneyFields.objects.filter( amount1__in=(Money(4, "USD"), F("amount2"))).count() == 2 def test_isnull_lookup(self): NullMoneyFieldModel.objects.create(field=None) NullMoneyFieldModel.objects.create(field=Money(100, "USD")) queryset = NullMoneyFieldModel.objects.filter(field=None) assert queryset.count() == 1 def test_null_default(self): instance = NullMoneyFieldModel.objects.create() assert instance.field is None
class TestFExpressions: parametrize_f_objects = pytest.mark.parametrize( "f_obj, expected", ( (F("money") + Money(100, "USD"), Money(200, "USD")), (F("money") + OldMoney(100, "USD"), Money(200, "USD")), (Money(100, "USD") + F("money"), Money(200, "USD")), (F("money") - Money(100, "USD"), Money(0, "USD")), (Money(100, "USD") - F("money"), Money(0, "USD")), (F("money") * 2, Money(200, "USD")), (F("money") * F("integer"), Money(200, "USD")), (Money(50, "USD") * F("integer"), Money(100, "USD")), (F("integer") * Money(50, "USD"), Money(100, "USD")), (Money(50, "USD") / F("integer"), Money(25, "USD")), (Money(51, "USD") % F("integer"), Money(1, "USD")), (F("money") / 2, Money(50, "USD")), (F("money") % 98, Money(2, "USD")), (F("money") / F("integer"), Money(50, "USD")), (F("money") + F("money"), Money(200, "USD")), (F("money") - F("money"), Money(0, "USD")), ), ) @parametrize_f_objects def test_save(self, f_obj, expected): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, "USD"), integer=2) instance.money = f_obj instance.save() instance.refresh_from_db() assert instance.money == expected @parametrize_f_objects def test_f_update(self, f_obj, expected): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, "USD"), integer=2) ModelWithVanillaMoneyField.objects.update(money=f_obj) instance.refresh_from_db() assert instance.money == expected def test_default_update(self): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, "USD"), integer=2) second_money = Money(100, "USD") ModelWithVanillaMoneyField.objects.update(second_money=second_money) instance.refresh_from_db() assert instance.second_money == second_money @pytest.mark.parametrize( "create_kwargs, filter_value, in_result", ( ({ "money": Money(100, "USD"), "second_money": Money(100, "USD") }, { "money": F("money") }, True), ({ "money": Money(100, "USD"), "second_money": Money(100, "USD") }, { "money": F("second_money") }, True), ({ "money": Money(100, "USD"), "second_money": Money(100, "EUR") }, { "money": F("second_money") }, False), ({ "money": Money(50, "USD"), "second_money": Money(100, "USD") }, { "second_money": F("money") * 2 }, True), ( { "money": Money(50, "USD"), "second_money": Money(100, "USD") }, { "second_money": F("money") + Money(50, "USD") }, True, ), ({ "money": Money(50, "USD"), "second_money": Money(100, "EUR") }, { "second_money": F("money") * 2 }, False), ( { "money": Money(50, "USD"), "second_money": Money(100, "EUR") }, { "second_money": F("money") + Money(50, "USD") }, False, ), ), ) def test_filtration(self, create_kwargs, filter_value, in_result): instance = ModelWithVanillaMoneyField.objects.create(**create_kwargs) assert (instance in ModelWithVanillaMoneyField.objects.filter( **filter_value)) is in_result def test_update_fields_save(self): instance = ModelWithVanillaMoneyField.objects.create(money=Money( 100, "USD"), integer=2) instance.money = F("money") + Money(100, "USD") instance.save(update_fields=["money"]) instance.refresh_from_db() assert instance.money == Money(200, "USD") INVALID_EXPRESSIONS = [ F("money") + Money(100, "EUR"), F("money") * F("money"), F("money") / F("money"), F("money") % F("money"), F("money") + F("integer"), F("money") + F("second_money"), F("money")**F("money"), F("money")**F("integer"), F("money")**2, ] @pytest.mark.parametrize("f_obj", INVALID_EXPRESSIONS) def test_invalid_expressions_access(self, f_obj): instance = ModelWithVanillaMoneyField.objects.create( money=Money(100, "USD")) with pytest.raises(ValidationError): instance.money = f_obj
class TestGetOrCreate: @pytest.mark.parametrize( "model, field_name, kwargs, currency", ( (ModelWithVanillaMoneyField, "money", { "money_currency": "PLN" }, "PLN"), (ModelWithVanillaMoneyField, "money", { "money": Money(0, "EUR") }, "EUR"), (ModelWithVanillaMoneyField, "money", { "money": OldMoney(0, "EUR") }, "EUR"), (ModelWithSharedCurrency, "first", { "first": 10, "second": 15, "currency": "CZK" }, "CZK"), ), ) def test_get_or_create_respects_currency(self, model, field_name, kwargs, currency): instance, created = model.objects.get_or_create(**kwargs) field = getattr(instance, field_name) assert str( field.currency ) == currency, "currency should be taken into account in get_or_create" def test_get_or_create_respects_defaults(self): value = Money(10, "SEK") instance = ModelWithUniqueIdAndCurrency.objects.create(money=value) instance, created = ModelWithUniqueIdAndCurrency.objects.get_or_create( id=instance.id, money_currency=instance.money_currency) assert not created assert instance.money == value def test_defaults(self): money = Money(10, "EUR") instance, _ = ModelWithVanillaMoneyField.objects.get_or_create( integer=1, defaults={"money": money}) assert instance.money == money def test_currency_field_lookup(self): value = Money(10, "EUR") ModelWithVanillaMoneyField.objects.create(money=value) instance, created = ModelWithVanillaMoneyField.objects.get_or_create( money_currency__iexact="eur") assert not created assert instance.money == value @pytest.mark.parametrize( "model, create_kwargs, get_kwargs", ( (NullMoneyFieldModel, { "field": Money(100, "USD") }, { "field": 100, "field_currency": "USD" }), (ModelWithSharedCurrency, { "first": 10, "second": 15, "currency": "USD" }, { "first": 10, "currency": "USD" }), ), ) def test_no_default_model(self, model, create_kwargs, get_kwargs): model.objects.create(**create_kwargs) instance, created = model.objects.get_or_create(**get_kwargs) assert not created def test_shared_currency(self): instance, created = ModelWithSharedCurrency.objects.get_or_create( first=10, second=15, currency="USD") assert instance.first == Money(10, "USD") assert instance.second == Money(15, "USD")
class TestVanillaMoneyField: @pytest.mark.parametrize( 'model_class, kwargs, expected', ( (ModelWithVanillaMoneyField, {'money': Money('100.0')}, Money('100.0')), (ModelWithVanillaMoneyField, {'money': OldMoney('100.0')}, Money('100.0')), (BaseModel, {}, Money(0, 'USD')), (BaseModel, {'money': '111.2'}, Money('111.2', 'USD')), (BaseModel, {'money': Money('123', 'PLN')}, Money('123', 'PLN')), (BaseModel, {'money': OldMoney('123', 'PLN')}, Money('123', 'PLN')), (BaseModel, {'money': ('123', 'PLN')}, Money('123', 'PLN')), (BaseModel, {'money': (123.0, 'PLN')}, Money('123', 'PLN')), (ModelWithDefaultAsMoney, {}, Money('0.01', 'RUB')), (ModelWithDefaultAsFloat, {}, Money('12.05', 'PLN')), (ModelWithDefaultAsStringWithCurrency, {}, Money('123', 'USD')), (ModelWithDefaultAsString, {}, Money('123', 'PLN')), (ModelWithDefaultAsInt, {}, Money('123', 'GHS')), (ModelWithDefaultAsDecimal, {}, Money('0.01', 'CHF')), ) ) def test_create_defaults(self, model_class, kwargs, expected): instance = model_class.objects.create(**kwargs) assert instance.money == expected retrieved = model_class.objects.get(pk=instance.pk) assert retrieved.money == expected def test_old_money_defaults(self): instance = ModelWithDefaultAsOldMoney.objects.create() assert instance.money == Money('.01', 'RUB') @pytest.mark.parametrize( 'model_class, other_value', ( (ModelWithVanillaMoneyField, Money('100.0')), (BaseModel, Money(0, 'USD')), (ModelWithDefaultAsMoney, Money('0.01', 'RUB')), (ModelWithDefaultAsFloat, OldMoney('12.05', 'PLN')), (ModelWithDefaultAsFloat, Money('12.05', 'PLN')), ) ) def test_revert_to_default(self, model_class, other_value): if hasattr(model_class._meta, 'get_field'): default_instance = model_class._meta.get_field('money').get_default() else: default_instance = model_class._meta.get_field_by_name('money').default instance1 = model_class.objects.create() pk = instance1.pk # Grab a fresh instance, change the currency to something non-default # and unexpected instance2 = model_class.objects.get(id=pk) instance2.money = Money(other_value.amount, "DKK") instance2.save() instance3 = model_class.objects.get(id=pk) assert instance3.money == Money(other_value.amount, "DKK") # Now change the field back to the default currency instance3.money = copy(default_instance) instance3.save() instance4 = model_class.objects.get(id=pk) assert instance4.money == default_instance @pytest.mark.parametrize( 'value', ( (1, 'USD', 'extra_string'), (1, None), (1, ), ) ) def test_invalid_values(self, value): with pytest.raises(ValidationError): BaseModel.objects.create(money=value) @pytest.mark.parametrize('Money', (Money, OldMoney)) @pytest.mark.parametrize('field_name', ('money', 'second_money')) def test_save_new_value(self, field_name, Money): ModelWithVanillaMoneyField.objects.create(**{field_name: Money('100.0')}) # Try setting the value directly retrieved = ModelWithVanillaMoneyField.objects.get() setattr(retrieved, field_name, Money(1, 'DKK')) retrieved.save() retrieved = ModelWithVanillaMoneyField.objects.get() assert getattr(retrieved, field_name) == Money(1, 'DKK') def test_rounding(self): money = Money('100.0623456781123219') instance = ModelWithVanillaMoneyField.objects.create(money=money) # TODO. Should instance.money be rounded too? retrieved = ModelWithVanillaMoneyField.objects.get(pk=instance.pk) assert retrieved.money == Money('100.06') @pytest.fixture(params=[Money, OldMoney]) def objects_setup(self, request): Money = request.param ModelWithTwoMoneyFields.objects.bulk_create(( ModelWithTwoMoneyFields(amount1=Money(1, 'USD'), amount2=Money(2, 'USD')), ModelWithTwoMoneyFields(amount1=Money(2, 'USD'), amount2=Money(0, 'USD')), ModelWithTwoMoneyFields(amount1=Money(3, 'USD'), amount2=Money(0, 'USD')), ModelWithTwoMoneyFields(amount1=Money(4, 'USD'), amount2=Money(0, 'GHS')), ModelWithTwoMoneyFields(amount1=Money(5, 'USD'), amount2=Money(5, 'USD')), ModelWithTwoMoneyFields(amount1=Money(5, 'EUR'), amount2=Money(5, 'USD')), )) @pytest.mark.parametrize( 'filters, expected_count', ( (Q(amount1=F('amount2')), 1), (Q(amount1__gt=F('amount2')), 2), (Q(amount1__in=(Money(1, 'USD'), Money(5, 'EUR'))), 2), (Q(id__in=(-1, -2)), 0), (Q(amount1=Money(1, 'USD')) | Q(amount2=Money(0, 'USD')), 3), (Q(amount1=Money(1, 'USD')) | Q(amount1=Money(4, 'USD')) | Q(amount2=Money(0, 'GHS')), 2), (Q(amount1=OldMoney(1, 'USD')) | Q(amount1=OldMoney(4, 'USD')) | Q(amount2=OldMoney(0, 'GHS')), 2), (Q(amount1=Money(1, 'USD')) | Q(amount1=Money(5, 'USD')) | Q(amount2=Money(0, 'GHS')), 3), (Q(amount1=Money(1, 'USD')) | Q(amount1=Money(4, 'USD'), amount2=Money(0, 'GHS')), 2), (Q(amount1=Money(1, 'USD')) | Q(amount1__gt=Money(4, 'USD'), amount2=Money(0, 'GHS')), 1), (Q(amount1=Money(1, 'USD')) | Q(amount1__gte=Money(4, 'USD'), amount2=Money(0, 'GHS')), 2), ) ) @pytest.mark.usefixtures('objects_setup') def test_comparison_lookup(self, filters, expected_count): assert ModelWithTwoMoneyFields.objects.filter(filters).count() == expected_count @pytest.mark.skipif(VERSION[:2] == (1, 8), reason="Django 1.8 doesn't support __date lookup") def test_date_lookup(self): DateTimeModel.objects.create(field=Money(1, 'USD'), created='2016-12-05') assert DateTimeModel.objects.filter(created__date='2016-12-01').count() == 0 assert DateTimeModel.objects.filter(created__date='2016-12-05').count() == 1 @pytest.mark.parametrize('lookup, rhs, expected', ( ('startswith', 2, 1), ('regex', '^[134]', 3), ('iregex', '^[134]', 3), ('istartswith', 2, 1), ('contains', 5, 2), ('lt', 5, 4), ('endswith', 5, 2), ('iendswith', 5, 2), ('gte', 4, 3), ('iexact', 3, 1), ('exact', 3, 1), ('isnull', True, 0), ('range', (3, 5), 4), ('lte', 2, 2), ('gt', 3, 3), ('icontains', 5, 2), ('in', (1, 0), 1) )) @pytest.mark.usefixtures('objects_setup') def test_all_lookups(self, lookup, rhs, expected): kwargs = {'amount1__' + lookup: rhs} assert ModelWithTwoMoneyFields.objects.filter(**kwargs).count() == expected def test_exact_match(self): money = Money('100.0') instance = ModelWithVanillaMoneyField.objects.create(money=money) retrieved = ModelWithVanillaMoneyField.objects.get(money=money) assert instance.pk == retrieved.pk def test_issue_300_regression(self): date = datetime.datetime(year=2017, month=2, day=1) ModelIssue300.objects.filter(money__created=date) ModelIssue300.objects.filter(money__created__gt=date) def test_range_search(self): money = Money('3') instance = ModelWithVanillaMoneyField.objects.create(money=Money('100.0')) retrieved = ModelWithVanillaMoneyField.objects.get(money__gt=money) assert instance.pk == retrieved.pk assert ModelWithVanillaMoneyField.objects.filter(money__lt=money).count() == 0 def test_filter_chaining(self): usd_instance = ModelWithVanillaMoneyField.objects.create(money=Money(100, 'USD')) ModelWithVanillaMoneyField.objects.create(money=Money(100, 'EUR')) query = ModelWithVanillaMoneyField.objects.filter().filter(money=Money(100, 'USD')) assert usd_instance in query assert query.count() == 1 @pytest.mark.parametrize('model_class', (ModelWithVanillaMoneyField, ModelWithChoicesMoneyField)) def test_currency_querying(self, model_class): model_class.objects.create(money=Money('100.0', 'ZWN')) assert model_class.objects.filter(money__lt=Money('1000', 'USD')).count() == 0 assert model_class.objects.filter(money__lt=Money('1000', 'ZWN')).count() == 1 @pytest.mark.usefixtures('objects_setup') def test_in_lookup(self): assert ModelWithTwoMoneyFields.objects.filter(amount1__in=(Money(1, 'USD'), Money(5, 'EUR'))).count() == 2 assert ModelWithTwoMoneyFields.objects.filter( Q(amount1__lte=Money(2, 'USD')), amount1__in=(Money(1, 'USD'), Money(3, 'USD')) ).count() == 1 assert ModelWithTwoMoneyFields.objects.exclude(amount1__in=(Money(1, 'USD'), Money(5, 'EUR'))).count() == 4 assert ModelWithTwoMoneyFields.objects.filter(amount1__in=(1, Money(5, 'EUR'))).count() == 2 assert ModelWithTwoMoneyFields.objects.filter(amount1__in=(1, 5)).count() == 3 @pytest.mark.usefixtures('objects_setup') def test_in_lookup_f_expression(self): assert ModelWithTwoMoneyFields.objects.filter(amount1__in=(Money(4, 'USD'), F('amount2'))).count() == 2 def test_isnull_lookup(self): NullMoneyFieldModel.objects.create(field=None) NullMoneyFieldModel.objects.create(field=Money(100, 'USD')) queryset = NullMoneyFieldModel.objects.filter(field=None) assert queryset.count() == 1 def test_null_default(self): instance = NullMoneyFieldModel.objects.create() assert instance.field is None
class TestGetOrCreate: @pytest.mark.parametrize('model, field_name, kwargs, currency', ((ModelWithVanillaMoneyField, 'money', { 'money_currency': 'PLN' }, 'PLN'), (ModelWithVanillaMoneyField, 'money', { 'money': Money(0, 'EUR') }, 'EUR'), (ModelWithVanillaMoneyField, 'money', { 'money': OldMoney(0, 'EUR') }, 'EUR'), (ModelWithSharedCurrency, 'first', { 'first': 10, 'second': 15, 'currency': 'CZK' }, 'CZK'))) def test_get_or_create_respects_currency(self, model, field_name, kwargs, currency): instance, created = model.objects.get_or_create(**kwargs) field = getattr(instance, field_name) assert str( field.currency ) == currency, 'currency should be taken into account in get_or_create' def test_get_or_create_respects_defaults(self): value = Money(10, 'SEK') instance = ModelWithUniqueIdAndCurrency.objects.create(money=value) instance, created = ModelWithUniqueIdAndCurrency.objects.get_or_create( id=instance.id, money_currency=instance.money_currency) assert not created assert instance.money == value def test_defaults(self): money = Money(10, 'EUR') instance, _ = ModelWithVanillaMoneyField.objects.get_or_create( integer=1, defaults={'money': money}) assert instance.money == money def test_currency_field_lookup(self): value = Money(10, 'EUR') ModelWithVanillaMoneyField.objects.create(money=value) instance, created = ModelWithVanillaMoneyField.objects.get_or_create( money_currency__iexact='eur') assert not created assert instance.money == value @pytest.mark.parametrize('model, create_kwargs, get_kwargs', ( (NullMoneyFieldModel, { 'field': Money(100, 'USD') }, { 'field': 100, 'field_currency': 'USD' }), (ModelWithSharedCurrency, { 'first': 10, 'second': 15, 'currency': 'USD' }, { 'first': 10, 'currency': 'USD' }), )) def test_no_default_model(self, model, create_kwargs, get_kwargs): model.objects.create(**create_kwargs) instance, created = model.objects.get_or_create(**get_kwargs) assert not created def test_shared_currency(self): instance, created = ModelWithSharedCurrency.objects.get_or_create( first=10, second=15, currency='USD') assert instance.first == Money(10, 'USD') assert instance.second == Money(15, 'USD')