2018-12-07

Flaskで多対多のデータベース構成にするときにやるあれ

Flaskで簡単なWebアプリを作るときに、DBの持ち方をどうするか問題にぶち当たったので、忘備録。

* Flask-SQLAlchemy
* Flask-Migrate

を使って、DBを管理している。

モデルは

* User
* Followers

というテーブルを持っている。User間のフォロー機能を実装したい。


app/init.py
from flask import Flask
from config import Config, CONSUMER_KEY, CONSUMER_SECRET, CALLBACK_URL
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager


app = Flask(__name__)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

app.config.from_object(Config)
login = LoginManager(app)

from app import routes, models
from app.models import User

でモデルが

models.py

from datetime import datetime
from app import db
from flask_login import UserMixin
from app import login

followers = db.Table('followers',
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)

likers = db.Table('likers',
    db.Column('liker_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('liked_id', db.Integer, db.ForeignKey('user.id'))
)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    registered_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    last_updated_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    last_logined_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    twitter_id = db.Column(db.String(64), index=True, unique=True) 
    screen_name = db.Column(db.String(64), index=True, unique=True)
    username = db.Column(db.String(64), index=True, unique=True)
    image_url = db.Column(db.String(128), index=True, unique=True)
    description = db.Column(db.String(300), index=True, unique=True)

    followed = db.relationship(
    'User', secondary=followers,
    primaryjoin=(followers.c.follower_id == twitter_id),
    secondaryjoin=(followers.c.followed_id == twitter_id),
    backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

    liked = db.relationship(
    'User', secondary=likers,
    primaryjoin=(likers.c.liker_id == twitter_id),
    secondaryjoin=(likers.c.liked_id == twitter_id),
    backref=db.backref('likers', lazy='dynamic'), lazy='dynamic')

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def mutual_ids_list(self):
        print ("twitter_id".format(self.twitter_id))
        followed_ids = self.followed.filter(
            followers.c.followed_id == self.twitter_id)
        following_ids = self.followed.filter(
            followers.c.follower_id == self.twitter_id)
        print ("followed_ids:".format(followed_ids))
        print ("follower_ids:".format(following_ids))
        print (list(set(following_ids & set(followed_ids))))

    def like(self, user):
        if not self.is_liking(user):
            self.liked.append(user)

    def unlike(self, user):
        if self.is_liking(user):
            self.liked.remove(user)

    def is_likeing(self, user):
        return self.likeed.filter(
            likers.c.liked_id == user.id).count() > 0

    def __repr__(self):
        return '<User {}>'.format(self.twitter_id)

class Follower(db.Model):
    id = db.Column(db.Integer, primary_key=True)

class Liker(db.Model):
    id = db.Column(db.Integer, primary_key=True)

的な感じ。これで、pythonから多対多のモデルが作れるのか確認。

python3

>>> from app.models import User, Follower
>>> from app import db

>>> u1 = User(twitter_id=3)
>>> u2 = User(twitter_id=4)
>>> db.session.add(u1)
>>> db.session.add(u2)
>>> db.session.commit()
>>> u1.follow(u2)
>>> u2.follow(u1)
>>> db.session.commit()
>>> u1.followed.all()
[<User 4>]


できてる。sqlite3にアクセスして、followerのテーブルにデータがあるのか確認。

bash

$ sqlite3 app.db

SQLite version 3.11.0 2016-02-15 17:29:24
Enter ".help" for usage hints.
sqlite> select * from followers
   ...> ;
|
|
1|2
2|1
1|2
1|2
2|1
3|4
4|3

できているはず。
あとはこのfollowersテーブルにsqlalchemyからアクセスしたいんだけど、そこがようわからんので調査が必要。

FlaskでWebアプリを作るときの忘備録

FlaskでWebアプリを作っているので、今後使いそうなところの忘備録

## flask-login


主な機能

  • login/logoutが login_user(user)/logout_user()で簡単に管理できる
  • ログインが必要なページを @login_requiredデコレーターで簡単に管理できる
  • グローバル変数current_userがスクリプトからもテンプレートからも使える
  • ログインしてない時はcurrent_userにAnonymousUserMixinオブジェクトがセットされ、いい感じに使える
    • is_active, is_authenticated == False
    • is_anonymous == True
    • get_id() returns None

## SQLAlchemy


  • Railsでいうrake db:migrateみたいな感じで、マイグレーション管理ができるパッケージ
  • flask db init -> DBの初期化
  • flask db migrate -> マイグレーションファイルの作成
  • flask db upgrade -> マイグレーションファイルの実行
  • flask db downgrade -> ロールバック

## One to One, One to Manyのリレーション


  • One to Manyのリレーションは下記コードの通り


class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    addresses = db.relationship('Address', backref='person', lazy=True)

class Address(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), nullable=False)
    person_id = db.Column(db.Integer, db.ForeignKey('person.id'),
        nullable=False)

  • One to Oneのリレーションは下記コードの通り
    • modelsの外部にForeignKey指定でユーザーIDを指定してあげる
    • 引用元はflask mega tutorial


app/models.py: Followers association table
followers = db.Table('followers',
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)

  • ユーザーモデルの中に、以下のように書いてあげる。


class User(UserMixin, db.Model):
    # ...
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

  • フォローするなら、こんな感じ。


class User(UserMixin, db.Model):
    #...

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

routingしてあげるときはこんな感じ。

app/routes.py: Follow and unfollow routes.
@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot follow yourself!')
        return redirect(url_for('user', username=username))
    current_user.follow(user)
    db.session.commit()
    flash('You are following {}!'.format(username))
    return redirect(url_for('user', username=username))

@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('User {} not found.'.format(username))
        return redirect(url_for('index'))
    if user == current_user:
        flash('You cannot unfollow yourself!')
        return redirect(url_for('user', username=username))
    current_user.unfollow(user)
    db.session.commit()
    flash('You are not following {}.'.format(username))
    return redirect(url_for('user', username=username))




参考

2018-08-26

『Googleアナリティクス分析・改善のすべてがわかる本』を読んだので学んだこととか調べたことをまとめる。

独学でGoogle Analyticsを使っていたので、ちゃんとプロの人がどうやって使っているんのかを知りたくなったので、『Googleアナリティクス分析・改善のすべてがわかる本』を購入し、2-3日で読了。

2018-08-25

ビジネスマンも読むべき!ゆうこすの『SNSで夢を叶える ニートだった私の人生を変えた発信力の育て方』

モテクリエイターのゆうこすさんの書籍ゆうこすの『SNSで夢を叶える ニートだった私の人生を変えた発信力の育て方』を読んだ。

SNSで夢を叶える ニートだった私の人生を変えた発信力の育て方
KADOKAWA / 角川マガジンズ (2017-09-14)
売り上げランキング: 3,567

SNSの運用、ブランディングの教科書としてめちゃくちゃよかった


普通にこれ一冊読めば、SNSの運用方法が包括的に理解できると思った。小手先のノウハウだけではなく、ブランディングとは何かとか、なにをKPIにすべきかとか、そういうところもビジネスマンじゃない人でもわかるような言葉で書かれていたので、おすすめでした。

あとそれ以上に、モテクリエイターってめちゃくちゃ大事な仕事だなぁと。突き詰めるとブランディングのお仕事につながるなぁと。


「モテる」って言葉、とても男性中心的にみてたけど、実はみんなに好かれるというブランディングに近い概念なんだなぁ〜と思って、読んでて実に納得してしまった。彼女は多分マーケティングとかそういう意図じゃなくて、本当にモテたい!と思っている人に対して価値を提供しようとしているとおもうんだけど、本質的に企業でもなんでも、そのサービスに対して、ユーザーからモテたいとおもっていると考えていて。そうなるとこの「モテクリエイター」ていう仕事はどの領域でも応用が聞く。個人事業主であれ、法人であれ、なんであれわからんけど。

今情報が溢れている時代、商品そのもの自体の価値って差別化できなくて、誰が提供しているのかとか、どうやって作られているのかとか、そういう体験込みで商品の価値がきまると思うんだけど、その商品が個人に紐づいているパターンの場合、モテるってすごく大事だよなぁ。

あと、ソーシャルの運用方法がすごくよかった。ちゃんとターゲティングと、メディアの強みからSNSを選ぶべきで、相当考えて運用されているんだなぁと感動した。YoutubeやTwitter、Instagramなどの導線設計とかもものすごく考えられているし、そのコミュニティでなにが求められているのかとかもものすごく考えれられていて、学ぶことが多かった。

あと、実はデータで分析しながらツイートやらインスタの投稿とかみられているということで、本気で伸ばしている人はこういう努力しているんだなぁと感じた。

いつもは要約や引用しているけど、ちょっと時間がないので感想だけまとめた。

読みながらツイッターで書いた感想とか








SNSで夢を叶える ニートだった私の人生を変えた発信力の育て方
KADOKAWA / 角川マガジンズ (2017-09-14)
売り上げランキング: 3,567




17年前に書かれた『インターネット的(糸井重里)』が現代の予言書だった

前にFictyの二宮さんに、「え、読んでないの?」と煽られたので、糸井重里さんの「インターネット的」を読んでみた。2-3日で読了。面白かったところメモしておく。2001年に出版された本なんだけど、現代の予言書っぽくてすさまじかった。この時はスマホもないし、ソーシャルネットワークもない。ビビる。


インターネット的 (PHP新書)
PHP研究所 (2007-11-13)
売り上げランキング: 36,246

糸井 重里(いとい しげさと、1948年11月10日 - )は、日本のコピーライター、エッセイスト、タレント、作詞家。株式会社ほぼ日[1]代表取締役社長。フィールズ株式会社社外取締役。妻は女優の樋口可南子。愛犬はジャック・ラッセル・テリアのブイヨン、日本モノポリー協会会長。血液型はA型[2]。
wikipediaより引用
https://ja.wikipedia.org/wiki/%E7%B3%B8%E4%BA%95%E9%87%8D%E9%87%8C








2018-08-22

ブランド人になるには、ソーシャルメディアの影響力を上げろ!『ブランド人になれ』田端信太郎

田端さんの『ブランド人になれ!』を読んだ。以前読んだ彼の著書「Media Maker」も以前読んでいた。前の本に共通する部分もあったが、こっちはもっとシンプルで面白かった。


ブランド人になれ! 会社の奴隷解放宣言 (NewsPicks Book)
幻冬舎 (2018-07-05)
売り上げランキング: 828


MEDIA MAKERS―社会が動く「影響力」の正体 宣伝会議
宣伝会議 (2012-12-19)
売り上げランキング: 3,137


読んで、面白かったところだけ引用してまとめておく。

SNS運用、グロース施策の考え方として面白かった『ファンベース』まとめ

ファンベースというマーケ本読んだんだけど、ものすごく示唆深かったので読書メモ。



注目の投稿

めちゃくちゃ久しぶりにこのブログ書いたw 更新3年ぶりw > 多様性というゲームは尊厳と自由を勝ち取るゲームなのかもしれないな。  もともとツイッターでツイートした内容なんだけど、ちょっと深ぼる。 ----- 自分は男 x 30代x 二児の父 x 経営者 x 都心(共働き世...