django queryset weirdness

I needed to reset the django admin password and found this page which tells us how to do this. It did not work for me, however I tried to get the user object explicitly, reset the password and save it and it worked!
Here is a session describing the behavior:

> python manage.py shell
In [1]: from django.contrib.auth.models import User
In [2]: u=User.objects.all()
In [3]: u[0].password
Out[3]: 'sha1$0913d$6c5cfefb89b3c77dc8573e466a943c7acd177f6b'
In [9]: u[0].set_password('testme')
In [10]: u[0].save()
In [11]: u[0].password
Out[11]: 'sha1$0913d$6c5cfefb89b3c77dc8573e466a943c7acd177f6b'
In [12]: q=User.objects.get(id=1)
In [13]: q
Out[13]: <user : root>
In [14]: q.set_password('testme')
In [15]: q.save()
In [16]: q.password
Out[16]: 'sha1$24e9c$868ff39f08c3bde96397e33ea6a8847a658a16bc'

Maybe this is related to queryset caching, which would print the same password even after calling set_password. But it looks like in the first method, the password does not even get written to the database. This might be a bug. I will need to run more tests and report (or possibly patch) it...

Update: Perhaps I am not clear in the above post. The weirdness is that in the above two scenarios u[0] and q should essentially reference the same user object, but calling set_password (and later save) method on the u[0] reference does not seem to work (i.e. the password attribute remains unchanged and changed password cannot be used on the admin pages) but calling the same method on q reference seems to work!

Update2:I got it finally. Thanks all (Esp SmileyChris) ! every array slice is a new query! so in the first solution, I need this to make it work:

u=User.objects.all()
u[0].password # to display current hash (Query #1)
q=u[0] # (Query #2)
q.set_password('testme')
q.save()
q.password
u[0].password # (Query #3) this should print updated hash!

5 thoughts on “django queryset weirdness

  1. SmileyChris: But shouldn’t u[0].save() change the value in the database ? Infact, after executing the u[0].save() function, I did another User.objects.all() call and it still shows the old password.

    Gábor: watch the password attribute, it needs to change after save() function, but it did not change the first time, but changed when the save method is accessed via q reference.

  2. Saving a password isn’t done inside of set_password so this is what happens:
    1. >>> u[0].save_password(‘testme’)
    2. The user’s details are retrieved from the database and a User instance is created.
    3. The password is changed for this instance (but it hasn’t been saved).
    4. >>> u[0].save()
    5. The user’s details are retreived *again* from the database and a *new* User instance is created.
    6. This new instance is saved (and since no data has changed for this second instance, you won’t get your new password).

  3. SmileyChris is correct *everytime* you slice a queryset it runs the query. That is: every u[0] is a different object, so u[0].save() just resaves exactly what’s in the database already, because it refreshes on u[0]. Unfortunately, QuerySet just doesn’t have any sort of “slice caching”, so everytime you use [] you need to cache it yourself by saving it to a seperate name (in the case of a list, eg [:3], you want to pass it through list() to run the query if you aren’t using a for loop). This should probably be better documented…

    (I’ve been bitten by this myself… One example I’ve yet to fix is the random photo link on my frontpage: due to the order_by(‘?’) the random photo has an entirely different random alt tag and links to a further different random photo.)

Leave a Reply