公司有一个外部博客,大家能够在下面创立本人的账号,而后写文章在全公司分享。昨天这个外部博客开明了API,因而我筹备写一个Python程序,把本人文章都搬运下来。
而后我就发现这个API接口有一个bug。并且依据它的景象,猜到它问题出在哪里。
我先来简略形容一下景象。
假如我硬盘上当初有50个Markdown文件。当初我要把它公布到网站上。简化代码如下:
import globimport requestsfor path in glob.glob('blog/*.md'): with open(path) as f: article = f.read() requests.post('https://xxx.yyy.com/post?token=abcasdf', json={'content': content})
公布实现当前,文章的确都曾经在网页上呈现了,并且每篇文章都能失常显示。但我粗略浏览了一下,发现外面有一些文章的开端带上来二维码。我不想让公司的人晓得我的公众号,所以筹备批改一下文章。
有一些文章有二维码,有一些没有,一个一个改起来很麻烦,所以我做了两步操作。首先写了一个程序,扫描所有Markdown文件,发现二维码就删掉。而后,我间接在网站上把刚刚公布的所有文章都删了(懒得去看哪篇有二维码,哪篇没有,罗唆全删了重发)。
接下来,我再次运行程序批量从新公布文章。2秒钟当前公布实现。
原本所有看起来都很失常,然而当我到网站上查看的时候,发现有很多文章点开当前,都提醒『该文章曾经删除』。
我一开始在想是不是我的程序写得不对,漏掉了这些文章。我从新独自一篇一篇公布这篇文章,API接口返回公布胜利,可在网页上还是显示文章曾经删除。
而后我一篇一篇查看这些公布失败的文章,发现有一个独特的特点:他们是一开始就没有二维码的文章。相当于这些文章我在网站上删除当前原样从新又发了一次。
那我就有了一个初步的猜想,大略晓得起因是什么了:
- 因为每篇文章有一个docid,当第一次公布文章的时候,这个docid就是文章正文内容的md5值。只有文章齐全一样,间断发多少次,它的docid都一样。这样就能够防止出现反复文章。(更新的时候,须要用户被动提供docid,防止从新生成新的)。
- 这个网站的删除性能,必定是假删除。也就是当我点了删除文章的按钮时,文章其实仍然在数据库外面,只不过减少了一个字段removed=True。网页显示文章的时候,查问条件必定是col.find({'removed': {"$ne": True}}),所以就不会把这些被软删除的文章显示进去。
- API公布新文章的时候,必定应用的是更新操作。并且应用了upsert=True。
以MongoDB为例,这个API背地的逻辑必定是这样的:
def post_article(docid, article_info): mongo.update_one({'_id': docid}, {'$set': article_info}, upsert=True)
upsert=True的作用,是先检查数据是否存在,如果存在就更新,如果不存在就插入。
第一次公布的时候,文章不存在,直接插入,失常。如果用户失常应用批改接口,批改了注释,因为用户被动提供了docid,所以也能失常更新。
但如果用户先删除了数据,此时数据库中,减少了一个字段removed=True。而后用户又一成不变从新发一次文章。那么docid必定还是原来那个。这条文章曾经在数据库中存在了。于是逐个更新了每个字段。然而新公布的字段外面是没有removed这个字段的,所以更新的时候不会更新它,它还在数据库外面。所以就呈现了公布胜利,然而关上新闻又提醒文章曾经删除。
我去问了一下做这个API的同学,果然它的bug起因跟我构想的截然不同。
这个bug解决办法非常简单,公布新文章的时候,把update_one改成replace_one就能够了:
def post_article(docid, article_info): mongo.replace_one({'_id': docid}, {'$set': article_info}, upsert=True)
以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈 ,发送 “J” 即可收费获取,每日干货分享