Python:分页工具

简述

ps[2016.2.13]:更新一个问题,下面的源代码有误,具体往下看.
ps[2016.2.27]:将原来的函数组成一个类,完全兼容原来的使用方式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 错误代码*****************************
'''
htmls = htmls.append(
            liahref % (
                currentpage, url, lenindex, ipagesize, currentpage,
                lenindex) if lenindex == noformatpage else liahref % (
                pageclass,
                url, lenindex, ipagesize, pageclass, lenindex))
'''
# 错误代码***********************************************************


# 正确代码*****************************
htmls.append(
            liahref % (
                currentpage, url, lenindex, ipagesize, currentpage,
                lenindex) if lenindex == noformatpage else liahref % (
                pageclass,
                url, lenindex, ipagesize, pageclass, lenindex))
# 正确代码***********************************************************

这是一个伪代码,因分页需要数据库配合,所以这里只给出分页的代码.

分页效果图
image-2402

python Logo
image-2403

源码

代码里面有很详细的注释,不在过于解释了.

v1.0[2016.2.27]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""

分页程序. 2016.2.27 add
"""
class Pager(object):
    """
    生成代码示例[使用了妹子UI的分页.参照:http://amazeui.org/widgets/pagination]:
    page: 当前页数,从1开始.
    pagesize: 每页里面多少数据,一般为10.
    rowscount: 查询数据库中的总数(一共有多少条数据,不能添加查询条件,直接count即可).
    url: 访问地址[注意:这里没有处理url本身带有参数的问题],这里自动添加了page和pagesize参数(!!丢弃了原来的所有参数,若有的话)
    countshow=Flase:默认不显示(总XXX条,共XX页,当前X页),如需开启,请传递True.
    """

    def repage(self, page, pagesize, rowscount, url,countshow=False):
        # 页码
        ipage = int(page)
        # 原始page ,数值不会被改变
        noformatpage = int(page)  # 原始page
        #  页码大小
        ipagesize = int(pagesize)
        # 总数量
        irowscount = int(rowscount)
        # 进行数值限定
        if ipage <= 1:
            ipage = 1
        # 进行分页查询时,需要跳过的数据.
        # 这个ipage和noformatpage是有区别的!!!
        # 例如,当前是第二页,那么下面这个ipage是10,而noformatpage是不变的2.
        ipage = (ipage - 1) * ipagesize
        # 给当前页添加一个css标记
        currentpage = "am-active"  # 当前
        # 不添加标记
        pageclass = ""  # 正常
        # 空包标记,用于替换.
        black = ""

        '''
        格式化数据.
        主要作用:
            大于/小于当前页5个的自动隐藏.
        比如:
            当前是第6页,那么只显示1-11页,并且第6页在中间.
        示例如下:
            1,2,3,4,5,[6],7,8,9,10,11
            第8页的时候:
                3,4,5,6,7,[8],9,10,11,12,13
            以此类推...
        '''

        def formatstyle(item, noformatpage):
            # 标记最大隐藏的.可配置.
            maxhidden = noformatpage + 5
            # 标记最小隐藏的.可配置
            minhidden = noformatpage - 5
            # 为当前页添加css标记.
            if item == noformatpage:
                return liahref % (currentpage, black, url, item, ipagesize, currentpage, item)
            elif (minhidden) <= item <= (maxhidden):
                # 这个地方是控制显示的,只有符合当前页+5或者-5之内的才显示.
                return liahref % (
                    pageclass, black,
                    url, item,
                    ipagesize,
                    pageclass,
                    item)
            else:  # 不满足条件的全都隐藏.
                return liahref % (
                    pageclass, hiddenstyle,
                    url, item,
                    ipagesize,
                    pageclass,
                    item)

        # 隐藏数据.
        hiddenstyle = "display:none;"
        '''
        %1: 添加各种class,在这里就是:标记当前页.
        %2: 设置隐藏/显示(通过css控制)
        %3: url地址,不带参数的url地址.
        %4: 当前页码,从1开始.
        %5: 数据大小.
        %6: 标记当前页.
        %7: 显示的名称.
        '''
        liahref = "<li class='%s' style='%s'><a href='%s?page=%s&pagesize=%s' class='%s' >%s</a></li>"
        # 循环生成数据,并且使用formatstyle控制显示或隐藏.
        htmls = [formatstyle(item, noformatpage) for item in range(0, irowscount // ipagesize)]
        # 获取总页数
        countpage = len(htmls)
        # 处理余数的情况
        if irowscount % ipagesize != 0:
            # 最后一页.
            lenindex = len(htmls) + 1
            # 赋值给总页数
            countpage = lenindex
            # 新增到最后.
            print(pageclass)
            print(url)
            print(lenindex)
            print(ipagesize)
            print(pageclass)
            print(lenindex)
            # liahref % ("", "http://localhost:5000/admin/list",68,10, "", 68)
            htmls.append(liahref % (
                currentpage,black, url, lenindex, ipagesize, currentpage,
                lenindex) if lenindex == noformatpage else liahref % (
                pageclass,black, url, lenindex, ipagesize, pageclass, lenindex))
            # 正确代码***********************************************************
        # 参考上面的a,只是去掉了外层的li标记
        ahref = "<a href='%s?page=%s&pagesize=%s' class='%s'>%s</a>"
        # 第一页和上一页
        firsthrefdisabled = "am-disabled"
        # 生成默认禁用数据
        prevhref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>上一页</a>"
        firsthref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>第一页</a>"
        # 只有当大于1的时候第一页和上一页才可以用.
        if noformatpage > 1:
            firsthrefdisabled = black
            prevhref = ahref % (url, (noformatpage - 1), ipagesize, currentpage, '上一页')
        firsthref = ahref % (url, 1, ipagesize, currentpage, '第一页') if 1 == noformatpage else ahref % (
            url, 1, ipagesize, pageclass, '第一页')
        # 新增到最前面
        htmls.insert(0,
                     "<li class='am-pagination-first %s '>%s</li><li class='am-pagination-prev %s'>%s</li>" % (
                         firsthrefdisabled, firsthref, firsthrefdisabled,
                         prevhref))
        # 第一页和上一页END.
        # 下一页和最末页
        lasthrefdisabled = "am-disabled"
        # 生成默认禁用数据
        nexthref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>下一页</a>"
        lastindexhref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>最末页</a>"
        # 只有当小于总数的时候下一页和最末页才可用.
        if noformatpage < (countpage):
            lasthrefdisabled = black
            nexthref = ahref % (url, (noformatpage + 1), ipagesize, currentpage, '下一页')
            lastindexhref = ahref % (url, countpage, ipagesize, currentpage, '最末页')

        htmls.append(
                " <li class='am-pagination-next  %s '>%s</li><li class='am-pagination-last %s '>%s</li>" % (
                    lasthrefdisabled, nexthref, lasthrefdisabled, lastindexhref))
        # 下一页和最末页END.
        # 显示最后的数据.
        if countshow:
            htmls.append("总%s条,共%s页,当前%s页" % (irowscount, (len(htmls) - 2), page))
        return ipage, ipagesize, ''.join(htmls)

v0.9[建议使用最新版本.]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
'''
v0.9[建议使用最新版本.]
这是一个分页的伪代码[使用了Flask(不过实际使用什么也无所谓)].
效果可以看[Pagination.png]图片.
涉及到数据库和返回给前台.
这里只提供了一个分页的方法,注释很详细.
prd.
pruidong@gmail.com
2015.12.30
'''
'''
分页程序.
生成代码示例[使用了妹子UI的分页.参照:http://amazeui.org/widgets/pagination]:
<ul data-am-widget="pagination" class="am-pagination am-pagination-default">
   <li class="am-pagination-first "><a href="'http://localhost:5000/index?page=1&amp;pagesize=10" class="">第一页</a>.</li>
   <li class="am-pagination-prev "><a href="'http://localhost:5000/index?page=5&amp;pagesize=10" class="am-active">上一页</a>.</li>
   <li class="" style=""><a href="'http://localhost:5000/index?page=1&amp;pagesize=10" class="">1</a>.</li>
   <li class="" style=""><a href="'http://localhost:5000/index?page=2&amp;pagesize=10" class="">2</a>.</li>
   <li class="" style=""><a href="'http://localhost:5000/index?page=3&amp;pagesize=10" class="">3</a>.</li>
   <li class="" style=""><a href="'http://localhost:5000/index?page=4&amp;pagesize=10" class="">4</a>.</li>
   <li class="" style=""><a href="'http://localhost:5000/index?page=5&amp;pagesize=10" class="">5</a>.</li>
   <li class="am-active" style=""><a href="'http://localhost:5000/index?page=6&amp;pagesize=10" class="am-active">6</a>.</li>
   <li class="am-pagination-next "><a href="'http://localhost:5000/index?page=7&amp;pagesize=10" class="am-active">下一页</a>.</li>
   <li class="am-pagination-last "><a href="'http://localhost:5000/index?page=83&amp;pagesize=10" class="am-active">最末页</a>.</li>
  </ul>总840条,共83页,当前6页
page: 当前页数,从1开始.
pagesize: 每页里面多少数据,一般为10.
rowscount: 查询数据库中的总数(一共有多少条数据,不能添加查询条件,直接count即可).
url: 访问地址[注意:这里没有处理url本身带有参数的问题],这里自动添加了page和pagesize参数(!!丢弃了原来的所有参数,若有的话)
v0.9[建议使用最新版本.]
'''
def repage(page, pagesize, rowscount, url):
    # 页码
    ipage = int(page)
    # 原始page ,数值不会被改变
    noformatpage = int(page)  # 原始page
    # 页码大小
    ipagesize = int(pagesize)
    # 总数量
    irowscount = int(rowscount)
    # 进行数值限定
    if ipage <= 1:
        ipage = 1
    # 进行分页查询时,需要跳过的数据.
    # 这个ipage和noformatpage是有区别的!!!
    # 例如,当前是第二页,那么下面这个ipage是10,而noformatpage是不变的2.
    ipage = (ipage - 1) * ipagesize
    # 给当前页添加一个css标记
    currentpage = "am-active"  # 当前
    # 不添加标记
    pageclass = ""  # 正常
    # 空包标记,用于替换.
    black = ""
   
    '''
    格式化数据.
    主要作用:
        大于/小于当前页5个的自动隐藏.
    比如:
        当前是第6页,那么只显示1-11页,并且第6页在中间.
    示例如下:
        1,2,3,4,5,[6],7,8,9,10,11
        第8页的时候:
            3,4,5,6,7,[8],9,10,11,12,13
        以此类推...
    '''
    def formatstyle(item, noformatpage):
        # 标记最大隐藏的.可配置.
        maxhidden = noformatpage + 5
        # 标记最小隐藏的.可配置
        minhidden = noformatpage - 5
        # 为当前页添加css标记.
        if item == noformatpage:
            return liahref % (currentpage, black, url, item, ipagesize, currentpage, item)
        elif (minhidden) <= item <= (maxhidden):
            # 这个地方是控制显示的,只有符合当前页+5或者-5之内的才显示.
            return liahref % (
                pageclass, black,
                url, item,
                ipagesize,
                pageclass,
                item)
        else: # 不满足条件的全都隐藏.
            return liahref % (
                pageclass, hiddenstyle,
                url, item,
                ipagesize,
                pageclass,
                item)
    # 隐藏数据.
    hiddenstyle = "display:none;"
    '''
    %1: 添加各种class,在这里就是:标记当前页.
    %2: 设置隐藏/显示(通过css控制)
    %3: url地址,不带参数的url地址.
    %4: 当前页码,从1开始.
    %5: 数据大小.
    %6: 标记当前页.
    %7: 显示的名称.
    '''
    liahref = "<li class='%s' style='%s'><a href='%s?page=%s&pagesize=%s' class='%s' >%s</a>.</li>"
   
    # 循环生成数据,并且使用formatstyle控制显示或隐藏.
    htmls = [formatstyle(item, noformatpage) for item in range(1, irowscount // ipagesize)]
   
    # 获取总页数
    countpage = len(htmls)  
    # 处理余数的情况
    if irowscount % ipagesize != 0:
        # 最后一页.
        lenindex = len(htmls) + 1
        # 赋值给总页数
        countpage = lenindex
        # 新增到最后.
        # 错误代码*****************************
        '''
        htmls = htmls.append(
        '''
        # 错误代码***********************************************************

        # 正确代码*****************************
        htmls.append(liahref % (currentpage, url, lenindex, ipagesize, currentpage,lenindex) if lenindex == noformatpage else liahref % (pageclass,url, lenindex, ipagesize, pageclass, lenindex))
        # 正确代码***********************************************************
    #参考上面的a,只是去掉了外层的li标记
    ahref = "<a href='%s?page=%s&pagesize=%s' class='%s'>%s</a>."
   
    # 第一页和上一页
    firsthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    prevhref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>上一页</a>"
    firsthref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>第一页</a>"
    # 只有当大于1的时候第一页和上一页才可以用.
    if noformatpage > 1:
        firsthrefdisabled = black
        prevhref = ahref % (url, (noformatpage - 1), ipagesize, currentpage, '上一页')
        firsthref = ahref % (url, 1, ipagesize, currentpage, '第一页') if 1 == noformatpage else ahref % (
            url, 1, ipagesize, pageclass, '第一页')
    # 新增到最前面
    htmls.insert(0,
                 "<li class='am-pagination-first %s '>%s</li><li class='am-pagination-prev %s'>%s</li>" % (
                     firsthrefdisabled, firsthref, firsthrefdisabled,
                     prevhref))
    # 第一页和上一页END.
    # 下一页和最末页
    lasthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    nexthref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>下一页</a>"
    lastindexhref = "<a href='javascript:void(0)' class='am-disabled' disabled='disabled'>最末页</a>"
    # 只有当小于总数的时候下一页和最末页才可用.
    if noformatpage < (countpage):
        lasthrefdisabled = black
        nexthref = ahref % (url, (noformatpage + 1), ipagesize, currentpage, '下一页')
        lastindexhref = ahref % (url, countpage, ipagesize, currentpage, '最末页')
    htmls.append(
        " <li class='am-pagination-next  %s '>%s</li><li class='am-pagination-last %s '>%s</li>" % (
            lasthrefdisabled, nexthref, lasthrefdisabled, lastindexhref))
    # 下一页和最末页END.
   
    # 显示最后的数据.
    htmls.append("总%s条,共%s页,当前%s页" % (irowscount, (len(htmls) - 2), page))
    return ipage, ipagesize, ''.join(htmls)
@app.route('/indexs', methods=['POSTS', 'GET'])
def seetext():
    page = request.args.get('page', 1)
    pagesize = request.args.get('pagesize', 10)
    see = SeeText()
    # 获取总数--不要带条件!!!
    rowscount = see.count()
    # 参数请参考方法说明.
    pageinfo = repage(page=page, pagesize=pagesize, rowscount=rowscount, url=request.base_url)
    # 查询数据
    data = see.findAll(page[0], pagesize)
    # 转换一下,第二项是分页的数据.
    pageinfo = ''.join(pageinfo[2])
    # 返回数据给前台
    return render_template("/index/index.html", seetextdata=data, pageInfo=pageinfo)

2015:逆风飞翔

开篇

你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒

上面这段话,我曾用在很多地方.

对于即将过去的这一年,也很适用.因此,写在最亮的地方.

其实这一年说起来并非一帆风顺,但无论如何,都已经走过来了.想起这一年,像在慢速放一个电影,一张张画面划过眼前.熟悉,却又感叹时光匆匆.有人说,无论何事,咬咬牙总会过去.何尝不是这样呢.任何一切,都躲不过时间的流逝.小四有一段话:

很多我们以为一辈子都不会忘记的事情,就在我们念念不忘的日子里,被我们遗忘了 。–《左手倒影 右手年华》

若不回忆,岂不就忘记了?

站在三岔路口

当站在大街上,围满人的棋桌前,看的人会比真正下棋的人着急。总会思考,怎么不走那,怎么不走那..等等.当你真正坐在那个地方,你也会感受到来自周围”热切的目光”.有一句话是”观棋不语真君子”.自己做到就好.扯远了.2015发生了很多事.去了一些地方,换了一份工作.

其实在换工作之前,有过很多纠结.面临的问题,总是一个接着一个.简单总结如下:

选择
image-2387

  • 人际关系:其实对于新公司所面临的挑战,最大的莫过于人际关系,可能在原来公司已经辛辛苦苦经营了很久的人际关系.一换工作,就消失殆尽了.同样也存在一些风险,也可能无法融入一个新的团队等等.
  • 工作层面:每个人性格都是不同的,但还好,我觉得我很幸运.遇到的很多同事,基本都比较聊得来.
  • 技术层面:每个公司所面临的事情不尽相同,所使用到的技术肯定也千差万别.可能无法使用最喜欢//最熟悉的语言,而要从头开始接触一门或者几门新的语言.虽然在公司中接触一门或者几门语言会是一个好的学习机会(因为往往都带着目标去学习),但同样会出现一些小问题.

大概就是以上几点.每一次选择都意味着未来的方向,站在一个三岔路口或者十字路口,所面临的问题都是不一样的.我相信每一个人都会有自己不同的选择,对于他人或许很重要,也可能不重要.毕竟没有人会一直跟在身后,除了影子.

Code……

2015依旧写了一些代码.不多不少,数下来才发现:真的没几个啊!(内心几乎是崩溃的,咋才写了这么些)..

  1. 在线服务:提供了三个东西(每天一张图//一大堆段子//一个网站的离线chm[抱歉已经许久没更新了..]),其实是很简单的一个东西,是业余练手和一些乐趣.在完成这个小东西的时候,会发现写代码的那种快乐[不必思考太多,拿起就写,想到就开始写].并且依据此服务,发布了一个工具集(已经开源:地址).在线服务地址..PS:这几个均使用Python脚本完成.
  2. Android客户端[地址]:从2014开始自学Android,刚学的时候,做了几个简单的拿不出手的APP.只好按下,继续发力,在今年的年末的前几天,终于发布了一个APP(还有很多功能和细节有待优化),学习进度最快的那段时间,恰好是拼命写代码的那几天.面对一个新东西是有点痛苦的,这种痛苦建立在思维上,Android打破了固有思维(令我万万没想到的是:Android使用XML布局[当然也可以用Java代码进行布局,但是官方推荐使用XML]).就因为思维差异,导致学习路线陡增,学习时间也就无限增长了.也就这时候,忽然想起来之前一个前辈说的:之前做C++,后来Web发展,去学习了jQuery.难度可想而知.往前的每一步,无论大小,都是进步.偶尔害怕踏出第一步,因其中的不确定.战胜自己,才可以勇敢前行.
  3. 其余的技术部分就是一些乱七八糟的不值得说的了,也没啥亮点.

编程语言
image-2388

2015也接触不少新东西:Python3.5发布,AngularJS2.0快要发布,Android 6.0发布等等等等(目前已经从Python2+切换到Python3+,大部分问题已解决).每一次变革,都意味着新东西的到来.另外我还想说,这几年我一直在接触PHP,从买到的第一本《PHP与MySQL程序设计(第四版)》到现在,它依旧是崭新的.而目前PHP7都已经发布了.实在愧疚,感觉PHP是学不会了.希望后面有时间可以多了解一下PHP.

对于其它方面的技术就了解的越来越少了,依稀记得有过这样一个约定:从某天起,我要把系统更换成Linux(一直使用Fedora,从Fedora18->>到目前的Fedora23).事实是,到现在依旧还没使用上.原因很多,我想技术分散,学习力减退,时间紧张这些可能都是借口.慢慢我在发现,我好像只能做一个Linux的”虚拟机使用者”.为此,深感遗憾.回想当初,刚接触Linux,先是去看了Ubuntu(一个很优秀的Linux发行版,那时候好像还是Ubuntu12),结果没看明白官网的安装介绍(当时不知道有翻译这个神器..)..然后就找到了Fedora的官网(那时候就已经有中文了..赞一个).一步一步跟着学习,在学习群里有一句知名的话:如果没有重装过几遍Linux,肯定就是没用过Linux的.没错,如你上面看到的,我重装了很多遍Linux.说实话,每次都很心疼,因为里面都是我的数据啊,学习数据各种资料各种工具(一开始重装,不知道备份,然后就弄丢了).那时候也喜欢折腾,各种折腾.后来慢慢就不太敢大折腾了,毕竟装了那么些工具,也写了一些代码,重装简直是花样作死啊.当时啃过很多遍《鸟哥的Linux私房菜》,很遗憾,就是没看完(大多数时候,就看一半多就没看后面的了).想起来,发现很多时候,书只看了一半就没有继续看下去了.原因和借口都很多.只是希望有时间能好好看完已经看过一半的那些书.还是希望有机会可以直接换到Linux系统,接触更多的Linux知识(毕竟她是如此这般迷人).

除了PHP还有另外一个是没有想明白,也没有时间去想明白的——没错,是NodeJS.

等有时间好好研究下PHP和NodeJS.免得以后遇到初级问题,还需要去请教别人.可能会有疑问,为啥想了解这么多?刚开始接触程序的时候,想学的东西很多:Java,PHP,C++,JavaScript,Linux,DataBase(里面区分了很多)等等这些,也收集了很多书,但大部分都没有看过.其中的一些不在最开始想学的里面,但也还是接触了,在列表里面的,却很少有时间去接触了.

其实,我们都在梦想的路上,越来越远.

站在家门口,盼望远方

前几天回家,特地拍了一张照.不知道我所在的城市,在哪个方向.但我所知道的是,家在哪个方向.

现在所在的城市,跟大多数城市一样,夜晚灯火通明.外面一点都不寂寞,没有伸手不见五指的黑暗.而家则截然不同,关灯后伸手不见五指,四处安静的能听见心跳声.习惯了喧闹,忽然觉得那份安静很难得.之前回去的时候,偶尔会失眠,因为实在不习惯那太安静的环境——没有光线,没有喧闹,没有车的喇叭声.后来回去的次数渐渐多了也就习惯了,只是总觉得,在这反复的切换中,仿佛丢失了什么.认真的想,却又想不起来.

家
image-2389

家是没有雾霾的那种地方,偶然拍一张,竟然会发现不一样的美.像是很久没看见那样的景色,熟悉却又陌生.最让我感到震撼的是:当走在路上,打招呼的人很多,人们不是神色匆匆,也不是一脸愁容,也没有低头看着手机.我似乎刻意忘记了,在不远的地方,有一群看着我长大的人.他们能随口喊出我的名字,能随口说出我小时候的趣事.这样的发现,让我觉得惊讶——难道这不是自然而然的吗?那是我的家,这不是必然的吗?在城市的生活,似乎忘记了还有那样的一个地方.

去年从更远的地方回来,回到离家坐车5小时的地方.这是一种接近,在有一年的十一,忽然有点想家.电话中熟悉的声音,让我在挂断电话之后,泣不成声.当即决定,靠近,慢慢靠近.在离家更近的地方,仿佛能感觉到家人的抚摸,亲昵的话语.在很多时候,会觉得很近,仿佛就在眼前,就在脑海里.当然了,还有家乡从小吃到大的美食,各种辣味的食品,那可都是小时候最喜欢的零食.在前几年去外面的时候,曾去超市买过和小时候类似的零食,一口咬下去才发现那味道,跟小时候的差别太大了.我想,即使回到当初上学的地方,也找不回来小时候吃零食的那种快乐.但家的味道一直存在.

“当我站在家门口眺望你所在的方向,你能听到我内心的期盼吗?”——来自家的声音.

逆水行舟

2016看起来还有很多事情要去完成.为什么是逆水行舟?因为对前途的不可知,对未来的迷惘.

面对困难,可能不会在使用之前的方式去解决.
面对技术问题,可能不会去群里问.
面对选择,可能会有更多顾虑.

我想这便是成长吧,《大话西游》里说,”成长不一定会得到,但一定会失去”.

那么,2016:

  • 做该去做的事情.
  • 面对困难,咬咬牙,总会过去的.
  • 累了就回头看看,看完之后,立刻前行.别留恋曾经,因为留恋也没有用.
  • 遇到技术问题,先去看官方文档.因为可能官方文档里稍微写了一下(有很多问题确实在官方文档里找不到答案),然后再去找.
  • 你所经历的痛苦,在人群中都太渺小了.根本不值得一提.
  • 总有人在过着你想要的生活.
  • 其实上面大部分都是鸡汤,没错.鸡汤就是这么激励人.

废话发表完毕,2015无论如何是即将过去了.希望在2016可以心安.加油!

展望2016

看上面一段.

写在2015年末.献给2015,和即将到来的2016.

无论要去哪,先战胜自己,迈出勇敢的第一步!

你的人生,不会辜负你的.

PS:prd 于 2015.12.21 [CD]

Android:第一个Android APP发布

第一个..

开源了APP的所有代码[访问地址].

初试牛刀,学了这么久的Android.总算在磕磕绊绊中完成了第一个APP(虽然问题还有一大堆)…

只记得是从去年开始学习的Android(自学),期间学习进度比较缓慢,但也还是”捣鼓”出了几个不叫APP的APP.现在发布的这个,拖拉拖拉的做了4个月,其实折算下来,时间应该是不长的.没办法,期间有各种问题.大部分时间都花在解决大问题上了.

其实写了这么久,多少还是有点感情的.我有一个不知道是不是不太好的习惯,有时候习惯在代码里面码一些注释,就是写完那个功能或者那个模块的感受……

下面也会贴一些其中的注释.更重要的是,我开源了这个APP[访问地址],如果不愿意去看开源的源码,也要去那边的文件夹才能下载的…(因为哥的服务器实在架不住大楼量啊).还需要说明的一个问题是:因服务器的原因,在代码里面所有跟服务器交互的地方都被去掉了,这也是逼不得已,希望能谅解一下.但我保证,其余部分的代码是完整的(跟发布版的一毛一样的).

很遗憾,这个APP没能在任何市场上架,想必这也是一个不太美好的地方了.后来想想不上就不上吧,直接发布也挺好,反正也是要开源的.(源码地址在上面找)写这个东西的时候,其实已经没多少兴奋劲了,因为都已经打包一段时间了,才把源码公开出来(也是希望新手可以学习下,虽然我也是新手).

啰嗦了一大堆,想起后面还有年终总结要写.好吧……停止啰嗦,下面发干货(对了这个APP叫:凉夜).

大概做了什么

1. 看看(文字内容,采集自网络):
详细:自有国外服务器,提供数据支撑,文字内容同样基于该服务器提供
(对于访问速度是最大的性能瓶颈,希望后期有能力可以改善)。当前更新频率为3天更新一次。
在Android客户端提供了缓存及下拉刷新操作。在分享方面,目前集成了微信、易信、QQ这几个分享。

2. 有图(图片,每天定时更新):
详细:图片源自某门户每天更新的图片,后台服务器每天零时左右定时进行更新。
客户端对于服务器端有相应延迟(避免提前请求导致的加载错误)。
该项同样提供文字方式的分享(在分享方式上有差别),并提供保存本地及即时设置桌面功能。

3. 关于(关于APP、设置等操作):
详细:可在客户端开启[每天自动设置桌面功能(使用两个Service相互监听,
保障可正确更新,若开启,该功能会延迟更新.)]、提交反馈、关于等。

Android LOGO
image-2382

代码里面的啰嗦

下面的都是来自代码里面的.可以在[地址]里面的MainActivity.java里面找到.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
* 今天开始重写之前的"闲聊",只是现在改名字了--凉夜。
初次的意思是,在每一个凉的夜,都需要一些快乐或者温暖。
刚写还是遇到一些困难,也挺头疼的。
加油!
prd - 2015.8.15 2:20
今天调整了很久ListView的显示,加上泛型也用不了导致解析JSON数据出了很大问题,只能暂时找个办法先解决了。2015.8.16 3:30
今天在一个群的朋友的帮助下,总算解决了Fragment加载图片的问题,真心觉得很开心。也谢谢那个朋友。因为这个问题实在困扰了好几天啊。。。。。。
2015.8.22 1:10
这可能是到目前为止,单日增加功能最多的一次了。
本次共实现以下功能:
        1.为自动设置桌面增加两个Service(保证Service一直存在);
        2.为Image增加一个从底部弹出的框;
        3.将NetworkUtils从new,改为单例.
看起来功能并不多,但是代码量真心不小.
2015.8.29 23:40
=====================================================================================
这次更新了很多东西,已更新如下:
            1.完成ActionBar右侧只在[看图]中显示.
            2.在[我]菜单下,新增一个无限量滚动广告图.
            3.修复在[我->有话要说]里面输入完之后,点击空白处关闭虚拟键盘.
            4.-->重大修改-->修改工具类,获取JSON数据的方法(工具类不在处理,具体的对象转换),代码结构更加清晰.
            5.修复ActionBar的图标大小问题.
            6.修复下载图片的路径,并实时刷新系统图片媒体库.
待优化以下问题:
            1.多次点击ActionBar中的图标按钮,会导致内存溢出.
            2.实现一个留言反馈功能(注意过滤非法字符,可以考虑新增一个图片选择功能..)
            3.实现一个关于界面.
            4.对APP整体颜色进行优化.
            5.对相应代码进行优化或简化.
            6.开源.
2015.9.4 1:40
===================================================================
今天优化了一下界面颜色,有以下改进:
        1.修改[我]界面中的图标颜色.
        2.修正[反馈][关于]中文字不居中的问题.
        3.优化所有颜色,从COLOR文件获取.
        4.优化[看图]底部弹出框颜色.
        5.优化[自动设置桌面]弹窗的颜色.
        6.[看]新增一个复制功能.
       2015.9.8 22:29
2015.9.10  实现了微信的分享,接下来可能需要实现微博,易信的分享功能.
            并且优化了[我]下面的轮播展示.
            2015.9.13 为工具类添加更加详细的注释.并针对代码进行部分优化.
            2015.8.15 2:20 开始开发.
            2015.12.1  0:30 完成开发.
                                  不再开发新的功能,后续几天将进入统一测试.
                                  预计12.5左右发布第一个正式版本.
                                  从8.15开始第一行代码,拖拖拉拉接近3个多月,三大功能模块,最占时间莫过于[分享]部分.
                                  无论如何,这都是一个开始.从刚开始的菜鸟,到如今的菜鸟,虽然不知到进步多少,但只能说,每一次更新
                                  都是一个大的进步.对于下一个版本,将着重优化服务器端,及客户端逻辑代码.
                                  之前的[闲聊]算是一个失败的典型,而当前这个则是完全站立在失败之上.
                                  加油,不为什么!
                                  ->:你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒
* */

一个小网络工具类

别着急,还要发布个Android的网络工具类,使用了Volley.封装了一些简单的操作.源码里面说的很清楚,就不重复解释了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.*;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.*;
import com.android.volley.toolbox.*;
import com.w1520.liangye.app.R;

import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>工具类</p>
 * <p/>
 * <p>此类使用Volley实现网络操作.</p>
 * <p/>
 * <p>当前类封装了:文字,图片的加载,Toast的各种实现,以及</p>
 * <p>保存图片信息到本地,复制文本数据,转换ImageView等操作.</p>About
 * <p/>
 * <p>这个类不可以被继承或重写,并且使用单例模式保证无论任何时候,</p>
 * <p>内存中仅存在一个当前类的实例.</p>
 * <p/>
 * <p><strong>注意:当前类的加载数据,不适合下载,或者需要操作大量数据.</strong></p>
 * <p>
 * <p>
 * Created by puruidong on 8/14/15.
 */
public final class NetworkUtils {

    //网络请求队列实例.
    private static RequestQueue queue;
    //上下文实例.
    private Context context;
    //当前类实例.默认为null
    private static NetworkUtils network = null;


    /**
     * 创建一个实例时,内部初始化.
     * 外部无需关心此构造函数的实现.
     *
     * @param context
     */
    private NetworkUtils(Context context) {
        this.context = context.getApplicationContext();
        if (queue == null) {
            queue = Volley.newRequestQueue(context);
        }
    }

    /**
     * 获取一个当前类的实例.
     * 注意:当前类的实例仅能通过此方式获得.
     * 以保证在内存中仅存在唯一一个当前类的实例.
     *
     * @param context 上下文实例.
     * @return 当前类的实例.
     */
    public static NetworkUtils getInstance(Context context) {
        if (network == null) {
            network = new NetworkUtils(context);
        }
        return network;
    }

    /**
     * 检查网络连接是否可用.
     * 不区分是3G还是WIFI.
     *
     * @return 若网络可用返回true, 反之返回false.
     */
    public boolean isOnline() {
        ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /**
     * 获取文本数据.
     * <p>
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     *
     * @param url          获取数据的url,此方法不检测url的正确性.
     * @param volleyResult 数据加载成功后的回调接口,用于传递数据
     * @param dialog       数据加载提示框.
     */
    public void getTextData(String url, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {
            StringRequest strArray = new StringRequest(url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    volleyResult.onSuccess(response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                }
            }
            );
            queue.add(strArray);
        }
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /*-------------------------------------[POST]提交请求,从网络获取数据---------------------------------------------*/

    /**
     * POST方式提交普通参数数据.
     * --------------------------------------
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     *
     * @param url          获取数据的url,此方法不检测url的正确性.
     * @param params       POST方式提交数据的参数.* :@see {@link android.support.v4.util.ArrayMap}
     * @param volleyResult 数据加载成功后的回调接口,用于传递数据
     * @param dialog       数据加载提示框.
     */
    public void postData(String url, final Map<String, String> params, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {
            StringRequest postRequest = new StringRequest(Request.Method.POST, url,
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            volleyResult.onSuccess(response);
                        }
                    }, null) {
                @Override
                protected Map<String, String> getParams() {
                    //在这里设置需要post的参数
                    return params;
                }
            };
            queue.add(postRequest);
        }
    }


    /**
     * 使用Volley请求文本数据,
     * 实现此接口自行处理相关String数据.
     * 即可获取数据.
     */
    public interface VolleyResultData {
        void onSuccess(String response);
    }


    /*-------------------------------------[POST]提交请求,从网络获取文字数据---------------------------------------------*/



    /*-------------------------------------从网络获取图片数据---------------------------------------------*/


    /**
     * 通过ImageRequest加载并设置网络图片.
     * 注意:方法不对url的有效性进行检测.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * <p>
     * 此方法使用了默认实现的缓存.
     *
     * @param url       图片url
     * @param view      图片控件所在的View对象
     * @param imgViewId ImageView或其它图片展示控件的Id
     * @param dialog    图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, onImageLoaderListener, CustomProgressDialog)}
     */
    public void getImageByImageRequest(String url, final View view, final int imgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {

            ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    //显示加载成功的图片.
                    ImageView img = (ImageView) view.findViewById(imgViewId);
                    img.setImageBitmap(response);
                }
            }, 0, 0, ImageView.ScaleType.CENTER_INSIDE, Bitmap.Config.RGB_565, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    //显示加载失败的图片.
                    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                    ImageView img = (ImageView) view.findViewById(imgViewId);
                    img.setImageResource(R.mipmap.image_loader_error);
                }
            });
            queue.add(request);
        }
    }


    /**
     * 通过ImageRequest加载并传递值给回调接口.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * 注意:方法不对url的有效性进行检测.
     * <p>
     * 此方法使用了默认实现的缓存.
     *
     * @param url            图片url地址.
     * @param loaderlistener 接收Bitmap的回调接口.若请求响应为空,回调接口返回空.
     * @param dialog         图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, View, int, CustomProgressDialog)}
     */
    public void getImageByImageRequest(String url, final onImageLoaderListener loaderlistener, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if (null != dialog) {
                if(null!=dialog) {
                    dialog.hide();
                }
            }
        } else {
            ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    //显示加载成功的图片.
                    loaderlistener.onSuccessImage(response);
                }
            }, 0, 0, null, null, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    //显示加载失败的图片.
                    //showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                    //error.printStackTrace();
                }
            });
            queue.add(request);
        }
    }


    /**
     * 通过NetworkImageView加载
     * 并设置网络图片,若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.此方法会给予一个默认图片及
     * 一个加载失败时候的图片.注意:方法不对url的有效性进行检测.
     * <p>
     * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
     * {@link com.w1520.liangye.utils.BitmapCache}
     *
     * @param url              图片url地址.
     * @param view             图片控件所在的View对象activitd
     * @param netWorkImgViewId ImageView或其它图片展示控件的Id
     * @param dialog           图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageLoader(String, View, int, CustomProgressDialog)}
     */
    public void getImageByNetworkImageView(String url, final View view, final int netWorkImgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
            return ;
        }
        ImageLoader loader = new ImageLoader(queue, new BitmapCache());
        NetworkImageView networkImageView = (NetworkImageView) view.findViewById(netWorkImgViewId);
        networkImageView.setDefaultImageResId(R.mipmap.image_loading);
        networkImageView.setErrorImageResId(R.mipmap.image_loader_error);
        networkImageView.setImageUrl(url, loader);
    }

    /**
     * 通过ImageLoader加载并设置网络图片
     * 注意:方法不对url的有效性进行检测.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * <p>
     * 此方法会设置一个默认加载图片,及一个加载失败时
     * 显示的图片.
     * <p>
     * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
     * {@link com.w1520.liangye.utils.BitmapCache}
     *
     * @param url       图片url
     * @param view      图片控件所在的View对象
     * @param imgViewId ImageView或其它图片展示控件的Id
     * @param dialog    图片加载提示框对象.
     * @return 返回一个响应图片的资源.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByNetworkImageView(String, View, int, CustomProgressDialog)}
     */
    public View getImageByImageLoader(String url, View view, int imgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        }
        ImageLoader loader = new ImageLoader(queue, new BitmapCache());
        ImageView imageView = (ImageView) view.findViewById(imgViewId);
        ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, R.mipmap.image_loading, R.mipmap.image_loader_error);
        loader.get(url, listener);
        return imageView;
    }


    /**
     * 获取网络响应的图片实现此接口,重写接口中的方法即可获得该图片资源.
     * 若网络数据返回为null,则接口中的方法将返回null.
     */
    public interface onImageLoaderListener {
        void onSuccessImage(Bitmap bitmap);
    }

    /*-------------------------------------从网络获取图片数据---------------------------------------------*/


    /**
     * 快速输出Toast,默认显示时长为 {@link  android.widget.Toast#LENGTH_SHORT}.
     * 不支持布局自定义和显示时长自定义.
     *
     * @param message 要显示的信息.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
     */
    public void showToast(String message) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }


    /**
     * 显示Toast,可以使用自定义的布局,自定义显示时长.
     *
     * @param view     一个针对Toast的自定义布局.
     * @param message  要显示的信息.
     * @param duration 显示时长,请使用Toast中的常量进行设置.
     *                 可选值:{@link android.widget.Toast#LENGTH_SHORT}
     *                 {@link android.widget.Toast#LENGTH_LONG}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
     */
    public void showToast(View view, String message, int duration) {
        Toast toast = new Toast(context);
        toast.setView(view);
        toast.setDuration(duration);
        toast.setText(message);
        toast.show();
    }

    /**
     * 显示带有图片,居中的Toast.
     *
     * @param message  要显示的信息.
     * @param duration 显示时长,请使用Toast中的常量进行设置.
     *                 可选值:{@link android.widget.Toast#LENGTH_SHORT}
     *                 {@link android.widget.Toast#LENGTH_LONG}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
     */
    public void showToast(String message, int duration) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View toast_view = inflater.inflate(R.layout.custom_toast_center, null);
        ((TextView) toast_view.findViewById(R.id.toast_message)).setText(message);
        Toast toast = new Toast(context);
        toast.setView(toast_view);
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.setDuration(duration);
        toast.show();
    }


    /**
     * 判断进程是否处于正在运行状态.
     *
     * @return 正在运行返回true, 反之返回false.
     */
    public boolean isProessRunning(String proessName) {
        boolean isRunning = false;
        ActivityManager am = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo info : lists) {
            if (info.processName.equals(proessName)) {
                isRunning = true;
            }
        }
        return isRunning;
    }


    /**
     * 将一张图片(Bitmap资源)设置为手机桌面壁纸.
     * <p>
     * 需要权限:{@code
     * <uses-permission android:name = "android.permission.SET_WALLPAPER"/>}
     *
     * @param bitmap 要设置为桌面的资源.
     * @throws IOException 资源不存在或资源损坏导致的IO异常.
     */
    public void setWallPaper(Bitmap bitmap) throws IOException {
        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
        wallpaperManager.setBitmap(bitmap);
    }

    /**
     * 获取SD卡或者内置存储空间可以保存资源的路径.
     * <em>此处未实现对存储空间是否充足进行判断</em>.
     *
     * @return 返回保存数据的路径, 有SD卡则是SD上的路径, 反之内置存储空间上的路径.
     */
    private String getSDPath() {
        boolean hasSDCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        if (hasSDCard) {
            return Environment.getExternalStorageDirectory().toString() + "/Pictures/liangye";
        } else
            return "/data/data/com.w1520.liangye/liangye";
    }

    /**
     * 判断当前系统LEVEL
     * Android在4.4之后不允许第三方发起系统广播.
     * 所以需要判断版本.
     *
     * @return 当前版本是否大于等于19[4.4].
     */
    private boolean hasKitkat() {
        return Build.VERSION.SDK_INT >= 19;
    }

    /**
     * 刷新媒体库
     *
     * @param filePath
     * @param context
     */
    private void scanPhotos(String filePath) {
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(new File(filePath));
        intent.setData(uri);
        context.sendBroadcast(intent);
    }

    /**
     * 刷新媒体库.
     *
     * @param currFilePath
     */
    private void refreshPicture(String currFilePath) {
       /* ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DATA,currFilePath);
        values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
        context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);*/

        if (hasKitkat()) {
            MediaScannerConnection.scanFile(context,
                    new String[]{currFilePath}, new String[]{"image/*"},
                    new MediaScannerConnection.OnScanCompletedListener() {
                        public void onScanCompleted(String path, Uri uri) {
                            context.sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, uri));
                            context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri));
                        }
                    });
            scanPhotos(currFilePath);
        } else {
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + currFilePath)));
        }
    }


    /**
     * 转换ImageView为Bitmap.
     *
     * @param view 需要转换的View,通常是ImageView或其它类似的类.
     * @return 转换后的Bitmap.
     */
    public Bitmap convertViewToBitmap(View view) {
        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        return bitmap;
    }


    /**
     * <p>保存图片到本地
     * <p/>
     * <p>需要权限:</p>
     * {@code
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
     * }
     *
     * @param imgView 要保存的ImageView对象.
     * @throws IOException           写文件时导致的异常.
     * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(Bitmap bitmap)}
     */
    public void saveImages(ImageView imgView) {
        Bitmap bitmap = convertViewToBitmap(imgView);
        String strPath = getSDPath();
        DateUtils dateutils = DateUtils.getInstance();
        String strFileName = dateutils.getCurrentTimeById() + ".jpg";
        FileOutputStream fos = null;
        try {
            File destDir = new File(strPath);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }
            final String filePaths = strPath + "/" + strFileName;
            File imageFile = new File(filePaths);
            imageFile.createNewFile();
            fos = new FileOutputStream(imageFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
            fos.flush();
            showToast("已成功保存到相册", Toast.LENGTH_SHORT);
            refreshPicture(filePaths);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 保存图片到本地
     * <p/>
     * <p>需要权限:</p>{@code
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>}
     *
     * @param bitmap 要保存的图片.
     * @throws IOException           写文件时导致的异常.
     * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(ImageView imgView)}
     */
    public void saveImages(Bitmap bitmap) {
        String strPath = getSDPath();
        DateUtils dateutils = DateUtils.getInstance();
        String strFileName = dateutils.getCurrentTimeById() + ".jpg";
        FileOutputStream fos = null;
        try {
            File destDir = new File(strPath);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }
            final String filePaths = strPath + "/" + strFileName;
            File imageFile = new File(filePaths);
            imageFile.createNewFile();
            fos = new FileOutputStream(imageFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
            fos.flush();
            showToast("已成功保存到相册", Toast.LENGTH_SHORT);
            refreshPicture(filePaths);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 图片按比例大小压缩方法
     *
     * @param image
     * @return
     * @see {@link http://104zz.iteye.com/blog/1694762 }
     */
    public Bitmap comp(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        if (baos.toByteArray().length / 1024 > 1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;//这里设置高度为800f
        float ww = 480f;//这里设置宽度为480f
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;//be=1表示不缩放
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;//设置缩放比例
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        return compressImage(bitmap);//压缩好比例大小后再进行质量压缩
    }

    /**
     * 质量压缩
     *
     * @param image
     * @return
     */
    private Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
            options -= 10;//每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return bitmap;
    }


    /**
     * 复制文本
     *
     * @param label 标记.
     * @param data  要被复制的文本.
     */
    public void copyText(String label, String data) {
        ClipboardManager clipManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText(label, data);
        clipManager.setPrimaryClip(clip);
        showToast("复制成功", Toast.LENGTH_SHORT);
    }


    /**
     * 判断email格式是否正确
     *
     * @param email
     * @return
     */
    public boolean isEmail(String email) {
        String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
        Pattern p = Pattern.compile(str);
        Matcher m = p.matcher(email);
        return m.matches();
    }


}

可能是因为代码的原因,好久都没”写”这么长的文了…偶然有点不习惯……

嗯,没错–你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒

Python:操作MongoDB工具实例

前提

Python 连接MongoDB.
目前实现:
增删改查(大部分已经实例运行过).
对于聚合函数的实现并没有细化(没有提供相关示例).
官方提供了更多的方法,目前只集成了常用的部分函数(其余可以去官方网站参考).

python Logo
image-2366

源码

代码已经托管在Github[访问].

PS:2015.12.19 更新//修复数据库表创建的问题//修复数据库集合的使用问题,下面的源码已经替换为最新.
PS:2015.12.28 更新//修复查询分页的问题.
PS:2016.1.6 更新//优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.并增加一项功能:设定返回列,或排除返回列.
(文件名:DaoUtil.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#!/usr/bin/env python3
# !-*-coding=utf-8-*-

"""

Python 连接MongoDB.

目前实现:
    增删改查(大部分已经实例运行过).
    对于聚合函数的实现并没有细化(没有提供相关示例).
    官方提供了更多的方法,目前只集成了常用的部分函数(其余可以去官方网站参考).

依赖:
    Python3+
    MongoDB3+
    pymongo3+

参考:
MongoDB官方文档:
        https://docs.mongodb.org/getting-started/python/

pymongo下载提示页面:
        https://pypi.python.org/pypi/pymongo/


相关运行信息:
        运行平台:Linux
        MongoDB版本:3.03
        Python版本:Python 3.4.3
        pymongo(Python连接MongoDB的驱动)版本:3.2

author : prd
version : 0.9 2015.12.11
              2015.12.18 修复几个问题:
                                修改原有的获取数据库名称方式不正确.
                                修改原有获取数据库集合方式的问题.
              2015.12.28 修复一个问题:
                                在查询时,进行分页之前的方式,无法进行正常分页.
                                之前的错误原因(现在已经修复):满足其一条件将不再往下执行,现在已修改方式.
                                举例:
                                    错误的:
                                        if page:
                                            pass
                                        if pageSize:
                                            pass
                                        if page and pageSize:
                                            pass
                                    # 若传递page和pageSize也无法正常获取数据.因为判断完第一个将不再继续执行.

                                    正确的:
                                        if page and pageSize:
                                            pass
                                        if page:
                                            pass
                                        if pageSize:
                                            pass
              2016.1.6 更新:
                                 优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.
                                并增加一项功能:设定返回列,或排除返回列.
                                更多细节:http://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find
              2016.1.10 v2.0.0 更新:
                                    为更新操作,新增参数(可选,若文档不存在时,是否进行新增).
                                    查询时的参数:dataLimit,修改默认为0(之前默认为None会导致错误!).

email : pruidong#gmail.com

"""
import pymongo


class PyDaoUtil(object):
    dbname = None
    collection = None

    def __init__(self, dbname, collection):
        self.dbname = dbname
        self.collection = collection

    """ 返回MongoDB客户端对象. """

    def getClient(self):
        return pymongo.MongoClient("localhost", 27017)

    """ 返回MongoDB的一个数据库 """

    def getDB(self):
        client = self.getClient()
        # 修改原有的获取数据库名称方式不正确. TODO 2015.12.18
        dbdatabase = client['%s' % (self.dbname)]
        return dbdatabase

    """
    添加数据

    可新增单条或者多条.

    单条:json对象,
    多条:json数组.

     """

    def insertData(self, bsonData):
        if self.dbname:
            db = self.getDB()
            collections = self.collection
            if isinstance(bsonData, list):
                # 修改原有获取数据库集合方式的问题. TODO 2015.12.18 get_collection(collections)
                result = db.get_collection(collections).insert_many(bsonData)
                return result.inserted_ids
            return db.get_collection(collections).insert_one(bsonData).inserted_id
        else:
            return None

    """
    删除数据!!!!

    关键字参数:
    oneDeleteFilter->单条删除
    manyDeleteFilter->多条删除

    """

    def deleteData(self, **kwargs):
        if self.dbname:
            collections = self.collection
            db = self.getDB()

            def deleteOne(self, oneDeleteFilter=None):  # 单个删除
                result = db.get_collection(collections).delete_one(oneDeleteFilter)
                return result.deleted_count

            def deleteMany(self, manyDeleteFilter=None):  # 全部删除
                result = db.get_collection(collections).delete_many(manyDeleteFilter)
                return result.deleted_count

            onedel = kwargs.get("oneDeleteFilter", "")
            manydel = kwargs.get("manyDeleteFilter", "")
            if onedel:
                return deleteOne(self, **kwargs)
            elif manydel:
                return deleteMany(self, **kwargs)

    """
    更新数据

    oldData:过滤原有数据,

    关键字参数:

    oneUpdate->单条更新
    oneUpsert->True:如果文档不存在,执行插入.
               (默认)False:如果文档不存在不执行执行操作.
    manyUpdate->多条更新(如果过滤条件结果存在多条)
    manUpsert->True:如果文档不存在,执行插入.
               (默认)False:如果文档不存在不执行执行操作.

    """

    def updateData(self, oldData=None, **kwargs):
        if self.dbname:
            collections = self.collection
            db = self.getDB()

            def updateOne(self, oneOldData=None, oneUpdate=None,oneUpsert=False):  # 单个更新
                result = db.get_collection(collections).update_one(filter=oneOldData,update=oneUpdate,upsert=oneUpsert)
                return result.matched_count

            def updateMany(self, manyOldData, manyUpdate=None,manUpsert=False):  # 全部更新
                result = db.get_collection(collections).update_many(filter=manyOldData,update=manyUpdate,upsert=manUpsert)
                return result.matched_count

            if oldData:
                oneup = kwargs.get("oneUpdate", "")
                manyup = kwargs.get("manyUpdate", "")
                if oneup:
                    return updateOne(self, oldData, **kwargs)
                elif manyup:
                    return updateMany(self, oldData, **kwargs)

    """
    查询数据

    关键字参数:

    dataLimit - > 限定最多返回多少条.
    dataSkip  - > 跳过多少条.
    dataQuery - > 查询限定条件
    dataSortQuery -> 排序条件
    dataProjection ->  返回指定的列或者排除指定的列

    ----------------------------------------------------------------

    oneDataQuery -> 查询单条条件 -- 弃用!!!!

    """

    def findAll(self, **kwargs):
        if self.dbname:
            collections = self.collection
            db = self.getDB()

            def findAllDataQuery(self, dataLimit=0, dataSkip=0, dataQuery=None, dataSortQuery=None,
                                 dataProjection=None):
                '''
                TODO :
                    2016.1.6 更新:
                         优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.
                         并增加一项功能:设定返回列,或排除返回列.
                         更多细节:http://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find
                '''
                return db.get_collection(collections).find(filter=dataQuery, projection=dataProjection, skip=dataSkip,
                                                           limit=dataLimit, sort=dataSortQuery)

            return findAllDataQuery(self, **kwargs)

    """
    聚合函数.

    具体参考:
    https://docs.mongodb.org/manual/meta/aggregation-quick-reference/

    """

    def aggregation(self, aggreg):
        if self.dbname:
            collections = self.collection
            db = self.getDB()
            return db.get_collection(collections).aggregate(aggreg)

    """
    统计数据库中的数量,

    关键字参数参考:
    http://api.mongodb.org/python/current/api
    /pymongo/collection.html
    ?_ga=1.21101243.1811534224.1449817089#pymongo.collection.Collection.count

    """

    def countData(self, countQuery=None, **kwargs):
        if self.dbname:
            collections = self.collection
            db = self.getDB()
            if countQuery and kwargs:
                return db.get_collection(collections).count(countQuery, kwargs)
            elif countQuery:
                return db.get_collection(collections).count(countQuery)
            elif kwargs:
                return db.get_collection(collections).count(filter=None, **kwargs)
            else:
                return db.get_collection(collections).count()

    """ 删除集合中所有数据!!!!谨慎调用!!!! """

    def dropAllData(self, dataPassword=None):
        if self.dbname:
            collections = self.collection
            db = self.getDB()
            if dataPassword and isinstance(dataPassword, list):
                db.get_collection(collections).drop()


if __name__ == '__main__':
    # dao = PyDaoUtil("test", "my_collection")
    # # 新增数据.
    # dao.insertData(
    #     {
    #         "title": "MongoDB Overview",
    #         "description": "MongoDB is no sql database",
    #         "by": "百度一下你就知道",
    #         "url": "http://www.baidu.com",
    #         "tags": [
    #             "mongodb",
    #             "database",
    #             "NoSQL"
    #         ],
    #         "likes": 100
    #     })#单个新增.

    # print(dao.insertData([{"a": "67"}, {"a": "67"}, {"x": "5"}]))  # 批量新增.

    # 新增数据.END.

    # 查询数据

    # print(dao.findAll())
    # print(dao.findAll(dataLimit=2, dataSkip=1))
    # print(dao.findAll(dataSkip=1))
    # print(dao.findAll(dataLimit=2))
    # print(dao.findAll(oneDataQuery={"x": "67"}))
    # print(dao.findAll(dataSortQuery=[("x", "55")]))
    # print(dao.findAll(dataQuery={"x": "55"}, dataSortQuery=[("x", "55")]))

    # 查询数据END.

    # 删除数据

    # print(dao.deleteData(manyDeleteFilter={"a": "10000"}))
    # print(dao.deleteData(oneDeleteFilter={"a": "10000"}))


    # 删除数据END.

    # 更新数据

    # print(dao.insertData([{"a": "67"}, {"a": "67"}, {"x": "5"}]))  # 批量新增.
    # print(dao.updateData(oldData={"x": "5"}, oneUpdate={'$set': {"x": "300"}}))
    # print(dao.updateData(oldData={"a": "67"}, manyUpdate={'$set': {"a": "10000"}}))

    # 更新数据END.

    # 清除所有数据[不限定条件,直接删除]
    """

    警告:会删除所有数据,请谨慎调用!!!!!!!!!!!!!!!!!!


    """
    # 清除所有数据END.
    # dao.dropAllData(dataPassword=[123])
    # 查询显示所有数据.
    # print(dao.insertData([{"测试": "测试"}, {"a": "67"}, {"x": "5"}]))

    # print(dao.findAll())
    # 获得数据库中一共有多少条数据.
    # print(dao.countData())
    # print(dao.countData(countQuery={"a":"10000"}))

Python:关键字参数的使用

起源

下午在写一个Python连接MongoDB的工具(使用pymongo 3.2).并且使用Python 3.4.但是遇到一个问题,就是调用的时候,发现报错.

报错的程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 def findAll(self, **kwargs):
        print(kwargs)
        limit = kwargs.get('limit', None)
        skip = kwargs.get('skip', None)
        query = kwargs.get('query', None)
        sortQuery = kwargs.get('sortQuery', None)
        if self.dbname:
            collections = self.collection
            db = self.getDB()
            print(limit, skip)
            resultData = None

            def findPage(self, limit, skip):  # 分页查询数据
                print(limit, skip)
                if isinstance(limit, int) and isinstance(skip, int):
                    resultData = db.collections.find().limit(limit).skip(skip)
                else:
                    def findAllData(self, query, sortQuery):  # 查询数据
                        print(self.dbname + "----" + self.collection)
                        resultData = db.collections.find(query).sort(sortQuery)

            return resultData

调用:

1
2
3
print(dao.findAll(limit=2, skip=1))
print(dao.findAll(query={"x": "55"}))
print(dao.findAll(query={"x": "55"}, sortQuery={"x": "55"}))

报错:

Traceback (most recent call last):
File “DaoUtil.py”, line 84, in
print([item for item in dao.findAll(limit=2, skip=1)])
TypeError: ‘NoneType’ object is not iterable

写这段程序的时候,参照了网上一个例子,但是忽略了一个重要的部分{也是最重要的部分}.所以就报错了.

python Logo
image-2360

修改后的程序

下面的程序是最新的,可以运行的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
""" 查询数据 """

    def findAll(self, **kwargs):
        # print(kwargs)
        if self.dbname:
            collections = self.collection
            db = self.getDB()

            def findPage(self, dataLimit=None, dataSkip=None):  # 分页查询数据
                if dataLimit and isinstance(dataLimit, int):  # limit 最多只显示多少.
                    return db.collections.find().limit(dataLimit)

                if dataSkip and isinstance(dataSkip, int):  # 跳过多少条
                    return db.collections.find().limit(10).skip(dataSkip)

                if isinstance(dataLimit, int) and isinstance(dataSkip, int):
                    return db.collections.find().limit(dataLimit).skip(dataSkip)

            def findAllData(self, dataQuery=None, dataSortQuery=None):  # 查询数据
                if dataQuery:
                    return db.collections.find(dataQuery)
                else:
                    return db.collections.find()

                if dataSortQuery:
                    return db.collections.find().sort(dataSortQuery)
                if dataQuery and dataSortQuery:
                    return db.collections.find(dataQuery).sort(dataSortQuery)

            if len(kwargs) == 2:
                limit = kwargs.get('dataLimit', "")
                skip = kwargs.get('dataSkip', "")
                query = kwargs.get('dataQuery', "")
                sortQuery = kwargs.get('dataSortQuery', "")
                if limit or skip:
                    return findPage(self, **kwargs)
                elif query or sortQuery:
                    return findAllData(self, **kwargs)
            else:
                return findAllData(self, "", "")

调用如下:

1
2
3
4
5
6
7
    print([item for item in dao.findAll()])
    print([item for item in dao.findAll(dataLimit=2, dataSkip=1)])
    print([item for item in dao.findAll( dataSkip=1)])
    print([item for item in dao.findAll(dataLimit=2)])
    print([item for item in dao.findAll(dataQuery={"x": "55"})])
    print([item for item in dao.findAll(dataSortQuery=[("x", "55")])])
    print([item for item in dao.findAll(dataQuery={"x": "55"}, dataSortQuery=[("x", "55")])])

输出MongoDB里面的数据.

最重要,工具目前暂未完成,剩余部分暂时不发了.等写完了再发整个工具.(不会安装MongoDB?自己搜索下.)

PS:工具已经完整发布(地址).