Python实战项目之博客网站搭建

news/2024/5/9 20:14:40/文章来源:https://zhuxiaoxia.blog.csdn.net/article/details/79175646

参考:廖雪峰网站

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

http://blog.csdn.net/lm409/article/details/63687293


Day 1 - 搭建开发环境

搭建开发环境

首先,确认系统安装的Python版本是3.5.x:

$ python3 --version
Python 3.5.1

然后,用pip安装开发Web App需要的第三方库:

异步框架aiohttp:

$pip3 install aiohttp

前端模板引擎jinja2:

$ pip3 install jinja2

MySQL 5.x数据库,从官方网站下载并安装,安装完毕后,请务必牢记root口令。为避免遗忘口令,建议直接把root口令设置为root

MySQL的Python异步驱动程序aiomysql:

$ pip3 install aiomysql

项目结构

选择一个工作目录,然后,我们建立如下的目录结构:

awesome-python3-webapp/  <-- 根目录
|
+- backup/               <-- 备份目录
|
+- conf/                 <-- 配置文件
|
+- dist/                 <-- 打包目录
|
+- www/                  <-- Web目录,存放.py文件
|  |
|  +- static/            <-- 存放静态文件
|  |
|  +- templates/         <-- 存放模板文件
|
+- ios/                  <-- 存放iOS App工程
|
+- LICENSE               <-- 代码LICENSE

创建好项目的目录结构后,建议同时建立git仓库并同步至GitHub,保证代码修改的安全。了解git和GitHub的用法:Git教程。

开发工具

自备,推荐用Sublime Text,请参考使用文本编辑器。

Day 2 - 编写Web App骨架

由于我们的Web App建立在asyncio的基础上,因此用aiohttp写一个基本的app.py

import logging; logging.basicConfig(level=logging.INFO)import asyncio, os, json, time
from datetime import datetimefrom aiohttp import webdef index(request):return web.Response(body=b'<h1>Awesome</h1>')@asyncio.coroutine
def init(loop):app = web.Application(loop=loop)app.router.add_route('GET', '/', index)srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9000)logging.info('server started at http://127.0.0.1:9000...')return srvloop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

运行python app.py,Web App将在9000端口监听HTTP请求,并且对首页/进行响应:

$ python3 app.py
INFO:root:server started at http://127.0.0.1:9000...

这里简单地返回一个Awesome字符串,在浏览器中可以看到效果:

awesome-home

这说明Web App骨架已经搭好了,可以进一步往里面添加更多的东西。

Day 3 - 编写ORM


在一个Web App中,所有数据,包括用户信息、发布的日志、评论等,都存储在数据库中。在awesome-python3-webapp中,选择MySQL作为数据库。

Web App里面有很多地方都要访问数据库。访问数据库需要创建数据库连接、游标对象,然后执行SQL语句,最后处理异常,清理资源。这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用。

所以,首先把常用的SELECT、INSERT、UPDATE和DELETE操作用函数封装起来。

由于Web框架使用了基于asyncio的aiohttp,这是基于协程的异步模型。在协程中,不能调用普通的同步IO操作,因为所有用户都是由一个线程服务的,协程的执行速度必须非常快,才能处理大量用户的请求。而耗时的IO操作不能在协程中以同步的方式调用,否则,等待一个IO操作时,系统无法响应任何其他用户。

这就是异步编程的一个原则:一旦决定使用异步,则系统每一层都必须是异步,“开弓没有回头箭”。

幸运的是aiomysql为MySQL数据库提供了异步IO的驱动。

创建连接池

我们需要创建一个全局的连接池,每个HTTP请求都可以从连接池中直接获取数据库连接。使用连接池的好处是不必频繁地打开和关闭数据库连接,而是能复用就尽量复用。

连接池由全局变量__pool存储,缺省情况下将编码设置为utf8,自动提交事务:

@asyncio.coroutine
def create_pool(loop, **kw):logging.info('create database connection pool...')global __pool__pool = yield from aiomysql.create_pool(host=kw.get('host', 'localhost'),port=kw.get('port', 3306),user=kw['root'],password=kw['root'],db=kw['db'],charset=kw.get('charset', 'utf8'),autocommit=kw.get('autocommit', True),maxsize=kw.get('maxsize', 10),minsize=kw.get('minsize', 1),loop=loop)

Select

要执行SELECT语句,我们用select函数执行,需要传入SQL语句和SQL参数:

@asyncio.coroutine
def select(sql, args, size=None):log(sql, args)global __poolwith (yield from __pool) as conn:cur = yield from conn.cursor(aiomysql.DictCursor)yield from cur.execute(sql.replace('?', '%s'), args or ())if size:rs = yield from cur.fetchmany(size)else:rs = yield from cur.fetchall()yield from cur.close()logging.info('rows returned: %s' % len(rs))return rs

