question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Instances returned by get due to django_get_or_create don't apply kwargs

See original GitHub issue

Model

class Currency(models.Model):
    iso = models.CharField(max_length=4, unique=True)
    fee = models.DecimalField(max_digits=8, decimal_places=2)

Factory

class CurrencyFactory(DjangoModelFactory):

    iso = factory.LazyAttribute(lambda x: fake.currency_code())

    class Meta:
        model = Currency
        django_get_or_create = ('iso',)

Test

>>> CurrencyFactory(iso='HKD').fee
Decimal('10.00')
>>> CurrencyFactory(iso='HKD', fee=5).fee
Decimal('10.00')
>>> CurrencyFactory(iso='ABC', fee=5).fee
Decimal('5.00')
>>> CurrencyFactory(iso='ABC', fee=6).fee
Decimal('5.00')

Despite the overriding value provided in the factory constructor, the overriding values are ignored. Is this by design and the docs should ideally clarify this or is this an oversight? It’s particularly annoying to debug this issue in a large test suite that may run in a random order and so sometimes collide.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
mattduckcommented, Jun 25, 2020

Hi! FWIW I lost some time today debugging this exact situation. I think the reasoning for not implementing the feature as update_or_create is fair, but it violates expectation to explicitly pass in a field value and receive back an object with a different value.

+1 for at least updating the docs to make it clear that kwargs are not applied when get_or_create is used. I’ll look into submitting a patch for the docs this weekend (but can’t promise I’ll get to it).

0reactions
francoisfreitagcommented, Apr 30, 2020

In the example you wrote I don’t see any confusion, because you have defined the get_or_create attribute in your Factory and you have changed that object in your test case.

I see two sources for confusion in my example:

  1. The same call (CurrencyFactory(iso='HKD')) has different results depending on whether it ran before or after the call to CurrencyFactory(iso='HKD', fee=5). I think that’s the ordering issue Federico was referring to.
  2. The reference to the initial CurrencyFactory(iso='HKD') gets outdated when CurrencyFactory(iso='HKD', fee=5) is executed. It updates the row in the DB, but (obviously) does not invalidates previously created test objects.

django_update_or_create adds side effects to a factory call, where the library modifies objects somewhat behind the users’ back. The job of factories is to create test objects, not be a wrapper to update entries in the database.

i think the single test should tell the entire history so I always try to avoid using fixtures (in order to ensure a better readability)

I agree, that’s why in general I prefer not to use django_get_or_create at all and create test data from scratch. The test database should ideally be empty and populated with the data that’s relevant for the test. In that case, I know whether the object should be created, or I can get a reference to it if it exists. If the database has an initial state, that state should be known and factories do not seem like the right tool to retrieve or modify existing objects from the database.

Read more comments on GitHub >

github_iconTop Results From Across the Web

django get_or_create depending on result from a custom ...
Seems to return expected results, although not fully tested in a ... is to try to get an existing instance from # the...
Read more >
QuerySet API reference | Django documentation
Django provides a range of QuerySet refinement methods that modify either the types of results returned by the QuerySet or the way its...
Read more >
problems with get_or_create - Google Groups
hi,. I have a model like this: name - unique slno - not null mode - not null. If the instance does not...
Read more >
Django: Using get_or_create to prevent race conditions
The problem is this: if you hit this part of the code in one thread (meaning the lookup has already taken place and...
Read more >
Source code for django.db.models.fields.related_descriptors
Related objects manager for related instances on the reverse side of a ... class (we don't need it) """ if instance is None:...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found