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!

Related posts:

  1. Django middleware
  2. django decorators
  3. Getting to know the django web framework
  4. django unicode integration: fix for venus djando template
  5. Password protect GNU screen sessions

Tags: , ,

  1. SmileyChris’s avatar

    The problem here isn’t queryset caching, but the fact that QuerySet objects use custom array slicing (http://www.djangoproject.com/documentation/db-api/#limiting-querysets). Every time you were doing u[0], you were getting the original object from the database again.

    Reply

  2. Gábor Farkas’s avatar

    hi,

    maybe i overlooked something, but i can’t see any problem with the example you showed. could you perhaps explain the problem in more detail?

    Reply

  3. amit’s avatar

    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.

    Reply

  4. SmileyChris’s avatar

    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).

    Reply

  5. WorldMaker’s avatar

    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.)

    Reply