質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

Q&A

解決済

1回答

176閲覧

タグ機能の実装をしたいです。Flaskでデータベースを使用したアプリ作成の学習をしています

mav

総合スコア1

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

0グッド

1クリップ

投稿2024/01/19 08:34

実現したいこと

データベース上の野菜の一覧から、同一の特徴を持つ野菜だけを抽出して表示する
webアプリを作っています。

欲しいものは、タグ機能の実装です。以下の条件を満たしたものを作成したいです。

  • タグの名称を後で変更できる
  • 一つのに複数のタグ(10個ほど)を付けることができる
  • 現状のFlask、sqlalchemyで実現したいです

発生している問題・分からないこと

https://senews.jp/toxi1/
リンク先の「TOXI法」を参考に
記事のテーブル(Vagitable)と、タグのテーブル(Tag)と、
記事とタグを紐づけるテーブル(Tag_map)の
三種類を用意する方法を実装したいのですが、
絞り込んだ記事を呼び出す方法がわかりません。

用意したテーブル

Vegetable

idname
1ピーマン
2トマト
3キャベツ

Tag

idname
1緑の野菜
2根菜類
3葉菜類

Tag_map

idvegetable_idtag_id
113
211
323
431

※ピーマンに緑の野菜&果菜類
トマトに果菜類
キャベツに緑の野菜&葉菜類 をタグ付けした例です

該当のソースコード

app.py

1from flask import Flask, render_template 2from flask_sqlalchemy import SQLAlchemy 3 4app = Flask(__name__) 5app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///log.db' 6db = SQLAlchemy(app) 7 8class Vegetable(db.Model): 9 id = db.Column(db.Integer, primary_key=True) 10 name = db.Column(db.String, nullable=False) 11 12class Tag(db.Model): 13 id = db.Column(db.Integer, primary_key=True) 14 name = db.Column(db.String, nullable=False) 15 16class Tag_map(db.Model): 17 id = db.Column(db.Integer, primary_key=True) 18 vegetable_id = db.Column(db.Integer, nullable=False) 19 tag_id = db.Column(db.Integer, nullable=False) 20 21@app.route('/vegetable') 22def vegetable(name): 23 tags = db.session.query(Tag).all() 24 vegetables = db.session.query(Vegetable).all() 25 return render_template('vegetable.html', tags=tags, vegetables=vegetables) 26 27@app.route('/vegetable/<tag_id>') 28def vegetable_filter(tag_id): 29 tags = db.session.query(Tag).all() 30 31# 不明なのは以下の行です。これでは全ての野菜が抽出されてしまいますが 32# 上で取得した<tag_id>を使用し、絞り込んだ野菜だけをvegetablesへ格納したいのです。 33# (「緑の野菜」タグidである「1」を取得したならば、ピーマンとキャベツのみ格納) 34 35 vegetables = db.session.query(Vegetable).all() 36 37 return render_template('vegetable.html', tags=tags, vegetables=vegetables)

vegetable.html

1# 絞り込みたいタグを選択できるように一覧表示 2{% for tag in tags %} 3 <a href="/vegetable/{{tag.id}}">{{tag.name}}</a> 4{% endfor %} 5 6# 絞り込んだ野菜の名前を表示 7{% for vegetable in vegetables %} 8<p>{{vegetable.name}}</p> 9{% endfor %}

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

野菜のテーブルにたくさんタグ付け用カラムを用意してみました。

Vegetable

idnameタグ1タグ2タグ3タグ4タグ5
1ピーマン果菜類緑の野菜
2トマト果菜類
3キャベツ緑の野菜

例はタグ少ないですが、たくさんのタグを使用できる設計にしたいのです。
管理が非常に大変で、リネームも難しく
絞り込みのコードも非常に長くなりやめました。

補足

沢山検索し沢山試し非常に苦戦しております。
知恵をお貸しください。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Vegetable(野菜)にTag(タグ)を付けて絞り込みたいということですよね?

SQLやDBの勉強をした方が良いと思いますが、とりあえず考え方を記載します。
コードの動作確認をしていないので、もしかしたら動かないかもしれません。
その場合は質問いただくか、デバッグをお願いします。

まず、自分がTOXI法で作成したテーブル3つをご覧ください。

野菜一覧はどこで定義されているかというと、Vegetableテーブルです。
タグ一覧は同様に、Tagテーブルです。
そして、最後に、野菜にタグを付けているのは、Tag_Mapテーブルです。

つまり、野菜はVegetableテーブル、タグはTagテーブル、野菜とタグの関連はTag_mapテーブルに記載されています。
よって、野菜のタグは、Tag_mapテーブルを参照すると分かります。

例えば、「ピーマン」が選択されたとします。

ピーマンは、野菜なので、Vegetableテーブルを参照し、nameが「ピーマン」である行を見つけます。
Flask_Sqlalchemyで検索する場合は、例えば以下の様になります。
(※ 書き方は質問文にある書き方に合わせています。
古い書き方です。最新の書き方は以下を参照ください。
Modifying and Querying Data: Select)

python

1vegetables = db.session.query(Vegetable).filter(Vegetable.name=='ピーマン').all()

all()で取得した場合は、Listが取得できるので例えば、 vegetables[0]にピーマンの行が入っています。

vegetablesは以下が入っているのが想定です。

python

1# vegetables 2 # List 3 # [0]: {id: 1, name: ピーマン}

movさんが定義したテーブルは、全てidが主キー(Primary Key)の様です。
Tag_mapテーブルvegetable_id が取得したidの行一覧を取得すれば、ピーマンのTag一覧が分かります。

Tag_mapテーブル からTag一覧を取得するのは、以下のようなコードです。

python

1# vegetables[0].idは'1' 2r_tags = db.session.query(Tag_map).filter(Tag_map.vegetable_id==vegetables[0].id).all() 3 4# これは以下を想定 5# r_tags 6 # [0]: {id: 1, vegetable_id: 1, tag_id: 3} 7 # [1]: {id: 2, vegetable_id: 1, tag_id: 1} 8

これで、ピーマンにくっついているタグ一覧が取得できました。

取得したのは、主キーの状態なので、それを表示しても意味がありません。
そのため、tagの名前を取得します。

tagの名前を取得するにあたり、取得できたタグ(Tag_map:r_tag)が複数あるので、全部取得します。
速度がどうのとかありそうですが、面倒なのでforで回して取得していまいましょう。

python

1tags = [] 2 3for tag in r_tags: 4 tmp = db.session_query(Tag).filter(Tag.id==tag.id).first() 5 if tmp is not None: 6 tags.append(tmp) 7 8# tagsはたぶん以下 9# tags 10 # [0]: {id: 3, name: 葉菜類} 11 # [1]: {id: 1, name: 緑の野菜} 12

これでピーマンのタグ一覧が取得できました。
あとはrender_templateに、tagsを渡してあげればピーマンのタグが表示できます。

と言うわけで、「試したこと・調べたこと」に書いてある、カラムを沢山用意しました、みたいなのは不要です。
現状のテーブルのままでタグが取得できます。
また、野菜に対して、無限にタグをくっつけることができます。(ストレージ容量の最大値が限界)

ちなみに、「カラムを沢山用意しました」というテーブルはSQLアンチパターンの7章に記載があるアンチパターンです。
7章. マルチカラムアトリビュート(複数列属性)
Qiitaの記事は簡易版なので、詳細は本を参照ください。
このパターンで設計すると、例えば10個とか決まった数までしか対応できません。
(10個以上追加しようとすると、RDBMSのカラムの最大値がネックになり、それ以上増やせなくなります。)

上記で分からない場合は、質問いただいても良いですが、実際にDB(例えばMySQLやMariaDB)をインストールして、そこに質問文に書いたテーブルを作成し、データを入れ、SQLを発行してみると良いです。
どういうデータがどういうSQLで取得できるか分かります。
なんどもやってればそのうち、DBの仕組みも分かるようになってくると思います。

投稿2024/01/22 10:38

FiroProchainezo

総合スコア2401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mav

2024/01/23 12:14

わかりやすく、ご丁寧な回答をありがとうございます。 「forで回して取得」という概念は知っていても、 実際に自分で実装することは困難であり、経験と知識の少なさを痛感しました。 今回は、理解できました。派生させていろいろなことができそうで嬉しいです! 新しい書き方の存在に気づきもせず進めておりましたが、 古い書き方に合わせていただき感謝しております。 一旦は仕組みを理解をするために古い書きで試し、理想通りに動作いたしました。 アンチパターンについての記事も非常に興味深く、 恥ずかしながら現段階では理解は難しいですが 徐々に理解できるよう立ち向かって行きたいと思います。 DBを扱えるようになりたいのでまずは何度でも挑戦いたします。 本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問