Django(9)|基于reseful-api风格的Django-framework

一、restful-api接口

1.定义: restful API 是一种符合rest风格的接口,rest是一种架构风格,采用http协议。

2.作用

  • 前后端分离一般会使用restful接口
  • 可以使前后端解耦,减少服务器压力,前后端分工明确,提高安全性

3.主要原则:

  • ①网络上的事务的都被抽象为资源
  • ②每个资源都有唯一的标识符,并且对资源的各自操作不会改变标识符
  • ③所有操作都是无状态的
  • ④同一个资源具有多种表现形式(xml/json等),数据传输一般使用的格式是json(全是双引号),以前用的是webservice,数据传输格式是xml

4.restful API的一些建议

  • ①建议使用https代替http,保证传输数据的安全
  • ②url中要体现api标识:https://www.xx.com/api/xx/?id=1 或者https://api.xx.com/xx/?id=1
  • ③url中要体现版本:https://www.xx.com/api/v1/xx/?id=1
  • ④api接口一般使用名字不使用动词
  • ⑤使用不同的方法来进行不同的操作(get/post/patch/put/delete).
  • ⑥给用户返回一些状态码
  • ⑦不同的请求方法返回不同类型的返回值,添加和更新一般返回添加的数据,删除不返回数据,获取数据一般返回列表套字典的形式
  • ⑧操作异常返回错误信息
  • ⑨推荐在返回数据的时候,返回其他相关联的一些数据的接口网址,例如分页器可以返回上一页下一页的网址

二、django-framework

1.安装并且简单使用django-framework

问题:为什么使用django-framework?
为了使前后端分离,我们需要始写数据接口,django-framework是基于restful风格的一种接口。

python安装三方包

pip install django-framework

接下来我们演示如何使用django-framework。

①创建项目并且注册应用

# 命令行执行以下命令
Django-admin startproject restful
python manage.py stratapp api

②创建一个模型类并且数据迁移

models.py中创建模型类

from django.db import models

class Category(models.Model):
    name=models.CharField(max_length=32,verbose_name='文章分类')

class Article(models.Model):
    title=models.CharField(verbose_name='标题',max_length=32)
    summary=models.CharField(verbose_name='简介',max_length=32)
    content=models.TextField(verbose_name='内容')
    category=models.ForeignKey(verbose_name='分类',to='Category',on_delete=models.CASCADE)

命令行执行以下命令,进行数据迁移

python manage.py makemigrations
python manage.py migrate

③注册framework(django中的framework相当于一个应用)

settings.py
在这里插入图片描述

④使用指定方式进行增删改查

views.py

from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from api import models
from rest_framework.views import APIView
from rest_framework.response import Response
from django.forms.models import model_to_dict

class DrfCategoryView(APIView):
    def get(self,request,*args,**kwargs):
        # 拿所有数据/拿一条数据
        pk=kwargs.get('pk')
        if not pk:
            queryset=models.Category.objects.all().values('id','name')
            data=list(queryset)
            return Response(data)
        data=models.Category.objects.filter(id=pk).first()#查出来是对象
        print(data)
        if data:
            data=model_to_dict(data)
        return Response(data)
        
    def post(self, request, *args, **kwargs):
        '''
        增加一条分类信息
        '''
        # 有的数据会用name='ada'&age="15"拼接,那么request.post会获取不到这种类型的值,            request.data会自动转换为字典格式的值
        # print(request.data)
        models.Category.objects.create(**request.data)#将信息打散再添加
        return Response('成功')

    def delete(self,request,*args,**kwargs):
        pk=kwargs.get('pk')
        models.Category.objects.filter(id=pk).first().delete()
        return Response('删除成功')

    def put(self, request, *args, **kwargs):
        pk=kwargs.get('pk')
        models.Category.objects.filter(id=pk).update(**request.data)
        return Response('更新成功')

