Django REST Framework 后端开发

环境安装:

安装新版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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352