Satu bungkus kerangka kerja-agnostik, mudah diintegrasikan untuk Sqlalchemy ORM.
Sangat terinspirasi oleh django orm dan orm yang fasih
Integrasi mudah untuk proyek Anda yang ada seperti Fastapi:
from sqlalchemy_mixins import BaseMixin
class User ( Base , BaseMixin ):
pass Gunakan pip
pip install SqlalchemyMixin
Inilah demo cepat tentang apa yang bisa dilakukan mixin kami.
bob = User . create ( name = 'Bob' )
post1 = Post . create ( body = 'Post 1' , user = bob , rating = 3 )
post2 = Post . create ( body = 'long-long-long-long-long body' , rating = 2 ,
user = User . create ( name = 'Bill' ),
comments = [ Comment . create ( body = 'cool!' , user = bob )])
# filter using operators like 'in' and 'contains' and relations like 'user'
# will output this beauty: <Post #1 body:'Post1' user:'Bill'>
print ( Post . where ( rating__in = [ 2 , 3 , 4 ], user___name__like = '%Bi%' ). all ())
# joinedload post and user
print ( Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ())
# subqueryload posts and their comments
print ( User . with_subquery ( 'posts' , 'posts.comments' ). first ())
# sort by rating DESC, user name ASC
print ( Post . sort ( '-rating' , 'user___name' ). all ())
# created_at, updated_at timestamps added automatically
print ( "Created Bob at " , bob . created_at )
# serialize to dict, with relationships import sqlalchemy as sa
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_mixins import BaseMixin
app = Flask ( __name__ )
app . config [ 'SQLALCHEMY_DATABASE_URI' ] = 'sqlite://'
db = SQLAlchemy ( app )
######### Models #########
class BaseModel ( db . Model , BaseMixin ):
__abstract__ = True
pass
class User ( BaseModel ):
name = sa . Column ( sa . String )
######## Initialize ########
BaseModel . set_session ( db . session )
######## Create test entity ########
db . create_all ()
user = User . create ( name = 'bob' )
print ( user ) Perpustakaan ini bergantung pada bendera autocommit Sqlalchemy. Itu perlu diatur ke true saat menginisialisasi sesi yaitu:
session = scoped_session ( sessionmaker ( bind = engine , autocommit = True ))
BaseModel . set_session ( session ) atau dengan Flask-SQLAlchemy
db = SQLAlchemy ( app , session_options = { 'autocommit' : True })Fitur utama adalah
disediakan oleh ActiveRecordMixin
Pola Mapper Data Sqlalchemy keren, tetapi pola catatan aktif paling mudah dan lebih kering.
Nah, kami mengimplementasikannya di atas Mapper Data! Yang kami butuhkan hanyalah menyuntikkan sesi ke dalam kelas ORM sambil melakukan bootstrap aplikasi kami:
BaseModel . set_session ( session )
# now we have access to BaseOrmModel.session propertyKita semua menyukai Sqlalchemy, tetapi melakukan crud agak rumit di sana.
Misalnya, membuat objek membutuhkan 3 baris kode:
bob = User ( name = 'Bobby' , age = 1 )
session . add ( bob )
session . flush ()Nah, memiliki akses ke sesi dari model, kami bisa menulis
bob = User . create ( name = 'Bobby' , age = 1 )Begitulah cara dilakukan di Django Orm dan Peewee
Perbarui dan menghapus metode juga disediakan
bob . update ( name = 'Bob' , age = 21 )
bob . delete ()Dan, seperti dalam django dan fasih, kita dapat dengan cepat mengambil objek dengan id
User . get ( 1 ) # instead of session.query(User).get(1)dan gagal jika ID seperti itu tidak ada
User . get_or_abort ( 123987 ) # will raise sqlalchemy_mixins.ModelNotFoundErrorSeperti di flask-sqlalchemy, peewee dan django orm, Anda dapat dengan cepat menanyakan beberapa kelas
User . query # instead of session.query(User)Kami juga dapat dengan cepat mengambil terlebih dahulu atau semua objek:
User . first () # instead of session.query(User).first()
User . all () # instead of session.query(User).all() disediakan oleh EagerLoadMixin
Jika Anda menggunakan pemuatan Sqlalchemy yang bersemangat, Anda mungkin merasa tidak terlalu nyaman, terutama ketika kami ingin, katakanlah, memuat pengguna, semua posting dan komentarnya untuk setiap postingnya dalam kueri yang sama.
Nah, sekarang Anda dapat dengan mudah mengatur hubungan Orm apa yang ingin Anda beban
User . with_ ({
'posts' : {
'comments' : {
'user' : JOINED
}
}
}). all ()Atau kita dapat menulis properti kelas alih -alih string:
User . with_ ({
User . posts : {
Post . comments : {
Comment . user : JOINED
}
}
}). all ()Terkadang kami ingin memuat hubungan dalam kueri terpisah, yaitu melakukan subqueryload. Misalnya, kami memuat posting di halaman seperti ini, dan untuk setiap posting kami ingin memiliki pengguna dan semua komentar (dan komentar penulis).
Untuk mempercepat kueri, kami memuat komentar dalam kueri terpisah, tetapi, dalam kueri terpisah ini, bergabunglah dengan pengguna
from sqlalchemy_mixins import JOINED , SUBQUERY
Post . with_ ({
'user' : JOINED , # joinedload user
'comments' : ( SUBQUERY , { # load comments in separate query
'user' : JOINED # but, in this separate query, join user
})
}). all ()Di sini, posting akan dimuat pada kueri pertama, dan komentar dengan pengguna - di yang kedua. Lihat dokumen Sqlalchemy untuk menjelaskan teknik pemuatan hubungan.
Untuk kasus sederhana, ketika Anda hanya ingin bergabung atau subqueryload beberapa hubungan, kami memiliki sintaks yang lebih mudah untuk Anda:
Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ()
User . with_subquery ( 'posts' , 'posts.comments' ). all ()Perhatikan bahwa Anda dapat membagi hubungan dengan titik seperti
post.commentskarena fitur Sqlalchemy ini
disediakan oleh SmartQueryMixin
Kami menerapkan pencarian lapangan seperti Django dan hubungan otomatis.
Ini berarti Anda dapat menyaring dan mengurutkan secara dinamis berdasarkan atribut yang ditentukan dalam string!
Jadi, memiliki model Post yang ditentukan dengan hubungan Post.user dengan model User , Anda dapat menulis
Post . where ( rating__gt = 2 , user___name__like = '%Bi%' ). all () # post rating > 2 and post user name like ...
Post . sort ( '-rating' , 'user___name' ). all () # sort by rating DESC, user name ASC ( ___ membagi relasi dan atribut, __ membagi atribut dan operator)
Jika Anda membutuhkan lebih banyak fleksibilitas, Anda dapat menggunakan metode
filter_exprtingkat rendahsession.query(Post).filter(*Post.filter_expr(rating__gt=2, body='text')), lihat contoh.Ini seperti
filter_bydi sqlalchemy, tetapi juga memungkinkan operator sihir sepertirating__gt.Catatan: Metode
filter_exprsangat rendah dan tidak melakukan ajaib seperti django. Gunakansmart_queryuntuk itu.
Semua hubungan yang digunakan dalam penyaringan/penyortiran harus diatur secara eksplisit , bukan hanya menjadi backref
Dalam contoh kami, hubungan
Post.userharus didefinisikan di kelasPostbahkan jikaUser.postsjuga didefinisikan.Jadi, Anda tidak bisa mengetik
class User ( BaseModel ): # ... user = sa . orm . relationship ( 'User' , backref = 'posts' )dan melewatkan hubungan
Post.useryang menentukan. Anda harus mendefinisikannya:class Post ( BaseModel ): # ... user = sa . orm . relationship ( 'User' ) # define it anyway
Untuk mengipasi kode Anda dan menanamkan logika bisnis, Anda dapat menggunakan atribut hybrid Sqlalchemy dan hybrid_methods. Menggunakannya dalam penyaringan/penyortiran kami secara langsung (lihat contoh dan tes).
Nah, karena SmartQueryMixin bergabung secara otomatis untuk memfilter/menyortir, ada perasaan untuk memberi tahu Sqlalchemy bahwa kita sudah bergabung dengan hubungan itu.
Sehingga hubungan secara otomatis diatur untuk digabungkan jika digunakan untuk memfilter/menyortir.
Jadi, jika kita menulis
comments = Comment . where ( post___public = True , post___user___name__like = 'Bi%' ). all ()maka tidak ada kueri tambahan yang akan dieksekusi jika kami akan mengakses hubungan yang digunakan
comments [ 0 ]. post
comments [ 0 ]. post . user disediakan oleh SmartQueryMixin
Di dunia nyata, kami ingin menyaring, mengurutkan, dan juga bersemangat memuat beberapa hubungan sekaligus. Nah, jika kita menggunakan hubungan yang sama, katakanlah, User.posts Posting dalam penyaringan dan penyortiran, itu tidak boleh bergabung dua kali .
Itu sebabnya kami menggabungkan filter, sortir, dan beban yang bersemangat dalam satu metode paling cerdas:
Comment . smart_query (
filters = {
'post___public' : True ,
'user__isnull' : False
},
sort_attrs = [ 'user___name' , '-created_at' ],
schema = {
'post' : {
'user' : JOINED
}
}). all ()Sebagai pengembang, kita perlu men -debug sesuatu dengan kenyamanan. Saat kita bermain di repl, kita bisa melihat ini
>>> session.query(Post).all()
[<myapp.models.Post object at 0x04287A50>, <myapp.models.Post object at 0x04287A90>]
Nah, menggunakan mixin kami, kami dapat memiliki output yang lebih mudah dibaca dengan ID pos:
>>> session.query(Post).all()
[<Post #11>, <Post #12>]
Terlebih lagi, dalam model Post , kita dapat mendefinisikan apa lagi (kecuali id) yang ingin kita lihat:
class User ( BaseModel ):
__repr_attrs__ = [ 'name' ]
# ...
class Post ( BaseModel ):
__repr_attrs__ = [ 'user' , 'body' ] # body is just column, user is relationship
# ...Sekarang kita punya
>>> session.query(Post).all()
[<Post #11 user:<User #1 'Bill'> body:'post 11'>,
<Post #12 user:<User #2 'Bob'> body:'post 12'>]
Dan Anda dapat menyesuaikan max __repr__ panjang:
class Post(BaseModel):
# ...
__repr_max_length__ = 25
# ...
>>> long_post
<Post #2 body:'Post 2 long-long body' user:<User #1 'Bob'>>
disediakan oleh DateMixin
Anda dapat melihat cap waktu yang dibuat dan diperbarui.
bob = User ( name = "Bob" )
session . add ( bob )
session . flush ()
print ( "Created Bob: " , bob . created_at )
# Created Bob: 2019-03-04 03:53:53.606765
print ( "Pre-update Bob: " , bob . updated_at )
# Pre-update Bob: 2019-03-04 03:53:53.606769
time . sleep ( 2 )
bob . name = "Robert"
session . commit ()
print ( "Updated Bob: " , bob . updated_at )
# Updated Bob: 2019-03-04 03:53:58.613044