Flask_0x04 數據庫 & 電子郵件

0x1 數據庫html

  1.1 Flask-SQLAlchemypython

    git checkout 5amysql

    Python數據庫框架能夠直接處理Python對象,不用處理表、文檔或查詢語言等數據庫實體git

    SQLAlcchemy是一個強大的關係型數據庫框架,提供了高層ORM和使用原生SQL的底層功能sql

(venv) $ pip install flask-sqlalchemy

    Flask-SQLAlchemy數據庫URLshell

MySQL mysql://username:password@hostname/database Postgres postgresql://username:password@hostname/database SQLite(Unix) sqlite:////absolute/path/to/database SQLite(Windows) sqlite:///c:/absolute/path/to/database

    SQLite不須要使用服務器,所以不用指定hostname、username、password 數據庫

    Flask-SQLAlchemy要求每一個模型都要定義主鍵,這一列常常命名爲idflask

    hello.pywindows

from flask.ext.sqlalchemy import SQLAlchemy basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] =\ 'sqlite:///' + os.path.join(basedir, 'data.sqlite') app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True db = SQLAlchemy(app)

    程序使用的數據庫URL必須保存到Flask配置對象的SQLALCHEMY_DATABASE_URI鍵中安全

    SQLALCHEMY_COMMIT_ON_TEARDOWN設爲True,每次請求結束後都會自動提交數據庫中的變更

    模型表示程序使用的持久化實體,ORM中,模型通常是一個Python類,類中屬性對應數據庫表中的列

 

    定義模型:Flask-SQLAlchemy建立的數據庫實例爲模型提供了一個基類和一系列輔助類和輔助函數,用於定義模型的結構

    hello.py:定義Role和User模型

class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) users = db.relationship('User',backref='role',lazy='dynamic') def __repr__(self): return '<Role %r>' % self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) role_id = db.Column(db.Integer,db.ForeignKey('roles.id')) def __repr__(self): return '<User %r>' % self.username

    __tablename__定義在數據庫中使用的表名

    __repr()__ 方法返回一個具備可讀性的字符串表示模型,可在調試和測試時使用

    添加到User模型中的role_id列被定義爲外鍵,傳給db.ForeignKey()的參數roles.id代表這列的值是roles表中行的id值

    db.Column類構造函數的第一個參數是數據庫列和模型屬性的類型

    最經常使用的SQLAlchemy列類型

類型名 Python類型 說  明 Integer int 普通整數,通常是 32 位 SmallInteger int 取值範圍小的整數,通常是 16 位 BigInteger int 或 long 不限制精度的整數 Float float 浮點數 Numeric decimal.Decimal 定點數 String str 變長字符串 Text str 變長字符串,對較長或不限長度的字符串作了優化 Unicode unicode 變長 Unicode 字符串 UnicodeText unicode 變長 Unicode 字符串,對較長或不限長度的字符串作了優化 Boolean bool 布爾值 Date datetime.date 日期 Time datetime.time 時間 DateTime datetime.datetime 日期和時間 Interval datetime.timedelta 時間間隔 Enum str 一組字符串 PickleType 任何 Python 對象 自動使用 Pickle 序列化 LargeBinary str 二進制文件

    經常使用的SQLAlchemy列選項

primary_key   若是設爲 True ,這列就是表的主鍵
unique        若是設爲 True ,這列不容許出現重複的值
index         若是設爲 True ,爲這列建立索引,提高查詢效率
nullable      若是設爲 True ,這列容許使用空值;若是設爲 False ,這列不容許使用空值
default       爲這列定義默認值

    對於Role類的實例,其users屬性將返回與角色相關聯的用戶組成的列表,db.relationship()第一個參數代表這個關係的另外一端是哪一個模型

    經常使用SQLAlchemy關係選項

backref       在關係的另外一個模型中添加反向引用
primaryjoin   明確指定兩個模型之間使用的聯結條件。只在模棱兩可的關係中須要指定
lazy          指定如何加載相關記錄。可選值有 select (首次訪問時按需加載)、 immediate (源對象加
              載後就加載)、 joined (加載記錄,但使用聯結)、 subquery (當即加載,但使用子查詢),
              noload (永不加載)和 dynamic (不加載記錄,但提供加載記錄的查詢)

  1.2 數據庫操做

    git checkout 5b

    建立表

(venv) $ python hello.py shell >>> from hello import db >>> db.create_all()

    刪除表

>>> db.drop_all()

    插入行

>>> from hello import Role, User >>> admin_role = Role(name='Admin') >>> mod_role = Role(name='Moderator') >>> user_role = Role(name='User') >>> user_john = User(username='john', role=admin_role) >>> user_susan = User(username='susan', role=user_role) >>> user_david = User(username='david', role=user_role)

    添加到會話

>>> db.session.add(admin_role) >>> db.session.add(mod_role) >>> db.session.add(user_role) >>> db.session.add(user_john) >>> db.session.add(user_susan) >>> db.session.add(user_david) 簡寫 >>> db.session.add_all([admin_role, mod_role, user_role, ... user_john, user_susan, user_david])

    把對象寫入數據庫,調用commit()方法提交會話

>>> db.session.commit()

    修改行:把 "Admin" 角色重命名爲 "Administrator"

>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role) >>> db.session.commit()

    刪除行:把 "Moderator" 角色從數據庫中刪除

>>> db.session.delete(mod_role) >>> db.session.commit()

    查詢行

>>> Role.query.all() [<Role u'Administrator'>, <Role u'User'>] >>> User.query.all() [<User u'john'>, <User u'susan'>, <User u'david'>] >>> User.query.filter_by(role=user_role).all() [<User u'susan'>, <User u'david'>]

    查看 SQLAlchemy 爲查詢生成的原生 SQL 查詢語句,把 query 對象轉換成字符串:

>>> str(User.query.filter_by(role=user_role))

    起一個查詢,加載名爲"User"的用戶角色

>>> user_role = Role.query.filter_by(name='User').first()

    filter_by()過濾器在query對象上調用,返回更精確query對象

    經常使用SQLAlchemy過濾器

完整SQLAlchemy文檔:http://docs.sqlalchemy.org/en/latest/
filter() 把過濾器添加到原查詢上,返回一個新查詢 filter_by() 把等值過濾器添加到原查詢上,返回一個新查詢 limit() 使用指定的值限制原查詢返回的結果數量,返回一個新查詢 offset() 偏移原查詢返回的結果,返回一個新查詢 order_by() 根據指定條件對原查詢結果進行排序,返回一個新查詢 group_by() 根據指定條件對原查詢結果進行分組,返回一個新查詢

    經常使用的SQLAlchemy查詢執行函數

all() 以列表形式返回查詢的全部結果 first() 返回查詢的第一個結果,若是沒有結果,則返回 None first_or_404() 返回查詢的第一個結果,若是沒有結果,則終止請求,返回 404 錯誤響應 get() 返回指定主鍵對應的行,若是沒有對應的行,則返回 None get_or_404() 返回指定主鍵對應的行,若是沒找到指定的主鍵,則終止請求,返回 404 錯誤響應 count() 返回查詢結果的數量 paginate() 返回一個 Paginate 對象,它包含指定範圍內的結果

    lazy='dynamic' 禁止自動執行查詢,user_role.users回返回一個還沒有執行的查詢,所以能夠添加過濾器

>>> user_role.users.order_by(User.username).all() [<User u'david'>, <User u'susan'>] >>> user_role.users.count() 2

  1.3 在視圖函數中操做數據庫

    hello.py:在視圖函數中操做數據庫

@app.route('/', methods=['GET','POST']) def index(): form = NameForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.name.data).first() if user is None: user = User(username=form.name.data) db.session.add(user) session['known'] = False else: session['known'] = True session['name'] = form.name.data form.name.data = ''
        return redirect(url_for('index')) return render_template('index.html', form=form, name=session.get('name'),know=session.get('know',False))

    提交表單後,程序使用filter_by()在數據庫中查找提交的名字

    index.html使用known參數顯示用戶是否在數據庫中

    templates/index.html

{% if not known %} <p>Pleased to meet you</p> {% else %} <p>Happy to see you again!</p> {% endif %}

    集成Python shell,讓Flask-Script的shell命令自動導入特定的對象

    git checkout 5c

    hello.py:爲shell命令添加一個上下文

from flask.ext.script import Shell def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context))

  1.4 Flask-Migrate數據庫遷移

    git checkout 5d

    更新表的更好方法是使用數據庫遷移框架

    數據庫遷移框架能跟蹤數據庫模式的變化,而後增量式的把變化應用到數據庫中

