Reputation: 551
I wanted to know if I am able to create a Token for a normal model class. Suppose that I have the following class:
class User(models.Model):
name = models.CharField(max_length=30, unique=True)
profile_pic = models.FileField(upload_to='None/',default='placeholder.jpg')
joined_date = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
Note that my User class does not extend auth.models.User
.
Now when I use a signal like this:
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Then Django states that Token.user
is not an instance of User
. Basically, the same as in this SO thread.
From that link above, I learned that my User
class has to extend the built-in auth.models.User
class provided by Django
for authentication.
But I am curious and would like to know if there is a way to generate a Token for classes that do not extend auth.models.User ? Is that possible ? If yes, how ?
Upvotes: 1
Views: 1889
Reputation: 1052
#1 approach
We are trying here to generate tokens. Token, by definition, are randomly generated sequences of chars. Taking inspiration from the rest auth token model, we can have a similar custom model. We will be overriding the save method of this model to generate the key.
import binascii
import os
from django.db import models
from django.utils.translation import ugettext_lazy as _
class NonBuiltInUserToken(models.Model):
...
key: str = models.CharField(_("Key"), max_length=40, primary_key=True)
user: User = models.OneToOneField(
User, related_name='auth_token',
on_delete=models.CASCADE,
verbose_name=_("User")
)
created = models.DateTimeField(_("Created"), auto_now_add=True)
...
class Meta:
verbose_name = _("Token")
verbose_name_plural = _("Tokens")
def save(self, *args, **kwargs):
if not self.key:
self.key = self.generate_key()
return super(NonBuiltInUserToken, self).save(*args, **kwargs)
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
return self.key
Copied from this stack overflow answer
#2 approach
You can also try to subclass the Token model of the auth token.
...
from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.auth_token.models import Token
...
class NonBuiltInUserToken(Token):
"""
Overrides the Token model to use the
non-built-in user model
"""
user = models.OneToOneField(
User, related_name='auth_token',
on_delete=models.CASCADE,
verbose_name=_("User")
)
If we check the Token
model in the rest_framework.authtoken.models, we will see that Token
class is abstract if rest_framework.authtoken
is not present in the settings.INSTALLED_APPS
.
class Meta:
# Work around for a bug in Django:
# https://code.djangoproject.com/ticket/19422
#
# Also see corresponding ticket:
# https://github.com/encode/django-rest-framework/issues/705
abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS
Thus in order to use this token model, you will be required to remove the rest_framework.authtoken
from the settings.INSTALLED_APPS
Now, you have a new token model for non-build in the user model. You will be using this model in the signals to create tokens.
Upvotes: 2