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'] }}"