url.py文件

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^drf/category/$', views.DrfCategoryView.as_view()),
    re_path('^drf/category/(?P<pk>\d+)/$', views.DrfCategoryView.as_view()),
]

通过postman来查看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.接口序列化

使用序列化可以在进行数据效验和序列化。

序列化后的增删改查views.py

from rest_framework import serializers # 序列化
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from api import models
from rest_framework.views import APIView
from rest_framework.response import Response
from django.forms.models import model_to_dict

class NewCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Category # 指定类
        # fields = "__all__" # 所有字段
        fields = ['id', 'name']# 展示的字段


class NewCategoryView(APIView):
    def get(self, request, *args, **kwargs):
        # 拿所有数据/拿一条数据
        pk = kwargs.get('pk')
        if not pk:
            data = models.Category.objects.all()  # 查出来是queryset
            ser = NewCategorySerializer(instance=data, many=True)
            return Response(ser.data)
        data = models.Category.objects.filter(id=pk).first()  # 查出来是对象
        ser = NewCategorySerializer(instance=data, many=False)
        print(ser.data)
        if data:
            data = model_to_dict(data)
        return Response(data)

    def post(self, request, *args, **kwargs):
        #    增加一条分类信息
        ser = NewCategorySerializer(data=request.data)
        if ser.is_valid():# 序列化
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        c_object = models.Category.objects.filter(id=pk).first()
        ser = NewCategorySerializer(instance=c_object, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        models.Category.objects.filter(id=pk).first().delete()
        return Response('删除成功')

urls.py

re_path('^new/category/$', views.NewCategoryView.as_view()),
re_path('^new/category/(?P<pk>\d+)/$', views.NewCategoryView.as_view()),

3.Django-framework的两种接口分页方式

①PageNumberPagination分页

1.只返回数据

首先我们在urls.py中定义一个新的url

#分页
re_path('^page/category/$', views.PageView.as_view()),
re_path('^page/category/(?P<pk>\d+)/$', views.PageView.as_view()),

然后视图文件写分页类和接口类

from rest_framework.pagination import PageNumberPagination
from rest_framework import serializers

# 分页类
class MyPageNumber(PageNumberPagination):
    # 每页最多两个数据,必须定义,因为底层page_size=None,如果不设置分页器name分页器不可用,
    # 我们也可以不在此处继承分页的类,那么根据源码需要在配置文件中设置page_size
	page_size = 2

# 序列化类
class PageSer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = "__all__"

# 接口视图类
class PageView(APIView):
    def get(self, request, *args, **kwargs):
        queryset = models.Category.objects.all()
        # 方式一
        page_object = MyPageNumber()
        result = page_object.paginate_queryset(queryset, request, self)
        # queryset是传入所有对象去进行分页,request是拿page页数需要request,self是因为需要用当前对象的东西

        print(result, type(result))
       '''
        [<Category: Category object (3)>, <Category: Category object (4)>]      		<class 'list'>
        因为此处返回的是一个列表,里面是数据对象,因此我们需要序列化,所以去定义序列化类
       '''
        ser = PageSer(instance=result,many=True)
        return Response(ser.data)

查看结果,由于底层内部规定页数为page,因此在地址栏写入该网址查看信息http://127.0.0.1:8000/page/category/?page=3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XwRtq7tt-1593167288341)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200626180509033.png)]
注意:

①如果我们不想继承分页的类,但是源码处又必须设置每页的页码对象的数量

在这里插入图片描述
②那么我们还可以在配置文件中设置

settings.py
REST_FRAMEWORK = {
    "PAGE_SIZE": 2
}

③ 然后views.py 改为

# ---------------------------分页
from rest_framework.pagination import PageNumberPagination
from rest_framework import serializers

class PageSer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = "__all__"

class PageView(APIView):
    def get(self, request, *args, **kwargs):
        queryset = models.Category.objects.all()
        # 方式一
        page_object = PageNumberPagination()
        result = page_object.paginate_queryset(queryset, request, self)
        print(result, type(result))
        ser = PageSer(instance=result,many=True)
        return Response(ser.data)

后边我们都使用这种设置后的方式

2.数据+分页信息
views.py

rom rest_framework.pagination import PageNumberPagination
from rest_framework.generics import ListAPIView, GenericAPIView
from rest_framework import serializers


class PageSer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = "__all__"

class PageView(APIView):
    def get(self, request, *args, **kwargs):
        # 方式二:数据+分页信息
        '''
        queryset = models.Category.objects.all()
        page_object = PageNumberPagination()
        result = page_object.paginate_queryset(queryset, request, self)
        ser = PageSer(instance=result, many=True)
        return page_object.get_paginated_response(ser.data)
        '''
      # 方式三:数据+部分分页信息
        queryset = models.Category.objects.all()       
        page_object = PageNumberPagination()
        result = page_object.paginate_queryset(queryset, request, self)
        ser = PageSer(instance=result, many=True)
        return Response({'count':page_object.page.paginator.count,'result':ser.data})

查看结果,在地址栏写入http://127.0.0.1:8000/page/category/?page=3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99eyZN9U-1593167288343)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200626180559034.png)]

②LimitOffsetPagination分页

视图文件中写接口类和序列化的类,urls.py同上

from rest_framework.pagination import PageNumberPagination
from rest_framework.pagination import LimitOffsetPagination
from rest_framework import serializers

class PageSer(serializers.ModelSerializer):
    class Meta:
        model = models.Category
        fields = "__all__"

class PageView(APIView):
    def get(self, request, *args, **kwargs):
        queryset = models.Category.objects.all()
        page_object = LimitOffsetPagination()
        result = page_object.paginate_queryset(queryset, request, self)
        ser = PageSer(instance=result, many=True)
        return Response(ser.data)

基本和上一个方法差不多,只不过继承的类不同,因此我们的网址变为http://127.0.0.1:8000/page/category/?offset=1&limit=2

跳过1条拿两条数据

在这里插入图片描述
但是如果我们limit数字设置很大,查看源码后我们可以设置最大的拿取数量,但是我们不建议直接修改源码,我们可以在views.py写个类

class NewLimitOffset(LimitOffsetPagination):
    max_limit = 2

修改后接口视图类别忘了使用新的类。

4.其他常见的视图类

①准备工作

准备工作,重新创建一个app。命令行输入

python manage.py startapp view

settings.py中注册应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'view.apps.ViewConfig',
]

