DjangoRestFramework框架簡介及基本使用


本文首發於:行者AI

在python項目開發中,前后端分離的技術框架越來越成熟,在前后端進行通信時,通常需要用統一的格式進行通信,目前應用比較廣泛的是RESTful API。那后端如何快速編寫基於Django的RESTful API呢?本篇將主要介紹使用DjangoRestFramework(drf)框架來快速開發符合REST風格的API。

1. drf概念及特點

1.1 概念

drf框架是基於Django框架,用於快速構建Web RESTful API的工具。

1.2 特點

(1) 提供了定義序列化器Serializer的方法,可以快速根據Django ORM 或者其他庫自動序列化、反序列化;

(2) 提供了豐富的類視圖、MIXIN擴展類,根據需求組合繼承,簡化視圖的編寫;

(3) 豐富的定制層級:函數視圖、類視圖、視圖集合到自動生成 API,滿足各種需要;

(4) 支持多種身份認證和權限認證方式;

(5) 內置了限流系統;

(6) 可視化API接口;

(7) 可擴展性 , 插件豐富。

2. drf的使用

drf對代碼的簡化主要是對視圖的增刪改查、請求數據的反序列化和響應數據的序列化進行簡化,所以主要介紹drf的序列化器和視圖集的使用。

2.1 搭建項目

搭建項目環境,創建項目exercise,創建app應用student,代碼如下:

# python==3.6.5
virtualenv -p /python/python.exe /virtualenv/exerciseenv
cd /virtualenv/exerciseenv/Scripts/
activate
pip install django==3.1.5 pymysql==1.0.2 djangorestframework==3.12.2
cd /study/
django-admin startproject exercise
cd exercise
django-admin startapp student

目錄結構如下:

然后打開項目在/exercise/settings.py文件中配置數據庫,注冊app(student和rest_framework)。

2.2 創建模型

/student/models.py文件中,建立三張表:班級(Grade)、課程(Course)、學生(Student)。

class Grade(models.Model):  # 班級
    name = models.CharField(max_length=16, null=False, unique=True)

    class Meta:
        db_table = 'grade'
        ordering = ['id']


class Course(models.Model):  # 課程
    name = models.CharField(max_length=32, unique=True, null=False)

    class Meta:
        db_table = 'course'
        ordering = ['id']


class Student(models.Model):  # 學生
    name = models.CharField(max_length=16, null=False)
    age = models.IntegerField(null=True)
    gender = models.BooleanField(null=False, default=0)
    g = models.ForeignKey(Grade, on_delete=models.CASCADE)
    c = models.ManyToManyField(Course)

    class Meta:
        db_table = 'student'
        ordering = ['id']

2.3 創建序列化器

/student/serializers.py文件中,建立三個模型對應的序列化器;序列化器有兩個主要功能:序列化和反序列化。如果前端是GET請求,則構造查詢集,將結果返回,這個過程為序列化;如果前端是POST請求,要對數據庫進行改動,則需要拿到前端發來的數據,進行校驗,校驗通過將數據寫入數據庫,這個過程稱為反序列化。這能極大的簡化視圖代碼量,后面會做個對比。代碼如下:

class GradeSerializer(serializers.ModelSerializer):
    class Meta:
        # 指定序列化器對應的模型
        model = Grade
        # 指定需要序列化的字段,‘__all__’表示所有字段
        fields = ['name']


class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = ['name']


class StudentSerializer(serializers.ModelSerializer):
    # 自定義序列化和反序列化字段校驗條件,默認使用建表約束校驗;也可以使用extra_kwargs
    # SlugRelatedField指定關聯對象的指定字段,關聯字段默認為關聯對象id
    c = serializers.SlugRelatedField(slug_field='name', many=True, queryset=Course.objects.all())
    g = serializers.SlugRelatedField(slug_field='name', queryset=Grade.objects.all())

    class Meta:
        model = Student
        # 自定義校驗
        extra_kwargs = {'age': {'max_value': 30, 'min_value': 0}}
        fields = '__all__'

    # 返回數據預處理
    def to_representation(self, instance):
        data = super().to_representation(instance)
        if data['gender'] == 0:
            data['gender'] = '女'
        else:
            data['gender'] = '男'
        return data

2.4 編寫視圖

/student/views.py文件中,編寫視圖,每個模型對應一個視圖,繼承drf的viewsets.ModelViewSet類,包含增刪改查四大操作,通過不同的請求方法映射到drf定義的對應的動作action方法上。action方法包括create(新增)、retrieve(查詢詳情)、destroy(刪除)、update(修改)、list(列表展示)。代碼如下:

class StudentView(viewsets.ModelViewSet):
    queryset = Student.objects.all()  # 指定查詢結果集
    serializer_class = StudentSerializer  # 指定序列化器


class GradeView(viewsets.ModelViewSet):
    queryset = Grade.objects.all()
    serializer_class = GradeSerializer


