Search code examples
pythondjangotestingstatedjango-unittest

Django Test - I can't save my object when I use queryset to get it


I have an model, let's say something like:

class Invoice(models.Model):
    client_name = models.CharField(max_length=100)

And I have a file that looks like:

def foo_1():
    for inv in Invoice.objects.all():
        inv.client_name = "Joe"
        inv.save()

def foo_2(inv):
    inv.client_name = "Joe"
    inv.save()

My issue occurred when I test it:

class FooCase(TestCase):
    def test_foo(self):
        inv = Invoice("Jack")

        print inv.client_name    # output Jack, OK
        foo_1()
        print inv.client_name    # output Jack, KO !
        print inv.client_name    # output Jack, OK
        foo_2(inv)
        print inv.client_name    # output Joe, OK

I don't understand why there is two differents behaviour :(

I added a modification date to my model, and try to output it in foo_1 before and after the loop for, the save seems to work properly, but it is like the object is different...

I don't know if that can help but I also output vars(invoice) from test_foo and then from foo_1() and finally from foo_2(). The state was the same in test_foo and foo_2() (<django.db.models.base.ModelState object at 0x32ca090>), but not in foo_1() (<django.db.models.base.ModelState object at 0x32cc650>)


Solution

  • Django returns a different object each time you retrieve a record from the database:

    >>> i1 = Invoice.objects.all()[0]
    >>> i2 = Invoice.objects.get(pk=i1.pk)
    >>> i1 == i2
    True // since model instances compare by their id field only
    >>> id(i1) == id(i2)
    False // since they are actually separate instances
    

    You instantiate inv = Invoice("Jack") before calling foo_1, so after foo_1 updates all invoices, you still have your old inv instance, which hasn't been updated (since foo_1 instantiates its model objects itself, which are separate instances and do not affect inv) and hasn't been reloaded from the database — nothing has modified inv.client_name, although the record in the database has been updated. foo_2, on the other hand, works on that specific instance that you pass as an argument, so you see the changed client_name; you would actually see that change even if you don't save the instance.