英文:
request.user returns AnonymousUser in a project with react, django and rest framework
问题
我有一个网页项目,后端使用 django
编码,前端使用 react
编码。
在我的项目中,我想为特定用户创建一个订单,因此项目的这部分需要知道用户,但当我使用 request.user
时它返回 AnonymousUser
。
这里是 react
,axios
代码和带有 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_framework
、corsheaders
和api
应用程序,如下所示:
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在浏览器中进行post
和get
数据的测试。首先转到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)不要忘记在用户的register
、login
和logout
组件的顶部添加以下代码:
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.
- JWT (Json Web Token)-based authentication.
- 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:
- 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
- Add the
rest_framework
,corsheaders
and api app toINSTALLED_APPS
insettings.py
like this:
INSTALLED_APPS = [
...
'corsheaders',
'rest_framework',
'api.apps.ApiConfig',
]
-
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
andpermissions
configurations:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication',], 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],}
-
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')),
]
- 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())
]
- Create
serializers.py
file in the api directory and define the serializer classes toregister
,login
andidentify 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')
- In the
views.py
file define theregister
,login
andlogout
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)
- 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
- Now, you should test your app with the default DRF ui to
post
andget
data in your browser. First go to thehttp:127.0.0.1:8000/register/
and post a user data. If ininspect> application tab> cookies
you can see thecsrftoken
after registeration like this:
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:
- 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
- Don't forget to add these codes at the top of your user
register
,login
andlogout
components:
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;
- 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 thesettings.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'),
]
- After changing the
settings.py
file, you should add a new pattern to yoururlpatterns
ina-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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论