1. 程式人生 > >RESTful規範(一)

RESTful規範(一)

一、學習restframework之前準備

1、json格式若想展示中文,需要ensure_ascii=False

import json

dic={'name':'你好'}
print(json.dumps(dic,ensure_ascii=False))

2、不基於restframework也可以通過django來做符合restframework的規範介面設計,Jsonresponse,若想在json展示中文,如下

def books(request):

    ll=[{'name':'python全站開發','price':20},{'name':'linux
','price':30}] # return HttpResponse(json.dumps(ll)) return JsonResponse(ll,safe=False,json_dumps_params={'ensure_ascii':False})

3、原聲cbv執行流程

url(r'^books/$', views.Book.as_view()),


原聲cbv執行流程---》as_view----》dispatch---》相應到具體的函式 from django.views import View class Book(View): def get
(self,reuquest): # reuquest.method return HttpResponse('get') def post(self,request): return HttpResponse('post')

3、分析restframework

一 restfu(規範)
    是什麼:
        -面向資源程式設計
        -getBooklist:獲取圖書列表
        -符合規範的:books
    規範:
        -method:get----》books----》取到所有的書
                
                 post———》books
---》新增圖書 put/patch--》books/id---》修改圖書 delete---》books/id---》刪除圖書 -https://api.example.com/v1/zoos?limit=10 - 二 drf 安裝(app):pip3 install djangorestframework -基於drf寫resful的介面,得寫CBV -request物件,原始碼分析 -APIView原始碼分析

二、restframework使用

1、POST沒法解析json格式,可以在body裡取值出來序列化反序列化操作轉成字典,目前drf可以在前臺提交post後臺取值的話用data,

from rest_framework.views import APIView

class Book(APIView):
    def get(self,request):
        # 拿原來的request物件
        # request._request
        # print(request.method)
        # print(request._request.method)
        # request.POST
        # request.method
        return HttpResponse('get')
    def post(self,request):
        print(request.method)
        print(request._request.method)
        print(request.POST)

        # 用apiview之後,再取資料,從request.data
        print(request.data)
        return HttpResponse('post')

2、用Postman模擬發http請求,網上直接下載,data可以類似於解析器,解析form-data,urlencoded,json格式,而django只能解析form-data,urlencoded兩種格式

三、序列化元件

1、從資料庫取出來的都是qs型別,裡面套了一個一個物件,要傳到前臺去必須要json格式,符合drf規範的第一種方式

資料庫配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'resful', 'USER':'root', 'PASSWORD':'', 'HOST':'127.0.0.1', 'PORT':3306, } }

models取值為qureryset型別,

from app01 import models
序列化組建
第一種方式
class Book(APIView):
    def get(self,request):
        response={'status':100,'msg':None}
        books=models.Book.objects.all()
        # ll=[]
        # for book in books:
        #     ll.append({'name':book.name,''})
        ll=[ {'name':book.name,'price':book.price} for book in books]
        response['msg']='查詢成功'
        response['data']=ll
        return JsonResponse(response,safe=False)

        # return HttpResponse('get')
    def post(self,request):

        return HttpResponse('post')

2、第二種方式,用django子自帶序列化元件serializers(Django內建的serializers(把物件序列化成json字串),但是有個缺點不能定製化

from django.core import serializers


class Book(APIView):
    def get(self,request):
        # response={'status':100,'msg':None}
        books = models.Book.objects.all()
        ret = serializers.serialize("json", books)
        return HttpResponse(ret)

        # return HttpResponse('get')
    def post(self,request):

        return HttpResponse('post')

3、用drf自帶的元件,建議的話新建個py在APP下,方便解耦,如myserial.py,為符合drf格式,裡面新建個response類,見下

    -1 匯入:from rest_framework import serializers
    -2 寫一個類(名字任意),繼承serializers.Serializer
        class BookSer(serializers.Serializer):
            nid=serializers.IntegerField()
            name3=serializers.CharField(source='name')
            price=serializers.CharField()
            # publish_date = serializers.DateField()
            publish_date = serializers.CharField()
            # publish=serializers.CharField(source='publish.email')
            publish=serializers.CharField(source='publish.name')
            xxx=serializers.CharField(source='test')
    -3 如果不指定source,欄位名,必須跟資料庫列名一致
    -4 source--》既可以指定資料屬性,又可以指定方法屬性,可以寫(publish.name)
    -5 使用:
        -查詢出要序列化的資料:books = models.Book.objects.all()
        -ret=myserial.BookSer(books,many=True)-----》多條(queryset物件),必須指定many=True
        -ret=myserial.BookSer(books,many=False)-----》一條(Book物件),必須指定many=False
    -6 aa=serializers.SerializerMethodField()
        -必須配套一個方法(get_aa(self,obj)),方法返回結果,會賦給aa
        -在方法內部,可以繼續用序列化元件

myserial.py

from rest_framework import serializers

class BookSer(serializers.Serializer):
    nid=serializers.IntegerField()
    name3=serializers.CharField(source='name')
    price=serializers.CharField()
    # publish_date = serializers.DateField()
    publish_date = serializers.CharField()
    # publish=serializers.CharField(source='publish.email')
    publish=serializers.CharField(source='publish.name')
    xxx=serializers.CharField(source='test')
    # authors=serializers.CharField(source='authors.all')
    # SerializerMethodField,可以寫一個方法方法名叫:get_欄位名字,方法返回值,會賦給authors
    aa=serializers.SerializerMethodField()
    # def get_authors(self,obj):
    #     authors=obj.authors.all()
    #     # ll=[ author.name for author in authors]
    #     ll=[ {'name':author.name,'age':author.age} for author in authors]
    #     return ll
    def get_aa(self, obj):
        authors = obj.authors.all()
        # ll=[ author.name for author in authors]
        ser=AuthorSer(authors,many=True)
        return ser.data

models.py

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)

    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors=models.ManyToManyField(to='Author')

    def test(self):
        return 'ttttttt'
    def __str__(self):
        return self.name

使用,為符合drf格式,裡面新建個response類,@property讓類方法變屬性,不需要(),直接點+屬性就ok了

class MyResponse():
    def __init__(self):
        self.status = 100
        self.msg = None

    @property
    def get_dic(self):
        return self.__dict__from app01 import myserial
class Book(APIView):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer
    def get(self,request):
        response=MyResponse()
        # 多條
        # books = models.Book.objects.all()
        # ret=myserial.BookSer(books,many=True)
        # 一條
        book = self.queryset
        # ret = myserial.BookSer(book, many=False)
        ret = self.serializer_class(instance=book, many=True)
        response.msg='查詢成功'
        response.data=ret.data
        return JsonResponse(response.get_dic,safe=False)

        # return HttpResponse('get')
    def post(self,request):
   
 # return HttpResponse('post')

4、若要進行多對多的查詢,book和author,可以用SerializerMethodField方法

   -6 aa=serializers.SerializerMethodField()
        -必須配套一個方法(get_aa(self,obj)),方法返回結果,會賦給aa
        -在方法內部,可以繼續用序列化元件

如下

class BookSer(serializers.Serializer):
    nid=serializers.IntegerField()
    name3=serializers.CharField(source='name')
    price=serializers.CharField()
    # publish_date = serializers.DateField()
    publish_date = serializers.CharField()
    # publish=serializers.CharField(source='publish.email')
    publish=serializers.CharField(source='publish.name')
    xxx=serializers.CharField(source='test')
    # authors=serializers.CharField(source='authors.all')
    # SerializerMethodField,可以寫一個方法方法名叫:get_欄位名字,方法返回值,會賦給authors
    aa=serializers.SerializerMethodField()
    # def get_authors(self,obj):
    #     authors=obj.authors.all()
    #     # ll=[ author.name for author in authors]
    #     ll=[ {'name':author.name,'age':author.age} for author in authors]
    #     return ll
    def get_aa(self, obj):
        authors = obj.authors.all()
        # ll=[ author.name for author in authors]
        ser=AuthorSer(authors,many=True)
        return ser.data


class AuthorSer(serializers.Serializer):
    id=serializers.IntegerField(source='nid')
    age=serializers.CharField()
    name=serializers.CharField()

四 序列化元件之serializers.ModelSerializer

    -用法同Serializer
    -不同點:
        class BookSer(serializers.ModelSerializer):
            class Meta:
                # 指定要序列號的表模型是book
                model=models.Book
                fields='__all__'
                exclude=['nid']
                depth=1

例子如下

from app01 import models
class BookSer(serializers.ModelSerializer):
    class Meta:
        # 指定要序列號的表模型是book
        model=models.Book
        # 把所有欄位都序列化
        # fields='__all__'
        # 可以傳列表,指定取幾個
        # fields=['name','authors','publish']
        # 除了nid都查
        exclude=['authors']
        #fields和exclude不能同時用
        # depth指定深度,個人建議最多用3
        # depth=2

五 序列化元件的欄位校驗和反序列化功能

