I currently have a backend setup where users can register by providing an email, name, and password. These fields are required for registration. I want to implement a guest login feature where a guest account is deleted when the browser is closed or the guest logs out.
How should I proceed to create a guest account, and what information should I include for the guest account (email, name, password)?
Here is the current code:
models.py
class UserManager(BaseUserManager):
"""Manager for user"""
def create_user(self, email, name, password=None, **extra_fields):
"""Create, save and return a new user."""
if not email:
raise ValueError('User must have an email address.')
user = self.model(email=self.normalize_email(email), name=name, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, password):
"""Create and save a new superuser with given details"""
user = self.create_user(email, name, password)
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""Database model for users in the system"""
id = models.AutoField(primary_key=True)
email = models.EmailField(unique=True)
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_guest = models.BooleanField(default=False)
phone_number = models.CharField(max_length=20, blank=True, null=True)
avatar_color = models.CharField(max_length=7)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
def save(self, *args, **kwargs):
if not self.pk:
self.avatar_color = random.choice([
'#FF5733', '#C70039', '#900C3F', '#581845',
'#8E44AD', '#1F618D', '#008000', '#A52A2A', '#000080'
])
super().save(*args, **kwargs)
serializers.py
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the user object."""
class Meta:
model = get_user_model()
fields = ['id', 'email', 'password', 'name', 'phone_number', 'avatar_color']
extra_kwargs = {
'email': {'required': False},
'password': {'required': False, 'write_only': True, 'style': {'input_type': 'password'}, 'min_length': 6},
'name': {'required': False},
'phone_number': {'required': False},
'avatar_color': {'read_only': True}
}
def create(self, validated_data):
"""Create and return a user with encrypted password."""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update and return user."""
password = validated_data.pop('password', None)
user = super().update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
class GuestSerializer(serializers.ModelSerializer):
"""Serializer for guests."""
class Meta:
model = get_user_model()
fields = ['id', 'email', 'name', 'is_guest', 'avatar_color']
extra_kwargs = {
'email': {'required': False},
'name': {'required': False},
'is_guest': {'default': True},
'avatar_color': {'read_only': True}
}
def create(self, validated_data):
# Create and return a guest user with default values.
validated_data['email'] = f'guest_{random.randint(100000, 999999)}@example.com'
validated_data['name'] = 'Guest'
validated_data['is_guest'] = True
return get_user_model().objects.create_user(**validated_data)
class AuthTokenSerializer(serializers.Serializer):
"""Serializer for the user auth token."""
email = serializers.EmailField()
password = serializers.CharField(
style={'input_type': 'password'},
trim_whitespace=False,
)
def validate(self, attrs):
"""Validate and authenticate the user."""
email = attrs.get('email')
password = attrs.get('password')
user = authenticate(
request=self.context.get('request'),
username=email,
password=password,
)
if not user:
msg = _('Unable to authenticate with provided credentials.')
raise serializers.ValidationError(msg, code='authorization')
attrs['user'] = user
return attrs
views.py
class CreateUserView(generics.CreateAPIView):
"""Create a new user in the system."""
serializer_class = UserSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [permissions.UpdateOwnProfile]
filter_backends = (filters.SearchFilter,)
search_fields = ('name', 'email',)
class UserViewSet(viewsets.ModelViewSet):
"""List all users."""
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
class CreateGuestView(generics.CreateAPIView):
"""Create a new guest."""
serializer_class = GuestSerializer
def perform_create(self, serializer):
user = serializer.save()
token, created = Token.objects.get_or_create(user=user)
print('TOKEN: ', token.key)
return Response({'token': token}, status=status.HTTP_201_CREATED)
class GuestLogoutView(APIView):
"""Logout a guest and delete."""
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request, *args, **kwargs):
if request.user.is_guest:
request.user.auth_token.delete()
request.user.delete()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class CreateTokenView(ObtainAuthToken):
"""Create a new auth token for user."""
serializer_class = AuthTokenSerializer
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
class UserLoginApiView(ObtainAuthToken):
"""Handle creating user authentication tokens"""
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
class LogoutView(APIView):
"""Logout the authenticated user."""
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request, *args, **kwargs):
logout(request)
return Response(status=status.HTTP_200_OK)
class ManageUserView(generics.RetrieveUpdateAPIView):
"""Manage the authenticated user."""
serializer_class = UserSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get_object(self):
"""Retrieve and return the authenticated user."""
return self.request.user
urls.py
urlpatterns = [
path('register/', views.CreateUserView.as_view(), name='register'),
path('register/guest/', views.CreateGuestView.as_view(), name='register_guest'),
path('logout/', views.LogoutView.as_view(), name='logout'),
path('logout/guest', views.GuestLogoutView.as_view(), name='logout_guest'),
path('token/', views.CreateTokenView.as_view(), name='token'),
path('me/', views.ManageUserView.as_view(), name='me'),
path('', include(router.urls)),
]
1. Add a new attribute to your AUTH_USER_MODEL
to recognize guests:
class User(AbstractBaseUser, PermissionsMixin):
"""Database model for users in the system"""
•••Rest of code•••
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_guest = models.BooleanField(default=False)
•••Rest of code•••
2. Have a different View
specifically to have guests:
class CreateGuestView(generics.CreateAPIView):
"""Create a new guest."""
serializer_class = GuestSerializer
•••Rest of code•••
3. Create the GuestSerializer
used above:
class GuestSerializer(serializers.ModelSerializer):
"""Serializer for guests."""
•••Rest of code•••
def create(self, validated_data):
"""Create and return a user with is_guest."""
return get_user_model().objects.create_user(is_guest = True, **validated_data)
•••Rest of code•••
4. Delete a guest on logout:
class GuestLogoutView(APIView):
"""Logout a guest and delete."""
•••Rest of code•••
def post(self, request, *args, **kwargs):
request.user.auth_token.delete()
request.user.delete()
return Response(status=status.HTTP_200_OK)
Remember to register all these new View
s to a URL. An assumption made was that settings.AUTH_USER_MODEL
is correctly setup. One good reason for making this assumption is your use of get_user_model()
.