Python Pyramid 使用SQLAlchemy
在这一章中,我们将学习如何在Pyramid Web应用程序中使用关系型数据库作为后端。Python可以使用相应的DB-API兼容的连接器模块或驱动程序与几乎每个关系型数据库进行交互。然而,我们将使用SQLAlchemy库作为Python代码与数据库之间的接口(我们将使用SQLite数据库,因为Python内置支持它)。SQLAlchemy是一个流行的SQL工具包和对象关系映射器。
对象关系映射是一种在面向对象编程语言中将数据在不兼容类型系统之间转换的编程技术。通常,在像Python这样的面向对象语言中使用的类型系统包含非标量类型。然而,大多数数据库产品(如Oracle、MySQL等)中的数据类型都是基本类型,例如整数和字符串。
在ORM系统中,每个类关联到底层数据库中的一个表。使用ORM,您无需编写冗长的数据库接口代码,它会为您解决这些问题,而您可以专注于编写系统的逻辑。
要使用SQLAlchemy,我们首先需要使用PIP安装程序安装库。
pip install sqlalchemy
SQLAlchemy旨在与针对特定数据库构建的DBAPI实现一起使用。它使用方言系统与各种类型的DBAPI实现和数据库进行通信。所有方言都要求安装适当的DBAPI驱动程序。
以下是包含的方言-
- Firebird
-
Microsoft SQL Server
-
-
Oracle
-
Sybase
数据库引擎
由于我们要使用SQLite数据库,我们需要为我们的数据库创建一个数据库引擎,名为 test.db。 从 sqlalchemy 模块中导入 create_engine() 函数。
from sqlalchemy import create_engine
from sqlalchemy.dialects.sqlite import *
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args = {"check_same_thread": False})
为了与数据库进行交互,我们需要获取它的句柄。一个会话对象是数据库的句柄。会话类是使用 sessionmaker() 定义的——这是一个绑定到引擎对象的可配置会话工厂方法。
from sqlalchemy.orm import sessionmaker, Session
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
接下来,我们需要一个声明性的基类,该基类将在Declarative系统中存储类和映射的表的目录。
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
模型类
学生 是 Base 的一个子类,它与数据库中的 students 表进行映射。 Students 类中的属性对应着目标表中的列的数据类型。注意,id 属性对应着 book 表中的主键。
class Students(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(63), unique=True)
marks = Column(Integer)
Base.metadata.create_all(bind=engine)
create_all() 方法在数据库中创建相应的表格。可以使用SQLite的可视化工具如 SQLiteStudio 进行确认。
现在,我们将定义用于在上述数据库中执行CRUD操作(即添加、显示、修改和删除行)的视图函数。
添加新的学生记录
首先,我们将创建一个HTML表单模板供用户输入学生数据,并定义一个视图来渲染模板。这是 myform.html 模板。
示例
<html>
<body>
<form method="POST" action="http://localhost:6543/add">
<p>Student Id: <input type="text" name="id"/> </p>
<p>student Name: <input type="text" name="name"/> </p>
<p>Percentage: <input type="text" name="percent"/> </p>
<p><input type="submit" value="Submit"> </p>
</body>
</html>
在Pyramid应用程序代码中,定义index()视图函数来渲染上述表单。
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config
@view_config(route_name='index', renderer='templates/myform.html')
def index(request):
return {}
在应用程序配置中,使用”/new”模式注册该视图的路由为 –
if __name__ == '__main__':
with Configurator() as config:
config.include('pyramid_jinja2')
config.add_jinja2_renderer(".html")
config.add_route('index', '/new')
config.scan()
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 6543, app)
server.serve_forever()
将上述模板中的HTML表单提交到 /add URL,并使用POST操作,我们需要将此URL映射到add路由,并注册add()视图,该视图将表单数据解析为Students类的对象。将此对象添加到数据库会话中,并通过调用其commit()方法完成操作。
@view_config(route_name='add', request_method='POST')
def add(request):
id=request.POST['id']
name=request.POST['name']
percent=int(request.POST['percent'])
student=Students(id=id, name=name, percent=percent)
session.add(student)
session.commit()
return HTTPFound(location='http://localhost:6543/')
确保在配置中添加了添加路由,映射到/add URL模式。
config.add_route('add','/add')
输出
如果我们启动服务器并在浏览器中打开 http://localhost:6543/new ,则输入表单将显示如下:
填写表单并点击“提交”按钮。将调用add()视图,并在students表中添加新记录。重复此过程几次以添加几条记录。这是一个示例数据-
显示所有记录列表
通过查询模型获得Students模型的所有对象(对应于students表中的行)。
rows = session.query(Students).all()
每一行都被转换成一个字典对象,所有的字典对象都被添加到一个字典对象的列表中,并作为上下文返回到list.html模板中,以HTML模板的形式显示。这个过程是由与list路由相关联的showall()视图函数执行的。
@view_config(route_name='list', renderer='templates/marklist.html')
def showall(request):
rows = session.query(Students).all()
students=[]
for row in rows:
students.append({"id":row.id, "name":row.name, "percent":row.percent})
return{'students':students}
示例
marklist.html 模板将学生列表以HTML表格的形式呈现。其HTML/jinja2脚本如下所示−
<html>
<body>
<table border=1>
<thead>
<tr>
<th>Student ID</th>
<th>Student Name</th>
<th>percentage</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for Student in students %}
<tr>
<td>{{ Student.id }}</td> <td>{{ Student.name }}</td>
<td>{{ Student.percent }}</td>
<td><a href="/show/{{ Student.id }}">edit</a></td>
<td><a href="/delete/{{ Student.id }}">delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<h3><a href="http://localhost:6543/new">Add new</a></h3>
</body>
</html>
将列表路由添加到配置中,并将其与’/’ URL注册。
config.add_route('list', '/')
输出
在启动服务器后,使用浏览器打开 http://localhost:6543/ 。学生表中现有记录的列表将会显示。
注意最后两列中的超链接。例如,在 “id=1” 前面的 “edit” 链接指向 http://localhost:6543/show/1 。这些链接用于执行更新和删除操作。
更新现有记录
在 /show/1 URL 中,有一个尾部路径参数。它被映射到配置中的 ‘show’ 路由。
config.add_route('show', '/show/{id}')
此路由调用show()函数。它根据给定的id参数获取相应的记录,将HTML表单填充为其内容,并允许用户更新name和/或percent字段的值。
@view_config(route_name='show', renderer='templates/showform.html')
def show(request):
id=request.matchdict['id']
row = session.query(Students).filter(Students.id == id).first()
student={'id':row.id, 'name':row.name, 'percent':row.percent}
return {'student':student}
示例
showform.html模板的HTML/jinja2代码如下:
<html>
<body>
<form method="POST" action="http://localhost:6543/update">
<p>Student Id: <input type="text" name="id" value="{{ student.id }} " readonly/> </p>
<p>student Name: <input type="text" name="name" value="{{ student.name }}"/> </p>
<p>Percentage: <input type="text" name="percent" value="{{ student.percent }}"/> </p>
<p><input type="submit" value="Submit"> </p>
</body>
</html>
输出
让我们用id=3更新记录。点击相应的编辑链接进入 http://localhost:6543/show/3
更改marks文本字段中的值并点击提交按钮。表单将被重定向到/update URL,并调用update()视图。它获取提交的数据并更新相应的对象,从而也更新了students表中的底层行。
@view_config(route_name='update', request_method='POST')
def update(request):
id=int(request.POST['id'])
student = session.query(Students).filter(Students.id == id).first()
student.percent=int(request.POST['percent'])
session.commit()
return HTTPFound(location='http://localhost:6543/')
return语句将浏览器重定向回’/’ URL,该URL指向list()函数并显示更新后的marklist。
在运行之前,请确保将更新路由添加到配置中。
config.add_route('update', '/update')
删除记录
要删除与marklist表中的一行对应的记录,请点击最后一列中的Delete链接。例如,点击第3行的Delete会发出以下URL: http://localhost:6543/delete/3 ,并调用以下视图函数:
@view_config(route_name='delete', renderer='templates/deleted.html')
def delete(request):
id=request.matchdict['id']
row = session.query(Students).filter(Students.id == id).delete()
return {'message':'Redcord has been deleted'}
示例
从URL中解析得到的路径参数对应的对象将被删除,并且以下模板将渲染出相应的信息 - deleted.html −
<html>
<body>
<h3>{{ message}}</h3>
<br><br>
<a href="http://localhost:6543/">Click here to refresh the mark list</a>
</body>
</html>
显然,删除路由必须添加到应用程序配置注册表中。
config.add_route('delete', '/delete/{id}')
输出
记录删除操作的结果如下所示:
按照以下步骤执行上述活动:
- 在Pyramid虚拟环境中创建一个名为 testapp 的文件夹。
-
在 testapp 中创建 templates 文件夹。
-
在testapp中创建一个空的 __init__.py 文件,使其成为一个包。
-
将marklist.html、myform.html、showform.html和deleted.html文件放在”testapp\templates”文件夹中。这些文件的代码已在上面给出。
-
将以下代码保存为 models.py 在 testapp 中。
from sqlalchemy.dialects.sqlite import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import Session
from sqlalchemy import Column, Integer, String
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
Base = declarative_base()
class Students(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(63), unique=True)
percent = Column(Integer)
def getsession():
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind = engine)
session = Session()
return session
将下面的代码保存为 views.py ,并放在 testapp 文件夹中。
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPFound
from models import Students
from main import session
@view_config(route_name='list', renderer='templates/marklist.html')
def showall(request):
rows = session.query(Students).all()
students=[]
for row in rows:
students.append({"id":row.id, "name":row.name, "percent":row.percent})
return{'students':students}
@view_config(route_name='index', renderer='templates/myform.html')
def index(request):
return {}
@view_config(route_name='add', request_method='POST')
def add(request):
id=request.POST['id']
name=request.POST['name']
percent=int(request.POST['percent'])
student=Students(id=id, name=name, percent=percent)
session.add(student)
session.commit()
return HTTPFound(location='http://localhost:6543/')
@view_config(route_name='update', request_method='POST')
def update(request):
id=int(request.POST['id'])
student = session.query(Students).filter(Students.id == id).first()
student.percent=int(request.POST['percent'])
session.commit()
return HTTPFound(location='http://localhost:6543/')
@view_config(route_name='show', renderer='templates/showform.html')
def show(request):
id=request.matchdict['id']
row = session.query(Students).filter(Students.id == id).first()
student={'id':row.id, 'name':row.name, 'percent':row.percent}
return {'student':student}
@view_config(route_name='delete', renderer='templates/deleted.html')
def delete(request):
id=request.matchdict['id']
row = session.query(Students).filter(Students.id == id).delete()
return {'message':'Redcord has been deleted'}
- 将以下代码另存为testapp文件夹中的main.py。
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from models import getsession
session=getsession()
if __name__ == '__main__':
with Configurator() as config:
config.include('pyramid_jinja2')
config.add_jinja2_renderer(".html")
config.add_route('list', '/')
config.add_route('index', '/new')
config.add_route('add','/add')
config.add_route('show', '/show/{id}')
config.add_route('update', '/update')
config.add_route('delete', '/delete/{id}')
config.scan('testapp')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 6543, app)
server.serve_forever()
- 从命令提示符中运行 main.py 。
Python main.py
-
使用 http://localhost:6543/ 网址在浏览器窗口中。将显示一个仅包含标题而没有记录的表格。
-
在表格下方点击“添加新记录”链接以添加记录。
-
在表格中点击“编辑”链接以更新一条记录。
-
在表格中点击“删除”链接以删除选定的记录。