美文网首页初见
Django REST Framework 后端开发

Django REST Framework 后端开发

作者: Xtuphe | 来源:发表于2020-05-19 16:12 被阅读0次

    环境安装:

    安装新版python

    # 查看python版本
    $ python -V
    

    建议安装最新版,看网上有些方法装下来的python版本也不是最新的,可以去官网查看最新版安装链接,下载后解压,安装。

    # 下载
    $ wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz
    # 解压
    $ tar zxvf Python-3.8.3.tgz
    $ cd Python-3.8.3/  
    # 配置
    $ ./configure
    # pip是python的包管理软件,如果没有的话需安装
    $ yum -y install python-pip
    

    DRF默认用SQLite作为数据库,公司的centos服务器没有SQLite需要安装

    # 安装SQLite
    $ yum install sqlite-devel
    

    (optional)为了使包配置独立于其他的项目,可以创建一个虚拟环境

    $ python3 -m venv env
    $ source env/bin/activate
    

    要退出虚拟环境,在任意地方deactivate即可

    $ deactivate
    
    # 安装Django
    $ pip install django
    # 安装DRF
    $ pip install djangorestframework
    

    创建项目

    # 创建项目
    $ django-admin startproject 项目名
    # 创建app
    $ python manage.py startapp forum
    

    然后需要在项目下settings.py中修改已安装的app

    INSTALLED_APPS = [
        ...
        'rest_framework',
        'forum',
    ]
    

    打🐎

    创建模型

    # models.py
    class Article(models.Model):
        title = models.CharField(max_length=100, default='')
        content = models.TextField(default='')
        created = models.DateTimeField(auto_now_add=True)
        last_modify_date = models.DateTimeField(auto_now=True)
        author = models.TextField()
        dingId = models.TextField(default='lost')
        avatar = models.TextField(default='0')
        type = models.TextField(default='0')
        likes = models.IntegerField(default=0)
        comments = models.IntegerField(default=0)
        reports = models.IntegerField(default=0)
        selected = models.BooleanField(default=False)
        deleted = models.BooleanField(default=False)
    
        class Meta:
            db_table = 'article'
    

    模型迁移至数据库

    $ python manage.py makemigrations snippets
    $ python manage.py migrate
    # 将会在SQLite建立名为article的表
    

    序列化,将实例转化为json

    # serializers.py
    from rest_framework import serializers
    from forum.models import Article
    class ArticleSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Article
            fields = '__all__' 
            # fields = ('id', 'title', 'content', 'last_modify_date', 'created')
    

    接口Views

    # views.py
    from rest_framework import viewsets
    class ArticleViewSet(viewsets.ModelViewSet):
        queryset = Article.objects.all()
        serializer_class = ArticleSerializer
    

    短短这几行就实现了增删改查,真是十分好用

    Router

    # urls.py
    from rest_framework.routers import DefaultRouter
    from forum import views
    
    
    forumPrefix = 'api/forum/'
    forumRouter = DefaultRouter()
    forumRouter.register('article', views.ArticleViewSet)
    forumRouter.register('comment', views.CommentViewSet)
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
        path(forumPrefix, include(forumRouter.urls)),
    ]
    

    运行

    一切准备就绪,此时cd到项目根目录,然后访问http://127.0.0.1:8000/api/forum/ 就可以看到新写的接口了,推荐用Postman测试

    $ python manage.py runserver
    
    Validating models...
    
    0 errors found
    Development server is running at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
    

    如果需要在服务器上长期运行:

    $ nohup python3 manage.py runserver 0.0.0.0:8000 > out.log 2>&1 &
    # 查看输出
    $ tail -f out.log
    

    参数过滤

    安装requests

    $ pip install requests
    

    安装django-url-filter
    安装django-filter

    $ pip install django-filter
    $ pip install django-url-filter
    
    # views
    from url_filter.integrations.drf import DjangoFilterBackend
    
    
    class ArticleViewSet(viewsets.ModelViewSet):
        queryset = Article.objects.all()
        serializer_class = ArticleSerializer
    
        filter_backends = [DjangoFilterBackend]
        filter_fields = ['selected', 'deleted', 'reports', 'type']
    

    比如我想筛选用户A点赞数为5到10的文章,前端可以直接把筛选条件拼在url后,十分好用!

    .../forum/articles/?author=A&likes__range=5,10
    

    详细的使用说明在:https://django-url-filter.readthedocs.io/en/latest/

    排序

    # views
    from rest_framework import filters
    class ArticleViewSet(viewsets.ModelViewSet):
        queryset = Article.objects.all()
        serializer_class = ArticleSerializer
    
        filter_backends = [filters.OrderingFilter, DjangoFilterBackend]
        filter_fields = ['selected', 'deleted', 'reports', 'type']
        ordering_fields = ('id', 'created', 'last_modify_date')
    

    比如要按创建时间倒序排列

    .../forum/articles/?ordering=-create
    

    真好用!详细说明在:https://www.django-rest-framework.org/api-guide/filtering/

    @action为viewsets添加更多功能

    
    class ArticleViewSet(viewsets.ModelViewSet):
        queryset = Article.objects.all()
        serializer_class = ArticleSerializer
    
        filter_backends = [filters.OrderingFilter, DjangoFilterBackend, filters.SearchFilter]
        ordering_fields = ('id', 'created', 'last_modify_date')
        filter_fields = ['selected', 'deleted', 'reports', 'type']
        search_fields = ['id', 'title', 'content']
        permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    
        @action(detail=True, method=['GET'])
        # detail=False时,对所有集合产生效果,method默认为GET可省略
        def like(self, request, pk=None):
            article = self.get_object()
            article.likes += 1
            article.save()
            return Response(article.likes)
    
    

    修改viewset的CURD方法

    class UserViewSet(viewsets.ViewSet):
      
        def list(self, request):
            pass
    
        def create(self, request):
            pass
    
        def retrieve(self, request, pk=None):
            pass
    
        def update(self, request, pk=None):
            pass
    
        def partial_update(self, request, pk=None):
            pass
    
        def destroy(self, request, pk=None):
            pass
    

    跨域

    由于我之前没接触过后端开发,跨域可真是恶心死我了,看网上有说用corsheaders的,我试了不太好使,也有说自己写一个middleware的,然后按网上的说法我上线后新款iPhone跟模拟器都没问题,但剩下的90%的设备还是存在跨域问题,最终发现,Access-Control-Allow-Methods不能为*

    在app下新建middlewares.py

    # middlewares.py
    from django.utils.deprecation import MiddlewareMixin
    
    
    class MiddleWare(MiddlewareMixin):
        def process_response(self, request, response):
            response['Access-Control-Allow-Origin'] = "*"
            if request.method == 'OPTIONS':
                response['Access-Control-Allow-Methods'] = "POST, GET, DELETE, OPTIONS, DELETE"
                response['Access-Control-Max-Age'] = 1728000
                response["Access-Control-Allow-Headers"] = "Content-Type, x-requested-with"
                return response
    
            return response
    

    修改settings

    # settings.py
    MIDDLEWARE = [
        ...
        'forum.middlewares.MiddleWare',
    ]
    

    Token

    安装Simple JWT

    $ pip install djangorestframework-simplejwt
    

    在setting中添加默认鉴权类

    REST_FRAMEWORK = {
        ...
        'DEFAULT_AUTHENTICATION_CLASSES': (
            ...
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        )
        ...
    }
    

    在urls.py添加对外接口

    from rest_framework_simplejwt.views import (
        TokenObtainPairView,
        TokenRefreshView,
    )
    
    urlpatterns = [
        ...
        path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
        ...
    ]
    

    创建超级用户

    $ python manage.py createsuperuser
    # 更改密码
    $ python manage.py changepassword [<username>]
    

    用curl获取token

    curl \
      -X POST \
      -H "Content-Type: application/json" \
      -d '{"username": "davidattenborough", "password": "boatymcboatface"}' \
      http://localhost:8000/api/token/
    
    ...
    {
      "access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU",
      "refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4"
    }
    

    返回的两个token,access默认5分钟过期,过期后用refresh刷新,refresh默认1天过期,可以在settings修改过期时间

    # Django project settings.py
    
    from datetime import timedelta
    
    ...
    
    SIMPLE_JWT = {
        'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
        'REFRESH_TOKEN_LIFETIME': timedelta(days=1)
    }
    

    详细使用方法:
    https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html

    Settings 中Debug改为false后static文件404

    正式发布时,Debug是要改为false的,否则系统会把所有的请求缓存到内存上,造成进程占用的内存越来越大,而debug=false后,DRF管理界面css样式就获取不到了

    简单粗暴的解决方式是

    $ python3 manage.py runserver --insecure
    

    这样做和debug=true时类似,效率十分低,且有安全隐患
    https://docs.djangoproject.com/en/3.0/ref/contrib/staticfiles/#module-django.contrib.staticfiles

    我看了半天,最后的处理方法是发布前collectstatic收集静态文件,把整个文件夹丢到服务器上,修改setting里STATIC_URL为服务器上文件夹存放的目录

    $ python3 manage.py collectstatic
    

    其他

    # settings.py
    # 修改时区
    TIME_ZONE = 'Asia/Shanghai'
    # 全局分页
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 5  # 每页数目
    }
    
    # 设置Shell共享库位置
    $ export LD_LIBRARY_PATH="/usr/local/lib"
    # 使设置生效
    $ source ~/.bashrc
    

    相关文章

      网友评论

        本文标题:Django REST Framework 后端开发

        本文链接:https://www.haomeiwen.com/subject/uekbohtx.html