栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Django|前后端分离|后端开发(一)postman接口学习

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Django|前后端分离|后端开发(一)postman接口学习

写在前面:写这篇文章的初衷是为了push自己做数据库的大项目。

我深知自己的惰性...项目从三月份出需求到现在快要到ddl了我一直都没有动,时间一拖再拖,不想成为赶ddl的战士。而且最近发生了一些事情,让我重新开始审视自己的方向。

大一的时候进来接触java的皮毛,之后就用各种借口逃避,觉得自己不适合后端开发。在此后的差不多一年时间内,我一边羡慕着别人项目经理丰富,一边给自己洗脑说大家都会,我以后也会会的。

时间来到java开发课,这门课程要求大家组队完成项目,我成功抱到了大佬的大腿,靠着拙劣的ui和需求分析在小组内拖后腿,直到结课也没有了解到任何关于开发的知识;

再到后面双创项目,我再次没有按时完成交给我的任务。在最后赶DDL的时候,我疯狂怀疑自己:我是不是真的学不会?

我一直觉得开发不是我不学不会,是我还没入门。当然入门之后也会有很多难点,但是起码有个抓手。我一直泛泛地游走在开发的大门外,用一些一知半解的术语和外行人搭话,但是怎么能一直这么下去呢?

本次数据库课程的大项目为小组形式开发论文管理系统,采用前后端分离架构。但是考虑到我自己java学的很烂,短时间上手SpringBoot很难,所以选择了Django。

python是在短学期课程上学的理论,之后实践课也是洒洒水就那么过去了。之后虽然做过一些简单的爬虫,但是对python的语法和特性实在不是很理解。此外,对于http、接口方面的知识也是浅浅地了解过。

所以写这样的文章,记录一下自己的django学习历程。希望日后回望的时候,不要再说出“我不适合敲代码”这样的话。

一.我想达到的效果

网上查了很多教程,甚至花钱买了复用看起来比较多的优秀源码,暂时发现是下面两种架构:

1.Django采用MTV模式,使用Templates模版,需要部分前端知识,前后端不分离;

2.前后端分离,只开发后端,后端开发将接口文档给到前端。

由于Django本身是MTV架构,所以可以预想到前后端分离其实并不是主流接受的,我在后续学习中也会补补前端的坑,但是现在敏捷开发,以实现为主。

关于接口文档,在Springboot中使用过swagger2自动生成;在django中则有restful风格的接口文档,但是restful是同一个url使用GET、POST、UPDATE、DELETE四种不同的http请求方法,而在日常开发中基本上只会用到GET和DELETE(其实本质是还没有研究透,所以不去使用这个。

希望能做到的是使用类似swagger的第三方库生成接口文档,但是本机一直安装失败,还需要再摸索一下。

二.简单接口测试

参考链接:使用Django开发简单接口:文章增删改查 - 秦无殇 - 博客园

1.准备工作不再赘述,建立blog_demo项目和blog_api应用,将csrf暂时注释掉;

2.blog-models 新建User类和Article类:

class User(models.Model):
    id = models.AutoField(primary_key = True)
    username = models.CharField(max_length=50)
    password = models.CharField(max_length=100)
    #active inactive
    status = models.CharField(max_length=100)

class Article(models.Model):
    id = models.AutoField(primary_key = True)
    title = models.CharField(max_length=50)
    content= models.TextField()
    #deleted alive
    status = models.CharField(max_length=10)

其中status字段原作者设置的不太好,用关键字-数字代替比较合适。

补充一点在model中学到的知识:

1.数字类

  • Django为每一个表隐式设计了自增的id字段,其作用相当于User类中的第一句id语句,在有对象实例化的时候不需要显式声明。Django Model默认提供,可以被重写.
  • BooleanField对应tinyint(1)。布尔类型字段,一般用于记录状态标记。
  • DecimalField对应decimal。开发对数据精准要求较高大的业务时考虑使用。比如:cash=models.DecimalField(max_length, decimal_places=2, default=0,verbose_name=“消费金额”),就是定义长度为8位、精度位2位的数字,例如数字:666666.66。)
  • IntergerField对应int(11)。同AutoField一样,唯一的差别就是不自增。
  • PositiveIntegerField。同IntegerField,只包含正整数.
  • SmallIntegerField对应smallint。小整数时一般会用到。

2.字符型

django对应到Mysql中有两种类型:longtext和varchar。

除了TextField是longtest类型外,其他属于varchar类型。

  • CharField对应varchar。基础的varchar类型。
  • URLField:继承自CharField,但是实现了对URL特特殊处理。用来存储URL数据,非URL数据可以在业务层就拒绝掉,不会存入数据库中。
  • UUIDField:对应char(32),除了在PostgreSQL中使用的是uuid类型外,在其他数据库中均是固定长度char(32),用来存放生成的唯一id。
  • EmailField:同URLfield一样继承自CharField,多了对email的特殊处理。
  • FileField:同URLField一样,它继承自CharField,对了对文件的特殊处理。
  • TextField:对应longtext,一般用于存放大量文本内容,比如新闻正文、博客正文。
  • ImageField:继承自FileField,用来处理图像。

