در توسعه حرفهای بکاند، جستجو در پایگاه داده به مقایسههای ساده با عملگر = محدود نمیشود.
Lookups در فریمورک جنگو یکی از قابلیتهایی است که به شما امکان میدهد فیلترهای دقیقتر و جست و جو های پیشرفتهتری را در Django ORM ایجاد کنید، بدون اینکه درگیر نوشتن مستقیم کد های SQL شوید.
اگر در نقش یک معمار نرمافزار فعالیت میکنید، بهخوبی میدانید که کارایی (Performance) و دقت در فیلتر کردن دادهها، از عوامل کلیدی در ساخت یک سیستم مقیاسپذیر و قابلاعتماد هستند. قابلیت Field Lookups در Django ORM این امکان را فراهم میکند که کوئری های پیچیده SQL را با کدی خوانا، مختصر و کاملاً هماهنگ با سبک پایتون پیادهسازی کنید.
بررسی مفهوم لوکاپ ها (Field lookups)
مکانیزم فیلتر کردن در جنگو بر پایه یک قرارداد مشخص و هوشمند طراحی شده است. هر بار که از متد .filter() استفاده میکنید، در حقیقت به ORM جنگو اعلام میکنید که شرط WHERE در کوئری SQL به چه شکلی ساخته و اجرا شود.
ساختار کلی به این صورت است:
Model.objects.filter(field__lookup=value)
- Model: کلاسی که نمایانگر جدول مربوطه در پایگاه داده است.
- objects: منیجر پیشفرض مدل که نقطه ورود برای اجرای پرسوجوها محسوب میشود.
- field: نام فیلدی که قصد دارید بر اساس آن دادهها را فیلتر کنید.
- دو زیرخط (
__): این بخش یکی از مهمترین قراردادهای جنگو است. دو زیرخط (UnderLine) وظیفه دارد نام فیلد را از نوع عملیات جستوجو (Lookup) جدا کند. دلیل استفاده از دو زیرخط این است که بسیاری از نام فیلدها خودشان شامل یک زیرخط هستند؛ مانندfirst_name. به همین دلیل، استفاده از__از هرگونه ابهام در نامگذاری جلوگیری میکند و به جنگو نشان میدهد که بخش بعدی یک دستور ویژه یا Lookup است. - lookup: نوع مقایسه یا عملیاتی که باید روی مقدار فیلد انجام شود؛ برای مثال «بزرگتر از».
- value: مقداری که فیلد با آن مقایسه خواهد شد.
اکنون که با ساختار و منطق Field Lookups آشنا شدید، وقت آن است که ببینیم چگونه میتوان از این قابلیت برای انجام جستوجوهای قدرتمند و انعطافپذیر روی دادههای متنی در پایگاه داده استفاده کرد.
جستجوی متن: کار با رشتهها (String Lookups) در جنگو
نکته: در جنگو، Lookupها بهصورت پیشفرض از نوع
exactهستند. بنابراین اگر Lookup را مشخص نکنید، مانندEntry.objects.get(id=1)، جنگو بهطور خودکار ازexactاستفاده میکند.
Lookup exact: این Lookup برای تطابق دقیق مقدار بهکار میرود. به بیان دیگر، همان مقداری که وارد میکنید، بدون هیچ تغییری مبنای جستجو و فیلتر قرار میگیرد. برای مثال، اگر مقدار 'hello' باشد، تنها رکوردهایی که دقیقاً همین مقدار را دارند پیدا خواهند شد. همچنین exact نسبت به حروف کوچک و بزرگ حساس (Case-sensitive) است؛ بنابراین 'Hello' و 'hello' دو مقدار متفاوت محسوب میشوند.
در جستجوهای متنی، حساس بودن یا نبودن به حروف کوچک و بزرگ نقش مهمی در نتیجه نهایی دارد. جنگو این امکان را فراهم کرده است که بسته به نیاز پروژه، تعیین کنید آیا تفاوت بین حروف بزرگ و کوچک در فرآیند جستجو لحاظ شود یا خیر.
| نام Lookup | کاربرد | نمونه کد |
|---|---|---|
| exact | تطابق دقیق و حساس به حروف | headline__exact="Hello" |
| iexact | تطابق دقیق بدون حساسیت به حروف | username__iexact="Admin" |
| contains | شامل بودن عبارت (حساس به حروف) | bio__contains="Python" |
| icontains | شامل بودن عبارت (بدون حساسیت به حروف) | content__icontains="django" |
| startswith | شروع شدن با یک عبارت مشخص (حساس) | code__startswith="PRO" |
| istartswith | شروع شدن با عبارت مشخص (بدون حساسیت) | email__istartswith="info" |
| endswith | پایان یافتن با عبارت مشخص (حساس) | file_path__endswith=".pdf" |
| iendswith | پایان یافتن با عبارت مشخص (بدون حساسیت) | domain__iendswith=".COM" |
| regex | تطابق با عبارت منظم (Regex) | phone__regex=r'^\d{3}$' |
| iregex | رجکس بدون حساسیت به حروف | slug__iregex=r'^[a-z]+$' |
استفاده از حرف i در Lookups Field
در جنگو، هر زمان حرف i در ابتدای یک Lookup قرار بگیرد، مانند icontains یا iexact، مخفف Ignore Case است. این یعنی هنگام جستجو، تفاوتی بین حروف بزرگ و کوچک در نظر گرفته نمیشود؛ برای مثال، 'A' و 'a' یکسان تلقی میشوند. استفاده از این Lookups بهویژه در فیلدهای جستجو، تجربه کاربری بهتری ایجاد میکند و نتایج منعطفتری در اختیار کاربران قرار میدهد.
البته جستجو تنها به رشتهها و کلمات محدود نمیشود؛ در بسیاری از سناریوها، اعداد و بازههای عددی نقش مهمی در فیلتر کردن دادهها و استخراج اطلاعات موردنیاز دارند.
منطق ریاضی: مقایسه مقادیر و اعداد (Comparison Lookups) در جنگو
عملگرهای مقایسهای پایه و اساس بسیاری از قوانین و منطقهای تجاری هستند. در جنگو، این مفاهیم ریاضی در قالب Lookupهای ساده و خوانا پیادهسازی شدهاند تا نوشتن کوئریها سریعتر و شفافتر باشد.
| Lookup | معادل ریاضی / مفهوم | نمونه استفاده |
|---|---|---|
| gt | > (بزرگتر از) | price__gt=50000 |
| gte | >= (بزرگتر یا مساوی) | stock__gte=10 |
| lt | < (کوچکتر از) | age__lt=18 |
| lte | <= (کوچکتر یا مساوی) | score__lte=100 |
| range | قرار گرفتن در یک بازه (بسته) | id__range=(10, 50) |
| in | عضویت در یک مجموعه | status__in=["active", "pending"] |
| isnull | بررسی مقدار NULL | deleted_at__isnull=True |
تفاوت کاربردی range و in در کوئری های جنگو
از نگاه یک برنامهنویس با تجربه، انتخاب میان این دو Lookup کاملاً به ماهیت داده و نیاز سناریو بستگی دارد.
-
rangeبرای مقادیر پیوسته کاربرد دارد و معمولاً یک Tuple شامل مقدار آغاز و پایان دریافت میکند. جنگو در سطح SQL این فیلتر را به دستورBETWEENتبدیل میکند؛ به همین دلیل برای بررسی قرار گرفتن یک مقدار در یک بازه مشخص، انتخابی مناسب و بهینه است. -
inبرای دادههای گسسته و مجموعهای از مقادیر مشخص طراحی شده است. این Lookup یک List از گزینهها را میپذیرد و زمانی به کار میآید که بخواهید بررسی کنید مقدار موردنظر در بین چند مقدار از پیش تعیینشده وجود دارد یا خیر.
بیشترین دقت در فیلتر کردن دادهها زمانی به دست میآید که وارد دنیای زمان و تاریخ شویم و رکوردها را بر اساس بازههای زمانی یا تاریخهای مشخص جستوجو کنیم.
جست و جو بر اساس زمان: (Date & Time Lookups) در کوئری های جنگو
فیلدهای DateTimeField اطلاعات تاریخ و زمان را بهصورت کامل در خود نگه میدارند، اما همیشه نیازی به بررسی تمام این دادهها نیست. در بسیاری از سناریوها کافی است رکوردها را بر اساس سال، ماه، روز یا حتی یک ساعت مشخص فیلتر کنید. برای چنین مواردی، Django مجموعهای از Date & Time Lookups را در اختیار شما قرار میدهد.
فیلترهای تاریخ و اجزای تقویمی
| Lookup | توضیح | مثال |
|---|---|---|
| date | تبدیل مقدار datetime به تاریخ، بدون درنظرگرفتن زمان |
created_at__date="2025-05-12" |
| year | فیلتر کردن دادهها بر اساس سال | birth_date__year=1995 |
| iso_year | فیلتر سال مطابق استاندارد ISO 8601 | created_at__iso_year=2024 |
| month | انتخاب رکوردها بر اساس ماه (۱ تا ۱۲) | joining_date__month=12 |
| day | فیلتر بر اساس روز ماه (۱ تا ۳۱) | event__day=25 |
| week | فیلتر با استفاده از شماره هفته سال (۱ تا ۵۳) | report__week=42 |
| quarter | انتخاب دادهها بر اساس فصل سال (۱ تا ۴) | sales__quarter=3 |
فیلترهای زمان و روزهای هفته
| Lookup | توضیح | مقدار عددی (نکته مهم) |
|---|---|---|
| week_day | روز هفته بر اساس استاندارد دیتابیس | 1=Sunday (یکشنبه) تا 7 |
| iso_week_day | روز هفته مطابق استاندارد ISO | 1=Monday (دوشنبه) تا 7 |
| hour | استخراج ساعت (۰ تا ۲۳) | log__hour=14 |
| minute | استخراج دقیقه (۰ تا ۵۹) | task__minute=30 |
| second | استخراج ثانیه (۰ تا ۵۹) | tick__second=0 |
| time | فیلتر کردن بر اساس زمان، بدون درنظرگرفتن تاریخ | start__time="14:30:00" |
این Lookupها زمانی کاربرد دارند که بخواهید بدون پردازش کامل مقدار DateTimeField، تنها بخش مشخصی از تاریخ یا زمان را در کوئریهای Date & Time Lookups مبنای فیلتر قرار دهید.
خلاصه مدیریتی و نقشه ذهنی برای مرور سریع
برای اینکه هنگام کدنویسی و توسعه، سریعتر به لوک آپ ها مسلط شوید، این نکات مهم را همیشه در دسترس داشته باشید:
# Field Name + Double Underscore + Lookup Type
field__lookup=value
چکلیست نهایی مرور
-
کستینگ (Casting): لوکآپهایی مانند
dateیاtimeدر عمل نوع داده را بهصورت موقت برای مقایسه تغییر میدهند تا فقط بخش مشخصی از مقدارdatetimeبررسی شود. -
قاعده i: همیشه به خاطر داشته باشید که حرف
iنشاندهنده Case-insensitive بودن است. برای جستجوهایی که توسط کاربر انجام میشوند، معمولاً باید از نسخههای دارایiاستفاده کنید. -
ریاضیات ساده: مخففهای
gt(Greater Than) وlt(Less Than) را مثل عملگرهای ریاضی در ذهن نگه دارید تا کاربردشان سریعتر به یاد بیاید. -
تفاوت روزهای هفته: در
week_dayعدد۱به معنی یکشنبه است، اما درiso_week_dayعدد۱به دوشنبه اشاره دارد.
مقایسه پرکاربردترینها
| قابلیت | contains |
icontains |
|---|---|---|
| حساسیت به حروف | حساس به بزرگی و کوچکی حروف (Ali != ali) |
بدون حساسیت به حروف (Ali == ali) |
| بهترین کاربرد | فیلدهای سیستمی و مواردی که به Case-sensitive بودن نیاز دارند | جستجوی کاربران و متنهای عمومی |
نکته پایانی
استفاده از دو زیرخط (__) در جنگو یک قانون مشخص برای جدا کردن بخشهای منطقی در ORM است. اگر بهجای آن فقط از یک زیرخط استفاده کنید، ORM تصور میکند نام یک فیلد واقعی را وارد کردهاید و در نهایت با خطای FieldError روبهرو میشوید.
همیشه مسیر ارتباطی خود با Lookupها را با دو زیرخط (__) ایجاد کنید.