Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I change existing token in the authtoken of django-rest-framework?

Saving fails:

from rest_framework.authtoken.models import Token

token = Token.objects.get(user=user1)
token.key = '1'
token.save()

gives

IntegrityError: duplicate key value violates unique constraint "authtoken_token_user_id_key"

like image 490
nmb.ten Avatar asked Dec 19 '13 14:12

nmb.ten


3 Answers

I had the same issue today so I thought I should leave a response for future reference explaining why this happens.

The key column is the primary key of the Token table.

If you edit a model instance, when you call the save() django has to figure whether it has to do an INSERT or an UPDATE. It does so by looking for an existing row (using the primary key) in the database, since you just changed the primary key it won't find it and will assume it has to insert a new row. Since you already have a row for that user and the user_id column has a unique constraint the insert will fail.

An example with the corresponding generated sql.

>>> t = Token.objects.get(user=User.objects.get(id=1))
>>> t.key
u'b0750c801a1b075051ed084841f3001bb55dd1f1'
>>> t.key = t.generate_key()
'cafb2efb9b26912d60fc5a0a75b2b7ba0348a2ec'
t.save()

That save() call renders

SELECT (1) AS "a"
  FROM "authtoken_token"
  WHERE "authtoken_token"."key" = 'cafb2efb9b26912d60fc5a0a75b2b7ba0348a2ec'
  LIMIT 1;
INSERT
  INTO "authtoken_token" ("key", "user_id", "created")
  VALUES ('cafb2efb9b26912d60fc5a0a75b2b7ba0348a2ec', 1, '2014-03-18 21:48:30.434677+00:00');

There it's clear that the SELECT is looking for a row with the edited token (cafb2efb9b26912d60fc5a0a75b2b7ba0348a2ec).

Solution:

I guess in this specific case you can just delete the old Token instance and create a new one (in a single transaction), and it will auto generate the key for you, something like:

token.delete()
Token.objects.create(user=user1)

Note that this is an issue at the ORM level, you can avoid deleting/inserting and just change the value of that row but not using on the save() method.

Using the update() method is a good option as mariodev suggested, you'll need to manually generate the token (se above how I did it) and yes it that will change the primary key, the key column, but that's what you wanted in the first place :)

like image 179
gonz Avatar answered Oct 12 '22 11:10

gonz


Seems like this would work:

Token.objects.filter(user=user1).update(key='1')
like image 22
mariodev Avatar answered Oct 12 '22 13:10

mariodev


This sample code work for me and update token in database:

t = Token.objects.filter(user=user1)
new_key = t[0].generate_key()
t.update(key=new_key)
like image 44
amir shahpoori Avatar answered Oct 12 '22 13:10

amir shahpoori