序列化元件是將物件序列化成字典,但是前臺post提交的json資料需要轉成字典,再反序列化成物件,最後save()方法才能儲存在資料裡,需要反序列功能

    -只有:ModelSerializer,能直接儲存
    - def post(self,request):
        print(request.data)
        #生成一個序列化物件 
        ser=myserial.BookSer(data=request.data)
        #判斷欄位是否校驗通過
        if ser.is_valid():
        #通過,直接儲存
            ser.save()
        else:
            #錯誤資訊        
            print(ser.errors)

        return HttpResponse('post')


from app01 import models
class BookSer(serializers.ModelSerializer):
class Meta:
# 指定要序列號的表模型是book
model=models.Book
exclude=['authors']

如下

 def post(self,request):
        # print(request.data)
        ser=myserial.BookSer(data=request.data)
        if ser.is_valid():
            ser.save()
            return HttpResponse('成功')
        else:
            print(ser.errors)

            return JsonResponse(ser.errors)

六 序列化元件區域性校驗和全域性校驗

 

    -區域性校驗
     name=serializers.CharField()
     def validate_name(self,value):
        if value.startswith('sb'):
            raise ValidationError('不能以sb開頭')
        else:
            return value
    -全域性校驗
        def validate(self,value):
            print(type(value))
            print(value)
            name=value.get('name')
            price=value.get('price')
            if name!=price:
                raise ValidationError('書名和價格不相等')
            else:

                return value

如下

from rest_framework.exceptions import ValidationError
from app01 import models
class BookSer(serializers.ModelSerializer):
    class Meta:
        # 指定要序列號的表模型是book
        model=models.Book
        # 把所有欄位都序列化
        # fields='__all__'
        # 可以傳列表,指定取幾個
        # fields=['name','authors','publish']
        # 除了nid都查
        exclude=['authors']
        #fields和exclude不能同時用
        # depth指定深度,個人建議最多用3
        # depth=2
    區域性校驗
    name=serializers.CharField(error_messages={'required':'該欄位必填'})
    def validate_name(self,value):
        if value.startswith('sb'):
            raise ValidationError('不能以sb開頭')
        else:
            return value
    全域性校驗
    def validate(self,value):
        print(type(value))
        print(value)
        name=value.get('name')
        price=value.get('price')
        if name!=price:
            raise ValidationError('書名和價格不相等')
        else:

            return value

views,主要驗證前臺post提交資料的區域性和全域性驗證

from app01 import myserial
class Book(APIView):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer
    def get(self,request):
        response=MyResponse()
        # 多條
        # books = models.Book.objects.all()
        # ret=myserial.BookSer(books,many=True)
        # 一條
        book = self.queryset
        # ret = myserial.BookSer(book, many=False)
        ret = self.serializer_class(instance=book, many=True)
        response.msg='查詢成功'
        response.data=ret.data
        return JsonResponse(response.get_dic,safe=False)

        # return HttpResponse('get')
    def post(self,request):
        # print(request.data)
        ser=myserial.BookSer(data=request.data)
        if ser.is_valid():
            ser.save()
            return HttpResponse('成功')
        else:
            print(ser.errors)

            return JsonResponse(ser.errors)

七、符合drf的檢視類基本寫法

1、views.py

 
 
class MyResponse():
def __init__(self):
self.status = 100
self.msg = None

@property
def get_dic(self):
return self.__dict__

class
BookDetail(APIView): def get(self,request,id): response=MyResponse() ret=models.Book.objects.filter(pk=id).first() ser=myserial.BookSer(instance=ret,many=False) response.msg='查詢成功' response.data=ser.data return JsonResponse(response.get_dic,safe=False) def put(self,request,id): # 修改 response=MyResponse() book=models.Book.objects.filter(pk=id).first() ser=myserial.BookSer(instance=book,data=request.data) if ser.is_valid(): # 可以新增,可以修改 ser.save() print(ser.data) print(type(ser.instance)) response.msg='修改成功' response.data=ser.data else: response.msg = '修改失敗' response.status = 101 response.data=ser.errors return JsonResponse(response.get_dic,safe=False) def delete(self,request,id): ret=models.Book.objects.filter(pk=id).delete() return HttpResponse('刪除成功')

2、序列化元件

url(r'^books/(?P<pk>\d+)/', views.BookDetail.as_view()),

from app01 import models
class BookSer(serializers.ModelSerializer):
class Meta:
# 指定要序列號的表模型是book
model=models.Book
exclude=['authors']