request.user在一个使用React、Django和Rest框架的项目中返回AnonymousUser。

huangapple go评论65阅读模式
英文:

request.user returns AnonymousUser in a project with react, django and rest framework

问题

我有一个网页项目,后端使用 django 编码,前端使用 react 编码。

在我的项目中,我想为特定用户创建一个订单,因此项目的这部分需要知道用户,但当我使用 request.user 时它返回 AnonymousUser

这里是 reactaxios 代码和带有 rest_framework 的 views.py:

react 代码

    // Hook 用于保存游戏 ID 和价格
    const [all, setAll] = useState({'game': 0, 'price': 0});
    
    // 获取状态
    const location = useLocation();
    const state = location.state;
    
    useEffect(() => {
        setAll({'game': state[0], 'price': suggestion});
    }, [])
    
    // 检查用户确认建议并调用创建订单的 API
    const orderApi = async (e) => {
        e.preventDefault();
        try {
            console.log(all);
            const {status} = await axios.post('http://127.0.0.1:8000/api-1/order/create/', all);
            if (status === 201) {
                console.log('created');
            }
        } catch (err) {
            console.log(err)
        }
    }

views.py

from rest_framework import status
from rest_framework.response import Response
from . import serializer
from rest_framework.decorators import api_view
from . import models
from game.models import Game

@api_view(['GET', 'POST'])
def orderCreate(request):
    
    # 这里我的程序返回 AnonymousUser
    print(str(request.user) + '\n')
    
    if request.method == 'POST':
    		.......
    			
    orders = models.Order.objects.all()
    serialized = serializer.OrderSerializer(orders, many=True)

    return Response(serialized.data, status=status.HTTP_200_OK)
英文:

I have a web project what back-end is coded with django and front-end is coded with react.

In my project, i want to create an order to a special user so this part of project needs to know the user but when i used request.user it returned AnonymousUser.

here are the react, axios code and views.py with rest_framework:

react code

    // Hook to save game id and price
    const [all, setAll] = useState({'game': 0, 'price': 0});
    
    // get state
    const location = useLocation();
    const state = location.state;
    
    useEffect(() => {
        setAll({'game': state[0]}, 'price': suggestion);
    }, [])
    
        // To check user confirm suggestion and make order api.
    const orderApi = async (e) => {
        e.preventDefault();
        try {
            // console.log({'state': state[0], 'suggestion': suggestion});
            console.log(all);
            const {status} = await axios.post('http://127.0.0.1:8000/api-1/order/create/', all);
            if (status === 201) {
                console.log('created');
            }
        } catch (err) {
            console.log(err)
        }
    }

views.py

from rest_framework import status
from rest_framework.response import Response
from . import serializer
from rest_framework.decorators import api_view
from . import models
from game.models import Game

@api_view(['GET', 'POST'])
def orderCreate(request):
    
    # Here my program return AnonymousUser
    print(str(request.user) + '\n')
    
    if request.method == 'POST':
    		.......
    		
    orders = models.Order.objects.all()
    serialized = serializer.OrderSerializer(orders, many=True)

    return Response(serialized.data, status=status.HTTP_200_OK)
  • To write a clean code i didn't write the front-end code and post method codes in my question.
  • Please help to retrieve the user object and create order.

答案1

得分: 1

REST API是无状态的,不处于用户会话中。
所以没有用户登录,Django会返回匿名用户。
您需要在POST参数中传递用户。

英文:

REST API's are stateless, not in a user session.
So there's no user logged on, and Django returns you the Anonymous user.
You will have to pass the user in the post parameters

答案2

得分: 1

以下是翻译好的部分:

在经过数天的研究后,最终我解决了我的问题。

我们可以使用两种方法来在Django中对用户进行身份验证。
1)基于JWT(JSON Web Token)的身份验证。
2)基于会话的身份验证。

我使用了基于会话的身份验证方法来解决我的问题。

现在你可以在这里看到一个简单的Django和React Web项目中基于会话的身份验证的完整代码,逐步说明:
1)在终端中启动项目和应用程序,然后安装DRF和corsheaders包:

django-admin startproject authentication
python manage.py startapp api
pip install djangorestframework
pip install django-cors-headers

