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

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

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

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

SQLAlchemy

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

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

Q&A

解決済

1回答

2694閲覧

flask-sqlalchemyで外部キー制約の設定時にエラーが出る

asasika_R

総合スコア25

Flask

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

SQLAlchemy

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

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

0グッド

0クリップ

投稿2022/06/10 07:04

編集2022/06/10 07:11

flaskを用いて家計簿のようなものを作成しようとしたのですが、外部キー制約の設定ができません。

プログラミング言語はpython,アプリケーションを作るためにflaskを使用し、データベース操作のために
pythonのormであるflask-sqlalchemyを使っています。

アプリケーションの概要ですが、まず、日用品や交際費などのようにジャンル別にカテゴリーを作成し、カテゴリーごとの予算を設定します。お金を使うごとに何にお金をどれくらい使ったかを記録し、それらの情報をウェブページ上に載せるというものです。なお一番下にpythonのコードすべてを載せています。
(HTMLは割愛)

データベースですが、Categoryというクラスにカテゴリーの情報を、Detailというクラスにお金を使用したときの情報を入れておきたいと考えています。また、これら二つのクラスをカテゴリ名を用いて一対多リレーションシップとしてデータベースを作成したいと考えています。

こちらがflask-sqlalchemyの公式ドキュメントのDeclearing Modelsのone to many relationshipを参考に作ったデータベースです。

python

1class Category(db.Model): 2 name = db.Column(db.String,db.ForeignKey('name'),primary_key=True) 3 budget = db.Column(db.Integer,nullable=False) 4 remaining_budget = db.Column(db.Integer,nullable=False) 5 month = db.Column(db.Integer,nullable=False) 6 7class Detail(db.Model): 8 id = db.Column(db.Integer,primary_key=True) 9 name = db.relationship('Category',backref='Detail',lazy=True) 10 used_money = db.Column(db.Integer,nullable=False) 11 purchased_item = db.Column(db.String,nullable=False) 12 date = db.Column(db.String,nullable=False) 13

こちらを実行すると出てきたエラー文がこちらです。
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Detail.name - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

エラー文を見るに、二つのテーブルをつなぐ外部キーがないとされているようですが、公式ドキュメント通りに外部キー制約を設定しているつもりなので、なぜこのようなことが起きているのかさっぱりわかりません。データベースについても勉強中なので根本的な間違いなのかもしれません。
なぜこのエラーが起きているのか、そしてその対応策を教えていただきたいです。

pythonのコード

python

1from flask import Flask 2from flask import render_template,redirect,request,session 3from flask_sqlalchemy import SQLAlchemy 4from flask_migrate import Migrate 5import os 6import datetime 7 8app = Flask(__name__) 9app.config['AQLALCHEMY_DARABASE_URI'] = 'sqlite:///database.db' 10db = SQLAlchemy(app) 11migrate = Migrate(app,db) 12today = datetime.date.today() 13 14class Category(db.Model): 15 name = db.Column(db.String,db.ForeignKey('name'),primary_key=True) 16 budget = db.Column(db.Integer,nullable=False) 17 remaining_budget = db.Column(db.Integer,nullable=False) 18 month = db.Column(db.Integer,nullable=False) 19 20class Detail(db.Model): 21 id = db.Column(db.Integer,primary_key=True) 22 name = db.relationship('Category',backref='Detail',lazy=True) 23 used_money = db.Column(db.Integer,nullable=False) 24 purchased_item = db.Column(db.String,nullable=False) 25 date = db.Column(db.String,nullable=False) 26 27@app.route('/') 28def index(): 29 display_categorys = Category.query.filter_by(month = today.month).all() 30 display_details = Category.query.filter_by(month = today.month).all() 31 month = today.month 32 day = today.day 33 return render_template('index.html',categorys=display_categorys,details=display_details,this_month=month,day=day) 34 35 36@app.route('/create_category' ,methods=['GET','POST']) 37def create_category(): 38 if request.method == 'GET': 39 return render_template('create_category.html') 40 else: 41 category_name = request.form.get('category_name') 42 budget = request.form.get('budget') 43 add_category = Category(category_name=category_name,budget=budget) 44 db.session.add(add_category) 45 db.session.commit() 46 return redirect('/index') 47 48@app.route('/create_detail' ,methods=['GET','POST']) 49def create_detail(): 50 if request.method == 'GET': 51 return render_template('create_detail.html') 52 else: 53 purchased_item = request.form.get('purchased_item') 54 used_money = request.form.get('used_money') 55 add_detail = Detail(purchased_item=purchased_item,used_money=used_money) 56 db.session.add(add_detail) 57 db.session.commit() 58

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

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

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

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

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

