آموزش جنگو (Basic) : جلسه بیست و هشت | لوکاپ ها (field lookups)

در این جلسه در رابطه با field lookup صحبت خواهیم کرد . با ما همراه باشید .آموزش جنگو (Basic) : جلسه بیست و هشت | لوکاپ ها (field lookups)بررسی Field Lookupsمقادیر Field lookups در واقع روشی هستند که مقدار ورودی به WHERE در SQL را با آنها مشخص می کنید . آنها به عنوان کلمات کلیدی ()filter و ()exclude و ()get به صورت آرگومان وجود دارند .در واقع انواع مختلف دارند و با هر کدام یک نوع از عملیات های مقایسه ای در فیلد ورودی انجام می شود . شکل کلی استفاده از آنها به صورت field__(loouktype)=value است . برای مثال :>>> Entry.objects.filter(pub_date__lte='2006-01-01')دستورات SQL زیر برای کد بالا تولید خواهند شد .SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';فیلد مشخص شده در Field lookup باید یکی از فیلد های مدل شما باشد (مدلی که manager آن را فراخوانی کرده اید) . البته یک استثنا وجود دارد . زمانی که فیلد شما یک ForeignKey باشد ,شما می توانید نام آن را به علاوه id_ بنویسید . در این زمان انتظار می رود که مقدار پارامتر شما برابر با مقدار id شی ارجاع داده شده به فیلد ForeignKey باشد (یعنی id شی ای که ForeignKey شما به آن ربط دارد ) . برای مثال :>>> Entry.objects.filter(blog_id=4)اگر یک آرگومان کلمه کلیدی نامعتبر ارسال کنید ,جنگو به شما ارور TypeErrorرا بازمیگرداند .بک اند و API دیتایس های جنگو مقدار زیادی از این گونه lookupها را پشتیبانی می کند . بعدها تمام آنها را بررسی خواهیم کرد .اما ابتدا برای آشنایی بیشتر بیایید به پرکاربرد ترین آنها نگاهی بیندازیم .1-اولین از لیست ما exact است . به معنای تطابق دقیق است . یعنی فیلد باید دقیقا همان مقدار ورودی را داشته باشد (نه کمتر و نه بیشتر) . برای مثال :>>> Entry.objects.get(headline__exact=&quotCat bites dog&quot)کد بالا مقدار SQL زیر را تولید می کند .SELECT ... WHERE headline = 'Cat bites dog';اگر شما هیچ نوع lookup را ارائه نکنید و فقط نام فیلد خالی را بنویسید ,به صورت پیش فرض نوع lookup شما exact فرض می کند .به عنوان مثال ,دو عبارت زیر معادل هستند .>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is impliedدر حالی که exact به حروف بزرگ و کوچک نیز حساس است ,ولی نوع دیگری وجود دارد که تنها تفاوتش با exact در همین است .2-دومین از لیست ما iexact است . این همان exact است با این تفاوت که به حروف بزرگ و کوچک حساس نیست . برای مثال :>>> Blog.objects.get(name__iexact=&quotbeatles blog&quot)کد بالا با مقدار Beatles Blog یا beatles blog یا حتی BeAtLeS BlOg مطابقت پیدا می کند .3-نوع بعدی contains است . مقدار هایی با این نوع از lookup مطابقت پیدا می کنند که حاوی عبارت ورودی هستند (حساس به حروف بزرگ و کوچک) . برای مثال :Entry.objects.get(headline__contains='Lennon')کد بالا مقدار SQL زیر را تولید می کند .SELECT ... WHERE headline LIKE '%Lennon%';توجه داشته باشید ,کد بالا با مقدار Today Lennon honored مطابقت دارد ولی با مقدار today lennon honored مطابقت نخواهد داشت .همچنین ورژن غیرحساس به حروف بزرگ و کوچک نیز وجود دارد که نام آن icontains است .نوع های startswith و endswith نیز وجود دارند که بررسی می کنند آیا ابتدا یا پایان مقدار , با عبارت شما همخوانی دارد یا خیر . ورژن غیرحساس به حروف بزرگ و کوچک آنها برابر با istartswith و iendswith است .لوکاپ (lookup) های مربوط به روابط دیتابیسیجنگو یک راه قدرتمند و ساده را برای هندل کردن روابط دیتابیسی (مانند ForeignKey) در lookup ,برای شما قرار داده است که در پشت صحنه اتوماتیک SQL JOIN را هندل می کند . برای دسترسی به مقدار یک فیلد از شی ارجاع داده شده در lookup خود ,از نام فیلدی که ارتباط را میان دو مدل برقرار کرده است (همان نام فیلد ForeignKey) به علاوه ‘__’ و سپس فیلد داخل مدلی که به آن ارجاع داده شده ایم استفاده کنید .مثال زیر تمام رکورد های مدل Blog را دریافت می کند که حداقل به یک رکورد Entryکه فیلد headlineدر آن رکورد ,مقدار Lennonرا داشته باشد ,مرتبط باشند .قدار Beatles Blog است .>>> Entry.objects.filter(blog__name='Beatles Blog')این جستجو هرچقدر که بخواهید می تواند تودرتو و عمیق باشد .البته معکوس هم کار می کند و همچنین می توان آن را سفارشی کرد . به طور پیش فرض شما در lookup به وسیله نام کوچک مدل ارجاع داده شده ,مقدار reverse name را تعیین می کنید .مثال زیر تمام رکورد های مدل Blog را دریافت می کند که حداقل به یک رکورد Entry که فیلد headline در آن رکورد ,مقدار Lennon را داشته باشد ,مرتبط باشند .>>> Blog.objects.filter(entry__headline__contains='Lennon')فرض کنید روابط شما پیچیده هستند و می خواهید بین چند رابطه فیلتر را ایجاد کنید , اگر یکی از جدول های میانی شما ,با فیلتر های شما مطابقت پیدا نکند ,جنگو با آن به صورتی برخورد می کند که انگار یک رکورد خالی (مقادیر فیلد ها NULL هستند) اما معتبر در جدول وجود دارد . همه این ها به این معنی است که هیچ خطا یا اروری به شما در این شرایط بازگردانده نمی شود . برای مثال در این فیلتر :Blog.objects.filter(entry__authors__name='Lennon')اگر برای مثال در کد بالا شما به مدل entry رابطه ای داشته باشید و جنگو نتواند هیچ رکوردی با این مشخصات پیدا کنید , به جای بازگرداندن یک ارور به شما طوری رفتار می کند که انگار در رکورد مقدار name وارد نشده است (Null است) . معمولا این چیزی است که شما می خواهید .در بعضی از شرایط البته ممکن است این قانون گیج کننده باشد , برای مثال هنگامی که شما از isnull lookup استفاده کنید :Blog.objects.filter(entry__authors__name__isnull=True)کد بالا رکورد هایی از Blog را که مقدار name در قسمت author آنها خالی است و همچنین آنهایی که مقدار auhor خالی در فیلد رابطه به entry دارند را بازمیگرداند . اگر مقادیر دومی را نمی خواهید (معمولا نمی خواهید) بنویسید :Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)لوکاپ (lookup) های روابط دیتابیسی به صورت چند مقداریهنگامی که یک ManyToManyField یا یک ForeignKey معکوس داشته باشید (مانند Blog به Entry) ,فیلتر کردن آنها بر اساس چند ویژگی به صورت همزمان (یعنی چند فیلد از آنها را در فیلتر استفاده کنید) این سوال را مطرح می کند که آیا باید هر ویژگی در یک شی مرتبط با فیلتر مطابقت داشته باشد یا خیر ؟ جواب این سوال بله است . برای مثال ممکن است دنبال رکورد هایی از Blog باشید که به یک entry در سال 2008 و headline=“Lennon”مرتبط هستند (فیلد رابطه آنها به چنین رکوردی وصل است) .برای دریافت تمام رکورد های Blog که حداقل یک entry از سال 2008 ,با مقدار Lennon در فیلد headline آن , را دارا هستند ,کد زیر را بنویسید .Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)کد بالا تمام رکورد هایی را باز میگرداند که دو شرط را به صورت همزمان دارا هستند . برای ساخت یک فیلتر سهلگیرانه تر که صرفا مقدار Lennon را در فیلد headline داشته باشد و یا تاریخ آن مربوط به 2008 باشد (یا اولی یا دومی یا با هم ,اکنون فرقی نمی کند) کد زیر را بنویسید :Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)فرض کنید که فقط یک رکورد از Blog وجود دارد که همزمان فیلد entry Lennon , را دارا است و برخی از آنها برای سال 2008 است . اما هیچ کدام از entry هایی که برای سال 2008 است , Lennon را دارا نیستند . Query یا همان فیلتر اول شما هیچ رکوردی را باز نمیگرداند (زیرا دو شرط باید همزمان با هم وجود داشته باشند) . ولیکن فیلتر دوم شما رکورد هایی را باز خواهد گرداند (زیرا هنگامی که آنها را یکدیگر زنجیر می کنیم ,لازم نیست پارامتر های فیلتر شما حتما باهم وجود داشته باشند) .مقایسه مقدار فیلد با فیلدی از همان مدلدر مثال هایی که تاکنون ارائه شد ,فیلترهایی که ساختیم ,همگی مقدار یک فیلد از مدل را با یک مقدار ثابت مانند مقدار یک رشته مقایسه می کنند . اما اگر بخواهیم مقدار فیلد را با مقدار یک فیلد دیگر از همان مدل مقایسه کنیم ,چه کاری باید انجام بدهیم ؟جنگو کلاس ()F را در این مواقع ارائه می کند . در جلوتر درباره آن بیشتر صحبت خواهیم کرد ولی در ابتدا باید بدانید که نمونه هایی که از این کلاس ساخته می شوند به عنوان مرجعی که به یک فیلد از یک مدل query میزند و مقدار آن را بازمیگرداند ,استفاده می شوند . سپس می توان این مرجع ها را (نمونه های کلاس F) برای مقایسه دو فیلد از یک مدل با یکدیگر در یک فیلتر استفاده کرد .برای مثال برای دریافت یک لیست از تمام رکورد های Blog که با entry رابطه داشته باشند و مقدار فیلد comments در entry آنها بیشتر از مقدار فیلد pingbacks در entry آنها باشد ,یک نمونه ()F ایجاد می کنیم که به pingbacks در entry ارجاع داده شده است و سپس از آن برای مقایسه استفاده می کنیم .>>> from django.db.models import F >>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))نکته : ابتدا ()F را import کنید . (django.db.models)همچنین جنگو جمع ,تفریق ,ضرب و توان و تقسیم و مدول را بر روی ثابت ها (مانند رشته ها) و نمونه های ()F پشتیبانی می کند . برای یافتن رکورد هایی از Entry که مقدار comments آنها دو برابر مقدار فیلد pingbacks باشد ,کد زیر را می نویسیم :>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') *2)برای دریافت تمام رکوردهای Entry که مقدار فیلد rating در آنها کمتر از جمع مقادیر فیلد های pingbacks و comments آنها است ,کد زیر را بنویسید .>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))شما همچنین می توانید از __ نیز برای گسترش و بسط دادن روابط دیتابیسی در نمونه ی ()F استفاده کنید . این دقیقا مانند همان فیلتر هایی است که استفاده کردیم (سرفصل های قبلی). برای مثال برای دریافت تمام رکورد های Entry که فیلد name در قسمت authors آنها برابر با مقدار name در قسمت blog آنها است ,کد زیر را استفاده می کنید .>>> Entry.objects.filter(authors__name=F('blog__name'))برای فیلد های تاریخ و زمان می توانید یک شی timedelta را اضافه یا کم کنید . برای مثال ,کد زیر تمام رکورد هایی را باز می گرداند که 3 روز پس از انتشار ,ویرایش شده اند (از فیلد های pub_date و mod_date استفاده می کنیم).>>> from datetime import timedelta >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))نمونه های ()F همچنین از bitwise نیز پشتیبانی می کنند . برای آنها توابع bitand , bitor , bitxor bitrightshift , bitleftshift قرار داده شده است . برای مثال :>>> F('somefield').bitand(16)نکته :دیتابیس oracle از bitwise XOR پشتیبانی نمی کند .در این جلسه به بررسی lookup و انواع مقایسه مقادیر در فیلد ها پرداختیم . در جلسه بعدی به بررسی مفهوم transform در جنگو خواهیم پرداخت .