با عرض سلام و وقت بخیر خدمت کاربران محترم سایت ویرگول، در این مقاله آموزشی که چکیده آموزش جنگو ORM از سایت Django documentation بهمراه مطالب تکمیلی تر , سعی شده یک آموزش کاربردی و پروژه محور از جنگو ORM ارایه گردد.ایده این سری مقاله های آموزشی از این موضوع سرچشمه می گیرد که بخشی از خوانندگان وجود دارد که به محتوای نوشتاری آنلاین بهتر پاسخ می دهند و ترجیج می دهند مهارت های جدید را به سرعت از طریق خواندن افزایش دهند.این سری آموزش ها با ارایه اولین پکیج آموزشی در خصوص جنگو آغاز می شود که انتظار می رود با واکنش مثبت کاربران همراه شود.
توجه: این مقاله به مرور زمان، ویرایش و یا تکمیل میشود!
تقاضا: در صورتی که با مشکل تایپی، دستوری و یا مفهومی در این مقاله برخورد کردید، از شما دوست عزیز و گرامی، صمیمانه تقاضا میکنم که اینجانب را مطلع کرده، تا نسبت به تصحیح و یا تکمیل آن، در اسرع وقت، اقدام نمایم. با کمال تشکر جواد جهانگیری
شماره تلفن همراه: 09149431772
نشانی پست الکترونیکی: javad.jahangiri.niopdc@gmail.com
فیلمهای آموزشی در آپارات:جواد جهانگیری (CTO) - آپارات
فیلم آموزشی در یوتویب: javad jahangiri - YouTube
نسخه مقاله: ۱.۱ - تاریخ بروزرسانی: 1401/01/01
برای دیدن فیلم اموزشی مربوطه به کانال آپاراتی بنده به ادرس جواد جهانگیری (CTO) - آپارات مراجعه نمایید
دقت شود برای دانلود سورس پروژه می توانید به گیت هاب بنده به ادرس زیر مراجعه نمایید
https://github.com/javadjahangiriniopdc/djangoormtutorials
در این آموزش، بحث کاملی در مورد کوئری های ORM جنگو و نحوه استفاده از آنها برای دستکاری داده ها خواهیم داشت.
Django ORM یکی از بهترین ابزارهای جنگو است و نقش بسیار اساسی در انجام وظایف مربوط به پایگاه داده ایفا می کند.
Django ORM از انتزاع سهولت استفاده تشکیل شده است. "چیزهای ساده آسان و چیزهای سخت را ممکن می کند.
در اینجا توضیح مفصلی در مورد هر پرس و جوی ORM خواهیم داشت و همچنین پرس و جوهای SQL مرتبط با آنها را مشاهده می کنیم.
ابتدا یک پروژه جنگو ایجاد می کنیم پیشنهاد می شود برای فعالیت حرفه ای با جنگو از IDE حرفه ای PyCharm شرکت JetBrains که برای کار حرفه با پایتون و فریم های ورک های پایتونی ایجاد شده است استفاده شود
ابتدا یک myappجدید ایجاد می کنیم
و app جدید به پروژه اضافه می کنیم
سپس نسبت migrate اولیه پروژه اقدام می کنیم
و نسبت به ایجاد سوپر کاربر اقدام می کنیم:
سپس به روش ذیل به دیتابیس SQLLite وصل می شویم
سپس فایل SQLite را از مسیر پروژه باز می کنیم
سپس مطمن می شویم که سوپر کاربر ایجاد شده است
سپس از طریق Django Admin وارد پانل ادمین جنگو می شویم:
در نهایت پانل ادمین به شرح زیر قابل مشاهده خواهد بود:
ابتدا یک پایگاه داده نمونه با استفاده از مدل جنگو ایجاد می کنیم که شامل مقداری داده است و کوئری را روی آن پایگاه داده اجرا می کنیم.
برای این کار وارد فایل ماژول مربوط به app جدید می شویم:
model.py
# Create your models here. class Student(models.Model): username = models.CharField(max_length=20) first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) mobile = models.CharField(max_length=10) email = models.EmailField() def __str__(self): return "%s %s" % (self.first_name, self.last_name)
و سپس دستور زیر را در کنسول manage.py اجرا کنید.
python manage.py makemigrations
python manage.py migrate
سپس نسبت اضافه کردن مدل جدید به Admi جنگو به شکل زیر اقدام می کنیم:
اگر دقت کنید مدل دانشجو به پنل ادمین جنگو اضافه گردید:
و نسبت به درج چندین رکورد در این مدل به شرح ذیل اقدام می کنیم
ما قرار است پرس و جو را اجرا کنیم.
ما یک مدل داریم به نام دانشجو. برای دریافت تمام رکوردها از مدل، از Student.objects.all() استفاده می کنیم. برای انجام این کار، Shell جنگو را برای اجرای پرس و جو باز کنید.
ممکن است تعجب کنید که جنگو ORM چگونه پرس و جوهای ما را اجرا می کند یا کد مربوط به کدی که می نویسیم چیست. دریافت پرس و جوی SQL بسیار ساده است، ما باید از str() استفاده کنیم و آبجکت queryset را همراه با پرس و جو ارسال کنیم.
Corresponding SQL Query
ما از Student.objects.create() استفاده می کنیم و فیلدها را به همراه مقدار آن به عنوان آرگومان ارسال می کنیم. بیایید مثال زیر را ببینیم.
توجه داشته باشید که برای ذخیره رکورد جدید ایجاد شده در جدول، باید از متد .save() روی آبجکت query استفاده کنیم، در غیر این صورت در پایگاه داده نمایش داده نخواهد شد.
فرض کنید برای تطبیق نتیجه به یک شی خاص از یک مجموعه کوئری نیاز داریم. می توانیم این کار را با استفاده از متد get() انجام دهیم. get() مستقیماً شی واحد را برمی گرداند. بیایید مثال زیر را ببینیم.
مثال دوم:
همانطور که در هر دو مثال می بینیم، شی واحد را دریافت می کنیم نه مجموعه پرس و جو از یک شی واحد. اگر نتیجه ای وجود نداشته باشد، با پرس و جو مطابقت داشته باشید، get() یک استثنای DoesNotExist را ایجاد می کند. از طرف دیگر، اگر چندین فیلد منطبق وجود داشته باشد، MultipleObjectReturned را افزایش می دهد که یک ویژگی از خود کلاس مدل است.
مثالDoesNotExist :
مثال MultipleObjectReturned:
در مثال قبلی، QuerySet برگردانده شده توسط all() رکورد all را در جدول پایگاه داده توصیف می کند. اما گاهی لازم است زیرمجموعه مجموعه کامل شی را انتخاب کنیم و با افزودن شرایط فیلتر می توان این کار را انجام داد
نکته - تفاوت بین متد get() و filter() در این است که متد filter() مجموعه query شی(ها) را برمی گرداند که در آن متد get() یک شی را برمی گرداند.
یک QuerySet جدید حاوی اشیایی را برمی گرداند که با پارامتر جستجوی داده شده مطابقت ندارند. به عبارت دیگر، رکوردها را با توجه به شرایط جستجو حذف کرد. بیایید مثال زیر را درک کنیم
عملیات OR زمانی انجام می شود که به فیلترینگ رکورد با دو یا چند شرط نیاز داشته باشیم. در مثال زیر، دانشآموزی را دریافت میکنیم که نامش با «A» و نام خانوادگیاش با «M» شروع میشود.
گاهی اوقات می خواهیم چندین شی را در یک shot ایجاد کنیم. فرض کنید می خواهیم یکباره اشیاء جدیدی ایجاد کنیم و نمی خواهیم چندین کوئری را در پایگاه داده اجرا کنیم. bulk_create را برای ایجاد چندین شی به یک روش فراهم می کند
from myapp.models import Student Student.objects.all().count() 3 Student.objects.bulk_create([Student(first_name='fn1',last_name='ln1',mobile='0914',email='fn1@test.com'),Student(first_name='fn2',last_name='ln2',mobile='0913',email='fn2@test.com')]) [<Student: fn1 ln1>, <Student: fn2 ln2>] Student.objects.all().count() 5
ما میتوانیم با استفاده از دستور برش فهرست پایتون، محدودیت را در مجموعه query تنظیم کنیم. این عملیات معادل بندهای LIMIT و OFFSET SQL است. بیایید پرس و جو زیر را ببینیم.
جستجوی زیر اولین رکورد تا رکورد پنجم برمی گرداند.
دقت شود رکورد از جنگو از رکورد صفر شماره گذاری می شود
دقت شود در جنگو Negative indexing پشتیبانی نمی شود. با این حال، ما می توانیم از Stepدر QuerySets استفاده کنیم.
جنگو متد order_by را برای مرتب سازی مجموعه query ارائه می دهد. این متد نام فیلدی را می گیرد که می خواهیم نتیجه را مرتب کنیم (صعودی و نزولی). بیایید مثال زیر را ببینیم
Example - Ascending order
برای مرتب سازی نزولی، قبل از فیلد پرس و جو از Not '-' استفاده می کنیم.
همچنین می توانیم چندین فیلد را در تابع order_by پاس کنیم.
حال، یاد خواهیم گرفت که چگونه می توانیم داده ها را در مدل های رابطه ای واکشی کنیم . مدل دیگری به نام Teacher ایجاد می کنیم که یک مدل مرتبط با مدل Student است.
فایل Model پروژه را به شرح ذیل تغییر می دهیم:
Models.py
from django.db import models class Teacher(models.Model): teacher_name = models.CharField(max_length=200) def __str__(self): return f'{self.teacher_name}' class Student(models.Model): username = models.CharField(max_length=20) first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) mobile = models.CharField(max_length=10) email = models.EmailField() teacher = models.ForeignKey(Teacher, blank=True, null=True, on_delete=models.CASCADE) def __str__(self): return "%s %s" % (self.first_name, self.last_name)
ما معلمان را به مدل دانشجو اضافه کرده ایم و هر معلم با دانش آموز مرتبط است.حالا اگر بخواهیم از طریق مدل دانشجو اطلاعات معلمان را واکشی کنیم می توانیم به صورت زیر عمل کنیم.
admin.py
from django.contrib import admin # Register your models here. from myapp.models import Student, Teacher admin.site.register(Student) admin.site.register(Teacher)
چند تا معلم به جدول Teacher اضافه می کنیم
برای هر کدام از دانشجویان یک معلم اختصاص می دهیم:
from myapp.models import Student
q = Student.objects.select_related('teacher')
print(q.query)
SELECT "myapp_student"."id", "myapp_student"."username", "myapp_student"."first_name", "myapp_student"."last_name", "myapp_student"."mobile", "myapp_student"."email", "myapp_student"."teacher_id", "myapp_teacher"."id", "myapp_teacher"."teacher_name" FROM "myapp_student" LEFT OUTER JOIN "myapp_teacher" ON ("myapp_student"."teacher_id" = "myapp_teacher"."id")
جستجوی فیلد پرس و جو چیزی نیست جز شرطی که همان عبارت SQL WHERE را مشخص می کند. آنها به عنوان آرگومان های کلیدواژه متدهای QuerySet مانند filter()، exclude() و get() بیان می شوند.
Example -
زمانی که هیچ نوع جستجویی ارائه نمی شود (مانند Entry.objects.get(id=14)) برای راحتی، نوع جستجو exact فرض می شود.
exact
¶مطابقت کامل. اگر مقدار ارائه شده برای مقایسه None باشد، به عنوان یک SQL NULL تفسیر می شود
Examples:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
SQL equivalents:
SELECT ... WHERE id = 14; SELECT ... WHERE id IS NULL;
در MySQL، تنظیمات «collation» جدول پایگاه داده تعیین میکند که آیا مقایسههای دقیق به حروف بزرگ و کوچک حساس هستند یا خیر. این یک تنظیمات پایگاه داده است، نه یک تنظیم جنگو. این امکان وجود دارد که جداول MySQL خود را برای استفاده از مقایسههای حساس به حروف کوچک و بزرگ پیکربندی کنید،
iexact
¶تطابق دقیق بدون حساس به حروف کوچک و بزرگ. اگر مقدار ارائه شده برای مقایسه None باشد، به عنوان SQL NULL تفسیر می شود
Example:
Blog.objects.get(name__iexact='beatles blog') Blog.objects.get(name__iexact=None)
SQL equivalents:
SELECT ... WHERE name ILIKE 'beatles blog'; SELECT ... WHERE name IS NULL;
Note the first query will match 'Beatles Blog'
, 'beatles blog'
, 'BeAtLes BLoG'
, etc.
SQLite users
هنگام استفاده از باطن SQLite و رشته های غیر ASCII، یادداشت پایگاه داده در مورد مقایسه رشته ها را در نظر داشته باشید. SQLite تطبیق غیرحساس به حروف بزرگ و کوچک برای رشته های غیرASCII انجام نمی دهد.
contains
¶Case-sensitive containment test.
Example:
Entry.objects.get(headline__contains='Lennon')
SQL equivalent:
SELECT ... WHERE headline LIKE '%Lennon%';
Note this will match the headline 'Lennon honored today'
but not 'lennon honored today'
.
SQLite users
SQLite doesn’t support case-sensitive LIKE
statements; contains
acts like icontains
for SQLite.
icontains
¶Case-insensitive containment test.
Example:
Entry.objects.get(headline__icontains='Lennon')
SQL equivalent:
SELECT ... WHERE headline ILIKE '%Lennon%';
SQLite users
When using the SQLite backend and non-ASCII strings, bear in mind the database note about string comparisons.
in
¶In a given iterable; often a list, tuple, or queryset. It’s not a common use case, but strings (being iterables) are accepted.
Examples:
Entry.objects.filter(id__in=[1, 3, 4]) Entry.objects.filter(headline__in='abc')
SQL equivalents:
SELECT ... WHERE id IN (1, 3, 4); SELECT ... WHERE headline IN ('a', 'b', 'c');
You can also use a queryset to dynamically evaluate the list of values instead of providing a list of literal values:
inner_qs = Blog.objects.filter(name__contains='Cheddar') entries = Entry.objects.filter(blog__in=inner_qs)
This queryset will be evaluated as subselect statement:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
If you pass in a QuerySet
resulting from values()
or values_list()
as the value to an __in
lookup, you need to ensure you are only extracting one field in the result. For example, this will work (filtering on the blog names):
inner_qs = Blog.objects.filter(name__contains='Ch').values('name') entries = Entry.objects.filter(blog__name__in=inner_qs)
This example will raise an exception, since the inner query is trying to extract two field values, where only one is expected:
# Bad code! Will raise a TypeError. inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id') entries = Entry.objects.filter(blog__name__in=inner_qs)
Performance considerations
در مورد استفاده از پرس و جوهای تو در تو محتاط باشید و ویژگی های عملکرد سرور پایگاه داده خود را درک کنید برخی از پشتیبان های پایگاه داده، به ویژه MySQL، پرس و جوهای تودرتو را به خوبی بهینه نمی کنند. در این موارد، استخراج لیستی از مقادیر و ارسال آن به کوئری دوم کارآمدتر است. یعنی به جای یک کوئری از دو کوئری استفاده کنید:
values = Blog.objects.filter( name__contains='Cheddar').values_list('pk', flat=True) entries = Entry.objects.filter(blog__in=list(values))
startswith
¶Case-sensitive starts-with.
Example:
Entry.objects.filter(headline__startswith='Lennon')
SQL equivalent:
SELECT ... WHERE headline LIKE 'Lennon%';
SQLite doesn’t support case-sensitive LIKE
statements; startswith
acts like istartswith
for SQLite.
istartswith
¶Case-insensitive starts-with.
Example:
Entry.objects.filter(headline__istartswith='Lennon')
SQL equivalent:
SELECT ... WHERE headline ILIKE 'Lennon%';
SQLite users
When using the SQLite backend and non-ASCII strings, bear in mind the database note about string comparisons.
endswith
¶Case-sensitive ends-with.
Example:
Entry.objects.filter(headline__endswith='Lennon')
SQL equivalent:
SELECT ... WHERE headline LIKE '%Lennon';
SQLite users
SQLite doesn’t support case-sensitive LIKE
statements; endswith
acts like iendswith
for SQLite. Refer to the database note documentation for more.
iendswith
¶Case-insensitive ends-with.
Example:
Entry.objects.filter(headline__iendswith='Lennon')
SQL equivalent:
SELECT ... WHERE headline ILIKE '%Lennon'
SQLite users
When using the SQLite backend and non-ASCII strings, bear in mind the database note about string comparisons.
range
¶Range test (inclusive).
Example:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
SQL equivalent:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
You can use range
anywhere you can use BETWEEN
in SQL — for dates, numbers and even characters.
Warning
Filtering a DateTimeField
with dates won’t include items on the last day, because the bounds are interpreted as “0am on the given date”. If pub_date
was a DateTimeField
, the above expression would be turned into this SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
جنگو ORM تسهیلات گروه بندی را با استفاده از توابع تجمعی مانند Max، Min، Avg و Sum فراهم می کند. گاهی اوقات ما نیاز داریم که مقادیر کل را از اشیا بدست آوریم. بیایید مثال زیر را درک کنیم.
>>> from django.db.models import Avg, Max, Min, Sum, Count >>> Student.objects.all().aggregate(Avg('id')) {'id__avg': 5.5} >>> Student.objects.all().aggregate(Min('id')) {'id__min': 1} >>> Student.objects.all().aggregate(Max('id')) {'id__max': 10} >>> Student.objects.all().aggregate(Sum('id')) {'id__sum': 55}
توجه شود truncate کوتاه کردن در SQL به معنای پاک کردن داده های جدول برای استفاده در آینده است. جنگو متدهای داخلی را برای کوتاه کردن جدول ارائه نمی دهد، اما می توانیم از متد delete() برای به دست آوردن نتیجه مشابه استفاده کنیم. بیایید مثال زیر را درک کنیم.
>>> Student.objects.all().count() 5 >>> Student.objects.all().delete() (5, {'sampleapp.Student': 5}) >>> Student.objects.all().count() 0 >>> Student.objects.all() <QuerySet []>
توجه شود Union به معنای دریافت رکوردی است که در هر دو مجموعه پرس و جو رایج است. بیایید ببینیم چگونه می توانیم این کار را انجام دهیم.
gt
¶
Greater than.
Example:
Entry.objects.filter(id__gt=4)
SQL equivalent:
SELECT ... WHERE id > 4;
gte
¶Greater than or equal to.
lt
¶Less than.
lte
¶Less than or equal to.
q1 = Student.objects.filter(id__gte = 15) >>> q1 <QuerySet [<Student: Megha Bhardwaj>, <Student: Akash Mishra>]> >>> q2 = Student.objects.filter(id__lte = 15) >>> q2 <QuerySet [<Student: Ritesh Tiwari>, <Student: Yash Sharma>, <Student: Arpita Sharma>, <Student: Prince Sharma>, <Student: Megha Bhardwaj>]> >>> q1.union(q2 <QuerySet [<Student: Ritesh Tiwari>, <Student: Yash Sharma>, <Student: Arpita Sharma>, <Student: Prince Sharma>, <Student: Megha Bhardwaj>, <Student: Akash Mishra>]>
در جنگو، ما اغلب از null و blank استفاده می کنیم، به طور پیش فرض مقادیر آنها False است. هر دوی این مقادیر در سطح فیلدی کار می کنند که می خواهیم یک فیلد null یا blank نگه داریم. هر دو مقدار مشابه به نظر می رسند اما در استفاده متفاوت هستند.
If null=True means the field value is set as NULL i.e. no data. It is basically for the database column value.
date = models.DateTimeField(null=True)
The blank = True specifies whether field is required in forms.title = models.CharField(blank=True) // title can be kept blank. In the database ("") will be stored.
If we set null=True blank=True, means that the field is optional in all circumstances.
teacher = models.ForeignKey(null=True, blank=True) // The exception is CharFields() and TextFields(), which in Django are never saved as ?→NULL. Blank values are stored in the DB as an empty string ('').