码云地址:https://gitee.com/liuhaizhang/drf-project-initialization
项目目录结构:
study_drf
-home
-static
-study_drf
-util
-manage.py
pip install django #drf基于django
pip install djangorestframework #drf框架
pip install mysqlclient #连接数据库
pip install djangorestframework-jwt #认证
pip install django_filter #过滤
pip install django-cors-headers #跨域问题
#创建django项目
python django-admin startproject 项目名#创建应用
python manage.py startapp 应用名
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'study_drf',
"USER":"root",
"PASSWORD":"Huawei@123",
"PORT":3306,
"HOST":"127.0.0.1"
}
}
from django.db import models
# Create your models here.class UserModel(models.Model):
username = models.CharField(max_length=100,verbose_name='账户',unique=True)
password = models.CharField(max_length=256,verbose_name='密码')
role = models.SmallIntegerField(choices=((1,'管理员'),(2,'普通用户')))
phoneNumber = models.CharField(max_length=11,verbose_name='手机号码',unique=True)
realName = models.CharField(max_length=128,verbose_name='用户姓名')
#生成迁移文件
python manage.py makemigrations
#将生成的迁移文件真正执行到数据库中,生成对应的表格或修改数据库表
python manage.py migrate
#1、注册应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders', #放在新建的其他项目之前
'apps.users',
]
#2、中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', #【配置drf的跨域】注意顺序
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware', #【注销django】
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
#3、配置允许的跨域
#允许携带cookies
CORS_ALLOW_CREDENTIALS = True
#允许所有域名跨域,[有这个,就无需配置白名单]
CORS_ORIGIN_ALLOW_ALL = True
#跨域允许的请求
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
#跨域时,允许在请求头中携带的参数字段
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
#配置允许自定义的参数在请求头中,如token数据
'token'
)
#使用新的Response需要使用到
INSTALLED_APPS = [
.....
'rest_framework',
]
import jwt
import datetime
# from django.conf import settings
from django.conf import settings
import time
def create_token(user_id:int, timeout=1*24*60*60):
'''
:param user_id: 传递用户的id
:param timeout: token有效时间,默认是一天
:return:
'''
payload = {'user_id':user_id}
salt = settings.SECRET_KEY #加密的盐
# 构造header
headers = {
'type': 'jwt',
'alg': 'HS256'
}
now_datetime = time.time()
payload['exp'] = now_datetime + timeout #token过期时间,时间戳
# print(payload)
token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8')
return token
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from django.conf import settings
from jwt import exceptions
from home import models #模型类
class MyJWTAuthentication(BaseAuthentication):
def authenticate(self,request):
#获取token
token = request.META.get("HTTP_TOKNE") #从请求头中获取
if not token:
token = request.COOKIES.get('token') #从cookies中获取token
if not token:
msg = '没有携带token'
raise AuthenticationFailed({'code': 400, 'msg': msg})
'''
1、切割
2、解密第二段/判断过期
3、验证第三段合法性
'''
#导入settings中的字符串做盐
salt =settings.SECRET_KEY
payload = None
msg = None
try:
payload = jwt.decode(token,salt,True)
user = models.UserModel.objects.filter(id=payload.get('user_id')).first()
return (user,token)
#payload={'user_id':user.id}
except exceptions.ExpiredSignatureError:
msg='token过期了'
raise AuthenticationFailed({'code':400,'msg':msg})
except exceptions.DecodeError:
msg='token认证失败'
raise AuthenticationFailed({'code':400,'msg':msg})
except exceptions.InvalidTokenError:
msg='非法的token'
raise AuthenticationFailed({'code':400,'msg':msg})
#配置drf的全局认证
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
#这个是我们自己写的认证类所在的位置
'util.authentication.MyJWTAuthentication'
],
}
from rest_framework.permissions import BasePermission
from home import models
class SuperAdminPermission(BasePermission):
#重写has_permission方法
message='当前用户没有管理员权限'
def has_permission(self,request,view):
#view是视图类的对象,拿到视图类的东西
#不是超级用户不能访问,由于认证已经通过了,在request.user中就可以拿到用户
user= request.user
if user.role==1:
return True
#return True代表能够访问,负责不能访问
else:
return False
#全局使用:settings.py中配置,一般不会配置全局使用
REST_FRAMEWORK={
'DEFAULT_PERMISSION_CLASSES':['util.permission.SuperAdminPermission'],
}
# 这是一个对应ip进行限流的频率类
from rest_framework.throttling import SimpleRateThrottle
class IPThrottle(SimpleRateThrottle):
# 在settings中配置频率时使用到的关键字,有scope来指定
scope = 'IP'
def get_cache_key(self, request, view):
# 这里return什么,就以什么作为限制,这里限制ip,直接return ip就可以
return request.META.get('REMOTE_ADDR')
#可以在这里设置访问频率
# def get_rate(self):
# return '3/m'
在settings.py中
#全局使用
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
#这个是我们自己写的认证类所在的位置
'util.authentication.MyJWTAuthentication'
],
#全局的使用的频率限制
'DEFAULT_THROTTLE_CLASSES':(
'util.throttle.IPThrottle',
),
# 对于scope=IP的限制
'DEFAULT_THROTTLE_RATES':{
'IP':'3/m' #一个ip一分钟只能访问3次
}
}
#局部使用:在视图类开头设置
#setings中必须配置访问频率
REST_FRAMEWORK={
'DEFAULT_THROTTLE_RATES':{
'IP':'3/m' #一个ip一分钟只能访问3次
}
}
1、开放static目录
#设置static的访问路由,系統默認開放了路由,無需再手動配置
STATIC_URL = '/static/'
#对于以/static/开头的路由,从下面的设置的文件夹中查找
STATICFILES_DIRS=[os.path.join(BASE_DIR,'static')]
2、开放media目录:
#设置/media/ 路由,需要到根路由配置文件中开放该路由,系统没有默认开放
MEDIA_URL = '/media/'
#访问/media/路由时,从哪个目录下取资源
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
根urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ ]
#把media路由 加到路由列表中
urlpatterns+=static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
#就可以通过 http://域名:端口/media 访问media文件夹中的数据了
home/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from util.permission import SuperAdminPermission
from django.contrib.auth.hashers import make_password, check_password
from . import serializers
from . import models
from util.token import create_token
# Create your views here.
class LoginView(APIView):
authentication_classes = []
def post(self,request):
username = request.data.get('username')
password = request.data.get('password')
user = models.UserModel.objects.filter(username=username).first()
is_true = check_password(password,user.password)
if is_true:
token = create_token(user.pk)
response = Response({'code':200,"msg":'登录成功'})
response.set_cookie('token',token)
return response
else:
return Response({'code':400,'msg':'用户名或密码错误'})
study_drf/urls.py
from django.contrib import admin
from django.urls import path,include
from home import views as home_views
urlpatterns = [
path('admin/', admin.site.urls),
path('home/',include('home.urls')),
path('register/', home_views.RegisterView.as_view(), name='register'),
path('login/',home_views.LoginView.as_view(),name='login')
]
home/serializers.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from home import models
class UserLoginModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserModel
fields = ['username','role','phoneNumber','realName','id','password']
extra_kwargs = {
'id':{
'read_only':True,
},
'password':{
'write_only':True
}
}
def validate_phoneNumber(self,value):
if len(value) !=11:
raise ValidationError('手机号码验证失败')
return value
def validate_username(self,value):
if len(value)>20:
raise ValidationError('用户名长度过长')
name = 'abqwertyuiopasdfghjklzxcvbnm1234567890'
for key in value:
if key not in name:
raise ValidationError('用户名必须由小写字母数字组成')
if models.UserModel.objects.filter(username=value).first():
raise ValidationError('账户已经存在了,无法注册')
return value
def validate_role(self,value):
if value not in [1,2,'1','2']:
raise ValidationError('用户角色有问题')
if models.UserModel.objects.filter(role=1).first() and value==1:
raise ValidationError('管理员账户已经存在了')
return value
def create(self,validated_data):
#拿到传递进来的request
request = self.context.get('request')
try:
models.UserModel.objects.create(**validated_data)
except Exception as e:
raise ValidationError(str(e))
return validated_data
home/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from util.permission import SuperAdminPermission
from django.contrib.auth.hashers import make_password, check_password
from . import serializers
from . import models
from util.token import create_token
class RegisterView(APIView):
permission_classes = [SuperAdminPermission]
def get(self,request):
return Response({'code':100})
def post(self,request):
username = request.data.get('username')
name = request.data.get('name')
phoneNumber = request.data.get('phoneNumber')
role = request.data.get('role')
password = phoneNumber.strip()[-8:]
password = make_password(password)
data = {
'username':username,
'realName':name,
'phoneNumber':phoneNumber,
'role':role,
'password':password
}
ser = serializers.UserLoginModelSerializer(data=data, context={'request': request})
ser.is_valid(raise_exception=True)
ser.save()
return Response({'code':200,'method':'post'})
study_drf/urls.py
from django.contrib import admin
from django.urls import path,include
from home import views as home_views
urlpatterns = [
path('admin/', admin.site.urls),
path('home/',include('home.urls')),
path('register/', home_views.RegisterView.as_view(), name='register'),
path('login/',home_views.LoginView.as_view(),name='login')
]