Py学习  »  Django

用Django全栈开发(进阶篇)——02. 在Django中花样玩转PyMongo(上)

皮克啪的铲屎官 • 3 年前 • 394 次点击  

大家好,这是皮爷给大家带来的最新的学习Python能干啥?之Django教程的 进阶版

在之前《 用Django全栈开发 》系列专辑里面,皮爷详细的阐述了如何编写一个完整的网站,具体效果可以浏览线上网站:Peekpa.com

从进阶篇开始,每一篇文章都是干货满满。 这一节,我们来讲述一下,在Django中,如何花式玩转pymongo。pymongo如何模糊查询,排序

Peekpa.com的官方地址: http://peekpa.com

皮爷的每一篇文章,都配置相对应的代码。这篇文章的代码对应的Tag是“ Advanced_02 ”。

在这里插入图片描述

前瞻回顾

在上一节《用Django全栈开发(进阶篇)——01. 如何让Django连接MongoDB》,我们主要讲述了如何使用Django连接MongoDB数据库,并从里面读取数据的方法。

整体浏览

本文大概将会讲述以下内容:

  • 基本的增删改查;
  • 修改器的应用

由于内容很多,所以分开文章来说。接下来,就由我来给大家讲述以下如何玩转PyMongo。其实你只要会玩了PyMongo,MongoDB也就基本会个80%了,因为这俩差不多的。不信,你瞧。

插入

插入,非常的简单,PyMongo提供的插入函数主要有两个:

self.collection.insert()
self.collection.insert_many()
  • 1
  • 2

这里, insert() 主要是插入单个数据,插入的内容为 字典类型 的数据即可。

insert_many() 则是插入一组数据, 一个数组,里面存放的是字典类型的数据

具体可参考的代码,是我当初创建地震数据库信息的时候。从核心数据库读取的100条数据,然后直接调用 insert_many() 方法插入到新的数据库中。

查找

我们在上一篇的例子中,将100条地震信息全部显示,通过的是一条:

list_data = self.collection.find()
  • 1

来找到的。这个显然是返回了全部的数据。可以看到,如果调用 count() 方法,返回的数据是100:

count = list_data.count()
print(count)

#结果出入的是:100
  • 1
  • 2
  • 3
  • 4

这里要注意一下,这个 .count() 是PyMongo里面的方法,这个和MongoDB里面的 .count() 是一样的,返回的是cursor的结果数目。

接下来,我们来介绍一下关于搜索更加丰富的用法。

指定搜索条件精确查找

在实际开发中,很多情况,我们并不需要将全部数据都返回,可能只需要返回符合条件的特定数据,比如某天的数据,比如某个ID的数据,这种数据精确查找。

我们页面顶部右侧有个搜索输入框,我们就来通过这个给大家展示一下条件搜索怎么做:

在这里插入图片描述

这的搜索,我们主要是以地名为关键字的搜索。

首先,我们来到 front/template/datacenter/jpearth/manage.html 里面,来确认一下这个form是什么形式发送的关键字:

<form action="" method="get" class="form-inline ml-auto">
    <div class="form-group mr-4">
        <label for="title-input">标题:</label>
        <input type="text" class="form-control" name="search" id="title-input" placeholder="关键字">
    </div>
    <div class="form-group mr-4">
        <button class="btn btn-primary">查询</button>
    </div>
</form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

看到这里在 form 标签里面,是个 get 请求。判断 http://127.0.0.1:8000/jpearth/ 这个url是正常的显示全部还是说搜索,关键就在于url后面有没有跟 search 参数。而这个参数就是在上面的这个 input 标签里面,因为它的 name search

这下子就简单多了,我们只需要接着修改我们前一节的 JpEarthView 里面的 get 函数就可以了。

# 解析url,提取出来url里面的变量
def process_paramter(self, request):
    search_key = request.GET.get('search')
    handle_type = self.TYPE_ALL if search_key is None else self.TYPE_SEARCH
    return handle_type, search_key

