使用工具

Pycharm , Navicat , WebStorm等

使用库

Werkzeug用于实现框架的底层支撑,pymysql用于实现ORM,jinja2用于模板支持,json用于返回json数据功能的支持

实现视图基类

该视图基类用于被视图类所继承,并且提供了两个分别处理GET和POST请求的函数,程序收到请求时,会根据请求的方式将请求参数发送到对应的处理函数中进行处理(请求调度)。若请求方式未找到,则直接返回错误响应。

class View(object):
# 请求方式与处理函数对应
def __init__(self):
self.methods = {
'GET': self.GET,
'POST': self.POST
} # 定义两种基本请求方式
# 视图类可覆盖请求方式进行不同处理
def GET(self, request):
raise MethodNotAllowed() def POST(self, request):
raise MethodNotAllowed() # 请求调度
def dispatch_request(self, request, *args, **options):
# 保存request对象至全局变量中
global global_request
global_request = request
# 判断请求类型并将请求分发给相应的处理函数,返回该函数
if request.method in self.methods:
return self.methods[request.method](request, *args, **options)
else:
return '<h1>Unknown or unsupported require method</h1>'

在View类中,使用一个闭包,将视图函数类本身发送给请求调度函数,获取对应的处理函数(对应继承View类的视图类覆盖的请求方法函数)并返回

@classmethod
def get_func(cls):
def func(*args, **kwargs):
obj = func.view_class()
return obj.dispatch_request(*args, **kwargs) func.view_class = cls
return func

所有视图类必须继承自View类,并至少覆盖其中的GET或POST函数。如

class Index(View):
def GET(self,request):
return "hello world"

实现App类

App类拥有两个私有变量,view_func 以及 url_map 分别表示url和视端点的映射字典以及端点和视图函数的映射。依据WSGI_APP的原理,使用实例形式实现App时,必须实现__call__方法,并传入environ字典以及start_response函数,该方法内部根据environ的参数信息,调用特定的处理函数进行处理,并返回封装好的Response对象。

class App(object):

    def __init__(self):
# url和视端点的映射字典
# 端点和视图函数的映射
self.view_func = {}
self.url_map = Map() def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response) def wsgi_app(self, environ, start_response):
try:
# 构造request对象
request = Request(environ)
# 获取url适配器
adapter = self.url_map.bind_to_environ(request.environ)
# 获取端点值以及动态参数部分
endpoint, values = adapter.match()
# 保存端点值以便获取
request.endpoint = endpoint
# 获取端点对应的视图函数
view = self.view_func.get(endpoint, None)
if view:
# 将请求和url动态部分发送给视图函数获得响应对象
response = view(request, **values)
# 当视图函数返回重定向请求时不再包装成Response对象,直接返回
if response.__class__ != Response:
response = Response(response, content_type='text/html;charset=UTF-8')
else:
response = Response('<h1>404 Not Found<h1>', content_type='text/html; charset=UTF-8')
response.status_code = 404
except HTTPException as e:
response = e
# 返回响应
return response(environ, start_response)

在App类中,我还添加了一个成员方法,用于为该App对象添加路由规则。方法参数为一个字典列表,其中每个字典包括url和view两个键,值分别为视图类对应的路由和视图类。方法内部对该列表进行遍历,并添加到两个成员变量中。

# 添加路由规则
def add_url_rule(self, urls):
"""
添加路由规则
:param urls:一个列表,其中每一个项为一个字典,键为url和view,表示路径和对应的视图函数类
:return: None
"""
global url_map
for url in urls:
# 路由url与相应的端点组成键值对
# 默认端点为该视图函数类的类名小写形式
rule = Rule(url["url"], endpoint=url["view"].__name__.lower())
self.url_map.add(rule)
self.view_func[url['view'].__name__.lower()] = url['view'].get_func()
url_map = self.url_map
  def run(self, port=5000, ip='127.0.0.1', debug=False):
  run_simple(ip, port, self, use_debugger=debug, use_reloader=True)

添加模板支持

可使用jinja2的相关函数为框架添加模板支持

