POST http://localhost:8000/login/ 401 (Неавторизованный)
Я работаю над приложением на React Typescript и Django, но не могу заставить работать аутификацию. Я потратил на это несколько часов, но не могу найти решение. Любая помощь будет очень признательна.
App.tsx
import { useEffect, useState } from "react";
import { Home } from "./Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Login } from "./Login";
import Navbar from "./components/Navbar";
import axios from "axios";
export interface Movie {
id: number;
title: string;
genre: string;
year: number;
}
function App() {
const [movies, setMovies] = useState<Movie[]>([]);
const getMovies = async (): Promise<Movie[]> => {
try{
const { data } = await axios.get<Movie[]>('http://localhost:8000/api/movies')
setMovies(data)
console.log(data)
return data
}catch (error){
console.error("error",error);
return []
}
}
useEffect(() => {
getMovies();
},[])
return (
<main className="bg-slate-500 p-60 min-h-screen">
<Router>
<Navbar />
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/" element={<Home movies={movies} />} />
</Routes>
</Router>
</main>
)
}
export default App;
Login.tsx
import axios, { AxiosResponse } from "axios";
interface User {
email: string;
password: string;
}
async function logIn(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
console.log("Logging in");
try {
const user: User = { email: "e@a.com", password: "hei" }
const response: AxiosResponse = await axios.post('http://localhost:8000/login/', user, { withCredentials: true })
const responseData: User = response.data
console.log(responseData)
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Axios error", error.response?.data)
}
else {
console.error("error", error);
}
}
}
export const Login: React.FC = () => {
return (
<div className="flex flex-row">
<form onSubmit={(e) => logIn(e)} className="flex flex-col gap-12 p-40 m-auto bg-[#C7D2D6] rounded-xl w-1/2">
<div>
<h1 className="text-4xl">FilmFlix</h1>
<p className="text-lg">Sign In</p>
</div>
<div className="flex flex-col">
<label htmlFor="email">Email:</label>
<input id="email" className="border-2 rounded-md" type="text" placeholder="hei" />
</div>
<div className="flex flex-col">
<label htmlFor="password">Password:</label>
<input id="password" type="password" className="border-2 rounded-md" placeholder="password" />
</div>
<button className="bg-[#6A2DEB] p-4 w-1/2 m-auto text-white rounded-md">Sign In</button>
</form>
<div className="bg-slate-700 w-1/3 rounded-md">LOGO</div>
</div>
);
}
backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
managers.py
from django.contrib.auth.base_user import BaseUserManager
from django.utils.text import slugify
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifiers
for authentication instead of usernames.
"""
def create_user(self, email, password, username=None, **extra_fields):
"""
Create and save a User with the given email and password.
"""
from .models import MovieUser # Import MovieUser locally to avoid circular import
if not email:
raise ValueError(('The Email must be set'))
email = self.normalize_email(email)
username = slugify(email.split('@')[0])
user = MovieUser(email=email, username=username, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', 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)
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from .managers import CustomUserManager
class MovieUser(AbstractUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=150, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
serializers.py
from django.contrib.auth.password_validation import validate_password
from django.core import exceptions
from rest_framework import serializers
from django.contrib.auth import get_user_model
# establishes custom user model
User = get_user_model()
class MovieUserSerializer(serializers.ModelSerializer):
# By defining the model and fields in the Meta class, the UserCreateSerializer class inherits the default behavior
# and functionality provided by the ModelSerializer base class. It automatically generates serializers for the specified
# fields and handles the data validation and creation process.
class Meta:
model = User
# declare fields for creation
fields = ('email', 'password', 'username')
# responsible for performing additional validation and logic on the data before saving a new user instance.
def validate(self, data):
# logic for populating user fields
# This line creates a new User instance using the data dictionary, which contains the validated data from the serializer.
# By using the ** syntax, the dictionary values are unpacked and used as keyword arguments to initialize the User instance.
user = User(**data)
password = data.get('password')
# This block of code wraps the validate_password function call in a try-except block.
# It attempts to validate the password using the validate_password function provided by Django's password validation framework.
# If any validation errors occur, such as a weak password, it catches the exceptions.ValidationError and proceeds to the next step.
try:
validate_password(password, user)
except exceptions.ValidationError as e:
serializer_errors = serializers.as_serializer_error(e)
raise exceptions.ValidationError(
{'password': serializer_errors['non_field_errors']}
)
return data
def create(self, validated_data):
# Create a new user with the provided email and password
user = User.objects.create_user(
email=validated_data['email'],
password=validated_data['password'],
)
return user
urls.py
from django.urls import path
from .views import LoginAPIView
urlpatterns = [
path('', LoginAPIView.as_view(), name='login'),
]
views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth import authenticate, login
from .serializers import MovieUserSerializer
class LoginAPIView(APIView):
def post(self, request):
email = request.data.get('email')
password = request.data.get('password')
user = authenticate(request, email=email, password=password)
if user:
login(request, user)
serializer = MovieUserSerializer(user)
return Response(serializer.data)
else:
return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)
main urls.py
"""
URL configuration for core project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('core.api.urls')),
path('login/', include('registration.urls'))
]
registration - это папка, в которой находится код, связанный с пользователем
Вот что я получаю при попытке войти в систему с пользователем
Может быть, в LoginAPIView.post
следует вызывать authenticate
из EmailBackend
, а не django.contrib.auth
, как сейчас?