在Python的Flask框架中实现单元测试的教程

 概要

在前面的章节里我们专注于在我们的小应用程序上一步步的添加功能上。到现在为止我们有了一个带有数据库的应用程序,可以注册用户,记录用户登陆退出日志以及查看修改配置文件。

在本节中,我们不为应用程序添加任何新功能,相反,我们要寻找一种方法来增加我们已写代码的稳定性,我们还将创建一个测试框架来帮助我们防止将来程序中出现的失败和回滚。

让我们来找bug

在上一章的结尾谈到,我故意在应用程序中引入一个bug。接下来让我描述一下它是什么样的bug,然后看看当我们的程序不按照我们意愿执行的时候,它在其中又起了什么样的影响。

应用程序的问题在于,没有保证用户昵称的唯一性。用户昵称是由应用程序自动初始化的。我们首先会考虑使用OpenID provider给出的用户的昵称,然后再考虑使用Email信息中的用户名部分作为用户的昵称。但如果出现重复的昵称,则后面的用户将无法注册成功。更糟糕的是,在修改用户配置的表单中,我们允许用户任意更改他们的昵称,但我们仍然没有对昵称冲突进行检查。

当我们分析完错误产生时应用程序的行为之后,我们将会定位这些问题。

Flask 的调试功能

那么让我们看看当bug被触发时,会出现什么现象。

让我们从创建一个崭新的数据库,在linux下,执行:

rm app.db
./db_create.py

在Windows下,执行:

del app.db
flask/Scripts/python db_create.py

我们需要两个OpenID的账号来重现这个bug。当然这两个账号最理想的状态是来自来个不同的拥有者,那样可以避免他们的cookie把情况搞的更复杂。通过如下步骤创建冲突的昵称:

  • 用第一个账号登陆
  • 进入用户信息属性编辑页面,将昵称改为“dup”
  • 登出系统
  • 用第二个账号登陆
  • 修改第二个账号的用户信息属性,将昵称改为“dup”

哎哟!sqlalchemy中抛出了一个异常,来看一下错误信息:

lalchemy.exc.IntegrityError
IntegrityError: (IntegrityError) column nickname is not unique u'UPDATE user SET nickname=?, about_me=? WHERE user.id = ?' (u'dup', u'', 2)

错误的后面是这个错误的堆栈信息,事实上,这是一个相当不错的错误提示,你可以转向任何框架检查代码或者在浏览器里执行正确的表达式。

这个错误信息相当明确,我们试图在数据插入一个重复的昵称,数据库的昵称字段是一个卫衣键,因此这样的操作是无效的。

除了实际的错误,在我们手头上还有一个次要的错误。如果一个用户不注意在我们应用程序里引起了一个错误(这一个错误或者任何其他原因引起的异常),应用程序将向他/她暴漏错误信息和堆栈信息,而不是暴露给我们。对于我们开发者来说这是个很好的特性,但是很多时候我们不想让用户看到这些信息。

这么长时间以来,我们一直在debug模式下运行我们的应用程序,我们通过设置debug=True的参数来启用应用程序的debug模式。这里我们在运行脚本run.py里配置。

当我们这样开发应用是方便的,但是我们需要在生产环境上关闭debug模式。 让我们创建另一个启动脚本文件设置关闭dubug模式(filerunp.py):

#!flask/bin/python
from app import app
app.run(debug = False)

现在重新启动应用:

./runp.py

并且现在再尝试重命名第二个账号nickname成‘dup'

这次我们没有获取到一个错误信息,取而代之,我们得到了一个HTTP 500错误码,这是个内部服务器错误。虽然这不容易定位错误,但至少没有暴露我们应用程序的任何细节给陌生人。当调试关闭后出现一个异常时,Flask会产生一个500页面。

虽然这样好些了,但现在仍存在两个问题。首先美化问题:默认的500页面很丑陋。第二个问题更重要些,当用户操作失败时,我们无法获取到错误信息了,因为错误在后台默默的处理了。幸运的是有个简单方式来处理这两个问题。

定制HTTP错误处理程序

Flask为应用程序提供了一个机制来安装他们自己的错误页面,作为例子,让我们定义两个最常见的HTTP 404和500错误的自定义页面。定制其他错误页面也是同样的方式。

使用一个修饰来声明一个定制的错误处理程序 (fileapp/views.py):