2)在settings.py中的INSTALLED_APPS中添加rest_frameworkcorsheadersapi应用程序,如下所示:

INSTALLED_APPS = [
    ...
    'corsheaders',
    'rest_framework',
    'api.apps.ApiConfig',
]

3)允许任何主机向您的Django服务器发送请求:

a)将ALLOWED_HOSTS变量添加为'*'。

ALLOWED_HOSTS = ['*']

b)添加corsheaders变量。

CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True

c)添加rest_framework的身份验证和权限配置:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication',],
    'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],
}

4)在位于authentication目录中的urls.py中向urlpatterns列表添加一个新的模式:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    ...
    path('', include('api.a-urls')),
]

5)在api目录中创建一个新的Python文件(a-urls.py),并定义您的api应用程序的urlpatterns列表:

from django.urls import path
from django.views.generic import TemplateView

from . import views

urlpatterns  =[
    path('register/', views.RegisterView.as_view()),
    path('login/', views.LoginView.as_view()),
    path('logout/', views.LogoutView.as_view()),
    path('user/', views.UserView.as_view())
]

6)在api目录中创建serializers.py文件,并定义用于注册、登录和识别用户的序列化器类:

from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model, authenticate
from rest_framework.exceptions import ValidationError

UserModel = get_user_model()

class RegisterSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = '__all__'

    def create(self, clean_data):
        user_obj = self.model.objects.create_user(email=clean_data['email'],
                                                  password=clean_data['password'])
        user_obj.username = clean_data['username']
        user_obj.save()
        return user_obj

class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    email = serializers.EmailField()
    password = serializers.CharField()

    def check_user(self, clean_data):
        user = User.objects.filter(username=clean_data['username'],
                                   email=clean_data['email']).first()

        if not user:
            raise ValidationError('User not found')
        elif not user.check_password(clean_data['password']):
            raise ValidationError('Password incorrect')
        return user

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = ('username', 'email')

7)在views.py文件中定义注册、登录和注销类:

from rest_framework import status, permissions
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from django.contrib.auth import login, logout, authenticate
from rest_framework.views import APIView
from django.contrib.auth.models import User
from . import serializers
from . import validations
from rest_framework.decorators import api_view

class RegisterView(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)
    def post(self, request):
        print(request.data)
        # 这里我们可以验证整个数据
        clean_data = validations.UserValidation(request.data).registerValidation()
        serialized = serializers.RegisterSerializer(data=clean_data)
        if serialized.is_valid():
            user = serialized.create(clean_data)
            if user:
                return Response(data=serialized.data,
                                status=status.HTTP_201_CREATED)
        return Response(status=status.HTTP_400_BAD_REQUEST)

class LoginView(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)

    def post(self, request):
        data = request.data
        clean_data = validations.UserValidation(data).loginValidation()
        serialized = serializers.LoginSerializer(data=request.data)
        if serialized.is_valid(raise_exception=True):
            user = User.objects.filter(username=clean_data['username'], email=clean_data['email']).first()
            user.check_password(clean_data['password'])
            print(user)
            if user:
                login(request, user)
            return Response(serialized.data,
                            status=status.HTTP_202_ACCEPTED)

class LogoutView(APIView):
    def post(self, request):
        logout(request)
        return Response(status=status.HTTP_200_OK)

class UserView(APIView):
    permission_classes = (permissions.IsAuthenticated,)
    # authentication_classes = (SessionAuthentication,)

    def get(self, request):
        print(request.user)
        serialized = serializers.UserSerializer(request.user)
        return Response({'user': serialized.data},
                        status=status.HTTP_200_OK)

8)还可以在另一个文件中使用自定义验证类来验证数据,因此创建一个validations.py文件:

class UserValidation:
    def __init__(self, data):
        self.data = data

    def registerValidation(self):
        ...
        return self.data

    def loginValidation(self):
        ...
        return self.data

9)现在,您应该使用默认的DRF UI在浏览器中进行postget数据的测试。首先转到http:127.0.0.1:8000/register/并提交用户数据。如果在inspect > application tab > cookies中可以看到像这样的csrftoken,则注册成功:

10)如果默认的DRF视图中的一切都正常,请创建一个新的React应用程序,并转到其目录以启动React应用程序:

npx create-react-app frontend
cd frontend
npm start

