備忘録です。
今回は2つのDBのテーブルを関連付ける備忘録です。通常はユーザーと投稿記事を関連付けるなどで使用すると思うのですが、今回は2種類の投稿記事用のテーブルを関連づけます。
2つのテーブルを関連づける
今回はプロジェクトを管理するアプリケーションという設定で、
という2つのテーブルを作成して関連づけます。
プロジェクトを運用するとき、そのプロジェクトを成功させるための要件というものが必要ですよね。
例えばWebアプリケーションを作るというプロジェクトであれば、
- 開発環境を準備する
- 必要なライブラリをインストールする
- 必要なライブラリをインポートする
- DBを準備する
等々が要件になるかと思います。
これらを管理するアプリケーションを作る場合、
- プロジェクトという投稿
- プロジェクトを達成するための要件という投稿
が出てきます。
こんな感じ。
つまりあるプロジェクトとそれを運用するための要件は密接に関わってい流ので、それらを関連付けするようにテーブルに設定を行います。
今回のようなケースでは、特に関連づける必要もないのかもですが、今後、ユーザーと記事を関連づけたいというようなことがあっても同じように使えるかと思い備忘録とします。
※ユーザーと記事を関連づける場合、今回のプロジェクトがユーザー、要件が記事と考えれば大丈夫。
その1 関連づける2つのテーブルを作成
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
p_title = db.Column(db.String(255))
p_content = db.Column(db.Text)
p_status = db.Column(db.Integer)
p_crated_at = db.Column(db.DateTime, default=datetime.now(pytz.timezone('Asia/Tokyo')))
p_completed_at = db.Column(db.DateTime)
# 関連付けに必要
elements = db.relationship('Element', backref='projecter')
class Element(db.Model):
id = db.Column(db.Integer, primary_key=True)
e_title = db.Column(db.String(255))
e_content = db.Column(db.Text)
e_status = db.Column(db.Integer)
e_created_at = db.Column(db.DateTime, default=datetime.now(pytz.timezone('Asia/Tokyo')))
e_completed_at = db.Column(db.DateTime)
# 関連付けに必要
projecter_id = db.Column(db.Integer, db.ForeignKey('project.id'))
このプロジェクトと要件を関連づけますが、関連付けで必要なコードはそれぞれのモデルの最後の行です。
ちなみにプロジェクトは複数の要件を持ち、要件は一つのプロジェクトを持つ場合の設定になっています。
一つのプロジェクトを持つ要件の方にはそのプロジェクトのidをForeinKeyの引数に渡します。また、複数の要件を持つプロジェクトにはbackref=に要件でつけた変数名(projecter)を渡します。
その2 プロジェクトの要件追加ボタンにプロジェクトのidをセットする
# project.html(一部)※その4で全文を掲載します
<h1 class="mt-4">プロジェクト</h1>
<h2 class="mt-4 border-bottom border-secondary border-3">メインプロジェクト</h2><br/>
<div class="{% if project.p_status == 0 %}shadow p-3 mb-5 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
<h2>{{ project.p_title }}</h2>
<small>作成日:{{ project.p_crated_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
{% if project.p_completed_at %}
<small>完了日:{{ project.p_completed_at.strftime('%Y-%m-%d')}}</small><br/>
{% endif %}
{{ project.p_content }} <br/><br/>
<a href="{{ url_for('edit_project', id=project.id )}}" class="btn btn-outline-secondary btn-sm">編集</a>
<a href="{{ url_for('delete_project', id=project.id)}}" class="btn btn-outline-danger btn-sm">削除</a>
<a href="{{ url_for('add_element', p_id=project.id)}}" class='btn btn-outline-success btn-sm' >要件追加</a>
</div>
これはプロジェクトを表示するページですが、そのページに要件を作成するボタンをセットしています。(下記緑のボタン)
コードの最後の</div>の前のaタグ(要件を作成するボタン)の中でurl_forに引数としてこのプロジェクトのidを渡しています。つまり、要件を作成するためにボタンを押す段階で、どのプロジェクトの要件を作るのかを示すことになります。
その3 要件を作成する際にプロジェクトのidをDBに登録する
#app.py
@app.route('/add-element/<int:p_id>', methods=['GET', 'POST'])
def add_element(p_id):
form = ElementForm()
if form.validate_on_submit():
projecter = p_id
e_status = 0
element = Element(e_title=form.e_title.data, e_content=form.e_content.data, e_status=e_status, projecter_id=projecter)
form.e_title.data = ''
form.e_content.data = ''
db.session.add(element)
db.session.commit()
project = Project.query.get_or_404(projecter)
return render_template('project.html', project=project)
return render_template('add_element.html', form=form)
これで、要件が関連づいたプロジェクトのidを持つことができます。
その4 プロジェクトを表示するページで関連する要件を全て呼び出す。
# app.py
@app.route('/project/<int:id>')
def project(id):
project =Project.query.get_or_404(id)
return render_template('project.html', project=project)
プロジェクトのページのルーティングは上記のように単純です。
プロジェクト用のテーブルで、要件に関連づいたelementsという項目を追加しているのでこの変数を呼び出せば、このプロジェクトに関連付いている全ての要件を呼び出すことができます。
例えば、その2で掲載しているプロジェクトを表示するコードの下に
{{ project.element }}
このようにコードを書けば関連づいている要件がリスト形式で返ってきます。
では要件を表示します。
今回は要件の表示もプロジェクトと同じようにbootstrapのカード形式で表示したいのでコードは下記のようになります。
# project.html(全文)
{% extends "base.html" %}
{% block content %}
<h1 class="mt-4">プロジェクト</h1>
<h2 class="mt-4 border-bottom border-secondary border-3">メインプロジェクト</h2><br/>
<div class="{% if project.p_status == 0 %}shadow p-3 mb-5 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
<h2>{{ project.p_title }}</h2>
<small>作成日:{{ project.p_crated_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
{% if project.p_completed_at %}
<small>完了日:{{ project.p_completed_at.strftime('%Y-%m-%d')}}</small><br/>
{% endif %}
{{ project.p_content }} <br/><br/>
<a href="{{ url_for('edit_project', id=project.id )}}" class="btn btn-outline-secondary btn-sm">編集</a>
<a href="{{ url_for('delete_project', id=project.id)}}" class="btn btn-outline-danger btn-sm">削除</a>
<!-- ボタンをクリックした際にprojectのidを同時に渡し、add_element/p_idというurlを作る -->
<a href="{{ url_for('add_element', p_id=project.id)}}" class='btn btn-outline-success btn-sm' >要件追加</a>
</div>
<h2 class="mt-4 border-bottom border-secondary border-3">要件</h2>
<br/>
{% for element in project.elements %}
<div class="{% if element.e_status == 0 %}shadow p-3 mb-5 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
<h2>{{ element.e_title }}</h2>
<small>作成日:{{ element.e_created_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
{% if element.e_completed_at %}
<small>完了日:{{ element.e_completed_at.strftime('%Y-%m-%d')}}</small><br/>
{% endif %}
{{ element.e_content }} <br/><br/>
<a href="{{ url_for('edit_element', id=element.id)}}" class='btn btn-outline-secondary btn-sm'>編集</a>
<a href="{{ url_for('delete_element', id=element.id)}}" class='btn btn-outline-danger btn-sm'>削除</a>
</div>
{% endfor %}
{% endblock %}
その5 要件を編集した後、関連づいたプロジェクトのページに戻す
# app.py
@app.route('/elements/edit/<int:id>', methods=['GET', 'POST'])
def edit_element(id):
element = Element.query.get_or_404(id)
form = ElementForm()
if form.validate_on_submit():
element.e_title = form.e_title.data
element.e_content = form.e_content.data
element.e_status = form.e_status.data
element.e_completed_at = form.e_completed_at.data
db.session.add(element)
db.session.commit()
project = Project.query.get_or_404(element.projecter_id)
return render_template('project.html', project=project)
form.e_title.data = element.e_title
form.e_content.data = element.e_content
form.e_status.data = element.e_status
form.e_completed_at.data = element.e_completed_at
return render_template('edit_element.html', form=form)
要件を編集した後も、ちゃんとその関連づいているプロジェクトのページに戻す必要があります。これは要件テーブルの中に保存しているプロジェクトのidを使ってそのプロジェクトのデータを呼び出せば出大丈夫です。(中断コメント部分)