def render_template(template, **ctx):
"""
:param template: html文件名
:param ctx: 要传入html 的参数
:return: html标签代码
"""
# 定位template文件夹的路径
global context_processor, global_request
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "templates")
# 在渲染模板时将一些辅助函数和全局变量传入以便在html代码中使用
ctx["url_for"] = url_for
ctx["request"] = global_request
ctx["session"] = session
ctx["get_flash"] = get_flash
# 将render_template 方法中的键值对参数加入渲染参数列表中
for k, v in context_processor.items():
ctx[k] = v
# 创建jinja环境
jinja_env = Environment(loader=FileSystemLoader(path), autoescape=True)
t = jinja_env.get_template(template)
return t.render(ctx)

使用

from MyFrameWork.myFrame.MyApp import View, create_app, render_template

class Index(View):
def GET(self,request):
return "hello World"
   def urls = [
{
"url":"/index",
"view":Index
}
] app.create_app()
app.add_url_rule(urls)
app.run()

丰富框架功能

我还框架添加了类似flask中的session,flash,url_for,redirect,模板全局变量以及ORM支持等功能。实现方法都比较简单。故不在此赘述。

项目地址(包括框架本身以及使用框架实现的一个小型应用):https://github.com/YangZX1428/Simple-Python-Web-FrameWork.git

构建url

类似flask的url_for,使用了build方法根据端点构建url,可传入端点和附加参数获取路由路径,也可以访问static中的文件(在添加了支持静态文件的中间件的基础上)

def url_for(endpoint, server_name="127.0.0.1:5000", external=False, filename=None, **values):
"""
返回端点值对应的url
:param endpoint: 端点值(会自动转化为小写)
:param server_name: App实例程序所在的服务器ip
:param values: url动态参数部分
:param external: 生成绝对url
:param filename : static资源的路径
:return: 对应的url
"""
# filename不为空时返回静态资源路径
if filename is not None:
file_path = os.path.join('\%s' % endpoint, filename)
return file_path
# 绑定服务器地址
urls = url_map.bind(server_name)
# 通过端点获取对应的url
relative_url = urls.build(endpoint.lower(), values, force_external=external)
return relative_url

返回json格式数据

类似flask的jsonify,可传入键值对,内部将参数转成json数据,并指定响应的content_type为json以返回

def jsonify(**values):
"""
返回json格式的响应数据
:param values: 接收键值对
:return: json格式的response
"""
json_data = json.dumps(values)
response = Response(json_data, content_type="application/json;charset=UTF-8")
return response

添加中间件支持

要根据url访问到静态资源,故添加了SharedDataMiddleware中间件

该方法返回一个app对象,相当于创建app

