flask-sqlalchemyにおけるbackref lazyのパラメータインスタンスの解釈と選択
38187 ワード
公式ドキュメント:http://docs.sqlalchemy.org/en/rel_1_0/orm/basic_relationships.html#relationship-patterns
最近FlaskのSqlalchemyについて勉強していますが、
基本的な紹介
まず公式サイトの
LazyはSQLAlchemyがいつデータベースからデータをロードするかを決定した:(実際にはnoloadがあまり使われていない)
一般的には、
≪インスタンス|Instance|emdw≫
まず最初の一対の多関係では,
これでclassstudentsは結果リストを直接返します.
この場合、データ量が大きい場合や、さらに操作したい場合は、不便なので、この場合、
結果を見てみましょう.
実行
同じ実行結果が表示されます.
これは一対の多関係とよく似ているが,
結果を見てみましょう
まず、
すなわち,クエリで得られたstudentsの対応するclassエンティティもすべてクエリされた.しかし、この例では意味がないようです.このような多対多の関係は比較的簡単で、関連テーブルはモデルではなく、2つの外部キーのidしかなく、上記のコードの
データの事前準備:
結果を見てみましょう.
戻り値はRegistrationの2つのオブジェクトであり、
では問題が来ました.ここでRegistrationの
次に、実行されたsql文を表示します.
前述の例と同様に、
クエリ文を見てみましょう.
非常に簡単なsql文で、クエリだけで
転載:https://www.cnblogs.com/lpxblog/p/7485469.html
最近FlaskのSqlalchemyについて勉強していますが、
db.relations()
というパラメータはデータベース関係を見てもぼやけています.主にlazy
という本の中で注目と注目の関係をモデリングしているのを見て、lazyの使用にめまいがしました.公式文書を見ても、あまり情報が得られないので、自分で実践し、Flask Web
パラメータの異なる値から実行されるlazy
文から、sql
とone-to-many
の関係を結びつけて、lazyパラメータを分析して異なる値(many-to-many
)の異なるシーンでの選択を取り、データベースの性能の問題にかかわるため、選択の違いが大きい.特にデータ量が大きい場合.以下の例は、dynamic, joined, select
のbackrefのrelationship
属性を主に変更するモデルおよび表に基づいている.registrations = db.Table('registrations',
db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('class_id', db.Integer, db.ForeignKey('classes.id')))
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))
def __repr__(self):
return ' ' %self.name
class Class(db.Model):
__tablename__ = 'classes'
id = db.Column(db.Integer, primary_key=True)
students = db.relationship('Student', backref='_class', lazy="dynamic")
name = db.Column(db.String(64))
def __repr__(self):
return ' ' %self.name
基本的な紹介
まず公式サイトの
lazy
についての説明を見ます.LazyはSQLAlchemyがいつデータベースからデータをロードするかを決定した:(実際にはnoloadがあまり使われていない)
lazy
:(which is the default)means that SQLAlchemy will load the data as necessary in one go using a standard select statementの4つの値がある.select
: tells SQLAlchemy to load the relationship in the same query as the parent using a JOIN statement. joined
: works like ‘joined’ but instead SQLAlchemy will use a subquery. subquery
: is special and useful if you have many items. Instead of loading the items SQLAlchemy will return another query object whichyou can further refine before loading the items. This is usually what you want if you expect more than a handful of items for this relationship 一般的には、
dynamic
は属性にアクセスすると、その属性のデータがすべてロードされます.select
は、関連する2つのテーブルに対してjoined
操作を行い、関連するすべてのオブジェクトを取得する.join
は、属性にアクセスする際にメモリにデータをロードするのではなく、dynamic
のオブジェクトを返し、query
のようなオブジェクトを取得するには、対応する方法を実行する必要がある.これらの使用シーンを例に合わせて説明します.≪インスタンス|Instance|emdw≫
まず最初の一対の多関係では,
.all()
のlazyをselectに変更する.students = db.relationship('Student', backref='_class', lazy="select")
これでclassstudentsは結果リストを直接返します.
>>> from app.models import Student as S, Class as C
>>> c1=C.query.first()
>>> c1.students
['test'>, 'test2'>, 'test3'>]
この場合、データ量が大きい場合や、さらに操作したい場合は、不便なので、この場合、
が使用されます.students = db.relationship('Student', backref='_class', lazy="dynamic")
結果を見てみましょう.
>>> from app.models import Student as S, Class as C
>>> s1=S.query.first()
>>> c1=C.query.first()
>>> c1.students
>>> print c1.students
SELECT students.id AS students_id, students.name AS students_name
FROM students, registrations
WHERE :param_1 = registrations.class_id AND students.id = registrations.student_id
>>> c1.students.all()
['test'>, 'test2'>, 'test3'>]
実行
dynamic
は、c1.student
のオブジェクトを返し、そのオブジェクトのquery
文は、sql
の単純なクエリであることも分かる.Student
がいずれも直接結果を返す場合.なお、lazy=select joined
は1対多と多対の関係でのみ使用でき、1対1と多対1では使用できません.戻り結果が1つしかない場合は、データのロードを遅らせる必要はありません.前に述べたのはすべて現在の属性にlazy="dynamic"
の属性を加えて、backrefのlazyのデフォルトはすべてlazy
で、もし逆参照select
にlazyの属性を加えたら?backref
をそのまま使えばいいです.これは多対多関係で考慮する必要がある.まず、最も基本的な多対多関係を見てみましょう.registrations = db.Table('registrations',
db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('class_id', db.Integer, db.ForeignKey('classes.id')))
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# class_id = db.Column(db.Integer, db.ForeignKey('classes.id')) ,
def __repr__(self):
return ' ' %self.name
class Class(db.Model):
__tablename__ = 'classes'
id = db.Column(db.Integer, primary_key=True)
students = db.relationship('Student', secondary=registrations, backref='_class', lazy="dynamic") #
name = db.Column(db.String(64))
def __repr__(self):
return ' ' %self.name
同じ実行結果が表示されます.
>>> s1=S.query.first()
>>> c1=C.query.first()
>>> s1._class
['class1'>, 'class2'>]
>>> c1.students
>>> c1.students.all()
['test'>, 'test2'>, 'test3'>]
>>> print c1.students
SELECT students.id AS students_id, students.name AS students_name
FROM students, registrations
WHERE :param_1 = registrations.class_id AND students.id = registrations.student_id
これは一対の多関係とよく似ているが,
backref=db.backref('students', lazy='dynamic'
が集合形式となっているだけで,s1._class
のデフォルトはbackref="_class"
であるため,直接結果を返し,select
のsql文もstudentsをクエリしただけであることがわかる.ただし、逆参照を変更したc1.students
がlazy
である場合、students = db.relationship('Student', secondary=registrations,
backref=db.backref('_class', lazy="joined"), lazy="dynamic")
結果を見てみましょう
....
>>> print c1.students
SELECT students.id AS students_id, students.name AS students_name, classes_1.id AS classes_1_id, classes_1.name AS classes_1_name
FROM registrations, students LEFT OUTER JOIN (registrations AS registrations_1 JOIN classes AS classes_1 ON classes_1.id = registrations_1.class_id) ON students.id = registrations_1.student_id
WHERE :param_1 = registrations.class_id AND students.id = registrations.student_id
>>> c1.students.all()
['test'>, 'test2'>, 'test3'>]
>>> s1._class
['class1'>, 'class2'>]
まず、
joined
かs1._class
か、直接データを返します.変更されたのはc1.students
のsql文で、Student
オブジェクトをクエリーするだけでなく、関連テーブルとjoin
操作を行うことで、関連するClass
もクエリーしました.関連する意味は何ですか?sql文を直接実行した結果を見てみましょう.mysql> SELECT students.id AS students_id, students.name AS students_name, classes_1.id AS classes_1_id, classes_1.name AS classes_1_name FROM registrations, students LEFT OUTER JOIN (registrations AS registrations_1 JOIN classes AS classes_1 ON classes_1.id = registrations_1.class_id) ON students.id = registrations_1.student_id WHERE 1 = registrations.class_id AND students.id = registrations.student_id;
+-------------+---------------+--------------+----------------+
| students_id | students_name | classes_1_id | classes_1_name |
+-------------+---------------+--------------+----------------+
| 1 | test | 1 | class1 |
| 1 | test | 2 | class2 |
| 2 | test2 | 1 | class1 |
| 3 | test3 | 1 | class1 |
+-------------+---------------+--------------+----------------+
4 rows in set (0.00 sec)
すなわち,クエリで得られたstudentsの対応するclassエンティティもすべてクエリされた.しかし、この例では意味がないようです.このような多対多の関係は比較的簡単で、関連テーブルはモデルではなく、2つの外部キーのidしかなく、上記のコードの
registrations
はsqlalchemy
に直接引き継がれており、プログラムは直接アクセスできません.次の多対多の例では、上記のlazy
方式の利点を見ることができ、関連テーブルをエンティティモデルに変更し、時間情報を追加します.モデルコードは次のとおりです.class Registration(db.Model):
''' '''
__tablename__ = 'registrations'
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), primary_key=True)
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'), primary_key=True)
create_at = db.Column(db.DateTime, default=datetime.utcnow)
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
_class = db.relationship('Registration', foreign_keys=[Registration.student_id],
backref=db.backref('student', lazy="joined"), lazy="dynamic")
def __repr__(self):
return ' ' %self.name
class Class(db.Model):
__tablename__ = 'classes'
id = db.Column(db.Integer, primary_key=True)
students = db.relationship('Registration', foreign_keys=[Registration.class_id],
backref=db.backref('_class', lazy="joined"), lazy="dynamic")
name = db.Column(db.String(64))
def __repr__(self):
return ' ' %self.name
データの事前準備:
mysql> select * from classes;
+----+--------+
| id | name |
+----+--------+
| 1 | class1 |
| 2 | class2 |
+----+--------+
2 rows in set (0.00 sec)
mysql> select * from students;
+----+-------+
| id | name |
+----+-------+
| 1 | test |
| 2 | test2 |
| 3 | test3 |
+----+-------+
3 rows in set (0.00 sec)
mysql> select * from registrations;
+------------+----------+-----------+
| student_id | class_id | create_at |
+------------+----------+-----------+
| 1 | 1 | NULL |
| 2 | 1 | NULL |
| 3 | 1 | NULL |
| 1 | 2 | NULL |
+------------+----------+-----------+
4 rows in set (0.00 sec)
結果を見てみましょう.
>>> s1._class.all()
[, ]
>>> c1.students.all()
[, , ]
戻り値はRegistrationの2つのオブジェクトであり、
Student
とClass
のオブジェクトは直接戻りません.取得するには、Registrationに追加された逆参照を使用します.>>> map(lambda x: x._class, s1._class.all())
['class1'>, 'class2'>]
>>> map(lambda x: x.student, c1.students.all())
['test'>, 'test2'>, 'test3'>]
では問題が来ました.ここでRegistrationの
_class
とstudent
を呼び出すとき、データベースをもう一度調べる必要はありませんか?次に、実行されたsql文を表示します.
>>> print s1._class
SELECT registrations.student_id AS registrations_student_id, registrations.class_id AS registrations_class_id, registrations.create_at AS registrations_create_at, classes_1.id AS classes_1_id, classes_1.name AS classes_1_name, students_1.id AS students_1_id, students_1.name AS students_1_name
FROM registrations LEFT OUTER JOIN classes AS classes_1 ON classes_1.id = registrations.class_id LEFT OUTER JOIN students AS students_1 ON students_1.id = registrations.student_id
WHERE :param_1 = registrations.student_id
前述の例と同様に、
s1._class
は、対応するclass
情報を検索するだけでなく、join
の動作によって、対応するStudent
およびClass
のオブジェクトを取得することができる.すなわち、Registrationのstudent
および_class
の2つのインデックス属性は、対応するオブジェクト、すなわち、s1._class
というクエリー文は、上記の操作をすべて完了することができます.これがbackref=db.backref('_class', lazy='joined')
の役割です.lazy
をselect
に変更した場合を見てみましょう.###
_class = db.relationship('Registration', foreign_keys=[Registration.student_id],
backref=db.backref('student', lazy="select"), lazy="dynamic")
###
students = db.relationship('Registration', foreign_keys=[Registration.class_id],
backref=db.backref('_class', lazy="select"), lazy="dynamic")
クエリ文を見てみましょう.
>>> s1=S.query.first()
>>> print s1._class
SELECT registrations.student_id AS registrations_student_id, registrations.class_id AS registrations_class_id, registrations.create_at AS registrations_create_at
FROM registrations
WHERE :param_1 = registrations.student_id
>>> map(lambda x : x._class , s1._class)
['class1'>, 'class2'>]
非常に簡単なsql文で、クエリだけで
Registration
オブジェクトが返され、結果は同じですが、Registration
オブジェクトごとに_class
プロパティにアクセスすると、それぞれデータベースがクエリされます.これは重いですね.たとえばclassに100個のstudentがある場合、class.students
を取得するには100回のデータベースを追加する必要があります.データベースのクエリーのたびにコストがかかるため、joined
の役割を果たします.転載:https://www.cnblogs.com/lpxblog/p/7485469.html