1. 程式人生 > >DAY98 - Rest Framework(三)- 序列化元件之HyperlinkedIdentityField和初識認證元件

DAY98 - Rest Framework(三)- 序列化元件之HyperlinkedIdentityField和初識認證元件

一、序列化元件之HyperlinkedIdentityField

HyperlinkedIdentityField可以通過反向解析向前臺返回一個連結

url(r'^Books/(?P<id>\d+)', views.BooksDetail.as_view(),name='test'),
# 傳三個引數
# view_name='test':路由名字,用來反向解析
# lookup_field='publish_id':要反向解析的引數值
# lookup_url_kwarg='id':有名分組的名字
url = serializers.HyperlinkedIdentityField(view_name='test', lookup_field='publish_id',lookup_url_kwarg='id')
class Books(APIView):
    def get(self, request):
        response = {'status': 200, 'msg': '查詢成功', 'data': None}
        books = models.Book.objects.all()
        # context={'request':request}是必寫的
        ret = BooksSerializers(books, many=True,context={'request':request})
        response['data'] = ret.data
        return JsonResponse(response, safe=False)
# 返回結果
{
    "status": 200,
    "msg": "查詢成功",
    "data": [
        {
            "id": 1,
            "url": "http://127.0.0.1:8000/Books/1",
            "name": "紅樓夢",
            "price": "42.21",
            "publish": 1,
            "authors": [
                1,
                2
            ]
        },
        {
            "id": 3,
            "url": "http://127.0.0.1:8000/Books/2",
            "name": "西遊記",
            "price": "12.32",
            "publish": 2,
            "authors": [
                2
            ]
        }
    ]
}
# url": "http://127.0.0.1:8000/Books/1"
# context={'request':request}:得到了域名http://127.0.0.1:8000
# view_name:得到了Books/(?P<id>\d+)
# lookup_field 和lookup_url_kwarg:得到了1
# 把這三個拼接起來就成了一條路由http://127.0.0.1:8000/Books/1

二、序列化元件之資料校驗

1.基本使用

class BooksSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
# 新增資料
def post(self, request):
    response = {'status': 200, 'msg': '新增成功', 'data': None}
    # 反序列化把request.data中的JSON格式的資料傳入data中
    ret = BooksSerializers(data = request.data)
    # is_valid 對反序列化後的資料進行校驗
    if ret.is_valid():
        # 對校驗成功的資料儲存
        ret.save()
        response['data'] = ret.data
    else:
        response['status'] = 201
        # errors:錯誤資訊
        response['data'] = ret.errors
        response['msg'] = '新增失敗'
     return JsonResponse(response, safe=False)

# 如果是更新資料
def put(self, request, id):
    response = {'status': 200, 'msg': '修改成功', 'data': None}
    books = models.Book.objects.filter(pk=id).first()
    if books:
        # 不傳instance,調save(),往資料庫新增資料
        # 傳instance,調save(),修改資料
        # BooksSerializers(data=request.data.instance='要更新的物件')
        ret = BooksSerializers(data=request.data,instance=books)
        if ret.is_valid():
            ret.save()
            response['data'] = ret.data
        else:
            response['status'] = 201
            response['data'] = ret.errors
            response['msg'] = '修改失敗'
    else:
        response['status'] = 201
        response['msg'] = '修改物件不存在'
    return JsonResponse(response, safe=False)

2.自定義錯誤資訊

class BooksSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
    # 類似forms元件
    name = serializers.CharField(max_length=10, min_length=3, error_messages={'max_length': '最長為10','min_length': '最短為3','required':'不能為空'})

3.區域性鉤子以及全域性鉤子

# 區域性鉤子
# validate_欄位名
def validate_name(self,value):
    if value.startswith('sb'):
        raise ValidationError('不能以sb開頭')
    else:
        return value