# 通过传入的变量,来区分是全部搜索还是精确搜索
def get_data_from_db(self, handle_type, search_key):
    if handle_type == self.TYPE_ALL:
        result = self.collection.find()
    else:
        # 通过地址(jp_location)精确查找
        result = self.collection.find({"jp_location": search_key})
    return list(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这个时候我们来实验一下前端是否能够精确的搜索,我们以地名 福島県沖 为例:

在这里插入图片描述

然后点击 查询 ,看到结果如下:

在这里插入图片描述

可以看到结果正确,并且注意到上面的URL地址变成了 http://127.0.0.1:8000/jpearth/?fid=&search=福島県沖 ,说明和我们之前预想的完全一致。

但是,这个是精确查找,当我们在关键字地方输入 南部 的时候,我们希望得到类似 福島県沖 这样的信息,可是我们得到的确实以下信息:

在这里插入图片描述

返回结果为0。这个就是精确查找带来的不便之处。接下来我们来做模糊查找。

指定搜索条件模糊查找

如果我们按照地名的模糊查找也非常好办。只需要将之前的 get_data_from_db() 函数里面的search方法稍作修改就可以:

def get_data_from_db(self, handle_type, search_key):
    if handle_type == self.TYPE_ALL:
        result = self.collection.find()
    else:
        # 通过地址(jp_location) 精确查找
        #result = self.collection.find({"jp_location": search_key})
        # 通过地址(jp_location) 模糊查找
        result = self.collection.find({"jp_location": {'$regex': ".*" + search_key + ".*"}})
    return list(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可以看到,我们这里将原来的 search_key 替换成了 {'$regex': ".*" + search_key + ".*"} 。这个是使用正则表达式来做的包含处理,

因为MongoDB的搜索支持正则表达式,所以我们这里使用一个正则表达式的方法来做 QuerySet 中的 contains 处理,方便又快捷。

实际测试一下效果,看看这回 南部 会不会显示出来正确的结果:

在这里插入图片描述

完美,南部也显示出来。

接下来就友会有人疑问,如果我想组合查找呢?
就比如线上 Peekpa.com 中的数据中心:

在这里插入图片描述

比如番号的搜索,可能有大写,也有小写的,我希望输入一个小写的番号,能够返回来的信息既有大写的番号,也有小写的番号。这该怎么做?

指定搜索条件组合查找

这种组合查找,不难,我们需要修改的还是那个 get_data_from_db() 函数:

query_set = [{"post_title": {'$regex': ".*" + search_key + ".*"}},
{"post_title": {'$regex': ".*" + search_key.lower() + ".*"}},
{"post_title": {'$regex': ".*" + search_key.upper() + ".*"}}]
result = self.collection.find({"$or": query_set})
  • 1
  • 2
  • 3
  • 4

可以看到这么几个关键的知识点:

  • 创建了一个query_set,里面是一个list。list里面分别放的就是我们之前模糊条件搜索的条件:
{"post_title": {'$regex': ".*" + search_key + ".*"}}
  • 1

变化的只是 '$regex': 后面的内容。

  • find() 方法里,我们使用: {"$or": query_set} 让PyMongo条件搜索。这里的关键字是 $or ,这样就是 的关系。
  • 如果想使用 的关系,直接的列出条件就好,如下:
self.collection.find({"age":{"$gt":22}, "name":{"$regex":"user"}})
  • 1

这样,我们搜索一个番号,就能出来即使大写的,又是小写的结果:

在这里插入图片描述

搜索结果排序

接下来,我们就是想要实现搜索结果排序了。我们如果直接只调用 find() 方法,那么找出来的顺序是按照MongoDB的 _id 来排序的。如果想要实现结果排序,很简单:在 find() 后面添加 sort(id, order) 方法即可,这其中id为要排序的id name,order则是升序还是降序。

比如我们要实现地震信息升序排列,因为我们默认的数据是降序的(数据添加顺序导致),所以我们升序排列的话,就要将原有的方法修改成下面的样子:

def get_data_from_db(self, handle_type, search_key):
    if handle_type == self.TYPE_ALL:
        # 搜索全部结果
        #result = self.collection.find()
        # 降序排列
        #result = self.collection.find().sort('jp_time_num', pymongo.DESCENDING)
        # 升序排列
        result = self.collection.find().sort('jp_time_num', pymongo.ASCENDING)
    else:
        # 通过地址(jp_location) 精确查找
        #result = self.collection.find({"jp_location": search_key})
        # 通过地址(jp_location) 模糊查找
        result = self.collection.find({"jp_location": {'$regex': ".*" + search_key + ".*"}})
    return list(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

看到:

  • pymongo.ASCENDING:升序
  • pymongo.DESCENDING:降序

我们开看一下,发现我们换成了 pymongo.ASCENDING 之后,地震结果是升序排列的:

在这里插入图片描述

搜索返回指定字段

大部分时候,我们的搜索想返回指定字段,因为如果数据模型太过庞大,每回搜索都返回全部内容,前端的数据量肯定会崩溃的,所以想要做只返回指定字段的类型,这个也很简单,只需要在 find() 方法里面,再添加需要返回字段的id就可以。

比如我们只需要返回地震地点和地震发生时间的信息,我们需要把 find() 方法修改成以下样子:

# 只返回 地震地点 还有 地震时间
result = self.collection.find({},{'jp_title', 'jp_location'})
  • 1
  • 2

可以看到,这里 find() 第一个参数为 {} ,我们在第二个参数里面才设置的需要返回的字段,再来看一下后台和前端的结果:

在这里插入图片描述

后台看到,返回的只有 _id jp_title jp_location 这三个字段:

在这里插入图片描述

但是,这里就有个问题: 如果有些字段你不想返回呢?这又该怎么做 。比如这里,我们不想返回 jp_title jp_location 字段,很简单,find方法这么写:

# 不 返回 地震地点 还有 地震时间
result = self.collection.find({},{'jp_title':False, 'jp_location':False})
  • 1
  • 2

只要将第二个参数,变幻成字典形就可以,将不想返回的字段,值设置成 False 就可以。

我们看一下前端和后台的效果:

在这里插入图片描述

后台:

在这里插入图片描述

是不是非常的简单易懂?

单一查找

上面讲述的 collection.find() 方法,返回的是一个 Cursor ,但是它的查询结果是一个数组。如果要单一查找,我们只需要使用:

collection.find_one()
  • 1

具体里面的参数,和上面 find() 方法是一样的。这里就简单的给大家做个演示: 我们查找 jp_location 福島県沖 的数据:

result = self.collection.find_one({"jp_location":"福島県沖"})
print(result)

#{'_id': ObjectId('5f002078601bd55f57ca2130'), 'jp_create_time': '2020-7-4-14-23-52', 'jp_url': 'http://www.jma.go.jp/jp/quake/20200704062249395-04152004.html', 'jp_title': '2020年\u30007月\u30004日15時20分', 'jp_id': '2020070406224939504152004', 'jp_time_num': '2020-07-04-15-20-04', 'jp_location_image_url': 'http://www.jma.go.jp/jp/quake/images/japan/20200704062249395-04152004.png', 'jp_location': '福島県沖', 'jp_level': 'M3.7', 'jp_max_level': '震度1', 'jp_time_text': '\u30007月\u30004日15時22分'}

  • 1
  • 2
  • 3
  • 4
  • 5

可以看到,这条结果result其实就已经是一个单个的数据了,并不是数组。好了,今天的内容其实很多了,我们来总结一下。

技术总结

最后总结一下,

如何玩转PyMongo:

  1. 首先好好阅读文章,文章里面有生动的例子,和配套的代码,还有代码运行结果的配图,简直不要太VIP;
  2. PyMongo的插入有两个方法: insert() insert_many() ,分别对应的是单个插入和群体插入,怎么这么怪怪的;
  3. PyMongo的查找,也是有两种: find() find_one() ,分别是结果群体查找和结果单个查找;
  4. 在查找的时候,可以传入精确的变量进行精确查找,也可以把条件组合起来;
  5. 最后结果还支持排序;
  6. 进阶篇的**玩转PyMongo(上)**总结完毕。

获取代码的唯一途径:关注『 皮爷撸码 』,回复『 代码 』即可获得。

长按下图二维码关注,如文章对你有启发或者能够帮助到你,欢迎 点赞 在看 转发 三连走一发,这是对我原创内容输出的最大肯定。
在这里插入图片描述

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/72010
 
394 次点击