@app.errorhandler(404)
def internal_error(error):
  return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
  db.session.rollback()
  return render_template('500.html'), 500

这地方无需多言,因为他们都是不言而喻的。唯一有趣的地方时错误500处理中的rollack语句,这个地方是不可缺少的因为这个方法会被当做一个异常调用。如果因为数据库错误导致一个异常,那么数据库的会话将变成一个无效状态,因此我们需要回滚它,以防止一个会话转向一个500错误的模板。

这是一个404错误在模版

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>File Not Found</h1>
<p><a href="{{url_for('index')}}">Back</a></p>
{% endblock %}

这是一个500错误的模版

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>An unexpected error has occurred</h1>
<p>The administrator has been notified. Sorry for the inconvenience!</p>
<p><a href="{{url_for('index')}}">Back</a></p>
{% endblock %}

注意,我们会继续使用我们base.html 布局, 这样我们的错误页看起来比较舒服

通过email发送错误日志

为了处理第二个问题我们需要配置应用的错误报告机制。

第一个是每当有错误发生时把错误日志通过邮件发送给我们。

首先,我们需要在我们的应用配置邮件服务器和管理员列表 (fileconfig.py):

# mail server settings
MAIL_SERVER = 'localhost'
MAIL_PORT = 25
MAIL_USERNAME = None
MAIL_PASSWORD = None

# administrator list
ADMINS = ['you@example.com']

当然,你要把上面的配置改成你自己的才有意义

Flask 使用通用的Python logging模块, 所以设置发送错误日志邮件非常简单. (fileapp/__init__.py):

from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD

if not app.debug:
  import logging
  from logging.handlers import SMTPHandler
  credentials = None
  if MAIL_USERNAME or MAIL_PASSWORD:
    credentials = (MAIL_USERNAME, MAIL_PASSWORD)
  mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT), 'no-reply@' + MAIL_SERVER, ADMINS, 'microblog failure', credentials)
  mail_handler.setLevel(logging.ERROR)
  app.logger.addHandler(mail_handler)

Note that we are only enabling the emails when we run without debugging.
注意,我们要的非dubug模式下开启邮件功能.

在没有邮件服务器的pc上测试邮件功能也很容易,幸好Python有SMTP的测试排错的服务器(SMTP debugging server)。打开一个控制台窗口,并且运行下面的命令:

python -m smtpd -n -c DebuggingServer localhost:25

当程序运行的时候,应用接收和发送邮件会在控制台窗口中显示出来。

打印日志到文件

通过邮件接收错误日志非常不错,但是,这是不够的。有些导致失败的条件不会触发异常并且不是主要的问题,所以我们需要将日志保存到log文件中,在某些情况下,需要日志来进行排错。

出于这个原因,我们的应用需要一个日志文件。

开启文件日志和邮件日志很相似(fileapp/__init__.py):

if not app.debug:
  import logging
  from logging.handlers import RotatingFileHandler
  file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1 * 1024 * 1024, 10)
  file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
  app.logger.setLevel(logging.INFO)
  file_handler.setLevel(logging.INFO)
  app.logger.addHandler(file_handler)
  app.logger.info('microblog startup')

日志文件将在tmp目录下生成,文件名叫microblog.log。我们使用的RotatingFileHandler方法中有个限制日志数量的参数。在这种情况下,我们限制了一个日志文件大小为1M,并且把最后的十个文件作为备份。
logging.Formatter类提供了日志信息的定义格式,由于这些信息将写到一个文件中,我们想获取到尽可能多的信息,因此除了日志信息和堆栈信息,我们还写了个时间戳,日志级别和文件名、信息的行号。

为了使日志更有用,我们降低了应用程序日志和文件日志处理程序的日志级别,因为这样我们将有机会在没有错误情况下把有用信息写入到日志中。作为一个例子,我们启动时将日志的级别设置为信息级别。从现在开始,每次你启动应用程序将记录你的调试信息。

当我们没有使用日志时,调试一个在线和使用中的web服务时是件非常困难的事,把日志信息写入到文件中,将是我们诊断和解决问题的一个有用工具,所以现在让我们准备好使用这个功能吧。

bug修复

让我们来修复下昵称重复的bug.

前面讨论过,有两个地方目前还没有处理重复。首先是Flask-Login的after_login处理,这个方法将在用户成功登陆到系统后调用,我们需要创建一个新的User实例。这是受影响的一个代码片段,我们做了修复 (fileapp/views.py):

