4 Databases
1 pymongo
pymongo是python用来连接MongoDB数据库的一个库,可以pip安装:pip install pymongo
命令行mongod
运行mongodb服务器后,可以使用pymongo来进行连接
>>> import pymongo >>> client = pymongo.MongoClient('localhost', 27017) >>> db = client.emample # db = client['example']
这样就有了可以使用数据库db
collection
:一个数据库可以有任意多个collections(集合). 一个collection是放一些相关文档(documents)的地方,我们使用的大多数mongodb操作都是在collections上进行
查看数据库中的集合列表:
>>> db.collection_names() []
创建一个集合并插入一个键值对:
>>> widgets = db.widgets # widgets = db['widgets'] >>> widgets Collection(Database(MongoClient('localhost', 27017), u'example'), u'widgets') >>> widgets.insert({"foo": "bar"}) ObjectId('...') >>> db.colection_names() [u'system.indexes', u'widgets']
可以发现访问一个集合有两种方法,一种是作为数据库对象的一个属性,一种是以字典的形式通过集合名字作为关键字
mongodb的集合以document文档的形式来存储数据,如每次在集合中insert的一个字典就是一个文档,返回的ObjectId就是这个文档对象的id.
find_one
函数可以在集合中查找一个文档对象,参数是这个字典中的一个键值对,返回整个文档,也就是一个字典的形式,支持字典的操作,eg,
>>> widgets.insert({"name": "flibnip", "description": "grade-A industrial flibnip", "quantity": 3}) ObjectId(...) >>> widgets.find_one({"name": "flibnip"}) {u'description':u'grade-A', u'_id': ObjectId(...), u'name':..., u'quantity':...} >>> doc = widgets.find_one({"name": "flibnip"}) >>> type(doc) <type 'dict'> >>> print doc['name'] flibnip
直接在返回的字典上对数据进行修改,不会影响到数据库中的值,只有通过save
函数才能将修改写到数据库
>>> doc['quantity'] = 4 >>> db.widgets.save(doc) >>> print widgets.find_one({"name": "flibnip"})['quantity'] 4
在增加一些文档
>>> widgets.insert({"name": "smorkeg", "description": "for external use only", "quantity": 4}) ObjectId('4eadaa5c136fc4aa41000002') >>> widgets.insert({"name": "clobbasker", "description": "properties available on request", "quantity": 2}) ObjectId('4eadad79136fc4aa41000003')
find
函数可以查找拥有某些键值对的一组文档,不带参数则返回集合中的所有文档
>>> for doc in widgets.find(): >>> print doc {u'_id': ObjectId('4eada0b5136fc4aa41000000'), u'foo': u'bar'} {u'description': u'grade-A', u'_id': ObjectId('4eada3a4136fc4aa41000001'), u'name': u'flibnip', u'quantity': 4} {u'description': u'for external use only', u'_id': ObjectId('4eadaa5c136fc4aa41000002'), u'name': u'smorkeg', u'quantity': 4} {u'description': u'properties available on request', u'_id': ObjectId('4eadad79136fc4aa41000003'), u'name': u'clobbasker', u'quantity': 2} >>> for doc in widgets.find({"quantity":4}) ... print doc {u'description': u'grade-A', u'_id': ObjectId('4eada3a4136fc4aa41000001'), u'name': u'flibnip', u'quantity': 4} {u'description': u'for external use only', u'_id': ObjectId('4eadaa5c136fc4aa41000002'), u'name': u'smorkeg', u'quantity': 4}
remove
函数从一个集合中删除满足条件的所有文档,用法与find_one,find类似
2 simple word dictionary
创建一个web的简单字典,用mongodb作为数据库,可以通过url来进行查询,eg
$ curl http://localhost:8000/oarlock {definition: "A device attached to a rowboat to hold the oars in place", "word": "oarlock"}
使用post可以创建一个单词,如果该单词已经存在则修改它的定义
$ curl -d definition=a+leg+shirt http://localhost:8000/pants {"definition": "a leg shirt", "word": "pants"}
definitions_readwrite.py
import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import pymongo from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class Application(tornado.web.Application): def __init__(self): handlers = [(r"/(w+)", WordHandler)] conn = pymongo.MongoClient("localhost", 27017) self.db = conn.example tornado.web.Application.__init__(self, handlers, debug=True) class WordHandler(tornado.web.RequestHandler): def get(self, word): coll = self.application.db.words word_doc = coll.find_one({"word": word}) if word_doc: del word_doc["_id"] self.write(word_doc) else: self.set_status(404) self.write({"error": "word no found"}) def post(self, word): definition = self.get_argument("definition") coll = self.application.db.words word_doc = coll.find_one({"word": word}) if word_doc: word_doc["definition"] = definition coll.save(word_doc) else: word_doc = {"word":word, "definition": definition} coll.insert(word_doc) del word_doc["_id"] self.write(word_doc) if __name__ == "__main__": tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()
先看服务器是怎样链接数据库的,在Application类的初始化中,定义了一个db属性,这就是我们使用的数据库
conn = pymongo.Connection("localhost", 27017) self.db = conn.example
有了这个db属性后,我们就可以通过RequestHandler类的self.application.db
来进行访问
在WordHandler中,定义了get和post两种方式,get是简单的查询某个单词,在write(word_doc)之前要去掉字典中的'_id',因为write会自动把字典转化为JSON. post模式先通过self.get_argument("definition")获取单词定义,然后查询该单词是否存在,已经存在就用save函数保存新的修改,没有存在就用insert函数插入新单词
3 Burts books
read from database
前面的Burt's的例子,现在增加mongodb数据库的操作
mport os.path import tornado.auth import tornado.escape import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options import pymongo define("port", default=8000, help="run on the given port", type=int) class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", MainHandler), (r"/recommended/", RecommendedHandler), ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), "templates"), static_path=os.path.join(os.path.dirname(__file__), "static"), ui_modules={"Book": BookModule}, debug=True, ) conn = pymongo.MongoClient("localhost", 27017) self.db = conn["bookstore"] tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render( "index.html", page_title="Burt's Books | Home", header_text="Welcome to Burt's Books!", ) class RecommendedHandler(tornado.web.RequestHandler): def get(self): coll = self.application.db.books books = coll.find() self.render( "recommended.html", page_title="Burt's Books | Recommended Reading", header_text="Recommended Reading", books=books ) class BookModule(tornado.web.UIModule): def render(self, book): return self.render_string( "modules/book.html", book=book, ) def css_files(self): return "css/recommended.css" def javascript_files(self): return "js/recommended.js" def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start() if __name__ == "__main__": main()
主要就是在Application类里面增加数据库self.db = conn.bookstore,在RecommendedHandler中,通过books = coll.find()的到所有书目的字典,然后进行展示即可,其中每一个书目都用模块BookModule来处理。现在的功能只是可以显示数据库中已有的书目
editing and adding books
增加一个表格形式给用户增加书目,表格提交后需要一个handler来处理,将数据写进数据库中
实现类BookEditHandler,有get和post两种模式,关联的url如下:
handlers = [ ... (r"/edit/(0-9Xx-]+)", BookEditHandler), (r"/add", BookEditHandler), ]
用户要增加新书目时可以访问localhost:8000/add
,会返回填写表单的网页,然后点击提交后会以post的方式再次访问这个url,这时BookEditHandler的post类就会处理,将数据写进数据库。或者用户要修改某书目的数据,以get的访问如/localhost:8000/edit/0-123-456
,edit后面为书目的isbn,就可以返回该书目的表单修改界面,里面保留原本的书目数据,修改后点击提交也会重新以post的形式访问这个url来更新数据
下面是BookEditHandler类的实现:
class BookEditHandler(tornado.web.RequestHandler): def get(self, isbn=None): book = dict() if isbn: coll = self.application.db.books book = coll.find_one({"isbn": isbn}) self.render("book_edit.html", page_title="Burt's Books", header_text="Edit book", book=book) def post(self, isbn=None): import time book_fields = ['isbn', 'title', 'subtitle', 'image', 'author', 'date_released', 'description'] coll = self.application.db.books book = dict() if isbn: book = coll.find_one({"isbn": isbn}) for key in book_fields: book[key] = self.get_argument(key, None) if isbn: coll.save(book) else: book['date_added'] = int(time.time()) coll.insert(book) self.redirect("/recommended/")
get函数中,首先判断isbn,如果存在说明是要修改书目内容,没有则是增加新书目。在数据库中find_one根据isbn查找数据然后返回book_edit.html的表单填写页面
post函数中,也是通过isbn判断是否是新书目,最后分别用save或者insert来写进数据库,然后将用户重定向到/recommended/的页面来查看修改结果
book_edit.html
{% extends "main.html" %} {% autoescape None %} {% block body %} <form method="POST"> ISBN <input type="text" name="isbn" value="{{ book.get('isbn', '') }}"><br> Title <input type="text" name="title" value="{{ book.get('title', '') }}"><br> Subtitle <input type="text" name="subtitle" value="{{ book.get('subtitle', '') }}"><br> Image <input type="text" name="image" value="{{ book.get('image', '') }}"><br> Author <input type="text" name="author" value="{{ book.get('author', '') }}"><br> Date released <input type="text" name="date_released" value="{{ book.get('date_released', '') }}"><br> Description<br> <textarea name="description" rows="5" cols="40">{% raw book.get('description', '')%}</textarea><br> <input type="submit" value="Save"> </form> {% end %}
这里书目的每项属性都用get函数来获取,如果字典中没有这个感觉在则设置为'',表单每项的name都是和数据库中的属性名相同,方便提交之后写进数据库
最后在recommended的页面中,给显示的每个数目增加一个edit链接,点击可以直接进入修改页面
<div class="book" style="overflow: auto"> <h3 class="book_title">{{ book["title"] }}</h3> {% if book["subtitle"] != "" %} <h4 class="book_subtitle">{{ book["subtitle"] }}</h4> {% end %} <img src="{{ book["image"] }}" class="book_image"/> <div class="book_details"> <div class="book_date_released">Released: {{ book["date_released"]}}</div> <div class="book_date_added">Added: {{ locale.format_date(book["date_added"], relative=False) }}</div> <h5>Description:</h5> <div class="book_body">{% raw book["description"] %}</div> <p><a href="/edit/{{ book['isbn'] }}">Edit</a></p> </div> </div>
链接是href="/edit/{{ book['isbn'] }}"