實戰-DRF快速寫介面(認證許可權頻率)
阿新 • • 發佈:2022-04-05
實戰-DRF快速寫介面
開發環境
- Python3.6
- Pycharm專業版2021.2.3
- Sqlite3
- Django 2.2
- djangorestframework3.13
測試工具
Postman
需求
- 註冊介面,包含欄位使用者名稱,密碼,確認密碼,使用者型別
- 登陸介面,校驗使用者名稱,密碼,生成隨機字串
- 認證功能,除了註冊登陸介面外,所有介面都要登陸後訪問
- 頻率限制功能,每分鐘訪問5次,book的所有介面,使用這個頻率類
- 許可權限制功能,publish的所有操作需要超級使用者能訪問,其他的普通登陸使用者就可以操作
模型
from django.db import models class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=255) user_type = models.IntegerField(choices=((1, '超級管理員'), (2, '普通管理員'), (3, '普通使用者'))) class UserToken(models.Model): user = models.OneToOneField(to=User,on_delete=models.CASCADE) token = models.CharField(max_length=32) class Book(models.Model): title = models.CharField(max_length=11) price = models.DecimalField(max_digits=5, decimal_places=2) authors = models.ManyToManyField(to='Author') publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) @property def publish_detail(self): return {'name': self.publish.name,'Email':self.publish.email} @property def author_list(self): l = [] print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]> for author in self.authors.all(): print(author.authordetail) # AuthorDetail object (1) l.append({'name': author.username, 'gender': author.gender, 'address': author.authordetail.address,'telephone':author.authordetail.telephone}) return l class Author(models.Model): username = models.CharField(max_length=11) gender = models.IntegerField(choices=((1, '男'), (2, '女'), (3, '未知'))) authordetail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) @property def authordetail_info(self): return {'telephone': self.authordetail.telephone, 'address': self.authordetail.address} class AuthorDetail(models.Model): telephone = models.BigIntegerField() address = models.CharField(max_length=32) class Publish(models.Model): name = models.CharField(max_length=11) email = models.EmailField()
序列化器
from rest_framework import serializers from .models import * # 使用者序列化器 class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' # 書序列化器 class BookSerializer(serializers.ModelSerializer): class Meta: model = Book # fields = '__all__' fields = ['id', 'title', 'price', 'publish', 'authors', 'publish_detail', 'author_list'] extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True}, } # 作者序列化器 class AuthorSerializer(serializers.ModelSerializer): class Meta: # 指定和哪個表有關係 model = Author # fields = '__all__' fields = ['id', 'username', 'gender', 'telephone', 'address', 'authordetail_info'] # 重寫欄位telephone和addr telephone = serializers.CharField(write_only=True) address = serializers.CharField(write_only=True, max_length=8, required=False) # 重寫create,操作兩個表 def create(self, validated_data): # 先存作者詳情 authordetail = AuthorDetail.objects.create(telephone=validated_data.get('telephone'), address=validated_data.get('address')) # 存作者表 author = Author.objects.create(author_detail=authordetail, gender=validated_data.get('gender'), username=validated_data.get('username')) # 這樣只返回author物件就行,直接存了兩個表,返回反序列化的物件 return author # 出版社序列化器 class PublishSerializer(serializers.ModelSerializer): class Meta(): model = Publish fields = '__all__'
檢視
from django.contrib.auth.hashers import make_password, check_password from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.viewsets import ViewSet from rest_framework.viewsets import ModelViewSet from .auth import * from .serializer import * from .models import * # 註冊檢視 class UserRegisterView(ViewSet): @action(methods=["POST"], detail=False) def register(self, request): usernmae = request.data.get('username') password = request.data.get('password') re_password = request.data.get('re_password') user_type = request.data.get('user_type') if User.objects.filter(username=usernmae): return Response({'msg': f'使用者{usernmae}已註冊!', 'code': 4000}) else: if password == re_password: # make_password加密:make_password(password, salt=None, hasher='default') user_date = {'username': usernmae, 'password': make_password(password), 'user_type': user_type} user_serializer = UserSerializer(data=user_date) if user_serializer.is_valid(): user_serializer.save() return Response({'code': 2001, 'msg': f'使用者{usernmae}註冊成功'}) else: return Response({'code': 4001, 'msg': '註冊失敗', 'errors': user_serializer.errors}) else: return Response({'msg': '兩次密碼不一致', 'code': 4002}) # 登入檢視 class UserLoginView(ViewSet): @action(methods=["POST"], detail=False) def login(self, request): username = request.data.get('username') password = request.data.get('password') user = User.objects.filter(username=username).first() # check_password(password, encoded, setter=None, preferred='default') if user and check_password(password, user.password): import uuid token = str(uuid.uuid4()) UserToken.objects.update_or_create(user=user, defaults={'token': token}) return Response({'code': 2000, 'msg': f'使用者{user.username}登入成功', 'token': token}) return Response({'code': 4004, 'msg': '校驗失敗,使用者名稱或密碼錯誤'}) # 書接檢視 class BookView(ModelViewSet): authentication_classes = [LoginAuth,] throttle_classes = [IPThrottle,] queryset = Book.objects.all() serializer_class = BookSerializer # 作者檢視 class AuthorView(ModelViewSet): authentication_classes = [LoginAuth,] queryset = Author.objects.all() serializer_class = AuthorSerializer # 出版社檢視 class PublishView(ModelViewSet): authentication_classes = [LoginAuth, ] permission_classes = [UserPermission,] queryset = Publish.objects.all() serializer_class = PublishSerializer
認證許可權頻率
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
from rest_framework.throttling import SimpleRateThrottle
from app01 import models
# 認證類
class LoginAuth(BaseAuthentication):
# 重寫authenticate方法
def authenticate(self, request):
# 獲取前端攜帶的token,token放在哪是自己規定的,比如從查詢引數中獲取
token = request.query_params.get('token')
# 比對隨機字串
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
# 登入了,返回當前登入使用者和token
return user_token.user, token
else:
# 沒有登入,拋異常
raise AuthenticationFailed('您沒有登入,請登入')
# 許可權類
class UserPermission(BasePermission):
def has_permission(self, request, view):
# 沒有許可權的提示資訊
self.message = '您是:%s,沒有許可權' % request.user.get_user_type_display()
# 如果有許可權,返回True,沒有許可權返回False
# 許可權類,在認證類之後,request.user有了當前登入使用者
user_type = request.user.user_type
print(user_type)
if user_type < 3: # 只要不是1,2,就沒有許可權
return True
else:
return False
# 頻率類
class IPThrottle(SimpleRateThrottle):
scope = 'ip'
# get_cache_key返回什麼就以什麼方法做限制,限制條件必須唯一,比如使用者id
def get_cache_key(self, request, view):
# 限制ip地址,從request.META字典中獲取ip
'''
request.META:請求頭中的資料
'''
return request.META.get('REMOTE_ADDR') # 客戶端ip
配置檔案
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'ip': '5/m' # minute_3是scope的字串,一分鐘訪問5次
}, }
路由
from django.contrib import admin
from django.urls import path,include
from app01 import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user',views.UserLoginView,'user')
router.register('user',views.UserRegisterView,'user')
router.register('books',views.BookView,'books')
router.register('author',views.AuthorView,'author')
router.register('publish',views.PublishView,'publish')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
]
測試
下面是普通使用者,403了~