حزمة من الإطار-الغموض ، سهلة الاندماج ل sqlalchemy orm.
مستوحاة بشدة من Django orm و Orm البليغة
سهلة التكامل مع مشروعك الحالي مثل Fastapi:
from sqlalchemy_mixins import BaseMixin
class User ( Base , BaseMixin ):
pass استخدم PIP
pip install SqlalchemyMixin
إليك عرضًا سريعًا لما يمكن أن تفعله Mixins.
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 ) تعتمد هذه المكتبة على علم Sqlalchemy autocommit . يجب ضبطه على صحيح عند تهيئة الجلسة أي:
session = scoped_session ( sessionmaker ( bind = engine , autocommit = True ))
BaseModel . set_session ( session ) أو مع Flask-SQLAlchemy
db = SQLAlchemy ( app , session_options = { 'autocommit' : True })الميزات الرئيسية
المقدمة من ActiveRecordMixin
نمط Mapper لبيانات Sqlalchemy رائع ، ولكن نمط السجل النشط هو أسهل وأكثر جفافًا.
حسنًا ، قمنا بتنفيذها على رأس Mapper! كل ما نحتاجه هو حقن الجلسة فقط في فئة ORM أثناء محاذاة تطبيقنا:
BaseModel . set_session ( session )
# now we have access to BaseOrmModel.session propertyكلنا نحب Sqlalchemy ، لكن القيام Crud صعب بعض الشيء هناك.
على سبيل المثال ، يحتاج إنشاء كائن إلى 3 أسطر من التعليمات البرمجية:
bob = User ( name = 'Bobby' , age = 1 )
session . add ( bob )
session . flush ()حسنًا ، الوصول إلى الجلسة من النموذج ، يمكننا فقط الكتابة
bob = User . create ( name = 'Bobby' , age = 1 )هكذا يتم ذلك في Django Orm و Peewee
يتم توفير أساليب التحديث والحذف أيضًا
bob . update ( name = 'Bob' , age = 21 )
bob . delete ()وكما هو الحال في Django والبلاغة ، يمكننا استرداد الكائن بسرعة عن طريق ID
User . get ( 1 ) # instead of session.query(User).get(1)وفشل إذا لم يكن هذا المعرف موجودًا
User . get_or_abort ( 123987 ) # will raise sqlalchemy_mixins.ModelNotFoundErrorكما هو الحال في Flask-Sqlalchemy و Peewee و Django Orm ، يمكنك الاستعلام بسرعة بعض الفئة
User . query # instead of session.query(User)كما يمكننا أن نرجع أولاً أو جميع الكائنات:
User . first () # instead of session.query(User).first()
User . all () # instead of session.query(User).all() المقدمة من EagerLoadMixin
إذا كنت تستخدم تحميل Sqlalchemy المتحمس ، فقد تجد أنه غير مناسب للغاية ، خاصةً عندما نريد ، على سبيل المثال ، تحميل المستخدم ، جميع منشوراته وتعليقاته على كل منشوره في نفس الاستعلام.
حسنًا ، يمكنك الآن تعيين علاقات ORM التي تريد حريصها بسهولة
User . with_ ({
'posts' : {
'comments' : {
'user' : JOINED
}
}
}). all ()أو يمكننا كتابة خصائص الفصل بدلاً من الأوتار:
User . with_ ({
User . posts : {
Post . comments : {
Comment . user : JOINED
}
}
}). all ()في بعض الأحيان نريد تحميل العلاقات في استعلام منفصل ، أي القيام بحمولة subery. على سبيل المثال ، نقوم بتحميل المنشورات على صفحة مثل هذا ، ولكل منشور نريد أن يكون لدينا مستخدم وجميع التعليقات (ومؤلفي التعليقات).
لتسريع الاستعلام ، نقوم بتحميل التعليقات في استعلام منفصل ، ولكن في هذا الاستعلام المنفصل ، انضم إلى المستخدم
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 ()هنا ، سيتم تحميل المنشورات على الاستعلام الأول ، والتعليقات مع المستخدمين - في الثانية. انظر مستندات SQLAlchemy لشرح تقنيات تحميل العلاقة.
للحالات البسيطة ، عندما تريد فقط الانضمام إلى الحمل أو التحميل الفرعي لبعض العلاقات ، لدينا بناء جملة أسهل لك:
Comment . with_joined ( 'user' , 'post' , 'post.comments' ). first ()
User . with_subquery ( 'posts' , 'posts.comments' ). all ()لاحظ أنه يمكنك تقسيم العلاقات مع DOT مثل
post.commentsبسبب ميزة SQLAlchemy هذه
توفرها SmartQueryMixin
نحن ننفذ عمليات البحث عن المجال الشبيهة بـ Django وربط العلاقة التلقائية.
وهذا يعني أنه يمكنك التصفية وفرز الديناميكيات بواسطة السمات المحددة في الأوتار!
لذلك ، بعد تحديد نموذج Post مع علاقة Post.user بنموذج User ، يمكنك الكتابة
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 ( ___ تقسيم العلاقة والسمة ، __ سمة الانقسامات والمشغل)
إذا كنت بحاجة إلى مزيد من المرونة ، فيمكنك استخدام Session
filter_exprsession.query(Post).filter(*Post.filter_expr(rating__gt=2, body='text'))إنه مثل
filter_byفي SQLalChemy ، ولكنه يسمح أيضًا للمشغلين السحريين مثلrating__gt.ملاحظة: طريقة
filter_exprمنخفضة المستوى جدًا ولا تفعل صناديق تشبه Django السحرية. استخدمsmart_queryلذلك.
يجب تعيين جميع العلاقات المستخدمة في التصفية/الفرز بشكل صريح ، وليس فقط كونها backref
في مثالنا ، يجب تعريف علاقة
Post.userفي فئةPostحتى لو تم تعريفUser.posts.لذلك ، لا يمكنك الكتابة
class User ( BaseModel ): # ... user = sa . orm . relationship ( 'User' , backref = 'posts' )وتخطي تعريف
Post.userالعلاقة. يجب أن تحدده على أي حال:class Post ( BaseModel ): # ... user = sa . orm . relationship ( 'User' ) # define it anyway
من أجل تصنيف الكود الخاص بك وعدم وجود منطق أعمال ، يمكنك استخدام سمات SQLAlchemy الهجينة و hybrid_methods. استخدامها في التصفية/الفرز لدينا أمر واضح ومباشر (انظر الأمثلة والاختبارات).
حسنًا ، نظرًا لأن SmartQueryMixin يربح تلقائيًا للتصفية/الفرز ، فهناك شعور بإخبار SqlalChemy بأننا انضممنا بالفعل إلى هذه العلاقة.
بحيث يتم ضبط العلاقات تلقائيًا على التحميل إذا تم استخدامها للتصفية/الفرز.
لذلك ، إذا كتبنا
comments = Comment . where ( post___public = True , post___user___name__like = 'Bi%' ). all ()بعد ذلك ، لن يتم تنفيذ أي استعلام إضافي إذا كنا سنصل إلى العلاقات المستخدمة
comments [ 0 ]. post
comments [ 0 ]. post . user توفرها SmartQueryMixin
في العالم الواقعي ، نريد تصفية وفرز بعض العلاقات وفرزها أيضًا. حسنًا ، إذا استخدمنا نفس الشيء ، على سبيل المثال ، User.posts علاقة في التصفية والفرز ، لا ينبغي ربطها مرتين .
لهذا السبب قمنا بدمج الحمل المرشح والفرز والحمل في أذكى طريقة:
Comment . smart_query (
filters = {
'post___public' : True ,
'user__isnull' : False
},
sort_attrs = [ 'user___name' , '-created_at' ],
schema = {
'post' : {
'user' : JOINED
}
}). all ()كمطورين ، نحتاج إلى تصحيح الأشياء براحة. عندما نلعب في REPL ، يمكننا أن نرى هذا
>>> session.query(Post).all()
[<myapp.models.Post object at 0x04287A50>, <myapp.models.Post object at 0x04287A90>]
حسنًا ، باستخدام Mixin الخاص بنا ، يمكن أن يكون لدينا المزيد من الإخراج القابل للقراءة مع معرفات النشر:
>>> session.query(Post).all()
[<Post #11>, <Post #12>]
أكثر من ذلك ، في نموذج Post ، يمكننا تحديد ما (باستثناء المعرف) نريد أن نرى:
class User ( BaseModel ):
__repr_attrs__ = [ 'name' ]
# ...
class Post ( BaseModel ):
__repr_attrs__ = [ 'user' , 'body' ] # body is just column, user is relationship
# ...الآن لدينا
>>> session.query(Post).all()
[<Post #11 user:<User #1 'Bill'> body:'post 11'>,
<Post #12 user:<User #2 'Bob'> body:'post 12'>]
ويمكنك تخصيص طول Max __repr__ :
class Post(BaseModel):
# ...
__repr_max_length__ = 25
# ...
>>> long_post
<Post #2 body:'Post 2 long-long body' user:<User #1 'Bob'>>
المقدمة من DateMixin
يمكنك عرض الطوابع الزمنية التي تم إنشاؤها وتحديثها.
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