guest

回答1

0

ベストアンサー

公式ドキュメントの One-to-Many Relationships っていうのはこのリンクのものですかね?

公式とasasika_Rさんのコードを見比べると

python

1# asasika_Rさん 2class Category(db.Model): 3 name = db.Column(db.String,db.ForeignKey('name'),primary_key=True) 4# ^^^^ 5# 公式 6class Address(db.Model): 7 person_id = db.Column(db.Integer, db.ForeignKey('person.id'), nullable=False) 8# ^^^^^^^^^

だいたい似ていますが、公式ソースのAddressクラスのdb.ForeignKeyにはperson.idとありますが、asasika_Rさんの方は単にnameになっています。
試していませんが、category.nameにしたらいけるんじゃないかと推測できます。

python

1# asasika_Rさん 2class Detail(db.Model): 3 name = db.relationship('Category',backref='Detail',lazy=True) 4# ^^^^^^ 5# 公式 6class Person(db.Model): 7 addresses = db.relationship('Address', backref='person', lazy=True) 8# ^^^^^^ 9

また、どちらのClass名も先頭大文字ですが、公式のコードのbackrefとForeignKeyに指定するのは小文字です。

公式のClass指定そのままDBを作成してみると、小文字テーブルが作成されたので、テーブル名は小文字で指定する必要がありそうです。

最初の文字列はなんとなくクラス名と同じで、後のはテーブル名を入れる必要があるように感じます。

という感じの修正をしてみてはいかがでしょうか?

ちなみにクラスの小文字をテーブル名にするというのは以下に記載がありました。
Declaring Models

text

1Some parts that are required in SQLAlchemy are optional in Flask-SQLAlchemy. For instance the table name is automatically set for you unless overridden. It’s derived from the class name converted to lowercase and with “CamelCase” converted to “camel_case”. To override the table name, set the __tablename__ class attribute. 2 3↓ブラウザ翻訳 4SQLAlchemyで必要な一部のパーツは、 Flask-SQLAlchemy。 たとえば、テーブル名は自動的に設定されます オーバーライドされない限り、あなたのために。 変換されたクラス名から派生します 小文字に変換し、「CamelCase」を「camel_case」に変換します。 オーバーライドするには テーブル名を設定し、 __tablename__クラス属性。

投稿2022/06/13 04:58

FiroProchainezo

総合スコア2401

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

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

asasika_R

2022/06/14 11:39

とても丁寧な回答ありがとうございます。 公式ドキュメント通りしていると思っていましたが、まだまだ詰めが甘かったようです。 category.nameにして小文字にするところを小文字にしてみましたが、変わりませんでした。 そこでもう一度公式ドキュメントを見直し、Foreignkeyの後をdetail.idに変更してみるとうまくいきました。 うまくいったのはいいのですが、なぜDetailクラスのIDのカラムにするとうまくいくのでしょうか、わかりません。detail.nameにしてもうまくいかなかったので、外部キーの制約先は主キーにしないといけない前提条件などがあるのでしょうか。もし知っていらっしゃるならば教えていただきたいです。
FiroProchainezo

2022/06/14 12:31

公式との違いにだけ集中して対象カラムを気にしてませんでした。 すみません。 以下のサイトに中段あたりに記述がありますが、 https://www.dbonline.jp/postgresql/table/index11.html > FOREIGN KEY 制約によって参照されるカラム( refcolumn )には UNIQUE 制約または PRIMARY KEY 制約が設定されている必要があります。 とのことです。 個人的にはPK以外をFKにしたことはないので気にしたことがありませんでした・・・。
asasika_R

2022/06/20 02:38

返信が遅くなって申し訳ないです。FOREIGN KEYによって参照されるカラムに同じものがあれば、参照できないからなんですね。悩んでいたことが理解できました。ありがとうございましたい。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問