if user is None:
    nickname = resp.nickname
    if nickname is None or nickname == "":
      nickname = resp.email.split('@')[0]
    nickname = User.make_unique_nickname(nickname)
    user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
    db.session.add(user)
    db.session.commit()

我们解决这个问题的方法是让User类选择一个唯一的名字给我们,这也是 make_unique_nickname方法所做的(fileapp/models.py):

class User(db.Model):
  # ...
  @staticmethod
  def make_unique_nickname(nickname):
    if User.query.filter_by(nickname = nickname).first() == None:
      return nickname
    version = 2
    while True:
      new_nickname = nickname + str(version)
      if User.query.filter_by(nickname = new_nickname).first() == None:
        break
      version += 1
    return new_nickname
  # ...

这个方法简单的添加一个计数器来生成一个唯一的昵称名。例如,如果用户名“miguel”存在,这个方法将建议你使用“miguel2”,但是如果它也存在就会生成“miguel3”···。注意我们把这个方法设定为静态方法,因为这个操作不适用于任何类的实例。

第二个导致重复昵称的地方是编辑页面视图函数,这算是用户选择昵称的一个小恶作剧,正确的方式是不允许用户输入重复名称,让用户更换为另一个名称。我们通过添加form表单验证来解决这个问题,如果用户输入一个无效的昵称,将会得到一个字段验证失败信息,添加我们的验证只需重写form的validate方法 (fileapp/forms.py):


class EditForm(Form):
  nickname = TextField('nickname', validators = [Required()])
  about_me = TextAreaField('about_me', validators = [Length(min = 0, max = 140)])

  def __init__(self, original_nickname, *args, **kwargs):
    Form.__init__(self, *args, **kwargs)
    self.original_nickname = original_nickname

  def validate(self):
    if not Form.validate(self):
      return False
    if self.nickname.data == self.original_nickname:
      return True
    user = User.query.filter_by(nickname = self.nickname.data).first()
    if user != None:
      self.nickname.errors.append('This nickname is already in use. Please choose another one.')
      return False
    return True

表单的构造函数增加了一个新的参数original_nickname,验证方法validate使用这个参数来判断昵称是否修改了,如果没有修改就直接返回它,如果已经修改了,方法会确认下新的昵称在数据库是否已经存在。

接下来我们在视图函数中添加新的构造器参数:

@app.route('/edit', methods = ['GET', 'POST'])
@login_required
def edit():
  form = EditForm(g.user.nickname)
  # ...

完成这个修改我们还必须在表单的模板中启用错误显示字段 (文件app/templates/edit.html):

<td>Your nickname:</td>
    <td>
      {{form.nickname(size = 24)}}
      {% for error in form.errors.nickname %}
      <br><span style="color: red;">[{{error}}]</span>
      {% endfor %}
    </td>

现在这个bug已经修复了,阻止了重复数据的出现···除非这些验证方法不能正常工作了。在两个或者多个线程/进程并行存取数据库时,这仍然存在一个潜在的问题,但这些都是以后我们文章讨论的主题。

在这里你可以尝试选择一个重复的名称来看看表单如何处理这些错误的。
 
单元测试框架

先把上面关于测试的会话放一下,咱们来讨论下关于自动化测试的话题。

随着应用程序规模的增长,越来越难以确定代码的改变是否会影响到现有的功能。

传统的方法防止回归是一个很好的方式,你通过编写单元测试来测试应用程序所有不同功能,每一个测试集中于一个点来验证结果是否和预期的一致。测试程序通过定期的执行来确认应用程序是否在正常工作。当测试覆盖率变大时,你就可以自信的修改和添加新功能,只需通过测试程序来验证下是否影响到了应用程序现有功能。

现在我们使用python的unittest测试组件来创建个简单的测试框架 (tests.py):

#!flask/bin/python
import unittest

from config import basedir
from app import app, db
from app.models import User