3.时间型

django中有三种日期类型,分别对应Mysql的date、datetime和time。

  • DateField对应date
  • DateTimeField对应datetime
  • TimeField对应time

4.关系类型 ForeignKey

这里外键写在哪个表里一定要注意

a=models.ForeignKey(A)

  • OneToOneField,一对一
  • ManyToManyField,多对多

5.参数

上面所提到的各种field的参数,一般有如下:

  • default:设置默认值
  • db_column:数据库中字段名
  • verbose_name:后台显示对应的名称
  • help_text:对字段的解释说明
  • null=True/False:针对数据库,如果 null=True, 表示数据库的该字段可以为空。
  • blank=True/False: 针对表单,如果 blank=True,表示表单填写该字段的时候可以不填

需要注意的是,日期型(DateField、TimeField、DateTimeField)和数字型(IntegerField、DecimalField、FloatField)不能接受空字符串,如要想要在填写表单的时候这两种类型的字段为空的话,则需要同时设置null=True、blank=True;

另外,设置完null=True后需要重新更新一下数据库。

  • on_delete:用在外键属性中,on_delete=models.CASCADE/SET_NULL/NO_ACTION?SET_DEFAULT
  • default=timezone.now:时间类中创建时自动填充当前时间,一般用于存储创建时间
  • default=auto_now:时间类中修改时自动填充修改时间,一般用于存储update_time
  • choices=xx:需预定义xx的内容,一般是选择较少的性别/用户类型等。

3.blog_api.views.py下对Article的增删改查:

我们一个个来看

【查】为了方便验证后面增、删、改的结果,先实现查的功能。

查在我的理解里分为两种,list和search,由于是入门所以仅实现全部的list和对username查找的search。目前还没有做分页,后面会学习使用django的分页器paginator完善。

列出所有status是active的用户信息:

def list_articles(req):
    if req.method == 'GET':
        articles = {}
        query_art = Article.objects.all()
        query_art = query_art.filter(status='alive')
        for art in query_art:
            articles[art.title] = art.content

        return JsonResponse({
            'status': 200,
            'all_titles': articles,
            'msg': 'this query is successful!',
        })

因为使用的是逻辑删除,设置status,所以list在筛选的时候加了个filter;

使用字典key-value的模式,但是我自己现在有点迷,不知道怎么让每条记录都呈现出来,再摸索一下。

postman测试接口:

search:

def search_articles(request):
    #serializers.serialize("json",models)
    if request.method == 'POST':
        req = json.loads(request.body)
        info = req.get('data')
        articles={}
        filter_title=info['title']
        filter_content=info['content']
        ans=Article.objects.filter(Q(title__icontains=filter_title)&Q(content__icontains=filter_content))
        for art in ans:
            articles[art.title] = art.content
        return JsonResponse({
            'status':200,
            'data':articles,
            'msg':'ok'
        })

 这个接口是我自己写的,格式借鉴了上面list的结构。

使用Django自带的Q查询,icontains不区分大小写,contains区分大小写。

测试一下已经加入的五条数据:

title中含有test(大小写均可);

再测试一下:

 

可以看到这个简单的对title和content的模糊搜索已经完成了。 

【增】

#新增文章
def add_article(request):
    if request.method == "POST":
        req = json.loads(request.body)
        #print (req)
        key_flag = req.get("title") and req.get("content") and len(req)==2
        #判断请求体是否正确
        if key_flag:
            title = req["title"]
            content = req["content"]
            #title返回的是一个list
            title_exist = Article.objects.filter(title=title)
            #判断是否存在同名title
            if len(title_exist) != 0:
                return JsonResponse({"status":400,"msg":"title already exist,fail to publish."})

            '''插入数据'''
            add_art = Article(title=title,content=content,status="alive")
            add_art.save()
            return JsonResponse({"status":200,"msg":"publish article success."})
        else:
            return  JsonResponse({"status":401,"message":"please check param."})



json.load(request.body)表明在postman测接口的时候要使用body传参;

req.get("参数名")可以获得request中的参数,但是从原博主给出的代码猜测只能获得一个参数;

model类.objects.filter()可以对数据库中的数据进行筛选;

插入/修改数据之后一定要save();

对于原博主的JsonResponse的状态码,小白不太确定,感觉Json的状态码应该是用户自定义的,所以这里也可以正常事件返回0,异常时间流返回1/2/3之类的。

在postman中测试:

 

 尤其注意,当手动输入json时一定要是双引号;最后一个参数后没有逗号;url最后一个斜杠不要缺。

查看一下是否添加成功:

【删】