class CourseView(viewsets.ModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer

2.5 注冊路由

/exercise2/urls.py文件中,注冊根路由:

urlpatterns = [
    path('api/', include('student.urls'))
]

/student/urls.py文件中,注冊資源路由:

# 使用drf的視圖集就不需要編寫路由,通過DefaultRouter的register方法注冊就可以了
router = DefaultRouter()
router.register('student', views.StudentView)
router.register('course', views.CourseView)
router.register('grade', views.GradeView)

urlpatterns = [
    path('', include(router.urls))
]

2.6 列舉url

列舉一下資源為student的請求路徑和方式,其他資源類似:

GET http://127.0.0.1:8000/api/student/  查詢所有學生信息

POST http://127.0.0.1:8000/api/student/  傳入學生各項信息,創建該學生對象

GET http://127.0.0.1:8000/api/student/1/ 查詢id為1的學生信息 默認過濾字段為id,可自定義過濾器

DELETE http://127.0.0.1:8000/api/student/1/  刪除id為1的學生,默認過濾字段為id

PUT http://127.0.0.1:8000/api/student/1/  修改id為1的學生信息,默認過濾字段為id

3. drf框架與原生Django的對比

3.1 views.py文件

這里只寫了student模型對應的各項操作視圖,其他模型類似。StudentListCrate視圖包含展示學生列表(get)和插入學生數據(post)接口,StudentUpdateRetrieveDestroy視圖包含對單個學生數據查詢(get)、修改(put)、刪除(delete)接口。代碼如下:

class StudentListCrate(View):
    def get(self, request):
        students = Student.objects.all()
        students_list = []
        for stu in students:
            if stu.gender == 1:
                stu_gender = '男'
            else:
                stu_gender = '女'
            g_name = Grade.objects.filter(id=stu.g_id).first().name
            cou_name_list = []
            for cou in stu.c.all():
                cou_name_list.append(cou.name)
            students_list.append({'id': stu.id, 'name': stu.name, 'age': stu.age,
                                  'gender': stu_gender, 'courses': cou_name_list,
                                  'g_name': g_name})
        return JsonResponse(students_list, safe=False)

    def post(self, request):
        form = StudentForm(json.loads(request.body))
        if form.is_valid():
            name = form.cleaned_data['name']
            age = form.cleaned_data['age']
            gender = form.cleaned_data['gender']
            g = form.cleaned_data['g'].id
            c = [cou.id for cou in form.cleaned_data['c']]
            stu = Student.objects.create(name=name, age=age, gender=gender, g_id=g)
            stu.save()
            stu.c.add(*c)
            return JsonResponse({'code': 200, 'msg': '創建成功'})
        return JsonResponse({'code': 200, 'msg': form.errors})


class StudentUpdateRetrieveDestroy(View):
    def get(self, request, pk):
        stu = Student.objects.filter(pk=pk).first()
        if not stu:
            return JsonResponse({'code': 400, 'msg': '沒有這個學生'})
        g_name = Grade.objects.filter(id=stu.g_id).first().name
        if stu.gender == 1:
            stu_gender = '男'
        else:
            stu_gender = '女'
        cou_name_list = []
        for cou in stu.c.all():
            cou_name_list.append(cou.name)
        return JsonResponse({'id': stu.id, 'name': stu.name, 'age': stu.age,
                             'gender': stu_gender, 'courses': cou_name_list,
                             'g_name': g_name})

    def put(self, request, pk):
        stu_query = Student.objects.filter(pk=pk)
        stu = stu_query.first()
        if not stu:
            return JsonResponse({'code': 400, 'msg': '沒有這個學生'})
        form = StudentForm(json.loads(request.body))
        if form.is_valid():
            c = [cou.id for cou in form.cleaned_data['c']]
            stu.c.set(c)
            del form.cleaned_data['c']
            update_dict = form.cleaned_data
            stu_query.update(**update_dict)
            return JsonResponse({'code': 200, 'msg': '創建成功'})
        return JsonResponse({'code': 200, 'msg': form.errors})

    def delete(self, request, pk):
        stu = Student.objects.filter(pk=pk).first()
        if not stu:
            return JsonResponse({'code': 400, 'msg': '沒有這個學生'})
        stu.delete()
        return JsonResponse({'code': 200, 'msg': '刪除成功'})

3.2 forms.py文件

這是放置做表單校驗類的文件,是在模型約束之上再定義一層符合業務實際意義的校驗,並且校驗未通過,可以將錯誤提示信息返回給用戶,提高用戶體驗,代碼如下:

class StudentForm(forms.Form):
    name = forms.CharField(max_length=16, required=True, min_length=2,
                           error_messages={'max_length': '名字最長為16', 
                                           'min_length': '名字最短為2', 
                                           'required': '名字必填'})
    age = forms.IntegerField(max_value=30, min_value=10,
                             error_messages={'max_value': '年齡最大為30',
                                             'min_value': '年齡最小為10'})
    gender = forms.BooleanField(required=False, error_messages={'required': '性別必填'})
    g = forms.ModelChoiceField(queryset=Grade.objects.all(), error_messages={'queryset': '沒有這個班級'})
    c = forms.ModelMultipleChoiceField(queryset=Course.objects.all(), error_messages={'queryset': '不存在這個課程'})

3.3 總結

對比發現drf處理三個模型的代碼量比原生django處理一個模型的代碼都要少,說明drf框架極大地提高了RESTful API開發效率,不過仔細點還能發現由於drf封裝地太好,對於處理業務邏輯復雜的接口就需要我們重構相應的方法。drf豐富的各項功能使我們開發效率更高,但同時學習成本也是直線上升,本文只是闡述序列化器和視圖集類的應用,對於細分的各類的應用及組合使用還需進一步研究學習。


PS:更多技術干貨,快關注【公眾號 | xingzhe_ai】,與行者一起討論吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM