Flask-SQLAlchemyの使い方

最近仕事でFlask-SQLAlchemyをよく使うので自分のためにメモしておく。

Setup

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

...
db.create_all()

Basic (Insert,Update,Delete)

Insert

user = User(name="foo")
db.session.add(user)
db.session.commit()

Update

user = db.session.query(User).get(1)
user.name = "foooo"
db.session.flush()

Delete

user = db.session.query(User).get(1)
db.session.delete(user)
db.session.commit()

//Delete children of the user
user.posts.remove()

Queryの例

all()

users = db.session.query(User).all()

filter_by(**kwargs)

user = db.session.query(User).filter_by(name="foo").first()

get(id)

user = db.session.query(User).get(1)

Relationshipの例

One to Many (一対多)

ユーザーが複数の投稿を持っているケースを想定。

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    posts = db.retionship("Post",backref="uesr",lazy="dynamic")


class Post(db.Model):
    __tablename__ = "posts"
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

Many To Many (多対多)

投稿した記事がタグを持っていて、タグは複数のタグを持っているケースを想定。

posttags = db.Table("posttags",
        db.Column("post_id",db.Integer,db.ForeignKey("posts.id")),
        db.Column("tag_id",db.Integer,db.ForeignKey("tags.id"))
        )

class Post(db.Model):
    __tablename__ = "posts"
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship("Post",secondary=posttags,backref="posts")


class Tag(db.Model):
    __tablename__ = "tags"
    id = db.Column(db.Integer, primary_key=True)

GunicornをFabric経由で操作する

Gunicornで動いているアプリをサーバー側で起動、再起動ができるようにした。 プロセスを止めるにはpidを指定してkillすればいいということを学んだ。

gunicorn.conf.py

usr/bin/python
gunicorn.conf.py

bind = "0.0.0.0:5000"
workers = 2
worker_class = 'sync'
max_requests = 1000
timeout = 30
keep_alive = 2
preload = True
daemon = True

start.sh

GUNICORN=/usr/bin/gunicorn
ROOT=/your/app/path
PID=/var/run/gunicorn/your.pid
APP=run:app

if [ -f $PID ]; then rm $PID; fi
    cd $ROOT
    source venv/bin/activate
    exec $GUNICORN -c $ROOT/gunicorn.conf.py –pid=$PID $APP

fabfile.py

#coding: utf-8
from fabric.api import run,env,local,settings
from fabric.operations import sudo
from fabric.context_managers import cd
import os
import subprocess

from fabric.api import env, run
env.use_ssh_config = True
env.hosts = ["your ip address"]
env.key_filename = "/root/.ssh/authorized_keys"
env.user = "username"
env.password = "password"


def start():
    with settings():
        with cd("/your/path"):
            sudo("""
            source start.sh
            """,pty=False)

def stop():
    with settings():
        pid = get_pid()
        sudo("kill {}".format(pid))

def restart():
    try:
        stop()
    except:
        print("There isn't pid")
    start()

これで fab start/stop/restartで自由に再起動を行うことができる。

メモ

  • タスクをバックグラウンドで実行させたい時は pty=False としないと動作しない。

スクレイピングでDMMのサイトに載っているAV女優一覧を取り出す

早速ですが、DMMのサイトに載っているAV女優一覧を取り出すコード例を通してスクレイピングの楽しさを味わっていただければ幸いです。

def write_women(self):
    suffixes = [
    "a","ka","sa","ta","na","ha","ma",
    "ya","ra","wa"
    ]
    res =  ""
    for suffix in suffixes:
    base_url = "http://www.dmm.co.jp/digital/videoa/-/actress/=/keyword="
    url = base_url + suffix + "/"

    r = requests.get(url)
    soup = BeautifulSoup(r.text)
    if soup.find("ul",{"class":"act-box-100"}) is None:
        continue
    recommend_imgs = soup.find("ul",{"class":"act-box-100"}).find_all("img")

    for recommend_img in recommend_imgs:
        name  = recommend_img.get("alt")
        res += name + " "

        normal_imgs = soup.find("ul",{"class":"act-box-65"}).find_all("img")

        if normal_imgs is None:
            continue

        for normal_img in normal_imgs:
            name  = normal_img.get("alt")
            res += name + " "

    return res

たったの30行程度で実現できてよかった。今年一番の出来だと思う。

Flaskで作ったWebアプリをHerokuで最速公開するためのheroku-readyというテンプレートを作りました

Flask製のWebアプリを作るとき毎回同じ手順を踏んでいたりして面倒だったので、heroku-readyというテンプレートを作った。特徴は以下のとおり。

  • Heroku連携
  • Python3対応
  • AlembicによるDBのマイグレーション
  • Flask-SQLAlchemy連携
  • Postgresql連携
  • Blueprintを使った構成

現時点で自分が欲しかった機能は実装されている。

INSTALL

git clone https://github.com/okamurayasuyuki/heroku-ready
pyvenv-3.4 venv
source venv/bin/activate
pip install -r requirements.txt

SETUP

次にbasic_app.config.pyのdb_pathを以下のように置き換える。

db_path = 'postgresql://username:password@hostname/db_name'

SETUP For Heroku

$git add .
$git commit -m 'init'
$heroku create
$git push heroku master

HerokuでPostgresqlを使えるようにするためにAddonを追加する。

$heroku addons:add heroku-postgresql:dev

CREATE DB

ローカル

$python -i
>> from basic_app import create_app,db
>> create_app().app_context().push()
>> db.create_all()

リモート(Heroku)

$heroku run python -i

>> from basic_app import create_app,db
>> create_app().app_context().push()
>> db.create_all()

MIGRATE DB

ローカル

$alembic revision --autogenerate -m "add password"
$alembic upgrade head

リモート(Heroku)

$heroku run alembic revision --autogenerate -m "add password"
$heroku run alembic upgrade head

Finally

自分が使っていて不満に思っていたところは多分このテンプレートで解消できたはず。 良かったら使ってみてください。heroku-ready