Python Pyramid 简明教程
Python Pyramid - Using SQLAlchemy
在本章中,我们将学习如何使用关系数据库作为 Pyramid Web 应用程序的后端。Python 可以通过相应的与 DB-API 兼容的连接器模块或驱动程序与几乎所有关系数据库进行交互。但是,我们将使用 @ {s9}
库作为 Python 代码和数据库之间的接口(我们将使用 SQLite 数据库,因为 Python 已经为其提供了内置支持)。SQLAlchemy 是一个流行的 SQL 工具包和对象关系映射器。
对象关系映射是一种编程技术,用于在面向对象编程语言的不同类型系统之间转换数据。通常,像 Python 这种面向对象语言中使用的类型系统包含非标量类型。但是,大多数数据库产品的(例如 Oracle、MySQL 等)数据类型都是基本类型,例如整数和字符串。
在 ORM 系统中,每个类都映射到底层数据库中的一个表。ORM 负责处理这些问题,让你专注于对系统逻辑进行编程,而不再需要自己编写乏味的数据库接口代码。
为了使用 SQLALchemy,我们需要先使用 PIP 安装程序来安装该库。
pip install sqlalchemy
SQLAlchemy 被设计为使用专为某个特定数据库而构建的 DBAPI 实现来运行。它使用方言系统与各种类型的 DBAPI 实现和数据库通信。所有方言都要求已安装相应的 DBAPI 驱动程序。
以下是包含的方言:
-
Firebird
-
Microsoft SQL Server
-
MySQL
-
Oracle
-
PostgreSQL
-
SQLite
-
Sybase
Database Engine
由于我们将使用 SQLite 数据库,因此我们需要为名为 @ {s10}
的数据库创建一个数据库引擎。从 @ {s12}
模块导入 @ {s11}
函数。
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})
为了与数据库进行交互,我们需要获取其句柄。会话对象是数据库的句柄。使用 @ {s13}
定义会话类 - 一个可配置的会话工厂方法,绑定到引擎对象。
from sqlalchemy.orm import sessionmaker, Session
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
接下来,我们需要一个声明式基类,用于在声明式系统中存储类的目录和映射的表。
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
Model Class
Students , 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. )进行确认
现在,我们为上述数据库中的 student 表定义视图函数,用于执行 CRUD(即添加、显示、修改和删除行)操作。
Add a New Student Record
首先,我们将创建一个 HTML 表单模板,以便用户输入学生数据并定义渲染该模板的视图。以下是 myform.html 模板
Example
<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 表单已提交给带 POST 动作的 /add URL,我们需要将该 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 模式,并将其映射到 /add URL 模式。
config.add_route('add','/add')
Output
如果我们启动服务器并在浏览器中打开 http://localhost:6543/new ,则将显示录入表单,如下所示:
填写表单并按 "submit" 按钮。将调用 add() 视图,并在 students 表中添加新记录。重复此过程几次,以添加一些记录。以下是一些示例数据:
Show List of All Records
通过查询模型,可以获得 Students 模型的所有对象(对应于 students 表中的行)。
rows = session.query(Students).all()
将每一行转换为一个 dict 对象,将所有这些对象追加到一个 dict 对象列表中,并作为上下文返回给 list.html 模板,以便以 HTML 模板的形式显示。showall() 视图函数会执行此过程,该视图函数与 list 模式相关联。
@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}
Example
marklist.html 模板将 Students 列表呈现为 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>
在配置中添加 list 模式并使用 '/' URL 注册它。
config.add_route('list', '/')
Output
启动服务器后,在浏览器中打开 http://localhost:6543/ 。将显示 students 表中现有记录的列表。
请注意,最后两列中的超链接。例如,“id=1”前的“edit”链接指向 http://localhost:6543/show/1 。这些链接用于执行更新和删除操作。
Update Existing Record
在 /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}
Example
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>
Output
让我们使用 id=3 更新记录。单击相应的编辑链接以导航到 http://localhost:6543/show/3
更改分数文本字段中的值并按提交。该表单被重定向到 /update URL,且它调用了 update() 视图。它获取所提交的数据并更新相应的对象,从而也更新了学生表中的底层行。
@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/')
返回语句将浏览器重定向回 “/” URL,该 URL 指向 list() 函数并显示更新的分数表。
确保在运行之前已添加 update 路由至配置。
config.add_route('update', '/update')
Delete a Record
要删除与分数表中的行相对应的记录,请按照最后一列中的删除链接进行操作。例如,单击第 3 行中的删除将发出 http://localhost:6543/delete/3 URL 并调用以下视图函数:
@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'}
Example
从 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>
显然,必须在应用程序配置注册表中添加 delete 路由。
config.add_route('delete', '/delete/{id}')
Output
记录删除操作的结果如下所示:
采取以下步骤来执行上述解释的活动:
-
在 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'}
-
将以下代码另存为 main.py,并将其保存在 testapp 文件夹中。
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/ URL。将显示一张仅有标题且没有记录的表格。
-
按照表格下方的添加新链接来添加记录。
-
单击表格中的“编辑”链接更新记录。
-
单击表格中的“删除”链接删除所选记录。