tornado学习笔记(四)

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

posted @ 2015-05-17 19:55  Jolin123  阅读(440)  评论(0编辑  收藏  举报