SQL语句的占位符是?,而MySQL的占位符是%sselect()函数在内部自动替换。注意要始终坚持使用带参数的SQL,而不是自己拼接SQL字符串,这样可以防止SQL注入攻击

注意到yield from将调用一个子协程(也就是在一个协程中调用另一个协程)并直接获得子协程的返回结果。

如果传入size参数,就通过fetchmany()获取最多指定数量的记录,否则,通过fetchall()获取所有记录。

Insert, Update, Delete

要执行INSERT、UPDATE、DELETE语句,可以定义一个通用的execute()函数,因为这3种SQL的执行都需要相同的参数,以及返回一个整数表示影响的行数:

@asyncio.coroutine
def execute(sql, args):log(sql)with (yield from __pool) as conn:try:cur = yield from conn.cursor()yield from cur.execute(sql.replace('?', '%s'), args)affected = cur.rowcountyield from cur.close()except BaseException as e:raisereturn affected

execute()函数和select()函数所不同的是,cursor对象不返回结果集,而是通过rowcount返回结果数。

ORM

有了基本的select()execute()函数,我们就可以开始编写一个简单的ORM了。

设计ORM需要从上层调用者角度来设计。

我们先考虑如何定义一个User对象,然后把数据库表users和它关联起来。

from orm import Model, StringField, IntegerFieldclass User(Model):__table__ = 'users'id = IntegerField(primary_key=True)name = StringField()

注意到定义在User类中的__table__idname是类的属性,不是实例的属性。所以,在类级别上定义的属性用来描述User对象和表的映射关系,而实例属性必须通过__init__()方法去初始化,所以两者互不干扰:

# 创建实例:
user = User(id=123, name='Michael')
# 存入数据库:
user.insert()
# 查询所有User对象:
users = User.findAll()

定义Model

首先要定义的是所有ORM映射的基类Model

