About
RSS

Bit Focus


GAE 速成简易博客 - 更多数据库操作

Posted at 2012-01-20 11:43:58 | Updated at 2024-04-19 13:24:54

上节回顾 - 表单处理与基本的数据库操作

现在首页能显示文章列表了, 但是
那么, 现在就开始修改数据库吧.

为文章加上 ID 和日期

添加属性

修改 model.py
class Post(db.Model):
    pid = db.IntegerProperty()
    title = db.StringProperty(multiline=False)
    content = db.TextProperty()
    date = db.DateTimeProperty(auto_now_add=True)

def put_post(title, content):
    其中 pid 表示 post id, 是一篇文章的唯一标识; date 是文章的发布时间, 它被设置为对象被存入数据库时自动设置为当前时间 (auto_now_add=True).
    在 GAE 存储中, 并没有类似 auto_increment 的设置, 因此 pid 的管理需手动进行. 在数据库中, GAE 也有给每个对象设置一个全局唯一的 id, 可以通过如 post.key().id() 来获取, 但是这样获取的 id 值在发布服务器上没有规律可言, 不具备有序性, 不建议使用.

按 ID 排序查询和自增 ID

刚刚为 Post 添加的两个属性中, date 是会自动添加到数据库中的, 但 pid 并不会, 得手动给加上. 想要实现自增 ID 的功能, 一个简单的思路是, 从数据库中取出 pid 最大的那篇, 在它的基础上 +1 赋值给新文章即可. 那么继续修改 model.py
def next_post_id():
    posts = db.GqlQuery('SELECT * FROM Post ORDER BY pid DESC')
    return 0 if posts.count() == 0 else posts[0].pid + 1

def put_post(title, content):
    post = Post()
    post.pid = next_post_id()
    post.title = title
    post.content = content
    post.put()
这里 GQL 中的 ORDER BY pid DESC 表示按照 pid 排序, 而且是降序排列.

另外, 还得修改 add_post.py 里的 AddPostHandler 不能让它乱来了, 而应该改为调用 put_post
class AddPostHandler(webapp.RequestHandler):
    def post(self):
      # new_post = model.Post()
      # new_post.title = self.request.get('title')
      # new_post.content = self.request.get('content')
      # new_post.put()
        model.put_post(self.request.get('title'), self.request.get('content'))
        self.redirect('/add_post')
现在再来试试添加一篇文章吧.

警告 next_post_id 在并发环境下是不安全的, 由于现在只是搭建一个简单的博客, 因此这样写也不会出什么岔子. 此方案绝不可以用于有并发需求的生产环境.

    嗯, 数据库中原来有文章, 但那些文章都是没有 pid 的, 从现在开始, 新建的文章 pid 将是多少呢? 答案是, 从 0 开始, 依次增加. 看 next_post_id 中的 GQL 语句, 它要求让 Post 根据 pid 降序排列, 而以前的文章是没有 pid 这一列的, 于是 GAE 做了一件出乎意料的事情: 这次查询不会返回任何结果. 也就是说, 代码中的条件 posts.count() == 0 成立了.

修改旧数据

    之前加入数据库的文章没有 piddate, 现在得人肉给它们加上, 否则按时间排序就会漏掉这些文章, 按 pid 查看也无法看到它们.
    在本地搭建的开发服务器的后台入口地址为 http://localhost:8080/_ah/admin, 进入之后, 可以在 Data Viewer 中看到数据表. 选择 Post, 然后点击 List Entities 按钮, 便会列出所有的文章.
    每个数据库中的条目会包含一个 Key 域, 并且在后台页面显示为超链接, 点击该超链接, 即可进入条目修改页面. 修改好了后, 点击 Save Changes 按钮保存.

    旧的条目可能无法添加域, 这样的话, 可以选择 Create New Entity 来手工加入, 并通过 Delete 删掉旧的. 如果数据库中旧数据不多的话, 这样还是很方便的.

按时间排序查看

之前已经领教了按 pid 降序排列查询了, 按时间降序也差不多. 修改 index.py
class Index(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
        posts = db.GqlQuery('SELECT * FROM Post ORDER BY date DESC')
        self.response.out.write(template.render(path, {
                'posts': posts,
            }))
现在访问首页, 文章就都是由新到旧排序过的了.

按 ID 查看一篇文章

首先还是得新建一个 HTML 模板 templates/single_post.html
<html>
<head><title>{{ post.title }} - My Blog</title></head>
<body>
<h1>{{ post.title }}</h1>
<hr>
<p>{{ post.content }}</p>
接下来是请求处理器, 新建源码文件 single_post.py
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext import db
import os

class SinglePost(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__),
                            'templates/single_post.html')
        posts = db.GqlQuery('SELECT * FROM Post WHERE pid = :1',
                            int(self.request.get('id')))
        self.response.out.write(template.render(path, {
                'post': posts[0],
            }))
    这里 GQL 较之前见到的都不一样, 它包含了一个额外的整数参数. GQL 中的 WHERE 子句类似 SQL 中的 WHERE, 后面紧跟查询条件; 在条件中提到 pid = :1, 也就是说根据 pid 查询, 而 :1 则表示接下来的第一个参数, 也就是 int(self.request.get('id')).
    视图传递给模板文件的参数将只含有一篇文章, 但查询得到的结果仍然是一个集合, 因此需要从中选出首个作为模板文件参数.
    然后修改 main.py, 加上几行
import single_post

if __name__ == '__main__':
    application = webapp.WSGIApplication([
        ('/', index.Index),
        ('/p', single_post.SinglePost),
        ('/add_post', add_post.AddPostEntry),
        ('/add_post_do', add_post.AddPostHandler),
    ], debug=True)
    wsgiref.handlers.CGIHandler().run(application)
/p 映射到 SinglePost 处理. 那么, 现在访问 http://localhost:8080/p?id=0 则可以浏览 pid 为 0 的文章了.

不过这还不够, 再来修改一下 templates/index.html
{% for post in posts %}
<h1><a href='/p?id={{ post.pid }}'>{{ post.title }}</a></h1>
<p>{{ post.content }}</p>
{% endfor %}
此番一折腾, 在首页就可以通过点击标题链接进入某一篇文章浏览了.

下节预告 - 重构与 404 页面

Post tags:   Web Server  Python  Tutorial  Google AppEngine

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site