class TestCase(unittest.TestCase):
  def setUp(self):
    app.config['TESTING'] = True
    app.config['CSRF_ENABLED'] = False
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'test.db')
    self.app = app.test_client()
    db.create_all()

  def tearDown(self):
    db.session.remove()
    db.drop_all()

  def test_avatar(self):
    u = User(nickname = 'john', email = 'john@example.com')
    avatar = u.avatar(128)
    expected = 'http://www.gravatar.com/avatar/d4c74594d841139328695756648b6bd6'
    assert avatar[0:len(expected)] == expected

  def test_make_unique_nickname(self):
    u = User(nickname = 'john', email = 'john@example.com')
    db.session.add(u)
    db.session.commit()
    nickname = User.make_unique_nickname('john')
    assert nickname != 'john'
    u = User(nickname = nickname, email = 'susan@example.com')
    db.session.add(u)
    db.session.commit()
    nickname2 = User.make_unique_nickname('john')
    assert nickname2 != 'john'
    assert nickname2 != nickname

if __name__ == '__main__':
  unittest.main()

unittest测试组件的讨论超出了本文的范围了,我们这里只需知道TestCase是我们的测试类。setUp和tearDown方法有些特殊,它们分别在每个测试方法前后执行,复杂点的设置可以包含几组测试,每个代表一个单元测试,TestCase的子类和每个组都将拥有独立的setUp和tearDown方法。

这些特殊的setUp和tearDown方法都是非常通用的,在setUp可以方便的修改配置,例如,我们想测试不同的数据库作为主数据库,在tearDown里面只需简单设置下数据库内容就可以。

测试作为方法被实现,一个测试应该运行一些已知结果的应用程序方法,也应当能够断言出结果和预期的不同。

到目前为止,在我们的测试框架里有两个测试。第一个验证来自于上一篇文章的Gravatar avatar URLs生成的是否正确,注意预期的avatar被硬编码在测试中,和User类中返回的对象作比较。

第二个测试验证是test_make_unique_nickname方法,同样也是在User类中。这个测试有点详细,它创建了一个新的用户并且写入数据库中,同时确定名字的唯一性。接下来创建第二个用户,建议使用唯一名称,你可以尝试下使用第一个用户名称。在第二部分测试预期结果是建议使用与之前不同的名称。

运行这个测试套件你只需运行tests.py脚本:

./tests.py

如果出现错误信息,你将会在控制台得到一个报告。
结语

今天关于调试,错误和测试的讨论到此为止,我希望这篇文章能对你有用。

老规矩,如果你有任何评论请写在下面.

微博应用程序的代码今天修改的更新,你可以在这里下载:

下载 microblog-0.7.zip.

(0)