这里原博主采用了另外一种处理方法,将需要删除的文章的id通过url传递

def delete_article(request,art_id):
    if request.method =="POST":
        try:
            art = Article.objects.get(id=art_id)
            art.status='deleted'
            art.save()
            return JsonResponse({
                'status':200,
                'msg':'delete success'
            })
        except Article.DoesNotExist:
            return JsonResponse({
                'status':300,
                'msg':"article not exist"
            })

对应的url:

path('delete_articles/',delete_article),

原博主直接采用从数据库中删除的方法,但是我由于要做逻辑删除所以将status字段设置成了deleted(还需要完善)。

目前这种方式只能支持单个删除,而不支持批量删除,后续会再改进;

在postman中测试:

查看list:

 

虽然我这里并没有显示id,但是由于id是autoField,所以排在第一条的记录被删除,也印证了是正确的;

尝试删除并不存在的文章:

 【改】修改部分的url使用和delete一样的逻辑

path('update_articles/',modify_article),

views部分的代码如下:

def modify_article(request,art_id):
    if request.method =='POST':
        req = json.loads(request.body)
        try:
            art = Article.objects.get(id=art_id)
            key_flag = req.get('title') and req.get('content')
            if key_flag:
                title = req['title']
                content = req['content']
                title_exist=Article.objects.filter(title=title)
                if len(title_exist) > 1:
                    return JsonResponse({
                        'status':400,
                        'msg':'title already exists.'
                    })

                mod_art =Article.objects.get(id=art.id)
                mod_art.title=title
                mod_art.content=content
                mod_art.save()

                return JsonResponse({
                    'status':'200',
                    'msg':'change success.'
                })
        except Article.DoesNotExist:
            return JsonResponse({
                'status':300,
                'mag':'article does not exist,fail to change',
            })

但是原博主的代码有一点缺陷,前端form中出现的所有字段都必须传回后端,但是实际上修改的可能只有一个字段,因而使用这种模式可能会是消息体比较大,下面在user类中会有所改善。

在postman中测试:

 在list中查看修改结果:

 但是在测试中,我注意到我自己修改原博主的逻辑删除又很不完善的地方。此处应该限制用户只对active的文章做修改和删除操作,后面会修改。

对Article的CRUD简单操作基本上到这里就结束了。

其实我也有看到一些项目中,User继承AbstractUser、view继承CreateView、UpdateView等,但是由于当时连简单的增删改查都不会手写实现,也没有形成使用虚拟环境的好习惯,所以只是望而生畏,后面也会做尝试。如果观众老爷有比较推荐的方式也很希望大家能指点一下小白。

此外,原文章中还介绍了朴素的鉴权,实现简单的认证机制,防止在未登录时进行上述操作。

在urls.py中增加用于鉴权的接口:

 path("auth/",get_token)

在views.py前面新增函数get_token(request)

import hashlib

#获取token
def get_token(request):
    req = json.loads(request.body)
    uname = req["username"]
    upwd = req["password"]
    if request.method == "POST":
        try:
            tmppwd =User.objects.get(uname=uname).upwd
            if upwd == tmppwd:
                md5 = hashlib.md5()
                #把密码变成一个长度固定的字符串
                md5.update(upwd.encode("utf-8"))
                return JsonResponse({"status":"BS.201","X-Token":md5.hexdigest()})
            else:
                return JsonResponse({"status":"BS.401","msg":"username or password may wrong."})

        except User.DoesNotExist:
            return JsonResponse({"status":"BS.500","msg":"username is not exist."})

该函数使用md5加密明文密码。使用到了内建模块hashlib,hashlib提供了常见的摘要算法,如MD5、SHA1等。

之后做认证动作:

这里的认证动作本不应该这么简单,简单粗暴地将用户生成的token取出放在代码中是非常不可取的。

#认证动作
def user_auth(request):

    token = request.META.get("HTTP_X_TOKEN",b'')
    print (token)
    if token:
        #暂时先写上auth接口返回的数据
        if token=="e10adc3949ba59abbe56e057f20f883e":
            return "auth_sucess"
        else:
            return "auth_fail"
    else:
        return  "auth_fail"

在接口中调用user_auth函数,以增加文章接口为例,在文章前面加上鉴权语句:

auth_res=user_auth(request)
    if(auth_res == 'auth_fail'):
        return JsonResponse({
            'status':401,
            'msg':'user auth failed'
        })

测试该接口:

没有鉴权:

有鉴权:

 在读代码的过程中也了解到有内置的鉴权方法,如LoginRequiredMixin类的使用和@login_required的注解等。

明天会再巩固python的数据结构,优化CRUD的操作,实现1.多条件查询;2.查询结果分页;3.批量删除。并把自己项目中的简单增删改查写完,至于一些复杂的级联删除可以留在后续分析。学习鉴权(token、session、cache),把自己项目中的登录注册完成。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/928488.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号