class Model(dict, metaclass=ModelMetaclass):def __init__(self, **kw):super(Model, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Model' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = valuedef getValue(self, key):return getattr(self, key, None)def getValueOrDefault(self, key):value = getattr(self, key, None)if value is None:field = self.__mappings__[key]if field.default is not None:value = field.default() if callable(field.default) else field.defaultlogging.debug('using default value for %s: %s' % (key, str(value)))setattr(self, key, value)return value

Modeldict继承,所以具备所有dict的功能,同时又实现了特殊方法__getattr__()__setattr__(),因此又可以像引用普通字段那样写:

>>> user['id']
123
>>> user.id
123

以及Field和各种Field子类:

class Field(object):def __init__(self, name, column_type, primary_key, default):self.name = nameself.column_type = column_typeself.primary_key = primary_keyself.default = defaultdef __str__(self):return '<%s, %s:%s>' % (self.__class__.__name__, self.column_type, self.name)

映射varcharStringField

class StringField(Field):def __init__(self, name=None, primary_key=False, default=None, ddl='varchar(100)'):super().__init__(name, ddl, primary_key, default)

注意到Model只是一个基类,如何将具体的子类如User的映射信息读取出来呢?答案就是通过metaclass:ModelMetaclass

class ModelMetaclass(type):def __new__(cls, name, bases, attrs):# 排除Model类本身:if name=='Model':return type.__new__(cls, name, bases, attrs)# 获取table名称:tableName = attrs.get('__table__', None) or namelogging.info('found model: %s (table: %s)' % (name, tableName))# 获取所有的Field和主键名:mappings = dict()fields = []primaryKey = Nonefor k, v in attrs.items():if isinstance(v, Field):logging.info('  found mapping: %s ==> %s' % (k, v))mappings[k] = vif v.primary_key:# 找到主键:if primaryKey:raise RuntimeError('Duplicate primary key for field: %s' % k)primaryKey = kelse:fields.append(k)if not primaryKey:raise RuntimeError('Primary key not found.')for k in mappings.keys():attrs.pop(k)escaped_fields = list(map(lambda f: '`%s`' % f, fields))attrs['__mappings__'] = mappings # 保存属性和列的映射关系attrs['__table__'] = tableNameattrs['__primary_key__'] = primaryKey # 主键属性名attrs['__fields__'] = fields # 除主键外的属性名# 构造默认的SELECT, INSERT, UPDATE和DELETE语句:attrs['__select__'] = 'select `%s`, %s from `%s`' % (primaryKey, ', '.join(escaped_fields), tableName)attrs['__insert__'] = 'insert into `%s` (%s, `%s`) values (%s)' % (tableName, ', '.join(escaped_fields), primaryKey, create_args_string(len(escaped_fields) + 1))attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (tableName, ', '.join(map(lambda f: '`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey)attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName, primaryKey)return type.__new__(cls, name, bases, attrs)

这样,任何继承自Model的类(比如User),会自动通过ModelMetaclass扫描映射关系,并存储到自身的类属性如__table____mappings__中。

然后,我们往Model类添加class方法,就可以让所有子类调用class方法:

class Model(dict):...@classmethod@asyncio.coroutinedef find(cls, pk):' find object by primary key. 'rs = yield from select('%s where `%s`=?' % (cls.__select__, cls.__primary_key__), [pk], 1)if len(rs) == 0:return Nonereturn cls(**rs[0])

User类现在就可以通过类方法实现主键查找:

user = yield from User.find('123')

往Model类添加实例方法,就可以让所有子类调用实例方法:

class Model(dict):...@asyncio.coroutinedef save(self):args = list(map(self.getValueOrDefault, self.__fields__))args.append(self.getValueOrDefault(self.__primary_key__))rows = yield from execute(self.__insert__, args)if rows != 1:logging.warn('failed to insert record: affected rows: %s' % rows)

这样,就可以把一个User实例存入数据库:

user = User(id=123, name='Michael')
yield from user.save()

最后一步是完善ORM,对于查找,我们可以实现以下方法:

  • findAll() - 根据WHERE条件查找;

  • findNumber() - 根据WHERE条件查找,但返回的是整数,适用于select count(*)类型的SQL。

以及update()remove()方法。

所有这些方法都必须用@asyncio.coroutine装饰,变成一个协程。

调用时需要特别注意:

user.save()

没有任何效果,因为调用save()仅仅是创建了一个协程,并没有执行它。一定要用:

yield from user.save()

才真正执行了INSERT操作。

Day 4 - 编写Model


有了ORM,我们就可以把Web App需要的3个表用Model表示出来:

import time, uuidfrom orm import Model, StringField, BooleanField, FloatField, TextFielddef next_id():return '%015d%s000' % (int(time.time() * 1000), uuid.uuid4().hex)class User(Model):__table__ = 'users'id = StringField(primary_key=True, default=next_id, ddl='varchar(50)')email = StringField(ddl='varchar(50)')passwd = StringField(ddl='varchar(50)')admin = BooleanField()name = StringField(ddl='varchar(50)')image = StringField(ddl='varchar(500)')created_at = FloatField(default=time.time)class Blog(Model):__table__ = 'blogs'id = StringField(primary_key=True, default=next_id, ddl='varchar(50)')user_id = StringField(ddl='varchar(50)')user_name = StringField(ddl='varchar(50)')user_image = StringField(ddl='varchar(500)')name = StringField(ddl='varchar(50)')summary = StringField(ddl='varchar(200)')content = TextField()created_at = FloatField(default=time.time)class Comment(Model):__table__ = 'comments'id = StringField(primary_key=True, default=next_id, ddl='varchar(50)')blog_id = StringField(ddl='varchar(50)')user_id = StringField(ddl='varchar(50)')user_name = StringField(ddl='varchar(50)')user_image = StringField(ddl='varchar(500)')content = TextField()created_at = FloatField(default=time.time)

在编写ORM时,给一个Field增加一个default参数可以让ORM自己填入缺省值,非常方便。并且,缺省值可以作为函数对象传入,在调用save()时自动计算。

例如,主键id的缺省值是函数next_id,创建时间created_at的缺省值是函数time.time,可以自动设置当前日期和时间。

日期和时间用float类型存储在数据库中,而不是datetime类型,这么做的好处是不必关心数据库的时区以及时区转换问题,排序非常简单,显示的时候,只需要做一个floatstr的转换,也非常容易。

初始化数据库表

如果表的数量很少,可以手写创建表的SQL脚本:

-- schema.sqldrop database if exists awesome;create database awesome;use awesome;grant select, insert, update, delete on awesome.* to 'www-data'@'localhost' identified by 'www-data';create table users (`id` varchar(50) not null,`email` varchar(50) not null,`passwd` varchar(50) not null,`admin` bool not null,`name` varchar(50) not null,`image` varchar(500) not null,`created_at` real not null,unique key `idx_email` (`email`),key `idx_created_at` (`created_at`),primary key (`id`)
) engine=innodb default charset=utf8;create table blogs (`id` varchar(50) not null,`user_id` varchar(50) not null,`user_name` varchar(50) not null,`user_image` varchar(500) not null,`name` varchar(50) not null,`summary` varchar(200) not null,`content` mediumtext not null,`created_at` real not null,key `idx_created_at` (`created_at`),primary key (`id`)
) engine=innodb default charset=utf8;create table comments (`id` varchar(50) not null,`blog_id` varchar(50) not null,`user_id` varchar(50) not null,`user_name` varchar(50) not null,`user_image` varchar(500) not null,`content` mediumtext not null,`created_at` real not null,key `idx_created_at` (`created_at`),primary key (`id`)
) engine=innodb default charset=utf8;

如果表的数量很多,可以从Model对象直接通过脚本自动生成SQL脚本,使用更简单。

把SQL脚本放到MySQL命令行里执行:

$ mysql -u root -p < schema.sql

我们就完成了数据库表的初始化。

编写数据访问代码

接下来,就可以真正开始编写代码操作对象了。比如,对于User对象,我们就可以做如下操作:

import orm
from models import User, Blog, Commentdef test():yield from orm.create_pool(user='www-data', password='www-data', database='awesome')u = User(name='Test', email='test@example.com', passwd='1234567890', image='about:blank')yield from u.save()for x in test():pass

可以在MySQL客户端命令行查询,看看数据是不是正常存储到MySQL里面了。





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_737422.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Google 联合一些社交网站来对抗 facebook

谷歌(Google) 将与其它一些行业领先的社交网站联手&#xff0c;共同对抗互联网新贵Facebook。  谷歌将于周四推出一个通用标准集 ( OpenSocial )&#xff0c;允许软件开发者为谷歌旗下社交网站Orkut&#xff0c;以及LinkedIn、hi5、Friendster、Plaxo和Ning等其它社交网站开…

png压缩,jpg压缩不错的网站推荐

https://tinypng.com/ 这个网址对于手动批量压缩png和jpg挺不错的&#xff0c;mark一下。虽然用了多年&#xff0c;还是觉得不错。

使用开源WordPress和WebStack搭建导航网站

目录 一、前言 二、WordPress和WebStack简介 三、安装与部署 1. 服务器环境搭建LNMP 1.1 购买Linux服务器 1.2 安装Nginx 1.3 安装MariaDB 1.4 安装PHP 2. 安装WordPress 2.1 配置数据库 2.2 下载WordPress源文件 2.3 安装WordPress 3. 安装WebStack主题 四、Web…

盘点Web2.0峰会-社交网站领风骚

网易科技 讯 10月23日消息&#xff0c;美国第六届Web2.0峰会&#xff08;Web2.0 Summit 2009 &#xff09; 于当地时间10月20日-10月22日在旧金山举行。本届Web2.0峰会的主题是“Web Squared”&#xff0c;意思是说&#xff1a;Web2.0应用、技术和网站已经不仅仅是一组创新的网…

鄙视这些垃圾网站,居然篡改俺的注册表,实在气愤

不知是哪天&#xff0c;同事拿我本本上网下东西&#xff0c;可能是下什么东西吧&#xff0c;上了verycd。265。com&#xff08;为了不给它出现连接的机会故意把.改为。&#xff09;居然把我注册表都修改了&#xff0c;着实气愤&#xff0c;&#xff0c;而且在ie选项的主页设置里…

网站已死 互联网永生

从诞生到现在&#xff0c;网站已经走过了20个年头。相比当年的流行&#xff0c;如今它已经开始衰落&#xff0c;逐渐让位于更简单且时髦的智能应用。这些应用更关注的不是搜索效果&#xff0c;而是信息获取。克莱斯安德森(Chris Anderson)向我们解释了这些新应用所反映的资本聚…

网站动态背景线条跟随鼠标移动,吸附鼠标效果代码

实现如图所示的一个动态背景线条随鼠标移动而吸附聚集的一个效果&#xff0c;代码如下&#xff1a; <!--代码放置于</body>上方--><script>!function(){function n(n,e,t){return n.getAttribute(e)||t}function e(n){return document.getElementsByTagName…

10个非常棒的Ajax及Javascript实例资源网站

今天&#xff0c;暴风彬彬要向大家推荐10个相当棒的Ajax和Javascript国外资源网站或博客&#xff0c;它们提供了相当多的高质量Ajax、Javascript实例及教程&#xff0c;喜欢Ajax和Javascript的朋友绝对不能错过。彬Go曾经还介绍过300多个Javascript导航菜单,有兴趣的话&#xf…

网站全文检索设计

1、架构设计采 用OLTP交易数据库和OLAP数据仓库&#xff08;用于搜索和分析&#xff09;分离的模式&#xff0c;OLTP作为OLAP的数据源通过SQL Server Integration Services (SSIS)定期导入到OLAP数据仓库环境中&#xff0c;OLAP采用星型结构以便于更好地满足搜索和将来的数据挖…

美女SEO系列五:什么是外链?如何做高质量外链?

每天我们的工作就是做内容和发外链&#xff0c;那么对与新手SEO而言&#xff0c;什么是外链?高质量的外链怎么去做呢?那么&#xff0c;美女SEO周珍就来和大家一起来分享下吧! 一、什么是外链?外链&#xff0c;顾名思义就是指从别人的网站导入到自己网站的链接。导入链接对于…

网站合并...

今天把一个中文网站和一个英文网站合并到一起&#xff0c;也就是说在中文的根目录下新建一个目录然后把英文网站拷到里面就行了&#xff0c;可是打开的时候&#xff0c;中文的可以打开&#xff0c;但是英文的就报错了 <error statusCode"403" redirect"Promp…

使用 ASP.NET 2.0 增强网站的安全性

本文以 2004 年 3 月社区技术预览中的 ASP.NET 2.0 内容为基础。文中包含的所有信息有可能变更。 本文讨论&#xff1a; • ASP.NET 2.0 中的安全性增强 • 服务器端安全性控制 • 用户和角色数据库 • 无 cookie 的窗体身份验证 本文使用下列技术&#xff1a; ASP.NET、身份验…

php 发送mysql备份_PHP备份MySQL和网站发送到邮箱

欢迎进入Linux社区论坛&#xff0c;与200万技术人员互动交流 >>进入 使用PHP备份MySQL和网站发送到邮箱的方法。 代码&#xff1a; 1 #!/usr/local/bin/php.cli 2 ?php 3 require_once ./lib/swift_required.php; 4 //MySQL 5 $mysql_dbname "db"; 6 $mysql_…

bilibili小心心怎么获得_怎么优化关键词才能提高网站排名?

为了让自己的网站提高排名&#xff0c;每一个网站的管理者都会对自己的网站关键词进行优化&#xff0c;那么&#xff0c;怎么优化关键词才最合适呢&#xff1f;最能提高网站排名呢&#xff1f;关键词优化简单地说&#xff0c;就是把网站里面的关键词进行选词和排版&#xff0c;…

JavaScript网站设计实践(一)网站结构以及页面效果设计

这是JavaScript DOM编程艺术里的构建JavaScript网站设计的例子&#xff0c;这本书给我学习JavaScript带来许多启发&#xff0c;在这个乐队宣传网站中&#xff0c;把前面学到的知识点整合在这个项目了。在这里记录下实现这个乐队的宣传网站的具体过程&#xff0c;加深理解。好&a…

Sqlserver 数据库恢复常见错误及解决(网站转载 留着备用)

数据库恢复常见错误及解决 2009-04-13 11:25 1145人阅读 评论(0) 收藏 举报数据库databasesqlserverusermicrosoftsql server在sqlServer20005 的management studio里使用bak文件还原数据库的时候,总是失败!The backup set holds a backup of a database other than the existi…

抓取网站编码信息及内容

最近在编写一个读取网站内容的小东西&#xff0c;在网上一搜很多&#xff0c;但是在拿过来用时不太理想&#xff0c;有些内容读取还是出现乱码问题。于是我在loafinweb 这位兄弟代码的基础上做了一些小的调整&#xff0c;以达到个人需求&#xff0c;如有不对之处还请loafinweb …

视觉冲击!12个精美绝伦的扁平化网站设计

越来越多的人崇尚简单&#xff0c;简约的扁平化设计风格。扁平化设计对于印刷品设计、网页设计和移动操作系统的设计来都带去了新的变化。扁平化网页设计是指设计形式摒弃图案纹理背景&#xff0c;阴影文字以及网站盒模型的部分。 下面手机了12个来自全球各地的设计师们精工雕琢…

在网站前端中,你可能会用到的这些…

一、设置字体的行间距为3PX &#xff1a; letter-spacing:3PX ; 二、为投票所显示分值设置首字下沉并变大点 &#xff1a; XXX:first-letter {font-size:150%;font-weight:bold;float:left;} 注&#xff1a; float:left; 有起到下沉的效果&#xff1b; 三、当字体与图片、表单标…

Asp.net网站管理工具来配置membership

1.安装好sql express,创建自己的数据库 2.VS命令行下运行aspnet_regsql命令&#xff0c;弹出ASP.NET SQL Server Setup Wizard 3.server填入.\sqlexpress,选择自己的数据库 4.完成后&#xff0c;会自动在选择的数据库中生成11张和membership相关的表 5.修改web.config 1 <…