相关推荐

  • 在Python中进行自动化单元测试的教程

    一.软件测试 大型软件系统的开发是一个很复杂的过程,其中因为人的因素而所产生的错误非常多,因此软件在开发过程必须要有相应的质量保证活动,而软件测试则是保证质量的关键措施.正像软件熵(software entropy)所描述的那样:一个程序从设计很好的状态开始,随着新的功能不断地加入,程序逐渐地失去了原有的结构,最终变成了一团乱麻(其实最初的"很好的状态"得加个问号).测试的目的说起来其实很简单也极具吸引力,那就是写出高质量的软件并解决软件熵这一问题. 可惜的是,软件开发人员很少能在编码

  • Python中unittest模块做UT(单元测试)使用实例

    待测试的类(Widget.py) # Widget.py # Python 2.7.6 class Widget: def __init__(self, size = (40,40)): self.size = size def getSize(self): return self.size def reSize(self,width,height): if width <0 or height < 0: raise ValueError, 'illegal size' else: self.

  • 对Python的Django框架中的项目进行单元测试的方法

     Python中的单元测试 我们先来回顾一下Python中的单元测试方法. 下面是一个 Python的单元测试简单的例子: 假如我们开发一个除法的功能,有的同学可能觉得很简单,代码是这样的: def division_funtion(x, y): return x / y 但是这样写究竟对还是不对呢,有些同学可以在代码下面这样测试: def division_funtion(x, y): return x / y if __name__ == '__main__': print division

  • Python之PyUnit单元测试实例

    本文实例讲述了Python之PyUnit单元测试,与erlang eunit单元测试很像,分享给大家供大家参考.具体方法如下: 1.widget.py文件如下: 复制代码 代码如下: #!/usr/bin/python # Filename:widget.py class Widget: def __init__(self, size = (40, 40)): self.size = size   def getSize(self): return self.size   def resize(

  • 详解Python的单元测试

    如果你听说过"测试驱动开发"(TDD:Test-Driven Development),单元测试就不陌生. 单元测试是用来对一个模块.一个函数或者一个类来进行正确性检验的测试工作. 比如对函数abs(),我们可以编写出以下几个测试用例: 输入正数,比如1.1.2.0.99,期待返回值与输入相同: 输入负数,比如-1.-1.2.-0.99,期待返回值与输入相反: 输入0,期待返回0: 输入非数值类型,比如None.[].{},期待抛出TypeError. 把上面的测试用例放到一个测试模块

  • Python单元测试框架unittest简明使用实例

    测试步骤 1. 导入unittest模块 import unittest 2. 编写测试的类继承unittest.TestCase class Tester(unittest.TestCase) 3. 编写测试的方法必须以test开头 def test_add(self) def test_sub(self) 4.使用TestCase class提供的方法测试功能点 5.调用unittest.main()方法运行所有以test开头的方法 复制代码 代码如下: if __name__ == '__

  • 在Python编程过程中用单元测试法调试代码的介绍

    对于程序开发新手来说,一个最常见的困惑是测试的主题.他们隐约觉得"单元测试"是很好的,而且他们也应该做单元测试.但他们却不懂这个词的真正含义.如果这听起来像是在说你,不要怕!在这篇文章中,我将介绍什么是单元测试,为什么它有用,以及如何对Python的代码进行单元测试. 什么是测试? 在讨论为什么测试很有用.怎样进行测试之前,让我们先花几分钟来定义一下"单元测试"究竟是什么.在一般的编程术语中,"测试"指的是通过编写可以调用的代码(独立于你实际应用

  • python单元测试unittest实例详解

    本文实例讲述了python单元测试unittest用法.分享给大家供大家参考.具体分析如下: 单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情.虽然会很快熟悉内容,但是修改和调试将是一件痛苦的事情,如果你在修改了代码后出现问题的话,而单元测试可以帮助我们很快准确的定位到问题的位置,出现问题的模块和单元.所以这是一件很愉快的事情,因为我们知道其它修改或没有修改的地方仍然是正常工作的,而我们目前的唯一问题就是搞定眼前这个有点问题的"家伙&qu

  • Python单元测试框架unittest使用方法讲解

    概述 1.测试脚手架(test fixture) 测试准备前要做的工作和测试执行完后要做的工作.包括setUp()和tearDown(). 2.测试案例(test case) 最小的测试单元. 3.测试套件(test suite) 测试案例的集合. 4.测试运行器(test runner) 测试执行的组件. 命令行接口 可以用命令行运行测试模块,测试类以及测试方法. 复制代码 代码如下: python -m unittest test_module1 test_module2 python -m

  • 在Python的Flask框架中实现单元测试的教程

     概要 在前面的章节里我们专注于在我们的小应用程序上一步步的添加功能上.到现在为止我们有了一个带有数据库的应用程序,可以注册用户,记录用户登陆退出日志以及查看修改配置文件. 在本节中,我们不为应用程序添加任何新功能,相反,我们要寻找一种方法来增加我们已写代码的稳定性,我们还将创建一个测试框架来帮助我们防止将来程序中出现的失败和回滚. 让我们来找bug 在上一章的结尾谈到,我故意在应用程序中引入一个bug.接下来让我描述一下它是什么样的bug,然后看看当我们的程序不按照我们意愿执行的时候,它在其中

  • Python的Flask框架中的Jinja2模板引擎学习教程

    Flask的模板功能是基于Jinja2模板引擎来实现的.模板文件存放在当前目前下的子目录templates(一定要使用这个名字)下. main.py 代码如下: from flask import Flask, render_template app = Flask(__name__) @app.route('/hello') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html

  • 在Python的Flask框架中构建Web表单的教程

    尽管Flask的request对象提供的支持足以处理web表单,但依然有许多任务会变得单调且重复.表单的HTML代码生成和验证提交的表单数据就是两个很好的例子. Flask-WTF扩展使得处理web表单能获得更愉快的体验.该扩展是一个封装了与框架无关的WTForms包的Flask集成. Flask-WTF和它的依赖集可以通过pip来安装: (venv) $ pip install flask-wtf 1.跨站请求伪造(CSRF)保护 默认情况下,Flask-WTF保护各种形式对跨站请求伪造(CS

  • 在Python的Flask框架中使用模版的入门教程

     概述 如果你已经阅读过上一个章节,那么你应该已经完成了充分的准备工作并且创建了一个很简单的具有如下文件结构的Web应用:   microblog     |-flask文件夹     |-<一些虚拟环境的文件>     |-app文件夹     |  |-static文件夹     |  |-templates文件夹     |  |-__init__.py文件     |  |-views.py文件     |-tmp文件夹     |-run.py文件 亲,想要运行这个程序么?那就运行这

  • Python的Flask框架中web表单的教程

     概要 在前面章节我们为主页定义了一个简单的模板,部分尚未实现的模块如用户或帖子等使用模拟的对象作为临时占位. 本章我们将看到如何利用web表单填补这些空白. web表单是web应用中最基本的构建要素,我们将通过表单来实现用户发帖和应用登录功能. 完成本章内容你需要基于前面章节完成的微博应用代码,请确认这些代码已安装并能正常运行. 配置 Flask-WTF是WTForms项目的Flask框架扩展,我们将用他来帮助我们处理web表单. 大部分Flask扩展都需要定义相关配置项,所以我们先来在应用根

  • Python的Flask框架中Flask-Admin库的简单入门指引

    Flask-Admin是一个功能齐全.简单易用的Flask扩展,让你可以为Flask应用程序增加管理界面.它受django-admin包的影响,但用这样一种方式实现,开发者拥有最终应用程序的外观.感觉和功能的全部控制权. 本文是关于Flask-Admin库的快速入门.本文假设读者预先具有一些Flask框架的知识. 介绍 初始化 增加视图 身份验证 生成URL 模型视图 文件管理 介绍 这个库打算做到尽可能的灵活.并且开发者不需要任何猴子补丁就可以获得期望的功能. 这个库使用一个简单而强大的概念-

  • Python的Flask框架中实现简单的登录功能的教程

     回顾 在前面的系列章节中,我们创建了一个数据库并且学着用用户和邮件来填充,但是到现在我们还没能够植入到我们的程序中. 两章之前,我们已经看到怎么去创建网络表单并且留下了一个实现完全的登陆表单. 在这篇文章中,我们将基于我门所学的网络表单和数据库来构建并实现我们自己的用户登录系统.教程的最后我们小程序会实现新用户注册,登陆和退出的功能. 为了能跟上这章节,你需要前一章节最后部分,我们留下的微博程序.请确保你的程序已经正确安装和运行. 在前面的章节,我们开始配置我们将要用到的Flask扩展.为了登

  • Python的Flask框架中集成CKeditor富文本编辑器的教程

    CKeditor是目前最优秀的可见即可得网页编辑器之一,它采用JavaScript编写.具备功能强大.配置容易.跨浏览器.支持多种编程语言.开源等特点.它非常流行,互联网上很容易找到相关技术文档,国内许多WEB项目和大型网站均采用了CKeditor. 下载CKeditor 访问CKeditor官方网站,进入下载页面,选择Standard Package(一般情况下功能足够用了),然后点击Download CKEditor按钮下载ZIP格式的安装文件.如果你想尝试更多的功能,可以选择下载Full

  • 在Python程序和Flask框架中使用SQLAlchemy的教程

    ORM 江湖 曾几何时,程序员因为惧怕SQL而在开发的时候小心翼翼的写着sql,心中总是少不了恐慌,万一不小心sql语句出错,搞坏了数据库怎么办?又或者为了获取一些数据,什么内外左右连接,函数存储过程等等.毫无疑问,不搞懂这些,怎么都觉得变扭,说不定某天就跳进了坑里,叫天天不应,喊地地不答. ORM 的出现,让畏惧SQL的开发者,在坑里看见了爬出去的绳索,仿佛天空并不是那么黑暗,至少再暗,我们也有了眼睛.顾名思义,ORM 对象关系映射,简而言之,就是把数据库的一个个table(表),映射为编程语

  • Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程

    使用Flask-SQLAlchemy管理数据库 Flask-SQLAlchemy是一个Flask扩展,它简化了在Flask应用程序中对SQLAlchemy的使用.SQLAlchemy是一个强大的关系数据库框架,支持一些数据库后端.提供高级的ORM和底层访问数据库的本地SQL功能. 和其他扩展一样,通过pip安装Flask-SQLAlchemy: (venv) $ pip install flask-sqlalchemy 在Flask-SQLAlchemy,数据库被指定为URL.表格列出三个最受欢

随机推荐