11)不要忘记在用户的registerloginlogout组件的顶部添加以下代码:

axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;

12)在编写React

英文:

After several days researching on my question, at the end i solved it.

We can use 2 ways to authenticate our users in django.

  1. JWT (Json Web Token)-based authentication.
  2. session-based authentication.

I solved my question with the session-based authentication way.

Now you can see here the absolute code of session-based authentication in a simple django and react web project step by step:

  1. Start the project and app then install DRF and corsheaders packeages in your terminal:
django-admin startproject authentication
python manage.py startapp api
pip install djangorestframework
pip install django-cors-headers
  1. Add the rest_framework, corsheaders and api app to INSTALLED_APPS in settings.py like this:
INSTALLED_APPS = [
    ...
    'corsheaders',
    'rest_framework',
    'api.apps.ApiConfig',
]
  1. Allow any host to send request to your django server:

    a) add '*' to ALLOWED_HOSTS variable.

    ALLOWED_HOSTS = ['*']
    

    b) add the corsheaders variables.

    CORS_ALLOW_ALL_ORIGINS = True
    CORS_ALLOW_CREDENTIALS = True
    

    c) add the rest_framework authentication and permissions configurations:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication',],
        'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],}
    
  2. In your urls.py in the authentication directory add a new pattern to the urlpatterns list:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    ...
    path('', include('api.a-urls')),
]
  1. Create a new python file (a-urls.py) in your api directory and define a urlpatterns list to your api app:
from django.urls import path
from django.views.generic import TemplateView

from . import views

urlpatterns  =[
    path('register/', views.RegisterView.as_view()),
    path('login/', views.LoginView.as_view()),
    path('logout/', views.LogoutView.as_view()),
    path('user/', views.UserView.as_view())
]
  1. Create serializers.py file in the api directory and define the serializer classes to register, login and identify user:
from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model, authenticate
from rest_framework.exceptions import ValidationError

UserModel = get_user_model()

class RegisterSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = '__all__'

        def create(self, clean_data):
            user_obj = self.model.objects.create_user(email=clean_data['email'],
                                                      password=clean_data['password'])
            user_obj.username = clean_data['username']
            user_obj.save()
            return user_obj


class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    email = serializers.EmailField()
    password = serializers.CharField()

    def check_user(self, clean_data):

        user = User.objects.filter(username=clean_data['username'],
                                   email=clean_data['email']).first()

        if not user:
            raise ValidationError('User not found')
        elif not user.check_password(clean_data['password']):
            raise ValidationError('Password incorrect')
        return user


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = ('username', 'email')
  1. In the views.py file define the register, login and logout classes:
from rest_framework import status, permissions
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from django.contrib.auth import login, logout, authenticate
from rest_framework.views import APIView
from django.contrib.auth.models import User
from . import serializers
from . import validations
from rest_framework.decorators import api_view


class RegisterView(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)
    def post(self, request):
        print(request.data)
        # Here we can validate entire data
        clean_data = validations.UserValidation(request.data).registerValidation()
        serialized = serializers.RegisterSerializer(data=clean_data)
        if serialized.is_valid():
            user = serialized.create(clean_data)
            if user:
                return Response(data=serialized.data,
                                status=status.HTTP_201_CREATED)
        return Response(status=status.HTTP_400_BAD_REQUEST)


class LoginView(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)

    def post(self, request):
        data = request.data
        clean_data = validations.UserValidation(data).loginValidation()
        serialized = serializers.LoginSerializer(data=request.data)
        if serialized.is_valid(raise_exception=True):
            user = User.objects.filter(username=clean_data['username'], email=clean_data['email']).first()
            user.check_password(clean_data['password'])
            print(user)
            if user:
                login(request, user)
            return Response(serialized.data,
                            status=status.HTTP_202_ACCEPTED)


class LogoutView(APIView):
    def post(self, request):
        logout(request)
        return Response(status=status.HTTP_200_OK)


class UserView(APIView):
    permission_classes = (permissions.IsAuthenticated,)
    # authentication_classes = (SessionAuthentication,)

    def get(self, request):
        print(request.user)
        serialized = serializers.UserSerializer(request.user)
        return Response({'user': serialized.data},
                        status=status.HTTP_200_OK)
  1. Also we can validate data in another file with a custome validation class so create a validations.py file:
class UserValidation:
    def __init__(self, data):
        self.data = data

    def registerValidation(self):
        ...
        return self.data

    def loginValidation(self):
        ...
        return self.data
  1. Now, you should test your app with the default DRF ui to post and get data in your browser. First go to the http:127.0.0.1:8000/register/ and post a user data. If in inspect> application tab> cookies you can see the csrftoken after registeration like this:request.user在一个使用React、Django和Rest框架的项目中返回AnonymousUser。

register is ok, and then test the login with default view of DRF. After posting the user data it should save sessionid in cookies like this:request.user在一个使用React、Django和Rest框架的项目中返回AnonymousUser。

  1. If everything in default view of DRF is ok, create a new react app and go to its directory to start your react app:
npx create-react-app frontend
cd frontend
npm start
  1. Don't forget to add these codes at the top of your user register, login and logout components:
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;
  1. After coding your react app components, you should use the npm run build command to get an output directory called build in your frontend directory and add some configurations code to the settings.py in backend:
# Add a new direction to your templates directions
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR / 'templates',

            # This is new dir:
            os.path.join(BASE_DIR, 'frontend/build'),
        ]
        ....


# Add the static files configuration.
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/build/static'),
]
  1. After changing the settings.py file, you should add a new pattern to your urlpatterns in a-urls.py file like this:
from django.views.generic import TemplateView

urlpatterns  =[
    .....
    path('', TemplateView.as_view(template_name='index.html')),
]

Now a base code of backend is ready so if you open 127.0.0.1:8000 in your browser you will see the frontend view with react and an authentication with django.

At the end of my description i want to write a very simple react component to try django authenticatin:

import axios from 'axios';
import {useState} from 'react';

axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;

const Try = () => {

    const [user, setUser] = useState({'username': 'amiram', 'email': 'example@gmail.com', 'password': 'picodegit'});

    const registerApi = async (e) => {
        e.preventDefault();
        try {
            const status = await axios.post('http://127.0.0.1:8000/register/', user).then((res) => {
                console.log(res);
            });
            if (status.status === 201) {
                console.log('created');
            }
        } catch (err) {
            console.log(err);
        }
    }

    const loginApi = async (e) => {
        e.preventDefault();
        try {
            const status = await axios.post('http://127.0.0.1:8000/login/', user);
            if (status.status === 202) {
                console.log('accepted');
            }
        } catch (err) {
            console.log(err);
        }
    }

    const logoutApi = async (e) => {
        e.preventDefault();
        try {
            const status = await axios.post('http://127.0.0.1:8000/logout/');
            if (status.status === 200) {
                console.log('logged out');
            }
        } catch (err) {
            console.log(err);
        }
    }

    const check = async (e) => {
        e.preventDefault();
        try {
            const status = await axios.get('http://127.0.0.1:8000/user/').then((res) => {
                console.log(res.data);
            })
            if (status.status === 201) {
                console.log('got data');
            }
        } catch (err) {
            console.log(err);
        }
    }

    return (
        <>
            <div>hello</div>
            <button onClick={registerApi}>register</button>
            <button onClick={loginApi}>login</button>
            <button onClick={logoutApi}>logout</button>
            <button onClick={check}>check</button>
        </>
    )
}

export default Try;

答案3

得分: 0

正如 @stefano 提到的,REST 是无状态的。默认情况下,request.user 将是 AnonymousUser,因为 Django 并不隐式地知道发起请求的用户。

如果这个特殊用户是恒定的,你可能可以创建一个小的辅助函数来返回它。

def get_special_user():
    special_user_id = 1
    return User.objects.get(id=special_user_id)

然后在你的视图中调用这个函数。否则,你将需要设置身份验证。我推荐使用 django rest auth

英文:

As @stefano mentioned, REST is stateless. By default request.user will be AnonymousUser because Django isn't implicitly aware of the user making the request.
If this special user is constant you can probably create a small helper function to return it.

def get_special_user():
    special_user_id = 1
    return User.objects.get(id=special_user_id)

Then call this function in your view. Otherwise, you will have to set up authentication. I recommend django rest auth

huangapple
  • 本文由 发表于 2023年6月2日 05:38:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76385883.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定