在子应用中,创建自己的路由文件urls.py

from django.urls import path, re_path
from view import views

urlpatterns = [
    re_path('^tag/$', views.TagView.as_view()),
    re_path('^tag/(?P<pk>\d+)/$', views.TagView.as_view()),
]

models.py创建模型

from django.db import models

class Tag(models.Model):
    title=models.CharField(max_length=32)

数据迁移

python manage.py makemigrations
python manage.py migrate

加一些数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPq9EdBT-1593247393272)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627162240808.png)]

学习新的视图类

修改原视图

from .models import *
from rest_framework.serializers import ModelSerializer
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination

# 导入新的视图类包
from rest_framework.generics import GenericAPIView, ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView


class TagSer(ModelSerializer):
    class Meta:
        model = Tag
        fields = "__all__"


class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView):
    queryset = Tag.objects.all()
    serializer_class = TagSer
    pagination_class = PageNumberPagination

②原理解释

  • 其实原理底层就是ListAPIView继承了GenericAPIView,GenericAPIView继承了APIView,APIView又继承了原始view

  • 此视图已经实现我们原来视图中的增删改查四个功能

    视图解释
    ListAPIView源码实现了get方法
    CreateAPIView源码实现了post方法
    UpdateAPIView源码实现了put和patch方法
    DestroyAPIView源码实现了delete方法,注意是真删除,不是改变状态

例如我们看一下ListAPIVIEW中如何实现我们get方法