# 全域性鉤子
# 只有通過欄位校驗才會判斷全域性鉤子
def validate(self, value):
    # value是通過校驗的資料
    print(value)
    name = value.get('name')
    price = value.get('price')
    if name and price:
        if str(name) == str(price):
            # 通過判斷返回value
            return value
        else:
            # 沒通過就報錯
            raise ValidationError('名字跟價格不相等')
    return value

# 全域性鉤子error  
# non_field_errors:[錯誤資訊]

三、認證元件簡單使用

# 模型層
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)


class UserToken(models.Model):
    user = models.OneToOneField(to='User')
    token = models.CharField(max_length=64)
#mySer.py
from rest_framework.exceptions import APIException
class Auth():
    def authenticate(self,request):
        # 包裝後的request物件,請求來的所有東西都能拿出來
        token = request.GET.get('token')
        ret = models.UserToken.objects.filter(token=token).first()
        # 如果有值,說明登入過了,而且帶的隨機字串也是正確的
        # 如果認證通過,需要返回東西,如果認證不通過,要拋異常
        if ret:
            return None
        else:
            # 如果沒有值,拋異常
            raise APIException('您沒有登入')
from app01.mySer import *
import uuid

# 登陸檢視
class Login(APIView):
    def post(self, request):
        response = {'status': 200, 'msg': '登入成功'}
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        user = models.User.objects.filter(name=name, pwd=pwd).first()
        if user:
            token = uuid.uuid4()
            # 登陸成功後,存入token表
            models.UserToken.objects.create(token=token, user=user)
            response['token'] = token
        else:
            response['status'] = 201
            response['msg'] = '新增失敗'
        return JsonResponse(response, safe=False)
class Books(APIView):
    # 登入後才能操作,在所需的視圖裡區域性使用
    authentication_classes=[Auth,]
    def get(self, request):
        .......
    def post(self, request):
        .......
    
    
class BooksDetail(APIView):
    authentication_classes = [Auth, ]
    def get(self, request):
        .......
    def post(self, request):
        .......

原始碼分析

# 第一步
# APIView類
def dispatch(self, request, *args, **kwargs):
    ........
    # 重點是這個,這是認證、頻率相關的
    self.initial(request, *args, **kwargs)
    ........
# 第二步
# APIView類
def initial(self, request, *args, **kwargs):
    ........
    # 這個就是認證
    self.perform_authentication(request)
    
    self.check_permissions(request)
    self.check_throttles(request)
# 第三步
# APIView類
def perform_authentication(self, request):
    # 這個request是已經封裝好後的Request的物件
    request.user
# 第四步
# Request類
@property
def user(self):
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
            self._authenticate()
    return self._user
# 第五步
# Request類
def _authenticate(self):
    # 從下面回到這裡,就可以知道
    # self.authenticators=authentication_classes
    # 拿上面的例子舉例
    # self.authenticators=[Auth, ];authenticator就是Auth
    for authenticator in self.authenticators:
        try:
            user_auth_tuple = authenticator.authenticate(self)
# 注意:authenticator.authenticate(self)中的self,由於是在Request類裡,所以這個self就是Request例項化的物件request;
# 所以:authenticator.authenticate(self)=Auth.authenticate(self,request)
        except exceptions.APIException:
            self._not_authenticated()
            raise

        if user_auth_tuple is not None:
            self._authenticator = authenticator
            self.user, self.auth = user_auth_tuple
            return

   self._not_authenticated()


# self.authenticators的來歷
# APIView類
# self.authenticators 是Request例項化的時候傳進來的引數self.get_authenticators()
def initialize_request(self, request, *args, **kwargs):
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )


# self.get_authenticators()的來歷
# APIView類
def get_authenticators(self):
    # self.authentication_classes
    # 從子類找驗證類:authentication_classes = [Auth, ]
    # 從父類APIView裡找驗證類:authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    # 找到之後,迴圈並加上()執行
    return [auth() for auth in self.authentication_classes]