در این جلسه به بررسی lookup api در جنگو خواهیم پرداخت . با ما همراه باشید .
در این بخش به بررسی API مربوط به Lookup پرداختیم . آشنایی با این متد ها و ویژگی ها قبل از ساخت lookup های سفارشی یا custom موردنیاز است .
یک Lookup API دارای دو بخش است ; یک کلاس با نام RegisterLookupMixin که lookup در آن ثبت می شوند . به فرایند ثبت شدن lookup به اصطلاح register می گویند . بخش دوم Query Expression API است که شامل یک سری از متدها می باشد . این متد ها برای کلاس lookup باید نوشته شوند تا به عنوان یک lookup پذیرفته شوند .
جنگو دو کلاس پایه (base class) دارد که از Query Expression API پیروی می کنند و تمام lookup های پیش فرض جنگو (و نه سفارشی) از آنها مشتق می شوند :
بیایید به بررسی Lookup بپردازیم . هر عبارت lookup (مثال field_name__exact) که می نویسید ,دارای سه بخش است .
مفهوم RegisterLookupMixin :کلاسی از نوع mixin (بعدا توضیح می دهیم) است که Lookup API را روی مدل پیاده سازی می کند .
جنگو از RegisterLookupMixin استفاده می کند تا به یک کلاس interface بدهد . این برای register کردن lookup روی خود کلاس و یا اشیا آن به کار می رود . دو مثال خوب شامل Field (کلاس پایه که برای ساخت فیلد های مدل به کار می رود) و Transform (کلاس پایه که برای تمام transformها در جنگو بکار می رود) می شوند .
در ادامه متد های مربوط به این کلاس را بررسی می کنیم :
1-متد register_lookup(lookup, lookup_name=None) :یک Lookup جدید را در کلاس یا اشیا آن ایجاد می کند . برای مثال :
DateField.register_lookup(YearExact) User._meta.get_field("date_joined").register_lookup(MonthExact)
کد بالا دو lookup با نام YearExact و MonthExact را ایجاد می کند . YearExact را در DateField ایجاد می کند و MonthExact را در User.date_joined ایجاد می کند (register می کند) . این کد قرار است lookup را که از قبل وجود دارد ,بازنویسی کند (بنابراین هنوز کامل نیست) .
به یاد داشته باشید که lookup های ایجاد شده در اشیا به lookup های ایجاد شده در کلاس ها اولویت دارند . در صورتی که آرگومان lookup_name را مقداردهی کنید ,از آن برای lookup استفاده خواهد شد . در غیر این صورت , lookup.lookup_name مورد استفاده خواهد بود .
2-متد get_lookup(lookup_name) :آرگومان lookup_name را می گیرد و یک lookup را با همین lookup_name بازمیگرداند (lookup حتما باید در کلاس یا شی register شده باشد) . به صورت پیش فرض این متد ,تمام کلاس های والد را نیز (در صورت ارث بری) برای lookup موردنظر جستجو می کند تا آن را پیدا کند . در صورت پیدا کردن ,اولین مطابقت بازگردانده می شود .
همچنین lookup هایی که روی اشیا ثبت شده باشند ,می توانند lookup های ثبت شده روی کلاس را بازنویسی کنند (lookup_name های آنها برای بازنویسی باید یکسان باشد) .
3-متد get_lookups :یک دیکشنری از هر lookup_name که در کلاس یا اشیا آن ثبت شده باشد را بازمیگرداند . مقادیر value آنها برابر کلاس Lookup مربوط خواهند بود .
4- متد get_transform(transform_name) :آرگومان transform_name را دریافت می کند و یک Transform که با آن مطابقت داشته باشد را بازمیگرداند . Transformباید در کلاس یا اشیا آن ثبت شده باشد . به صورت پیش فرض این متد ,تمام کلاس های والد را نیز (در صورت ارث بری) برای Transformموردنظر جستجو می کند تا آن را پیدا کند . در صورت پیدا کردن ,اولین مطابقت بازگردانده می شود .
نکته : قابلیت ثبت کردن lookupبرای اشیا یک کلاس ,فقط در جنگو 4.1اضافه شده است .
در این بخش قرار است تمام متد هایی را بررسی کنیم که به وسیله آنها جنگو می تواند Query ها را به کد SQLترجمه کند . از این متد ها در Transformها و یا aggregatesو غیره استفاده می شود . زمانی که یک کلاس از متد های زیر استفاده کند و آنها را پیاده سازی کند ,گفته می شود که این کلاس از query expression APIپیروی می کند .
1-as_sql(compiler, connection) :قطعه کد SQLرا برای Queryایجاد می کند . یک تاپل به شکل (sql, params)بازمیگرداند که در آن sqlبرابر با رشته کد SQLتولید شده است و paramsبرابر یک لیست یا تاپل از پارامترهای Queryاست . علاوه بر اینها یک compilerنیز وجود دارد که شی ای از SQLCompilerاست . این شی دارای متد compileاست که می تواند برای کامپایل Queryهای دیگر به کار برود . Connectionنیز اتصال دیتابیسی است که برای اجرای Queryبه کار می رود . (قبلا با این مورد آشنا شده اید)
فراخوانی expression.as_sqlکه در آن expressionهمان عبارت Queryماست ,معمولا اشتباه است . بجای این روش از compiler.compile(expression)استفاده کنید . compiler.compile ,فراخوانی متد های خاص از Queryرا پوشش می دهد .
آرگومان های keyword argumentsنیز ممکن است در این متد جای بگیرند . البته در صورتی که برای مثال متد as_vendornameیا زیرکلاس ها نیاز به داده اضافی برای بازنویسی رشته SQLتولید شده داشته باشند .
2-as_vendorname(compiler, connection) :مانند متد as_sqlاست . وقتی یک عبارت SQLتوسط compiler.compileکامپایل می شود ,جنگو ابتدا سعی می کند تا as_vendornameرا فراخوانی کند . در اینجا vendorname همان نام دیتابیسی است که از موتور دیتابیسی آن برای اجرای Queryاستفاده می شود . Vendornameیکی از مقادیر sqlite , oracle , postgresqlو یا mysqlرا می پذیرد .
3- get_lookup(lookup_name) : Lookup ای را که با lookup_name داده شده مطابقت پیدا کند ,بازمیگرداند . برای مثال : self.output_field.get_lookup(lookup_name)
4-get_transform(transform_name) : Lookup ای را که با transform_nameداده شده مطابقت پیدا کند ,بازمیگرداند . برای مثال : self.output_field.get_transform(transform_name)
5-output_field :نوع کلاس بازگردانده شده توسط متد get_lookupرا تعریف می کند که باید یک شی از کلاس Field باشد .
Transformیک کلاس genericاست که برای پیاده سازی field transformationsبه کار می رود . یک مثال زیبا از Transformها , __year است که یک DateFieldرا به IntegerFieldتبدیل می کند .
Transformها نیز مانند Lookupها استفاده می شوند . برای مثال date__year
همانطور که قبلا گفتیم این کلاس از کلاس Query Expression APIپیروی می کند که به این معنی است که میتوانید Transformها را به یکدیگر زنجیر کنید . یعنی از چند Transformپشت سر هم استفاده کنید .
1-Bilateral :یک ویژگی Booleanاست که نشان می دهد آیا این Transformباید برای rhsو lhsبه صورت همزمان اعمال شود یا خیر . نام دیگر این ویژگی تبدیل دو طرفه است . تبدیل های دو طرفه به همان ترتیبی که در Queryظاهر می شوند روی rhsها تنظیم می شوند . به طور پیش فرض این ویژگی روی Falseاست . (در مبحث نوشتن lookupهای سفارشی از این ویژگی استفاده می کنیم)
2-Lhs : سمت چپ Transformرا نشان می دهد . آنچه که Transformروی آن تغییرات ایجاد می کند . از Query Expression APIپیروی می کند .
3-lookup_name :نام Lookupاست که قبلا از آن استفاده کردیم . برای شناسایی Lookupبه کار می رود و نمی تواند دارای رشته _باشد .
4-output_field : کلاسی که خروجی Transformرا ایجاد می کند را ,تعریف می کند . باید یک شی از Fieldباشد . به طور پیش فرض همان lhs.output_fieldاست .
Lookupیک کلاس genericاست که برای پیاده سازی lookupها به کار می رود . lookupیک عبارت است که در Queryقرار می گیرد و یک lookup_nameدارد که برای مقایسه از نوع Trueیا Falseبین lhs(سمت چپ Query) و rhs(سمت راست Query) به کار می رود . شکل کلی آن به صورت <lhs>__<lookup_name>=<rhs>است .
Lookupها همچنین می تواند مستقیما در QuerySetو متد filterقرار بگیرند . برای مثال :
Book.objects.filter(LessThan(F("word_count"), 7500))
یا حتی در متد annotateنیز استفاده شود .
Book.objects.annotate(is_short_story=LessThan(F("word_count"), 7500))
1-Lhs : سمت چپ Query ,آنچه lookupروی آن اجرا می شود . معمولا اشیایی که lookupروی آنها اجرا می شود ,از Query Expression API پیروی می کنند . همچنین ممکن است یک مقدار ساده باشند .
2-Rhs : سمت راست Query ,آنچه lhsبا آن مقایسه می شود . این می تواند یک مقدار ساده باشد که به SQLکامپایل می شود . معمولا یک شی Fیا یک QuerySet.
3-process_lhs(compiler, connection, lhs=None) : یک تاپل به شکل (lhs_string, lhs_params)بازمیگرداند که توسط compiler.compile(lhs)بازگردانده شده است . این متد می تواند برای تنظیم نحوه پردازش lhsبازنویسی شود .
آرگومان compilerدر آن یک شی از SQLCompilerاست که مانند compiler.compile(lhs) برای کامپایل lhsبه کار می رود . آرگومان connectionنیز برای کامپایل SQLخاص کد به کار می رود . در انتها ,اگر lhsبرابر با Noneنباشد ,می توانید از آن (چون پردازش شده است) بجای self.lhsاستفاده کنید .
4- process_rhs(compiler, connection) :مانند process_lhsاست اما برای rhsعمل می کند .
در این جلسه به بررسی api مربوط به لوکاپ ها (lookups) در جنگو پرداختیم و چگونگی کارکرد آن را بررسی کردیم . در جلسه بعدی به نحوه کار کردن روابط دیتابیسی در جنگو خواهیم پرداخت و api مربوط به آنها را نیز بررسی خواهیم کرد .