def create_app(with_static=True):
"""
创建app对象,加入了中间件
:param with_static:是否开启访问静态资源模式
:return: app对象
"""
app = App()
if with_static:
# 模板中可使用static中的资源
# <link rel=stylesheet href=/static/style.css type=text/css>
app.wsgi_app = SharedDataMiddleware(
app.wsgi_app, {"/static": os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")}
)
return app

重定向

重定向我使用了werkzeug中已经写好的redirect函数,但需要注意的是,redirect函数直接返回Response对象,而在wsgi_app中我们将视图函数的返回值又一次封装成了Response对象,此时当return的是redirect的时候会报错,故在封装时需先判断视图函数的返回值是否已经是Response,若是则直接返回,不是则再包装成Response对象返回

# 将请求和url动态部分发送给视图函数获得响应对象
response = view(request, **values)
# 当视图函数返回重定向请求时不再包装成Response对象,直接返回
if response.__class__ != Response:
response = Response(response, content_type='text/html;charset=UTF-8')

ORM支持

我实现的orm目前只支持三种字段类型,String,对应varchar,Integer,对应int,Text,对应text

class StringField(Field):
# 字符串字段,字段类型默认为varchar
def __init__(self, name=None, col_type="varchar(100)", primary_key=False, default=None):
super(StringField, self).__init__(name, col_type, primary_key, default) class IntegerField(Field):
def __init__(self, name=None, col_type="int(20)", primary_key=False, default=None):
super(IntegerField, self).__init__(name, col_type, primary_key, default) class TextField(Field):
def __init__(self, name=None, col_type="text", primary_key=False, default=None):
super(TextField, self).__init__(name, col_type, primary_key, default)

创建数据表

创建数据表与flask_sqlalchemy类似采用模型类的方式,创建操作在除了create_all创建所有表以外,我还添加了一个create方法,对单个表进行创建(默认字符集为utf8)

创建成功后会打印一条消息

@classmethod
def create(cls):
info = {}
for k, v in cls.__mapping__.items():
info[k] = [v.primary_key, v.col_type, v.default]
sql = "create table " + cls.__tablename__ + "("
primary = None
for k, v in info.items():
col_info = "%s %s not null" % (k, v[1])
if v[0]:
primary = k
if v[2] is not None:
col_info += " default '%s'" % v[2] if type(v[2]) == str else " default %d" % v[2]
sql += col_info + ","
sql += "primary key(`%s`))engine=innodb default charset=utf8;" % primary
rows, result = cls.db.execute_sql(sql)
if not rows:
print("Create table `%s` (in database `%s`) success!" % (cls.__tablename__, cls.__database__))

插入

与flask_sqlalchemy类似,先创建一个模型类对象,再对该对象调用insert方法,该函数返回受影响的行数

    def insert(self):
"""
将调用该方法的对象作为一条记录加入相应的表中

obj = User(id=1,name='yzx')
obj.insert()
:return: 插入是否成功,成功则返回1
"""
# 组成实参列表
insert_values = [self.getValue(key) for key in self.__fields__]
insert_values.insert(0, self.getValue(self.__primary_key__))
# 将问号占位符替换成%s
sql = self.__insert__.replace("?", "%s")
# 执行插入语句
rows, results = self.db.execute_sql(sql, insert_values)
return rows

更新

更新为类方法,对模型类直接调用

@classmethod
def setValue(cls, id, col, value):
"""
更新数据库的值
调用
ClassName.setValue(id,col,value)
:param id: id值
:param col: 要更新的列名
:param value: 要更新的目标值
:return: 是否更新成功,成功返回1否则0
"""
if type(value) == int:
sql = "update %s set %s=%d where id=%d" % (cls.__tablename__, col, value, id)
else:
sql = "update %s set %s='%s' where id=%d" % (cls.__tablename__, col, value, id)
rows, result = cls.db.execute_sql(sql)
if not rows:
raise RuntimeError("Can't find data where id = %d" % id)
return rows

查询

查询提供了获取所有数据,根据id获取数据,根据Filter获取数据,值得一提的是,查询方法除了根据id获取数据(返回字典)以外返回的都是字典列表,其中每个字典代表一个记录,键为字段名,值为字段值。

以查询所有数据为例

@classmethod
def getAll(cls):
"""
获取某个表的所有数据
返回数据格式为一个列表
列表的每一项为字典,键为列名。
:return:list
"""
rows, result = cls.db.execute_sql(cls.__select__)
cols = [cls.__primary_key__] + cls.__fields__
return toDict(cols, result) def toDict(cols, result):
result_list = []
for t in result:
d = {}
for k, v in zip(cols, t):
d[k] = v
result_list.append(d)
return result_list

用werkzeug实现一个简单的python web框架的更多相关文章

  1. 从零构建一个简单的 Python Web框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

  2. 浅谈Python Web 框架:Django, Twisted, Tornado, Flask, Cyclone 和 Pyramid

    Django Django 是一个高级的 Python Web 框架,支持快速开发,简洁.实用的设计.如果你正在建一个和电子商务网站相似的应用,那你应该选择用 Django 框架.它能使你快速完成工作 ...

  3. Python Web框架本质——Python Web开发系列一

    前言:了解一件事情本质的那一瞬间总能让我获得巨大的愉悦感,希望这篇文章也能帮助到您. 目的:本文主要简单介绍Web开发中三大基本功能:Socket实现.路由系统.模板引擎渲染. 进入正题. 一. 基础 ...

  4. Python Web框架Tornado的异步处理代码演示样例

    1. What is Tornado Tornado是一个轻量级但高性能的Python web框架,与还有一个流行的Python web框架Django相比.tornado不提供操作数据库的ORM接口 ...

  5. 一个简单的python爬虫程序

    python|网络爬虫 概述 这是一个简单的python爬虫程序,仅用作技术学习与交流,主要是通过一个简单的实际案例来对网络爬虫有个基础的认识. 什么是网络爬虫 简单的讲,网络爬虫就是模拟人访问web ...

  6. python web框架之Tornado的简单使用

    python web框架有很多,比如常用的有django,flask等.今天主要介绍Tornado ,Tornado是一个用Python写的相对简单的.不设障碍的Web服务器架构,用以处理上万的同时的 ...

  7. 选择一个 Python Web 框架:Django vs Flask vs Pyramid

    Pyramid, Django, 和 Flask都是优秀的框架,为项目选择其中的哪一个都是伤脑筋的事.我们将会用三种框架实现相同功能的应用来更容易的对比三者.也可以直接跳到框架实战(Framework ...

  8. 一个简单的python线程池框架

    初学python,实现了一个简单的线程池框架,线程池中除Wokers(工作线程)外,还单独创建了一个日志线程,用于日志的输出.线程间采用Queue方式进行通信. 代码如下:(不足之处,还请高手指正) ...

  9. 一个简单的Java web服务器实现

    前言 一个简单的Java web服务器实现,比较简单,基于java.net.Socket和java.net.ServerSocket实现: 程序执行步骤 创建一个ServerSocket对象: 调用S ...

随机推荐

  1. Command1

    命令 [-参数][操作对象] ls -la /etc -l 以长格式显示详细信息 -a 显示目录下全部文件(包括隐藏文件) ls 执行权限:所有用户 功能:显示目录下的文件 ls -ald -d 查看 ...

  2. Flutter 基础组件:进度指示器

    前言 Material 组件库中提供了两种进度指示器:LinearProgressIndicator和CircularProgressIndicator,它们都可以同时用于精确的进度指示和模糊的进度指 ...

  3. 【Docker】CentOS7 上无网络情况下安装

    自建虚拟机,但是连接不上网络,只能通过下载rpm包进行安装docker 环境:CentOS 7.3.1611 x64 rpm镜像下载地址用的阿里的https://mirrors.aliyun.com/ ...

  4. Vue MVVM模型原理

    最近反思了下自己,觉得自己很急躁,学技术总是觉得能用就行了,其实这样很不好,总是这样,就永远只能当用轮子的人.好了,废话不多说,转入正题: 要理解MVVM的原理,首先要理解它是什么,怎么运作起来的: ...

  5. wmic 查看主板信息

    查看主板信息的一个命令:wmic baseboard get 当然在命令提示符里查看,真的很费劲,所以我们将命令格式化一下:wmic baseboard get /format:HFORM >c ...

  6. 1.2V升3.3V芯片,大电流,应用MCU供电,3.3V稳压源

    MCU供电一般是2.5V-5V之间等等都有,1.2V需要升到3.3V的升压芯片来稳压输出3.3V给MCU供电. 同时1.2V的输入电压低,说明供电端的能量也是属于低能量的,对于芯片自身供货是也要求高. ...

  7. 前端知识(一)04 Vue.js入门-谷粒学院

    目录 一.介绍 1.Vue.js 是什么 2.初识Vue.js 二.基本语法 1.基本数据渲染和指令 2.双向数据绑定 3.事件 4.修饰符 5.条件渲染 6.列表渲染 7.实例生命周期 一.介绍 1 ...

  8. SQLHelper ------ python实现

    SQLHelper ------ python实现 1.第一种: import pymysql import threading from DBUtils.PooledDB import Pooled ...

  9. 30分钟带你了解「消息中间件」Kafka、RocketMQ

    消息中间件的应用场景 主流 MQ 框架及对比 说明 Kafka 优点 Kafka 缺点 RocketMQ Pulsar 发展趋势 各公司发展 Kafka Kafka 是什么? Kafka 术语 Kaf ...

  10. 《awk中文手册》-本人参考官方手册翻译

    01. 简介 AWK是一个文本(面向行和列)处理工具,同时它也是一门脚本语言. AWK其名称得自于它的创始人 Alfred Aho .Peter Weinberger 和 Brian Kernigha ...