ctrl+l进入ListApiView源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cT6klCA2-1593247393274)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627163109411.png)]

此处有我们的get方法,返回一个list方法,我们点进list方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTrnUA36-1593247393275)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627163205137.png)]

在list方法中,我们首先看到定义了查询的数据queryset,因此此处需要我们自己在类中写入我们的queryset

class TagView(ListAPIView):
    queryset = Tag.objects.all()
    #如果需要筛选的话,可以使用filter,例如id>3的
    #queryset = Tag.objects.filter(id__gt=3)

然后源码下边看到page,若我们分页的话则需要在类中继承分页的类,不写则全查询

class TagView(ListAPIView):
    queryset = Tag.objects.all()
    pagination_class = PageNumberPagination

再看源码,我们看到序列化,因此需要根据自己的业务逻辑写出序列化类,并且配置

class TagSer(ModelSerializer):
    class Meta:
        model = Tag
        fields = "__all__"

class TagView(ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView):
    queryset = Tag.objects.all()
    serializer_class = TagSer
    pagination_class = PageNumberPagination

最后源码返回了我们序列化后的数据,因此我们短短几行配置便实现了get方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyFw7sRO-1593247393277)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627163745501.png)]
其他的post、put、patch、delete都和该方法类似

三、使用Django-framework写一个简单博客的接口

创建一个项目后,并创建一个app

Python manage.py startapp hg

settings配置文件中,注册应用,建议加apps.config

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'hg.apps.HgConfig',#可以自动加载类中的东西
]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQLpFLk3-1593189287155)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200626222350728.png)]

models.py中创建模型

from django.db import models


# 用户表
class UserInfo(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名')
    password = models.CharField(max_length=32, verbose_name='密码')


# 文章表
class Article(models.Model):
    # 不会经常变化的值放在内存中:choices形式,避免跨表性能低
    category_choices = (
        (1, '咨询'),
        (2, '公司动态'),
        (3, '分享'),
        (4, '答疑'),
        (5, '其他'),
    )
    category = models.IntegerField(verbose_name='分类', choices=category_choices)
    title = models.CharField(verbose_name="标题", max_length=32)
    summary = models.CharField(verbose_name='简介', max_length=255)
    image = models.CharField(max_length=128, verbose_name='图片路径')
    author = models.ForeignKey(verbose_name='作者', to='UserInfo', on_delete=models.CASCADE)

    comment_count = models.IntegerField(verbose_name='评论数',default=0)
    read_count = models.IntegerField(verbose_name='阅读数',default=0)
    date = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')


# 文章详情表
class ArticleDetail(models.Model):
    # 一般公司推荐分开,因为列太多,所以水平分表
    article = models.OneToOneField(verbose_name='文章表', to=Article, on_delete=models.CASCADE)
    content = models.TextField(verbose_name='内容')


# 评论表
class Comment(models.Model):
    article = models.ForeignKey(verbose_name='文章', to='Article', on_delete=models.CASCADE)
    content = models.TextField(verbose_name='评论')
    user = models.ForeignKey(verbose_name='账户', to='UserInfo', on_delete=models.CASCADE)
    '''
    例如其他人在某个人的评论下引战,那么这个字段里写的就是某个人的id,如果就是自己单独
    评论文章那么就为null
    parent=models.ForeignKey(verbose_name='回复',to='self',null=True,blank=True)
    '''

给用户加两条数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Qnl5GtK-1593189287156)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200626234607148.png)]

在子应用中添加urls.py

写文章接口视图

urls.py

from django.urls import path,re_path,include

from hg import views
urlpatterns = [
    re_path('^article/$', views.ArticleView.as_view()),
    re_path('^article/(?P<pk>\d+)/$', views.ArticleView.as_view()),
]

views.py

from django.shortcuts import render
from rest_framework import serializers
from hg import models
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination

#此处序列化文章表,是为了post校验,但是略过作者,一般博客网站添加文章都是在后台添加
class ArticleSer(serializers.ModelSerializer):
    class Meta:
        model = models.Article
        # field = "__all__"
        exclude = ['author', ]  # 不校验某个字段
        
# 此处序列化文章表,是为了给我们的get请求
class ArticleListSer(serializers.ModelSerializer):
    class Meta:
        model = models.Article
        fields = "__all__"
        
# 此处序列化文章表,是为了给我们的get请求拿指定文章
class PageArticleSer(serializers.ModelSerializer):
    # 定义钩子,为了让get请求拿到的关联表数据的某些信息,而不是将关联表的信息全展示出
    content = serializers.CharField(source='articledetail.content')
    author = serializers.CharField(source='author.username')

    class Meta:
        model = models.Article
        fields = "__all__"
        

# 此处序列化文章内容表,是为了post请求校验
class ArticleDetailSer(serializers.ModelSerializer):
    class Meta:
        model = models.ArticleDetail
        # fields = "__all__"
        exclude = ['article', ]  # 不校验某个字段


# 文章的视图类
class ArticleView(APIView):
    def get(self, request, *args, **kwargs):
         # 获取文章列表
        pk = kwargs.get('pk')
        if not pk:
            quert_set = models.Article.objects.all().order_by('-date')
            pager = PageNumberPagination()
            result = pager.paginate_queryset(quert_set, request, self)
            ser = ArticleListSer(instance=result, many=True)
            return Response(ser.data)
        # 获取指定文章信息
        article_object = models.Article.objects.filter(id=pk).first()
        ser=PageArticleSer(instance=article_object,many=False)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        # 添加文章
        ser = ArticleSer(data=request.data)
        ser_detail = ArticleDetailSer(data=request.data)
        if ser.is_valid() and ser_detail.is_valid():#同时校验
            # 增加文章
            article_object = ser.save(author_id=1)  # save里可以增加校验时候略过的字段
            ser_detail.save(article=article_object) 
            return Response("添加成功")
        return Response('添加失败!错误')

settings配置文件写接口分页

REST_FRAMEWORK = {
    "PAGE_SIZE": 2
}

演示:查询到的文章列表http://127.0.0.1:8000/hg/article/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-il9g0bU6-1593189287157)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627000038124.png)]

演示:查询某个文章的信息http://127.0.0.1:8000/hg/article/2/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NJ4v9pdG-1593189287158)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20200627000116909.png)]



  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Django REST Framework Cache是一个Django框架的扩展,用于缓存REST API的响应数据。它提供了一种简单的方式来缓存API视图的响应,以减少数据库查询和加快请求的响应速度。 使用Django REST Framework Cache,你可以通过在视图类或视图函数上添加装饰器来启用缓存。这个扩展支持多种缓存后端,包括内存缓存、数据库缓存和文件缓存等。你可以根据你的需求选择适合的缓存后端。 为了使用Django REST Framework Cache,你需要在你的Django项目中安装并配置它。你可以通过pip安装它: ``` pip install djangorestframework-cache ``` 安装完成后,在你的项目的settings.py文件中添加以下配置: ```python INSTALLED_APPS = [ ... 'rest_framework_cache', ] CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } REST_FRAMEWORK_CACHE = { 'DEFAULT_CACHE_BACKEND': 'default', } ``` 然后,在你的API视图类或函数上使用`@cache_response`装饰器来启用缓存: ```python from rest_framework.decorators import api_view from rest_framework_cache.decorators import cache_response @api_view(['GET']) @cache_response(timeout=60*60*24) # 缓存一天 def my_view(request): ... ``` 这样,当该API被访问时,响应数据将会被缓存,并在下次请求相同的API时直接返回缓存的响应数据,而不需要执行相同的查询操作。 通过使用Django REST Framework Cache,你可以有效地提高REST API的性能和响应速度,并减少对数据库的负载。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张烫麻辣亮。

谢谢老板支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值