Search code examples
pythondjangoauthenticationviewdjango-views

django after submit login info, does not lead to intended detail view page


I don't know what to do here after a few days of trying different ideas. After I submit the the prop login email and password, the page does not do anything. It just says "Invalid password". I am not sure my login view is connected to the sqllite. I am thinking my mistake is where the login view file, but not sure. I also use chatgpt to see if it can identify where the mistake is, but it didn't come up with anything. If anyone can point me where I need to adjust, would be much appricated. If I go to the address of the http://127.0.0.1:8000/user/2/, then, the page shows correctly. Unfortunately, the codes are long.

Here are all the codes:

views.py:

import random
from django.shortcuts import render
from django.views.generic import (ListView, DetailView)
from django.views.decorators.csrf import csrf_exempt
from .models import CustomUser, Events
from rest_framework.views import APIView
from .serializers import CustomUserSerializer, EventsSerializer
from rest_framework.response import Response
from rest_framework import status
from rest_framework.renderers import TemplateHTMLRenderer
from django.contrib.auth import authenticate, login
from django.contrib import messages
from django.shortcuts import redirect, render
from django.urls import reverse
from .models import CustomUser  # Assuming CustomUser is your user model

def index(request):
    return render(request, "users_application/index.html", {})

def user_detail(request, id):
    user = CustomUser.objects.get(id = id)
    events = user.events_set.all()
    return render(request, "users_application/user_detail.html", {'user': user})
    
# Define a view function for the login page
def user_login(request):
    if request.method == "POST":
        email = request.POST.get('email')
        password = request.POST.get('password')
        if not CustomUser.objects.filter(email=email).exists():
            messages.error(request, 'Invalid Email')
            return redirect('login')

        user = authenticate(request, username=email, password=password)
        if user is None:
            messages.error(request, "Invalid Password")
            return redirect('login')
        else:
            login(request, user)
            # Redirect to the user's detail page after login
            return redirect('user-detail', id=user.id)

    return render(request, 'users_application/login.html')



class CustomUserAPIView(APIView):
    def get(self, request):
        users = CustomUser.objects.all()
        serializer = CustomUserSerializer(users, many=True)
        return Response(serializer.data)


class CustomUserDetailView(APIView):   
    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'users_application/user_detail.html'  # Create this template
    
    def get_object(self, id):
        try:
            return CustomUser.objects.get(id=id)
        except CustomUser.DoesNotExist:
            raise Http404

    def get(self, request, id=None):
        if id:
            user = self.get_object(id)
            serializer = CustomUserSerializer(user)
            return Response({'user': serializer.data})
        else:
            users = CustomUser.objects.all()
            serializer = CustomUserSerializer(users, many=True)
            return Response({'users': serializer.data})

urls.py

from django.urls import path
from .views import user_detail
from .views import CustomUserAPIView, EventAPIView, CustomUserDetailView, user_login
from . import views


urlpatterns = [
    path("", views.index, name="index"),  #index is the function name from view.py
    #path('users/', views.user_list, name = "users"),
    #path('user/<int:id>/', views.user_detail, name = "user_detail"),
    path('users/', CustomUserAPIView.as_view(), name='user-list'),
    path('user/<int:id>/', CustomUserDetailView.as_view(), name='user-detail'),
    path('login/', views.user_login, name='login'),    
    ]

backends.py

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
            if user.check_password(password):
                return user
        except UserModel.DoesNotExist:
            return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

settings.py

from pathlib import Path

ALLOWED_HOSTS = ["*"]


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users_application',
    'rest_framework',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'REST_PROJECT.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'REST_PROJECT.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = "users_application.CustomUser"

AUTHENTICATION_BACKENDS = ['users_application.backends.EmailBackend', 'django.contrib.auth.backends.ModelBackend']

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <!-- Login form -->
        <form class="col-6 mx-auto card p-3 shadow-lg" method="post" enctype="multipart/form-data">
            <h1 style="text-align: center;"><span style="color: green;">USER LOGIN</span></h1>

            {% csrf_token %}  <!-- CSRF token for security -->
            <!-- Login heading -->
            <h3>Login</h3>
            <hr>

            <!-- Display error/success messages -->
            {% if messages %}
            <div class="alert alert-primary" role="alert">
                {% for message in messages %}
                {{ message }}
                {% endfor %}
            </div>
            {% endif %}

            <!-- Email input field -->
            <div class="form-group">
                <label for="exampleInputEmail1">Email</label>
                <input type="email" class="form-control" name="email" id="exampleInputEmail1" aria-describedby="emailHelp"
                       placeholder="Enter email" required>
            </div>

            <!-- Password input field -->
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password" required>
            </div>

            <!-- Submit button -->
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
</body>
</html>

and fanlly, models.py

from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager  
from django.db import models
from django.conf import settings

class CustomUserManager(BaseUserManager): 
    
    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True')
        
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True')
        
        return self.create_user(email, password, **extra_fields)

    def create_user(self, email, password, **extra_fields): 
        if not email:  
            raise ValueError('The Email must be set')  
        email = self.normalize_email(email)  

        user = self.model(email=email, **extra_fields)  
        user.set_password(password)  
        user.save()  
        return user


class CustomUser(AbstractUser):
    
    username = None
    email = models.EmailField(("email address"), unique=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []
    
    objects = CustomUserManager()

    def __str__(self):
        return self.email
    

class Events(models.Model):
    
    TYPES = (
        ('PRI', 'Rides'),
        ('CLN', 'Cleaning'),
        ('CPN', 'Companionship'),
        )
    
    #event = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    event = models.ForeignKey(CustomUser, related_name='events', on_delete=models.CASCADE)
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    category = models.CharField(max_length=50, choices = TYPES)
    previous_balance = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    spent = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    add = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    remaining_balance = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    destination = models.TextField(blank=True)
    

 

Solution

  • I see that you have customized some functions but without them being different from the original. If it is in order to understand how they work, it is not bad otherwise it is not necessary, everything you have done in custom django already does it very well, except for the EmailBakend which must personalize the connection mode with email and password. So be careful when you customize or overload functions and classes, make sure that the framework does not already do what you want to modify.

    I have run your code and it's work, does your message appear for all user ?

    update: I suspected that it was the way you save your password, 'cause django don't reverse the saved hashed password but hash the one you give when you try to authenticate and compare the signature, here's what you're going to do to have a hashed password in your db: if you use django forms

    from django.contrib.auth.hashers import make_password
    
    class CustomUserForm(forms.ModelForm):
        class Meta:
            model = CustomUser
            fields = '__all__'
      
        def clean_password(self):
            # Retrieve password entered by user
            password = self.cleaned_data.get('password')
            # Hash the password
            hashed_password = make_password(password)
            return hashed_password
    

    else in your signup function do this :

    from django.contrib.auth.hashers import make_password
    
    def signup(request):
        if request.POST:
            ...
            password = request.POST.get('password')
            if password:
                hashed_password = make_password(password)
            ...