(venv) $ pip install flask-migrate

    hello.py:配置Flask-Migrate

from flask.ext.migrate import Migrate, MigrateCommand #...
migrate = Migrate(app, db) manager.add_command('db',MigrateCommand)

    用init子命令遷移倉庫:

(venv) $ python hello.py db init

    這個命令會建立一個migrations文件夾,全部遷移腳本都存放其中

    migrate 子命令用來自動建立遷移腳本:

(venv) $ python hello.py db migrate -m "initial migration"

    更新數據庫

(venv) $ python hello.py db upgrade

 

0x2 電子郵件

  2.1 Flask-Mail

    git checkout 6a

(venv) $ pip install flask-mail

    Flask-Mail SMTP服務器的配置

MAIL_SERVER localhost 電子郵件服務器的主機名或 IP 地址 MAIL_PORT 25 電子郵件服務器的端口 MAIL_USE_TLS False 啓用傳輸層安全(Transport Layer Security,TLS)協議 MAIL_USE_SSL False 啓用安全套接層(Secure Sockets Layer,SSL)協議 MAIL_USERNAME None 郵件帳戶的用戶名 MAIL_PASSWORD None 郵件帳戶的密碼

    hello.py:配置Flask-Mail使用Gmail

app.config['MAIL_SERVER'] = 'smtp.googlemail.com' app.config['MAIL_PORT'] = 587 app.config['MAIL_USE_TLS'] = True app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')

    hello.py:初始化Flask-Mail

from flask.ext.mail import Mail mail = Mail(app)

    環境中定義用戶名密碼

Linux: (venv) $ export MAIL_USERNAME=<Gmail username> (venv) $ export MAIL_PASSWORD=<Gmail password> windows: (venv) $ set MAIL_USERNAME=<Gmail username> (venv) $ set MAIL_PASSWORD=<Gmail password>

    Python shell中發送電子郵件

(venv) $ python hello.py shell >>> from flask.ext.mail import Message >>> from hello import mail >>> msg = Message('test subject', sender='xxx@qq.com', ... recipients=['xxx@qq.com']) >>> msg.body = 'text body'
>>> msg.html = '<b>HTML</b> body'
>>> with app.app_context(): ... mail.send(msg) ...

  2.2 程序中集成發電子郵件功能

    git checkout 6a

    避免每次手動編郵件信息,還可使用Jinja2渲染郵件正文

from flask.ext.mail import Mail,Message app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = 'Flask' app.config['FLASKY_MAIL_SENDER'] = 'xxx@gmail.com' app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN') def send_mail(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' '+subject, sender = app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) mail.send(msg)

    這個函數定義了郵件主題前綴和發件人地址

    電子郵件收件人保存在FLASKY_ADMIN環境變量中

    email函數參數分別爲收件人地址、主題、渲染郵件正文的模板和關鍵字參數列表

    指定模板時不能包含擴展名,這樣才能使用兩個模板分別渲染純文本正文和富文本正文

    index()要實現功能:表單提交數據庫沒有的用戶名,程序就會向管理員發送一封電子郵件

    hello.py:電子郵件示例

@app.route('/', methods=['GET','POST']) def index(): form = NameForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.name.data).first() if user is None: user = User(username=form.name.data) db.session.add(user) session['known'] = False if app.config['FLASKY_ADMIN']: send_mail(app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user) else: session['known'] = True session['name'] = form.name.data form.name.data = ''
        return redirect(url_for('index')) return render_template('index.html', form=form, name=session.get('name'),know=session.get('know',False))

    定義FLASKY_ADMIN環境變量

Linux:(venv) $ export FLASKY_ADMIN=<your-email-address> Windows:(venv) $ set FLASKY_ADMIN=<Gmail username>

2.3 異步發送電子郵件

    git checkout 6b

    mail.send()函數在發送電子郵件是停滯了幾秒,爲避免請求過程當中沒必要要的延遲,能夠把發送電子郵件函數移到後臺線程

    hello.py:異步發送電子郵件

from threading import Thread def send_async_email(app, msg): with app.app_context(): mail.send(msg) def send_mail(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' '+subject, sender = app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) thr = Thread(target=send_async_email, args=[app, msg]) thr.start() return thr

    發送大量電子郵件時能夠把執行send_async_email()函數操做發給Celery(http://www.celeryproject.org/)任務隊列

相關文章
相關標籤/搜索