<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های ?Django learn</title>
        <link>https://virgool.io/feed/@Django_learn</link>
        <description>تمام چیزی که برای یاد گرفتن جنگو لازم دارید... ?</description>
        <language>fa</language>
        <pubDate>2026-04-15 09:54:54</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2397342/avatar/vSNORD.jpg?height=120&amp;width=120</url>
            <title>?Django learn</title>
            <link>https://virgool.io/@Django_learn</link>
        </image>

                    <item>
                <title>آموزش جنگو : جلسه چهل و چهار | بررسی Raw SQL در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%DA%86%D9%87%D9%84-%D9%88-%DA%86%D9%87%D8%A7%D8%B1-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-raw-sql-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-chazwpcvdqgd</link>
                <description>در این جلسه در رابطه با Raw SQL در جنگو صحبت خواهیم کرد . با ما همراه باشید . آموزش جنگو : جلسه چهل و چهار | بررسی Raw SQL در جنگومفهوم Raw SQLهمانطور که قبلا گفتیم جنگو می تواند کد های SQL خام را نیز اجرا کند . یعنی کد های SQL را خودتان به صورت دستی بنویسید و جنگو فقط برای شما آنها را اجرا کند . اینکار به کمک یکی از متد ها با نام raw انجام می شد . پس از اجرای SQL ,اگر یک کوئری را در آن نوشته باشید نتایج آن را دریافت خواهید کرد . یک راه دیگر برای اجرای کد های خام SQL ,نوشتن آنها در ترمینال دیتابیس است که کاملا جدا از جنگو عمل خواهد کرد .نکته : تا حد ممکن توصیه می شود که از اجرای کد های خام جلوگیری کنید . قبل از هر استفاده ,بررسی کنید که آیا خود متد های جنگو می توانند آن کار را برای شما انجام دهند یا خیر . در صورتی که این را نمی دانید ,می توانید از کانال های پشتیبانی جنگو که در خود سایت رسمی آن وجود دارند , سوال کنید .نکته : در استفاده از raw به شدت مراقب باشید . بررسی نکردن نحوه کارکرد کد شما و پارامتر های ارسالی به کد SQLشما ,باعث ایجاد باگ SQL injection می شود .اجرای دستورات SQL خاممتد Manager.raw را می توان از manager مربوط به مدل فراخوانی کرد . در این بخش یاد می گیریم که چطور یک Query را با استفاده از raw بنویسیم . سینتکس کلی آن به شکل زیر است :Manager.raw(raw_query, params=, translations=None)این متد یک Query را به شکل کد SQL دریافت می کند ,آن را اجرا می کند و نتایج آن را به صورت شی ای از کلاس django.db.models.query.RawQuerySet بازمیگرداند . شی کلاس RawQuerySet ,می تواند مانند یک QuerySet معمولی در جنگو ,پیمایش شود و اشیایی را بازگرداند .بیایید با یک مثال آن را بررسی کنیم . فرض کنید مدل زیر را دارید :class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)می توانید کد SQL خود را مانند زیر اجرا کنید :&gt;&gt;&gt; for p in Person.objects.raw(&amp;quotSELECT * FROM myapp_person&amp;quot):
...          print(p)
...
John Smith
Jane Jonesمثال بالا مانند این است که کد Person.objects.all را فراخوانی کنیم . آنچنان هیجان انگیز نیست ,مگرنه؟ گرچه در ادامه با پارامتر و آرگومان هایی در این متد آشنا می شویم که بسیار شگفت انگیز هستند !نکته : شاید برای شما سوال باشد که نام جدول درون کد SQL از کجا آمده است ؟ به طور پیش فرض جنگو نام جدول مربوط به مدل را اینطور مشخص می کند :نام اپ شما که در settings.py وجود دارد +علامت _ +نام کلاس مدل شما . در این مثال ,ما فرض کردیم که مدل Person در اپ myapp قرار دارد ,پس نام جدول مربوط به آن برابر با myapp_person است .شما همچنین ممکن است با تنظیم db_table برای مدل خود ,نام جدول مربوطه را خودتان تنظیم کنید .نکته : جنگو هیچگاه کد SQL وارد شده در متد raw را بررسی نمی کند . جنگو انتظار دارد که کد شما یک مجموعه از ردیف ها (سطر ها) را بازگرداند . اگر کد شما این کار را انجام ندهد ,به ارور های عجیب و رمزگذاری شده (cryptic) برخواهید خورد .نکته : اگر از دیتابیس MySQL استفاده می کنید ,به یاد داشته باشید که قابلیت MySQL’s silent type coercion در این دیتابیس ممکن است باعث ایجاد نتایج غیرمنتظره شود . برای مثال اگر در ستونی که نوع آن str است , Query اجرا کنید ولی مقدار ارسال شده برای جستجوی خود را یک Integerبگذارید ,دیتابیس MySQL قبل از فرایند مقایسه و جستجو ,تمامی مقادیر جدول را به یک Integerتبدیل خواهد کرد . در این صورت اگر جدول شما دارای مقادیری مانند ‘abc’ و ‘def’ باشد و شما WHERE mycolumn=0 را اجرا کنید ,هر دو مقدار بازگردانده می شوند . برای جلوگیری از این امر , نوع صحیح برای نوشتن در کد SQL خود را پیدا کنید .اتصال فیلد های درون Query و مدلمتد raw به صورت خودکار فیلد های درون کد SQL شما را با فیلد های مدل متصل می کند (به این فرایند map می گویند) .ترتیب فیلد ها در کد شما مهم نیست . به عبارت دیگر دو کد زیر , هر دو یک نتیجه را بازمیگردانند :&gt;&gt;&gt; Person.objects.raw(&amp;quotSELECT id, first_name, last_name, birth_date FROM myapp_person&amp;quot)&gt;&gt;&gt; Person.objects.raw(&amp;quotSELECT last_name, birth_date, first_name, id FROM myapp_person&amp;quot)در واقع فرایند تطبیق فیلد ها با استفاده از نام آنها انجام می شود . این به این معنی است که می توانید از دستور AS(یکی از دستورات SQL) استفاده کنید تا خودتان فیلد های مدل و فیلد های درون کد SQL خود را متصل کنید . بنابراین اگر جدول دیگری داشتید که داده های Person را درونش داشتید ,شما می توانید به راحتی آن را در اشیای مدل Person ,به اصطلاح map کنید :&gt;&gt;&gt; Person.objects.raw(
...      &amp;quot&amp;quot&amp;quot
...          SELECT first AS first_name,
...          last AS last_name,
...          bd AS birth_date,
...          pk AS id,
...          FROM some_other_table
...      &amp;quot&amp;quot&amp;quot
...  )تا زمانی که نام های فیلدهای درون عبارت مطابقت پیدا کنند , کد شما به درستی اشیا را بازمیگرداند یا ایجاد می کند .از طرف دیگر می توانید از آرگومان translations نیز در متد raw استفاده کنید . این آرگومان یک دیکشنری را دریافت می کند و با استفاده از آن نام فیلد ها در کد SQL را به نام فیلد ها در مدل ,متصل می کند . به عنوان مثال ,کد بالا می تواند به صورت زیر نیز نوشته شود :&gt;&gt;&gt; name_map = {&amp;quotfirst&amp;quot: &amp;quotfirst_name&amp;quot, &amp;quotlast&amp;quot: &amp;quotlast_name&amp;quot, &amp;quotbd&amp;quot: &amp;quotbirth_date&amp;quot, &amp;quotpk&amp;quot: &amp;quotid&amp;quot}
&gt;&gt;&gt; Person.objects.raw(&amp;quotSELECT * FROM some_other_table&amp;quot, translations=name_map)پشتیبانی از indexمتد raw از indexing نیز پشتیبانی می کند . این یعنی اگر از میان نتایج ,فقط به اولین از آنها نیاز دارید ,میتوانید بنویسید :&gt;&gt;&gt; first_person = Person.objects.raw(&amp;quotSELECT * FROM myapp_person&amp;quot)[0]با این حال فرایند indexing و slicing در سطح دیتابیس انجام نمی شود و جنگو خودش آن را بر عهده می گیرد . اگر تعداد اشیایی که با آنها کار می کنید زیاد است ,توصیه می شود تا جستجوی خود را با استفاده از دستور LIMIT ,محدودتر کنید . برای مثال :&gt;&gt;&gt; first_person = Person.objects.raw(&amp;quotSELECT * FROM myapp_person LIMIT 1&amp;quot)[0]تاخیر در بارگذاری فیلد هافیلد ها همچنین می توانند در حالت معوق باشند (متد defer را به یاد دارید ؟) :&gt;&gt;&gt; people = Person.objects.raw(&amp;quotSELECT id, first_name FROM myapp_person&amp;quot)اشیای مدل Person که توسط این Query بازگردانده می شوند ,مانند این است که defer روی آنها فراخوانی شده باشد . در واقع معنی آن این است که فیلد هایی که از Query حذف شده باشند ,فقط در صورت استفاده ,از دیتابیس بارگذاری می شوند . برای مثال :&gt;&gt;&gt; for p in Person.objects.raw(&amp;quotSELECT id, first_name FROM myapp_person&amp;quot):
...         print(
...              p.first_name, # This will be retrieved by the original query
...              p.last_name, # This will be retrieved on demand
...         )
...
John Smith
Jane Jonesدر ظاهر به نظر می رسد که Query ,هر دو فیلد first_name و last_name را از دیتابیس بارگذاری کرده است . ولی در واقع این مثال 3 Query مختلف را انجام داده است . فقط فیلد first_name با متد raw از دیتابیس بارگذاری می شود . فیلد last_name فقط در صورتی که به آن اشاره شود ,بارگذاری خواهد شد .فقط یک فیلد وجود دارد که هیچ گاه نمی توانید آن را معوق کنید (کاری کنید که فقط وقتی آن را استفاده کردید ,بارگذاری شود) ;فیلد primary key .جنگو از این فیلد برای شناسایی اشیا استفاده می کند . بنابراین همیشه باید در کد SQL نوشته شده ,این فیلد ذکر شود . در صورتی که این کار را انجام ندهید , با یک ارور FieldDoesNotExist مواجه خواهید شد .استفاده از annotationsهمچنین می توانید از فیلد هایی در Query خود استفاده کنید که در مدل شما تعریف نشده اند . برای مثال می توانید از تابع age در PostgreSQL (جزو توابع خود دیتابیس است و ربطی به جنگو ندارد) استفاده کنید تا اشیایی بازگردانده شوند که سن آنها (فیلد age) با کمک دیتابیس محاسبه می شود :&gt;&gt;&gt; people = Person.objects.raw(&amp;quotSELECT *, age(birth_date) AS age FROM myapp_person&amp;quot)
&gt;&gt;&gt; for p in people:
...          print(&amp;quot%s is %s.&amp;quot % (p.first_name, p.age))
...
John is 37.
Jane is 42.
...البته این عملکرد زیاد مورد استفاده قرار نمی گیرد . معمولا برای این کار از توابع F و یا Func که در آینده با آن اشنا می شوید ,استفاده می شود .ارسال پارامتر به متد rawاگر نیاز به ارسال پارامتر به Query خود دارید ,از آرگومان params برای اینکار استفاده کنید . مانند :&gt;&gt;&gt; lname = &amp;quotDoe&amp;quot
&gt;&gt;&gt; Person.objects.raw(&amp;quotSELECT * FROM myapp_person WHERE last_name = %s&amp;quot, [lname])آرگومان params یک لیست یا دیکشنری از پارامتر ها را می پذیرد . شما باید از %s برای جایگذاری پارامتر های یک لیست و از (key)% برای جایگذاری پارامتر های یک دیکشنری (که در آن key با مقدار key در دیکشنری ارسالی جایگزین می شود) استفاده کنید . متغییر های تعریف شده در Query با محتویات آرگومان params جایگزین خواهند شد.نکته : در دیتابیس SQLite شما نمی توانید برای آرگومان params یک دیکشنری را ارسال کنید . بجای دیکشنری از لیست استفاده کنید .نکته : هیچگاه در Query خود از string formatting و یا quote placeholders استفاده نکنید . برای مثال شاید بخواهید Query بالا را به شکل زیر بنویسید (که نباید اینطور بنویسید) :&gt;&gt;&gt; query = &amp;quotSELECT * FROM myapp_person WHERE last_name = %s&amp;quot % lname
&gt;&gt;&gt; Person.objects.raw(query)شاید هم فکر کنید که می توانید مثال بالا را به شکل زیر نیز بنویسید که نباید این کار را نیز انجام بدهید (به کمک استفاده از ‘ در %s) :&gt;&gt;&gt; query = &amp;quotSELECT * FROM myapp_person WHERE last_name = &#039;%s&#039;&amp;quotانجام این اشتباهات ممکن است باعث ایجاد باگ هایی مانند SQL injection می شود . استفاده از آرگومان params و استفاده نکردن از ‘ در کد SQL خود ,باعث می شود تا جنگو شما را در برابر این گونه حملات تحت محافظت قرار بدهد .اجرای دستورات SQL به صورت مستقیمگاهی اوقات فقط استفاده از متد raw کافی نخواهد بود . ممکن است گاهی لازم باشد تا یک Queryرا ایجاد کنید که به مدل شما بطور واضح اشاره نمی کند و یا دستورات UPDATE , INSERT و DELETE را مستقیما خودتان بنویسید .در این موارد همیشه می توانید به طور کامل به دیتابیس دسترسی داشته باشید و عملیات های خود را برای مدل انجام بدهید .شی django.db.connection حاوی اطلاعات نحوه اتصال به دیتابیس پیش فرض شماست . برای استفاده و اتصال به دیتابیس , connection.cursor را فراخوانی کنید تا یک شی از کلاس cursor را دریافت کنید . سپس cursor.execute(sql, [params]) را فراخوانی کنید و کد SQL خود را درون آن قرار دهید . در پایان می توانید از cursor.fetchone یا cursor.fetchall برای دریافت نتایج و ردیف های موجود در نتیجه استفاده کنید .برای مثال :from django.db import connection

def my_custom_sql(self):
    with connection.cursor as cursor:
        cursor.execute(&amp;quotUPDATE bar SET foo = 1 WHERE baz = %s&amp;quot, [self.baz])
        cursor.execute(&amp;quotSELECT foo FROM bar WHERE baz = %s&amp;quot, [self.baz])
        row = cursor.fetchone
    return rowبرای حفظ امنیت و مقابله با sql injection ,نباید در اطراف %sو اینگونه متغییر ها در کد خود از علامت ‘استفاده کنید .اگر می خواستید دقیقا علامت درصد برای کد شما اعمال شود ,علامت درصد را دو بار بنویسید . برای مثال :cursor.execute(&amp;quotSELECT foo FROM bar WHERE baz = &#039;30%&#039;&amp;quot)
cursor.execute(&amp;quotSELECT foo FROM bar WHERE baz = &#039;30%%&#039; AND id = %s&amp;quot, [self.id])اگر از بیش از یک دیتابیس استفاده می کنید ,می توانید از django.db.connections استفاده کنید تا شی کلاس cursor را برای دیتابیس دلخواه خود دریافت کنید . django.db.connections یک دیکشنری مانند است که به شما اجازه می دهد با alias name دیتابیس خود ,اتصال مربوطه را پیدا کنید و در آخر با آن دیتابیس کار کنید . برای مثال :from django.db import connections

with connections[&amp;quotmy_db_alias&amp;quot].cursor as cursor:
    # Your code hereبه طور پیش فرض , API مربوط به دیتابیس ها در پایتون نتایج خود را بدون نام فیلد آنها بازمیگرداند , این یعنی شما بجای یک دیکشنری ,یک لیست از مقادیر را دریافت می کنید . با کدی شبیه به کد زیر و استفاده از مقدار کمی از حافظه ,می توانید نتایج را تبدیل به دیکشنری کنید :def dictfetchall(cursor):
    &amp;quot&amp;quot&amp;quot Return all rows from a cursor as a dict. Assume the column names are unique. &amp;quot&amp;quot&amp;quot
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall]یک راه دیگر استفاده از collections.namedtuple است که یکی از کتابخانه های استاندارد پایتون است . یک namedtuple در واقع شبیه تاپل است که دارای فیلد هایی است که می توانید به آنها بر اساس ویژگی هایشان دسترسی پیدا کنید . همچنین ویژگی های iterable و indexable را نیز دارد . به فیلد ها می توانید از طریق نام آنها یا ایندکس آنها دسترسی پیدا کنید . برای مثال :from collections import namedtuple

def namedtuplefetchall(cursor):
    &amp;quot&amp;quot&amp;quot Return all rows from a cursor as a namedtuple.
    Assume the column names are unique. &amp;quot&amp;quot&amp;quot
    desc = cursor.description
    nt_result = namedtuple(&amp;quotResult&amp;quot, [col[0] for col in desc])
    return [nt_result(*row) for row in cursor.fetchall]مثال های بالا (dictfetchall و namedtuplefetchall) فرض می گیرد که نام ستون ها در دیتابیس متفاوت و unique هستند ,زیرا شی های cursor نمی توانند ستون ها را از جداول مختلف تشخیص دهند .در اینجا یک مثال از تفاوت بین این سه وجود دارد :&gt;&gt;&gt; cursor.execute(&amp;quotSELECT id, parent_id FROM test LIMIT 2&amp;quot)
&gt;&gt;&gt; cursor.fetchall
((54360982, None), (54360880, None))
&gt;&gt;&gt; cursor.execute(&amp;quotSELECT id, parent_id FROM test LIMIT 2&amp;quot)
&gt;&gt;&gt; dictfetchall(cursor)
[{&#039;parent_id&#039;: None, &#039;id&#039;: 54360982}, {&#039;parent_id&#039;: None, &#039;id&#039;: 54360880}]

&gt;&gt;&gt; cursor.execute(&amp;quotSELECT id, parent_id FROM test LIMIT 2&amp;quot)
&gt;&gt;&gt; results = namedtuplefetchall(cursor)
&gt;&gt;&gt; results
[Result(id=54360982, parent_id=None), Result(id=54360880, parent_id=None)]
&gt;&gt;&gt; results[0].id
54360982
&gt;&gt;&gt; results[0][0]
54360982نکته ای درباره connectionsو cursorsتا اینجا دو مفهوم با نام های cursor و connection(اتصال شما به دیتابیس) بررسی شده است .این دو معمولا استاندارد های API مربوط به دیتابیس ها در پایتون را رعایت می کنند به جز زمانی که بحث مدیریت transaction در یک دیتابیس باشد .اگر با استاندارد های این API آشنا نیستید ,به یاد داشته باشید که کد SQL در cursor.execute ,از متغییر ها به شکل &quot;%s&quot; به جای اضافه کردن پارامتر ها به صورت مستقیم استفاده می کند .توجه داشته باشید که جنگو انتظار دارد تا از &quot;%s&quot; برای تعریف متغییر استفاده کنید و نه “?” (که فقط در ساختار های SQLite استفاده می شود) .در مثال زیر نیز از یک شی cursor به عنوان یک context manager استفاده کردیم :with connection.cursor as c:
    c.execute(...)کد بالا برابر با کد زیر خواهد بود :c = connection.cursor
try:
    c.execute(...)
finally:
    c.closeدر این جلسه در رابطه با Raw SQL در جنگو صحبت کردیم . پ ن : در اینجا دوره ما تموم میشه ! اگه قسمتمون بود , باز برمیگردیم و با جلسات بیشتر در خدمتتون هستیم . مارو از دعای خیر خودتون بی نصیب نزارید . مخلص شما :)</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sun, 17 Dec 2023 18:51:29 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه چهل و سه | بررسی Manager در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%DA%86%D9%87%D9%84-%D9%88-%D8%B3%D9%87-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-manager-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-ovxrrz3getcv</link>
                <description>در این جلسه در رابطه با مفهوم manager در جنگو صحبت می کنیم . با ما همراه باشید . آموزش جنگو : جلسه چهل و سه | بررسی Manager در جنگومفهوم Managerدر واقع Manager(در بعضی بخش ها آن را به عنوان مدیر خواندید) یک رابط میان مدل و کوئری های شماست . در واقع کاری که انجام می دهد این است که کوئری (query) را بررسی می کند و پس از اینکه فهمید این کوئری چکاری انجام می دهد , آن را روی مدل پیاده سازی می کند . حداقل یک manager برای هر مدل وجود دارد .همچنین managerیک مدل ,دسترسی به تمام متد های قابل اجرا برای دیتابیس را فراهم می کند . برای مثال برای فیلتر کردن رکورد های جدولی ,از یک کلاس manager استفاده می کنیم و می گوییم : model.manager_name.filterنحوه کار managerها را قبلا بررسی کردیم . در این بخش درباره شخصی سازی کلاس های manager است . (احتمالا تمام آنچه می دانید این است که manager یک کلاس قابل بازنویسی شدن است)نام گذاری managerبه طور پیش فرض ,هر manager یک نام دارد . جنگو به طور پیش فرض برای هر مدل یک manager را ایجاد می کند و نام آن را objects می گذارد . گرچه ,اگر می خواهید نام manager را تغییر بدهید و یا نام یکی از فیلد ها را objectsبگذارید ,کافی است آن را در همان کلاس مدل تغییر دهید . برای تغییر نام یک manager برای یک کلاس یا مدل مشخص یک ویژگی در همان کلاس تعریف کنید و مقدار آن را برابر با models.Manager قرار دهید . برای مثال :from django.db import models

class Person(models.Model):
    # ...
    people = models.Managerبا استفاده از این مثال ,استفاده از Person.objects برای دسترسی به manager باعث ایجاد AttributeError می شود. شما برای دسترسی به manager باید از Person.people استفاده کنید . برای مثال Person.people.allشخصی سازی managerشما می توانید به وسیله ارث بری از کلاس Manager و گسترش آن ,یک manager سفارشی را ایجاد کنید و آن را در مدل خود به شکلی که بالا گفتیم ,معرفی کنید .دو دلیل وجود دارد که بخواهید یک manager سفارشی را ایجاد کنید . اضافه کردن متد های سفارشی به manager و یا تغییر آنچه QuerySet اولیه یا پیش فرض می تواند بازگرداند . حال بیایید به معرفی هر کدام از این موقعیت ها بپردازیم .اضافه کردن متد سفارشی به managerافزودن متد های سفارشی به یک کلاس manager ,راهی است که می توانید توابعی را برای اجرا در سطح جدول از دیتابیس ,تعریف کنید . برای ساخت توابعی که در سطرها تاثیر بگذارند (برای مثال توابعی که فقط روی یک رکورد از جدول تاثیر بگذارند) از متد های Model استفاده کنید و کلاس manager را تغییر ندهید .برای مثال ,در کد زیر یک تابع (متد نیز می توان گفت) با نام with_counts را به یک manager اختصاص دادیم و از آن در یک مدل استفاده کردیم :from django.db import models
from django.db.models.functions import Coalesce

class PollManager(models.Manager):
    def with_counts(self):
        return self.annotate(num_responses=Coalesce(models.Count(&amp;quotresponse&amp;quot), 0))

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    objects = PollManager

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    # ...در مثال بالا ,می توانید از کد OpinionPoll.objects.with_counts ,برای دریافت یک QuerySet از اشیای مدل OpinionPoll که یک ویژگی num_responses در خود داشته باشند,استفاده کنید .یک تابع سفارشی در یک manager می تواند هر چیزی را بازگرداند و لازم نیست حتما یک QuerySet باشد .نکته دیگری که باید به آن توجه داشت این است که متد های Manager می توانند به self.model دسترسی داشته باشند . این یعنی دسترسی مستقیم به مدلی که از آن کلاس به عنوان manager استفاده کند .تغییر آنچه QuerySet بازمی گرداندمعمولا managerها برای مقدار QuerySet اولیه خود ,تمام اشیا مدل را بازمیگردانند . یعنی آنها روی تمامی اشیا عملیاتی را انجام می دهند و شما می توانید آنها را محدودتر کنید تا روی تعداد کمتری از اشیا عملیات ها را انجام بدهند . برای ادامه بخش ,مثال زیر را در نظر داشته باشید :from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)کد Book.objects.all تمامی اشیا درون مدل Book را بازخواهد گرداند . در واقع از زاویه ای دیگر می توان گفت تمامی رکورد های درون جدول Book را بازمیگرداند . می توان با بازنویسی (override) متد Manager.get_queryset در کلاس مربوط به manager ,آن QuerySet اولیه را بازنویسی کرد تا یک QuerySet با ویژگی های موردنظر شما را بازگرداند .به عنوان مثال ,مدل زیر دارای دو manager است . یکی که به صورت پیش فرض همه ی اشیا را بازمیگرداند و دیگری که به صورت پیش فرض فقط اشیایی را بازمیگرداند که مربوط به شخصی با نام Roald Dahl(نویسنده کتاب هایی که اکثر آنها یک فیلنامه شدند مانند چارلی و کارخانه شکلات سازی!) باشد .class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super.get_queryset.filter(author=&amp;quotRoald Dahl&amp;quot)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)
    objects = models.Manager  # The default manager.
    dahl_objects = DahlBookManager # The Dahl-specific manager.نکته : منظور از QuerySet پیش فرض یک manager ,در واقع اشیایی است که جنگو آنها را به صورت معمول پیدا می کند یا روی آنها عملیاتی انجام می دهد . برای مثال برای managerهای عادی تمام اشیا است و به این معنی است که متد filter در آن manager روی تمامی اشیا عملیات جستجوی خود را انجام می دهد . ولی برای manager شماره دو مثال بالا ,جستجو روی اشیایی انجام می شود که نویسنده آنها همان شخص نامبرده است .با مدل های بالا ,کد Book.objects.all تمامی اشیا درون مدل را بازمیگرداند و Book.dahl_objects.all فقط اشیایی را بازمیگرداند که نویسنده آنها آقای Dahl باشد .از آنجایی که متد get_queryset یک QuerySet را بازمیگرداند ,پس می توانید از تمامی متد های exclude یا filter روی آن استفاده کنید . بنابراین کدهای زیر ,تمامی کار خواهند کرد :Book.dahl_objects.all
Book.dahl_objects.filter(title=&amp;quotMatilda&amp;quot)
Book.dahl_objects.countالبته قبل از هر چیزی ,این مثال یک نکته کاربردی دیگر نیز داشت . اینکه می توانید از چند manager در یک مدل استفاده کنید . شما می توانید هر تعداد manager که می خواهید در یک مدل تعریف کنید . برای مثال :class AuthorManager(models.Manager):
    def get_queryset(self):
        return super.get_queryset.filter(role=&amp;quotA&amp;quot)

class EditorManager(models.Manager):
    def get_queryset(self):
        return super.get_queryset.filter(role=&amp;quotE&amp;quot)

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField( max_length=1, choices=[(&amp;quotA&amp;quot, _(&amp;quotAuthor&amp;quot)), (&amp;quotE&amp;quot, _(&amp;quotEditor&amp;quot))])
    people = models.Manager
    authors = AuthorManager
    editors = EditorManagerدر مثال بالا می توانید هر بار کد های Person.authors.all , Person.editors.all و Person.people.all را فراخوانی کنید و نتایج مختلف را که می خواهید ,داشته باشید .مفهوم default managerاگر چند ویژگی manager را درون مدل خود تعریف کردید ,به یاد داشته باشید که ترتیب تعریف آنها مهم است . معمولا اولین ویژگی manager تعریف شده در مدل به عنوان یک پیش فرض در نظر گرفته می شود و چندین بخش جنگو (مانند dumpdata) از همان manager استفاده می کنند . بعدا بیشتر درباره بخش هایی که از manager پیش فرض استفاده می کنند ,خواهید فهمید . در نتیجه کافی است در تعریف اولین ویژگی ,مراقب باشید .البته در کلاس متا مدل می توانید پیش فرض را با استفاده Meta.default_manager_name تغییر دهید . نام manager که می خواهید پیش فرض باشد را به عنوان مقدار به این ویژگی بدهید .اگر در حال نوشتن کدی هستید که باید یک مدل ناشناخته را مدیریت کند (به عنوان مثال در اپ third-party یا شخص ثالث که یک generic view را پیاده سازی می کند) , از همین manager استفاده کنید . البته می توانید از base_manager_ نیز استفاده کنید که جلوتر توضیح می دهیم .مفهوم base manager و کاربرد آن در روابط دیتابیسیجنگو برای دسترسی به اشیایی که در روابط (مانند one-to-one) وجود دارند ,به طور پیش فرض از base_manager_ مدل شما استفاده می کند . نام آن را به اختصار base manager می گویند .نکته : base_manager_ یک مدل با manager پیش فرض آن متفاوت است .می توانید در صورت نیاز با استفاده از ویژگی Meta.base_manager_name در کلاس متای مدل و ارسال مقدار نام manager مد نظر خودتان , base manager یک مدل را تغییر دهید .معمولا base manager در هنگام اجرای کوئری در مدل هایی که رابطه ای دارند ,یا هنگام دسترسی به یک رابطه one-to-many و many-to-many استفاده نمی شود . برای مثال ,اگر مدل Question(در آموزش های قبلی) یک فیلد حذف شده و یک base manager داشت که اشیا را به صورت deleted=True فیلتر می کند ,یک QuerySet مانند Choice.objects.filter(question__name__startswith=&#x27;What&#x27;) شامل اشیایی از Choice خواهد بود که رابطه ای با اشیا Question که حذف شدند ,داشته اند .فیلتر کردن متد get_querysetاین manager برای دسترسی به اشیایی استفاده می شود که از مدل های دیگر با آن شی رابطه ای ایجاد شده است . بنابراین جنگو باید به تمامی اشیا کاملا دسترسی داشته باشد تا هر چیزی را که بخواهد را از دیتابیس دریافت کند .پس ,اصلا نباید متد get_queryset بازنویسی شود تا سطرها را فیلتر یا محدود کند . اگر این کار را انجام بدهید ,جنگو نتایج را ناقص بازمیگرداند .فراخوانی متد های سفارشی QuerySet در managerدر حالی که اکثر متد های یک manager به صورت استاندارد از خودش قابل دسترسی هستند ,می توانید متد های اضافی نیز برای QuerySet مربوط به مدل تعریف کنید تا از آنها استفاده کنید . شکل تعریف آنها به صورت زیر است :class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role=&amp;quotA&amp;quot)

    def editors(self):
        return self.filter(role=&amp;quotE&amp;quot)

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset.authors

    def editors(self):
        return self.get_queryset.editors

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(
    max_length=1, choices=[(&amp;quotA&amp;quot, _(&amp;quotAuthor&amp;quot)), (&amp;quotE&amp;quot, _(&amp;quotEditor&amp;quot))])
    people = PersonManagerدر این مثال شما می توانید هم authors و هم editors را از Person.people فراخوانی کنید و اشیایی را دریافت کنید .ساخت manager با as_managerبه جای عملکرد بالا که نیاز دارد تا دوبار متدها در کلاس های manager و QuerySet تکرار شوند ,می توانید از QuerySet.as_manager استفاده کنید تا یک شی از Manager با کپی ای از متد های درون آن QuerySet ایجاد شود و از آن استفاده کنید . برای مثال :class Person(models.Model):
    ...
    people = PersonQuerySet.as_managerشی ای که با QuerySet.as_manager به عنوان manager تولید می شود تقریبا با PersonManager در مثال قبلی یکسان است .به یاد داشته باشید که نیاز نیست تا همه ی متد های QuerySet در manager وجود داشته باشند . برای مثال ممکن است شما عمدا بخواهید از کپی شدن متد QuerySet.delete جلوگیری کنید .کپی شدن متد ها قوانین خاص خود را دارد :متد های عمومی یا Public به صورت پیش فرض کپی خواهند شد .متد های شخصی یا Private(همان هایی که با _شروع می شوند) به طور پیش فرض کپی نمی شوند .متد هایی که ویژگی queryset_only=False دارند ,همیشه کپی می شوند .متد هایی که ویژگی queryset_only=True دارند ,هرگز کپی نمی شوند .برای مثال :class CustomQuerySet(models.QuerySet):
    # Available on both Manager and QuerySet.
    def public_method(self):
        return
    
    # Available only on QuerySet.
    def _private_method(self):
        return

    # Available only on QuerySet.
    def opted_out_public_method(self):
        return
    
    opted_out_public_method.queryset_only = True

    # Available on both Manager and QuerySet.
    def _opted_in_private_method(self):
        return

    _opted_in_private_method.queryset_only = Falseمتد from_querysetجنگو یک متد در کلاس های خود با عنوان from_queryset نیز دارد . سینتکس آن به شکل from_queryset(queryset_class) است . برای موارد پیشرفته ممکن است هم یک manager شخصی سازی شده و هم یک QuerySet شخصی سازی شده بخواهید . شما می توانید این کار را به وسیله Manager.from_queryset انجام بدهید که یک زیرکلاس از manager شما را با کپی ای از متد های QuerySet شخصی سازی شده شما بازمیگرداند:class CustomManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = CustomManager.from_queryset(CustomQuerySet)شما می توانید کلاس تولید شده را داخل یک متغییر نیز به شکل زیر ذخیره کنید :MyManager = CustomManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = MyManagerارث بری یک managerدر این بخش قرار است بررسی کنیم که جنگو چگونه با ارث بری مدل ها و manager آنها برخورد می کند .معمولا کلاس فرزند همیشه از manager تعریف شده در کلاس پدر ارث بری می کند . همچنین قواعد پایتون در این ارث بری رعایت می شوند . یعنی می توانید manager به ارث رسیده را در کلاس فرزند دوباره نامگذاری کنید و آن را بازنویسی کنید . سپس managerهای کلاس والد به ارث خواهند رسید .اگر کلاس والد هیچ ویژگی manager تعریف شده ای نداشته باشد ,به صورت اتوماتیک کلاس فرزند , objects را که پیش فرض جنگو است برای manager برمی گزیند .در جنگو , manager پیش فرض مدل اولین manager است که در مدل تعریف می شود یا Meta.default_manager_name آن را مشخص کرده است . در بحث ارث بری نیز manager پیش فرض اولین مدل والد به ارث خواهد رسید .مواردی که گفته شد ,اصول اولیه در بحث ارث بری است . اما ممکن است کمی در بحث ارث بری از کلاس های abstract تفاوت وجود داشته باشد . برای مثال ,فرض کنید این کلاس را دارید :(کلاس دارای ویژگی abstract=True)class AbstractBase(models.Model):
    # ...
    objects = CustomManager

    class Meta:
        abstract = Trueچون هیچ manager خاصی در کلاس والد تعریف نشده است ,اگر یک کلاس فرزند از این کلاس ارث بری کند , manager پیش فرض آن برابر با objects خواهد شد :class ChildA(AbstractBase):
    # ...
    # This class has CustomManager as the default manager.
    passاگر می خواهید همچنان از AbstractBase ارث بری کنید ولی یک manager پیش فرض دیگر را برای کلاس فرزند انتخاب کنید ,می توانید آن را در همان کلاس فرزند تعیین کنید :class ChildB(AbstractBase):
    # ...
    # An explicit default manager.
    default_manager = OtherManagerدر اینجا default_manager به عنوان پیش فرض مدل انتخاب می شود . در اصل objects نیز هنوز در دسترس است زیرا به ارث رسیده است ولی manager پیش فرض شما تغییر پیدا کرده است .در نهایت فرض کنید برای این مثال می خواهید یک سری از کلاس های manager را نیز به کلاس فرزند اضافه کنید ,اما همچنان می خواهید از پیش فرض AbstractBase که به ارث رسیده است ,استفاده کنید . نمی توانید به صورت مستقیم یک manager دیگر را در کلاس فرزند معرفی کنید ,زیرا این امر باعث می شود تا manager پیش فرض شما به ارث نرسد و بازنویسی شود . راه حل این است که manager اضافی را در یک کلاس دیگر قرار دهید و سپس آن را بعد از کلاس AbstractBase ,به ارث ببرید . برای مثال :class ExtraManager(models.Model):
    extra_manager = OtherManager

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    # ...
    # Default manager is CustomManager, but OtherManager is
    # also available via the &amp;quotextra_manager&amp;quot attribute.
    passتوجه داشته باشید که شما اجازه دارید تا یک متد شخصی سازی شده را در کلاس والد (کلاس های abstract البته) بنویسید ولی نمی توانید آنها را از همان کلاس والد فراخوانی کنید . برای مثال :ClassA.objects.do_somethingکد بالا کار خواهد کرد اما :AbstractBase.objects.do_somethingاین کد باعث ایجاد یک ارور خواهد شد . دلیل آن این است که manager برای این طراحی شده است تا مجموعه ای از اشیا را مدیریت کند . در مدل های abstract نمی توانید مجموعه ای از اشیا داشته باشید ,پس مدیریت آنها منطقی نیست .مشکلات احتمالی در ساخت یک managerهر قابلیتی که به manager خود اضافه می کنید ,باید امکان این را در manager ایجاد کند که یک کپی از همان manager بتوانید بسازید . برای مثال کد زیر کار می کند :&gt;&gt;&gt; import copy
&gt;&gt;&gt; manager = MyManager
&gt;&gt;&gt; my_copy = copy.copy(manager)جنگو در اینجا می تواند در حین اجرای یک کوئری ,کپی هایی از آن manager ایجاد کند . اگر اینکار قابل انجام نباشد ,کوئری ها تماما با شکست مواجه می شوند .البته این مشکل برای اکثر کلاس های manager شخصی سازی شده وجود ندارد . اگر در حال اضافه کردن متد های ساده ای به manager هستید ,بعید است که سهوا آن را غیرقابل کپی کنید . با این حال اگر شما در حال بازنویسی متد های private کلاس manager خود مانند __getattr__ هستید ,باید اطمینان حاصل کنید که manager همچنان بتواند کپی شود .در این جلسه به بررسی نحوه کار با یک manager در جنگو پرداختیم . در جلسه بعدی به بررسی مفهوم Raw SQL در جنگو خواهیم پرداخت .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sun, 17 Dec 2023 17:27:08 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه چهل و دو | بررسی Migrations در جنگو | پارت سوم</title>
                <link>https://virgool.io/codenevis/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%DA%86%D9%87%D9%84-%D9%88-%D8%AF%D9%88-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-migrations-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%B3%D9%88%D9%85-cihk6bb0kb76</link>
                <description>در این جلسه آخرین مباحث در رابطه با migrations را بررسی می کنیم و این مبحث را تمام می کنیم . با ما همراه باشید . آموزش جنگو : جلسه چهل و دو | بررسی Migrations در جنگو | پارت سومبررسی مفهوم SchemaEditorدر این بخش به طور تخصصی به بررسی مفهوم SchemaEditor خواهیم پرداخت . سیستم مدیریت migrations جنگو را می توان به دو بخش تقسیم کرد . بخش اول ,بخش محاسبه عملیاتی است که باید روی دیتابیس انجام شود . شما این کار را به وسیله تعیین Operations انجام می دهید . بخش دوم ,بخشی است که ارتباط جنگو با دیتابیس را برقرار می کند . کار هایی که توسط Operations تعیین می شوند (مانند ایجاد یک مدل) را این بخش به کد SQL تبدیل می کند . بخش دوم با نام SchemaEditor شناخته می شود .اگر هنوز در حد کمی از جنگو شناخت دارید ,لازم نیست که این بخش را بخوانید . به عنوان یک برنامه نویس معمولی , بعید است که نیاز داشته باشید تا متد های SchemaEditor را بشناسید و آنها را تغییر دهید . اما اگر زمانی مجبور بودید که سیستم مدیریت migrations خود را بنویسید یا کارهای پیشرفته تری انجام دهید ,پس این بخش برای شما مناسب است .هر دیتابیس در جنگو ,یک SchemaEditor مخصوص به خود را دارد . این SchemaEditor همیشه از طریق connection.schema_editor در دسترس است . از طریق آن می توانید متد های مختلف آن را روی دیتابیس اعمال کنید . جلوتر تمامی متد های SchemaEditor را توضیح می دهیم . ابتدا نگاهی به نحوه استفاده از آن بیاندازید :with connection.schema_editor as schema_editor:
    schema_editor.delete_model(MyModel)در کد بالا ما SchemaEditor را از طریق context manager (این یک مبحث مربوط به پایتون است) استفاده کردیم . این کار اجازه می دهد تا مواردی مانند transactions و deferred SQL ها را کنترل کنیم (یا چیز هایی مانند محدودیت های یک ForeignKey) .علاوه بر آن ,با این کار تمامی Operations و عملیات هایی که در فایل های migration می نویسید ,به شکل یک متد در دسترس خواهند بود . شما باید این متد ها را به ترتیبی که می خواهید تغییرات در دیتابیس انجام شوند ,فراخوانی کنید. برخی از عملیات ها یا Operations در تمامی دیتابیس ها قابل اعمال نیستند . برای مثال MyISAM (یک موتور دیتابیسی برای MySQL) از اعمال محدودیت یا constraints روی فیلد ForeignKey پشتیبانی نمی کند .اگر در حال نوشتن یک بک اند دیتابیس برای جنگو هستید ,باید SchemaEditor خودتان را در جنگو پیاده سازی کنید . البته تا زمانی که بک اند دیتابیس شما به استاندارد های جنگو نزدیک باشد ,می توانید یک زیرکلاس از SchemaEditor در جنگو ایجاد کنید و سینتکس آن را کمی تغییر دهید .بررسی متدهای SchemaEditorدر این بخش به بررسی تمامی متد های قابل دسترسی در SchemaEditor خواهیم پرداخت .1-متد execute :سینتکس آن به شکل BaseDatabaseSchemaEditor.execute(sql, params=) است . آرگومان اولی آن sql است که یک کد SQL را می پذیرد و آن را در دیتابیس اجرا می کند . params نیز پارامتر های ارسالی به آن کد می باشد . در صورتی که کاربر بخواهد می توانید کد SQL را به یک فایل sql. نیز تبدیل کند.2-متد create_model :سینتکس آن به شکل BaseDatabaseSchemaEditor.create_model(model) است. یک جدول جدید را در دیتابیس ایجاد می کند . آرگومان مدل , مدلی را می پذیرد که جدول از روی آن ساخته می شود . علاوه بر اینها ,تمامی مباحث محدودیت های unique بودن داده یا ایندکس ها نیز در جدول ایجاد می شود (از روی مدل) .3-متد delete_model :سینتکس آن به شکل BaseDatabaseSchemaEditor.delete_model(model) است. آرگومان مدل برابر مدل مدنظر خواهد بود . با توجه به این آرگومان , جدول مربوط به مدل را حذف یا Drop می کند . علاوه بر اینها ,تمامی مباحث محدودیت های unique بودن داده یا ایندکس ها نیز در جدول حذف می شود.4-متد add_index :سینتکس آن به شکل BaseDatabaseSchemaEditor.add_index(model, index) است. آرگومان اول مدل مدنظر است . آرگومان دوم نیز یک ایندکس را می پذیرد . پس از پذیرش آرگومان ها ,ایندکس را در جدول مربوطه ایجاد می کند .5-متد remove_index :سینتکس آن به شکل BaseDatabaseSchemaEditor.remove_index(model, index) است . مانند همان متد قبلی است . اما این متد ایندکس دریافت شده را از جدول حذف خواهد کرد .6-متد rename_index :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.rename_index(model, old_index, new_index) است . این متد در نسخه جنگو 4.1 به بعد فقط کارایی دارد . آرگومان old_name (نام قدیمی ایندکس) و new_name (نام جدید ایندکس) را می گیرد و نام ایندکس را در جدول مربوط به مدل تغییر می دهد .7-متد add_constraint :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.add_constraint(model, constraint)است . یک محدودیت یا constraint را به یک جدول اضافه می کند .8-متد remove_constraint :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.remove_constraint(model, constraint) است . یک محدودیت یا constraint را از یک جدول حذف می کند .9-متد alter_unique_together :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.alter_unique_together(model, old_unique_together, new_unique_together) است . مقدار unique_together را در مدل تغییر می دهد . در واقع این متد محدودیت های unique بودن داده ها را در جدول تا زمانی که با مقدار جدید برابر شوند ,اضافه یا حذف می کند .10-متد alter_index_together :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.alter_index_together(model, old_index_together, new_index_together) است . مقدار index_together را در مدل تغییر می دهد . در واقع این متد ایندکس ها را در جدول تا زمانی که با مقدار جدید برابر شوند ,اضافه یا حذف می کند .11-متد alter_db_table :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.alter_db_table(model, old_db_table, new_db_table) است . نام جدول را با توجه به آرگومان ها تغییر می دهد . old_db_table نام قدیمی جدول و new_db_table نام جدید جدول است .12-متد alter_db_table_comment :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.alter_db_table_comment(model, old_db_table_comment, new_db_table_comment) است . این متد در جنگو 4.2 تازه معرفی شد . آرگومان old_db_table_comment را دریافت می کند و comment موردنظر جدول را به new_db_table_comment تغییر می دهد .13-متد alter_db_tablespace :سینتکس آن به شکل BaseDatabaseSchemaEditor.alter_db_tablespace(model, old_db_tablespace, new_db_tablespace) است . جدول مربوط به مدل را از یک tablespace به یک tablespace دیگر جا به جا می کند .14-متد add_field :سینتکس آن به شکل BaseDatabaseSchemaEditor.add_field(model, field) است . یک ستون یا گاهی اوقات چند ستون را به جدول اضافه می کند . این ستون ها همان فیلد های شما هستند . اگر فیلد دارای db_index=True و یا unique=True باشد ,ایندکس ها و یا محدودیت های unique بودن داده را به ستون اضافه می کند .اگر فیلد یک ManyToManyField بدون مقدار برای through باشد ,به جای ایجاد یک ستون ,جدولی را برای نشان دادن و مدیریت رابطه ایجاد می کند . اگر مقداری برای through در نظر گرفته شده باشد ,عملیاتی برای ایجاد یک جدول انجام نمی شود .اگر فیلد یک ForeignKey باشد ,محدودیت foreign key به ستون اضافه خواهد شد . (اینها از مباحث های دیتابیس هاست)15-متد remove_field :سینتکس آن به شکل BaseDatabaseSchemaEditor.remove_field(model, field) است. یک ستون یا گاهی اوقات چند ستون را از جدول حذف می کند . این ستون ها همان فیلد های شما هستند . اگر فیلد دارای ایندکس و یا محدودیت های unique بودن داده باشد ,تمام آنها نیز از ستون حذف می شود .اگر فیلد یک ManyToManyField بدون مقدار برای through باشد ,جدولی را که برای نشان دادن و مدیریت رابطه ایجاد کرده بود را حذف می کند . اگر مقداری برای through در نظر گرفته شده باشد ,عملیاتی را انجام نمی دهد .16-متد alter_field :سینتکس آن به شکلکد BaseDatabaseSchemaEditor.alter_field(model, old_field, new_field, strict=False) است . این متد ,فیلد قدیمی را به فیلد جدید تبدیل می کند . در واقع تغییراتی که روی فیلد انجام دادید را در ستون جدول نیز اعمال می کند. تغییرات شامل تغییر نام ستون (نام فیلد) ,تغییر نوع فیلد (برای مثال از IntegerField به TextField) ,تغییر وضعیت NULL فیلد ,افزودن یا حذف محدودیت ها یا constraint در فیلد و ایندکس های آن ,تغییر primarykey آن و تغییر مدل ارجاع داده شده در فیلد های روابطی مانند ForeignKey هستند .بعضی از تغییرات نیز انجام نشدنی هستند . مانند تبدیل یک فیلد ManyToManyField به یک فیلد معمولی یا بالعکس است (تغییر نوع آن) . جنگو نمی تواند این کار را بدون از دست دادن داده انجام دهد ,بنابراین از اینکار جلوگیری می کند . در عوض باید remove_field و add_field به صورت جداگانه برای این کار فراخوانی شوند .اگر دیتابیس دارای ویژگی supports_combined_alters باشد ,جنگو سعی خواهد کرد تا می تواند تغییرات را در یک کد SQL و یکبار عملیات روی دیتابیس انجام دهد . در غیر این صورت مجبور است برای هر تغییر یکبار ALTER را اجرا کند . البته در مواردی که نیازی نباشد , ALTER فراخوانی نخواهد شد .بررسی ویژگی های SchemaEditorعلاوه بر تمامی متد ها , SchemaEditor دارای یک سری ویژگی است که read-only هستند .1-ویژگی connection :از SchemaEditor.connection در دسترس است . در واقع این ویژگی یک شی است که اتصال به دیتابیس را مشخص می کند . یک ویژگی مفید از این شی alias است که نام دیتابیسی که با آن کار می کنیم (پیش فرض) را تعیین می کند .این ویژگی هنگام اجرای data migrations در جایی که با چند دیتابیس کار می کنید ,کاربردی است .نحوه نوشتن فایل های migrationدر این بخش به بررسی چگونگی نوشتن فایل های migration به صورت دستی ,در شرایط مختلفی که ممکن است با آنها رو به رو شوید , می پردازیم .هنگام استفاده از چندین دیتابیس در یک پروژه جنگو ,ممکن است لازم باشد تا بفهمید که آیا فایل migration باید روی یکی از دیتابیس ها اجرا شود و یا خیر . به عنوان مثال , شاید بخواهید یک فایل migration فقط روی یکی از دیتابیس ها اجرا شود .برای انجام این کار می توانید ویژگی schema_editor.connection.alias را در تابع RunPython بررسی کنید تا به alias name دیتابیس خود برسید . برای مثال :from django.db import migrations

def forwards(apps, schema_editor):
    if schema_editor.connection.alias != &amp;quotdefault&amp;quot:
        return
    # Your migration code goes here

class Migration(migrations.Migration):
    dependencies = [
        # Dependencies to other migrations
    ]
    operations = [
        migrations.RunPython(forwards),
    ]همچنین می توانید راهنماهایی را بازگردانید که به متد allow_migrate در database routers یا روتر های دیتابیس به صورت hints** ارسال می شوند . برای مثال (فایل myapp/dbrouters.py) :class MyRouter:
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if &amp;quottarget_db&amp;quot in hints:
            return db == hints[&amp;quottarget_db&amp;quot]
        return Trueسپس برای استفاده از آن در فایل migration خود ,کد زیر را استفاده کنید :from django.db import migrations

def forwards(apps, schema_editor):
    # Your migration code goes here
    ...

class Migration(migrations.Migration):
    dependencies = [
        # Dependencies to other migrations
    ]
    operations =[
        migrations.RunPython(forwards, hints={&amp;quottarget_db&amp;quot: &amp;quotdefault&amp;quot}),
    ]اگر توابع RunPython و یا RunSQL شما فقط روی یک مدل عملیات خود را اعمال می کردند ,بهتر است تا model_name را به عنوان راهنمایی (hint) ارسال کنید تا برای روتر های دیتابیس واضح تر باشد . این کار برای ساخت اپ های شخص ثالث و قابل استفاده مجدد بسیار مفید است .فایل های migration با فیلد unique یا یکتااجرای یک فایل migration ساده که یک فیلد دارای ویژگی های non-nullable و unique=True را به یک جدول دارای رکورد اضافه کند ,باعث ایجاد ارور می شود . زیرا مقداری که برای پر کردن ردیف ها به کار می رود , تنها یکبار تولید می شود و بنابراین محدودیت unique باعث ایجاد خطا می شود .بنابراین مراحل زیر را باید برای رفع ارور در چنین مواقعی انجام داد . در این مثال یک فیلد UUIDField با ویژگی non-nullable و یک مقدار default را در نظر خواهیم گرفت . فیلد مربوطه را می توانید با توجه به نیاز خود تغییر دهید . این صرفا یک مثال است .1-ویژگی های default=uuid.uuid4 و unique=True را به فیلد اضافه کنید . البته برای default با توجه به فیلد خود می توانید مقدار آن را تغییر دهید و این صرفا یک مثال است .2-دستور makemigrations را اجرا کنید . یک فایل migration با عملیات (operation) AddField باید ایجاد شود.3-دو بار دستور makemigrations myapp –empty را اجرا کنید تا در همان اپ ,دو فایل migration خالی ایجاد شود. فایل های migration را می توانید تغییر نام دهید تا معنی دار تر باشند .4-عملیات AddField را از فایل migration ایجاد شده به صورت اتوماتیک (اولین فایل) کپی کنید و به اخرین migration انتقال دهید . AddField را به AlterField تغییر دهید و uuid و مدل ها را به آن اضافه کنید . برای مثال:(فایل 0006_remove_uuid_null.py)# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations, models
import uuid

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotmyapp&amp;quot, &amp;quot0005_populate_uuid_values&amp;quot),
    ]
    operations = [
        migrations.AlterField(
            model_name=&amp;quotmymodel&amp;quot,
            name=&amp;quotuuid&amp;quot,
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]5-فایل اولی و یا همانی که به صورت اتوماتیک ایجاد شده بود را تغییر دهید . فایل به صورت زیر باید باشد :(فایل 0004_add_uuid_field.py)class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotmyapp&amp;quot, &amp;quot0003_auto_20150129_1705&amp;quot),
    ]
    operations = [    
        migrations.AddField(
        model_name=&amp;quotmymodel&amp;quot,
        name=&amp;quotuuid&amp;quot,
        field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
    ]6-در این فایل باید مقدار unique=True را به null=True تغییر دهید . این کار باعث می شود تا فیلد nullable شود و محدودیت های unique بودن داده ها ,تا زمانی که تمام ردیف ها پر نشوند (با مقدار های unique) اعمال نشود.7-در اولین فایل migration که خالی است (همان دومین فایل ما) ,یک تابع RunPython یا RunSQL اضافه کنید تا یک مقدار unique یا یکتا را (برای مثال UUID) برای هر ردیف موجود ایجاد کنید . همچنین uuid را import کنید . برای مثال :(فایل 0005_populate_uuid_values.py)# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
import uuid

def gen_uuid(apps, schema_editor):
    MyModel = apps.get_model(&amp;quotmyapp&amp;quot, &amp;quotMyModel&amp;quot)
    for row in MyModel.objects.all:
        row.uuid = uuid.uuid4
        row.save(update_fields=[&amp;quotuuid&amp;quot])

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotmyapp&amp;quot, &amp;quot0004_add_uuid_field&amp;quot),
    ]
    operations = [
        # omit reverse_code=... if you don&#039;t want the migration to be reversible.
        migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
    ]8-حال می توانید دستور migrate را اجرا کنید و کار تمام است !به یاد داشته باشید که اگر در حالی که این migration در حال اعمال شدن است ,اجازه بدهید تا اشیا جدید در دیتابیس ثبت شوند ,شرایط به اصطلاح مسابقه ای ایجاد می شود . این یعنی ممکن است بعضی از اشیا با دیگر اشیا در مقادیر اختلاف شدیدی داشته باشند و خراب شوند . در اینجا چون فایل migration در حال اجرا خواهد بود ,اشیایی که بعد از اجرای AddField و قبل از اجرای RunPython ایجاد شوند , uuid اصلی خود را بازنویسی خواهند کرد .در دیتابیس هایی که از DDL transactions پشتیبانی می‌کنند (SQLite و PostgreSQL) ,فایل های migration به طور خودکار در یک transaction اجرا می شوند . در شرایطی مانند اجرای چند data migration روی جدول هایی بزرگ , ممکن است بخواهید ویژگی atomic=False را تنظیم کنید تا از اجرای فایل ها در یک transaction جلوگیری کنید . برای مثال :from django.db import migrations

class Migration(migrations.Migration):
    atomic = Falseبا تنظیم ویژگی atomic=False ,تمامی فایل های migration بدون هیچ transaction اجرا خواهند شد . البته امکان اینکه بتوانید بخش هایی از فایل را در داخل یک transaction و بقیه بخش ها را خارج از آن اجرا کنید ,وجود دارد . برای اینکار باید از atomic استفاده کنید و یا atomic=True را در تابع RunPython برای آن بخش ارسال کنید .در اینجا مثالی وجود دارد که یک data migration را در یک جدول بزرگ اجرا می کند . البته در بسته های کوچکتر و با atomic=False اینکار را انجام می دهد :import uuid
from django.db import migrations, transaction

def gen_uuid(apps, schema_editor):
    MyModel = apps.get_model(&amp;quotmyapp&amp;quot, &amp;quotMyModel&amp;quot)

    while MyModel.objects.filter(uuid__isnull=True).exists:
        with transaction.atomic:
            for row in MyModel.objects.filter(uuid__isnull=True)[:1000]:
                row.uuid = uuid.uuid4
                row.save

class Migration(migrations.Migration):
    atomic = False
    operations = [
        migrations.RunPython(gen_uuid),
    ]در دیتابیس هایی که از DDL transactions پشتیبانی نمی‌کنند (Oracle و MySQL) ,تنظیم ویژگی atomic تاثیر خاصی ندارد .کنترل کردن ترتیب فایل های migrationجنگو ترتیب اعمال شدن فایل های migration را با نام فایل ها مشخص نمی کند . بلکه ترتیب آنها بیشتر از تشخیص دو ویژگی در فایل مشخص می شود :ویژگی های dependencies و run_beforeاگر از دستور makemigrations استفاده کنید ,احتمالا شاهد ویژگی dependencies خواهید بود . زیرا این دستور به صورت اتوماتیک ترتیب ها را تشخیص می دهد و فایل ها را خودکار مرتب می کند تا ویژگی dependencies را مقداردهی کند . (البته قبلا در مورد این موارد صحبت کردیم)ویژگی dependencies به شکل زیر مقداردهی می شود :from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotmyapp&amp;quot, &amp;quot0123_the_previous_migration&amp;quot),
    ]معمولا مقداردهی این ویژگی کافی خواهد بود ,اما هرازگاهی لازم است مطمئن شوید که فایل migration شما حتما قبل از سایر فایل ها اجرا خواهد شد . به عنوان مثال فرض کنید که migrationهای یک اپ شخص ثالث را باید پس از تعیین متغییر AUTH_USER_MODEL در settings.py ,اجرا کنید .برای اینکار ,کافی است همه ی فایل های migration که نیاز است فایل شما قبل از اجرای آنها ,اجرا شود را در run_before در کلاس Migration بنویسید . برای مثال :class Migration(migrations.Migration):
    ...
    run_before = [
        (&amp;quotthird_party_app&amp;quot, &amp;quot0001_do_awesome&amp;quot),
    ]البته جنگو پیشنهاد می کند تا حد ممکن از dependencies بجای run_before استفاده کنید . فقط در صورتی از run_before استفاده کنید که مقداردهی dependencies بعد از چیزی که می نویسید ,غیرممکن باشد .اجرا و انتقال فایل های data migration میان دو اپشما می توانید از یک data migration برای انتقال داده ها میان دو اپ (معمولا شخص ثالث یا third-party) استفاده کنید .اگر می خواهید بعدا اپ قدیمی را حذف کنید ,بهتر است که ویژگی dependencies را در فایل های migration بر اساس وجود داشتن یا نداشتن اپ در آینده تنظیم کنید . در غیر این صورت , پس از حذف اپ ویژگی dependencies مقادیری خواهد داشت که اصلا وجود ندارند ! در استفاده از متد apps.get_model نیز باید دقت کافی را به خرج بدهید و ارور LookupError را در صورت نیاز هندل کنید . زیرا ممکن است متد شما به مدلی بخواهد دسترسی پیدا کند که دیگر وجود ندارد ! توجه داشتن به این نکات ,باعث می شود نیازی به نصب و حذف اپ قدیمی برای هر بار راه اندازی پروژه در سرور نیاز نباشد .در اینجا یک نمونه از یک migration استاندارد با شرایط بالا وجود دارد :from django.apps import apps as global_apps
from django.db import migrations

def forwards(apps, schema_editor):
    try:
        OldModel = apps.get_model(&amp;quotold_app&amp;quot, &amp;quotOldModel&amp;quot)
    except LookupError:
        # The old app isn&#039;t installed.
        return
    NewModel = apps.get_model(&amp;quotnew_app&amp;quot, &amp;quotNewModel&amp;quot)
    NewModel.objects.bulk_create(
        NewModel(new_attribute=old_object.old_attribute)
        for old_object in OldModel.objects.all
    )

class Migration(migrations.Migration):
    operations = [
        migrations.RunPython(forwards, migrations.RunPython.noop),
    ]
    dependencies = [
        (&amp;quotmyapp&amp;quot, &amp;quot0123_the_previous_migration&amp;quot),
        (&amp;quotnew_app&amp;quot, &amp;quot0001_initial&amp;quot),
    ]

    if global_apps.is_installed(&amp;quotold_app&amp;quot):
        dependencies.append((&amp;quotold_app&amp;quot, &amp;quot0001_initial&amp;quot))همچنین در نظر داشته باشید که در صورت معکوس کردن فایل migration ,چه اتفاقی بیافتد . شما می توانید مانند مثال بالا کاری انجام ندهید و یا برخی یا همه داده های اپ را حذف کنید . آرگومان دوم تابع RunPython برای همین منظور است . (قبلا به بررسی این تابع پرداختیم . به جلسات قبل مراجعه کنید)تغییر یک ManyToManyField برای استفاده از throughاگر یک ManyToManyField را تغییر دهید تا یک مدل را برای آرگومان through آن ارسال کنید (آن را مجبور کنید تا از through استفاده کند) , migration به صورت پیش فرض ,جدول موجود را حذف می کند و یک جدول جدید را ایجاد می کند . همچنین در این عملیات روابط میان جدول ها نیز از دست خواهند رفت . برای جلوگیری از این اتفاق , شما می توانید از یک SeparateDatabaseAndState استفاده کنید تا نام جدول قدیمی را به نام جدول جدید تغییر بدهید و به جنگو بگویید که یک جدول جدید ایجاد شده است .نام جدول موجود را می توانید با sqlmigrate و dbshell پیدا کنید . می توانید نام جدول جدید را با ویژگی meta.db_table_ بررسی کنید . جدول جدید شما باید از همان نامی که جدول قدیمی برای فیلد های ForeignKey استفاده میکرد ,استفاده کند . همچنین اگر نیاز دارید تا فیلد های بیشتری به جدول خود اضافه کنید , آنها را در operations قرار دهید و بعد از اجرای SeparateDatabaseAndState ,اجرا کنید .به عنوان مثال ,اگر یک مدل با نام Book داشتیم که دارای یک ManyToManyField بود و این فیلد به مدل Author رابطه برقرار کرده بود ,می توانیم یک مدل را به عنوان throughبه آن فیلد ارسال کنیم (فرض می کنیم نام آن AuthorBook باشد) و یک فیلد جدید با نام is_primary نیز در آن بسازیم . کدی که برای این مثال باید در فایل migration نوشت ,به صورت زیر است :from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotcore&amp;quot, &amp;quot0001_initial&amp;quot),
    ]
    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=[
                # Old table name from checking with sqlmigrate, new table
                # name from AuthorBook._meta.db_table.
                migrations.RunSQL( sql=&amp;quotALTER TABLE core_book_authors RENAME
                TO core_authorbook&amp;quot, reverse_sql=&amp;quotALTER TABLE core_authorbook
                RENAME TO core_book_authors&amp;quot, ),
            ],
            state_operations=[
                migrations.CreateModel(
                name=&amp;quotAuthorBook&amp;quot,
                fields=[
                                (
                                    &amp;quotid&amp;quot,
                                    models.AutoField(
                                        auto_created=True,
                                        primary_key=True,
                                        serialize=False,
                                        verbose_name=&amp;quotID&amp;quot,
                                    ),
                                ),
                            (
                                &amp;quotauthor&amp;quot,
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to=&amp;quotcore.Author&amp;quot,
                                ),
                            ),
                            (
                                &amp;quotbook&amp;quot,
                                models.ForeignKey(
                                    on_delete=django.db.models.deletion.DO_NOTHING,
                                    to=&amp;quotcore.Book&amp;quot,
                                ),
                            ),
                        ],
                    ),
                migrations.AlterField(
                    model_name=&amp;quotbook&amp;quot, name=&amp;quotauthors&amp;quot,
                    field=models.ManyToManyField(
                        to=&amp;quotcore.Author&amp;quot,
                        through=&amp;quotcore.AuthorBook&amp;quot,
                        ),
                    ),
                ],
            ),
            migrations.AddField(
                model_name=&amp;quotauthorbook&amp;quot, name=&amp;quotis_primary&amp;quot,
                field=models.BooleanField(default=False),
                ),
            ]تغییر یک مدل مدیریت نشده به مدیریت شدهاگر می خواهید یک مدل مدیریت نشده (مدلی که دارای ویژگی managed=False است) را به مدیریت شده (managed=True) تغییر بدهید ,باید ویژگی managed=False را حذف کنید و قبل از ایجاد سایر تغییرات در مدل یا جداول , یک فایل migration ایجاد کنید . دلیل این کار این است که گاهی اوقات فایل migration شامل عملیاتی برای تغییر Meta.managed خواهد بود و در این صورت باقی تغییرات دیتابیس و جداول آن فایل , اجرا نخواهند شد .در این جلسه مبحث migrations در جنگو را تمام کردیم . در جلسه بعدی در رابطه با مدیریت یک manager و نحوه ساخت آن صحبت می کنیم .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sat, 16 Dec 2023 14:18:41 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه چهل و یک | بررسی Migrations در جنگو | پارت دوم</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%DA%86%D9%87%D9%84-%D9%88-%DB%8C%DA%A9-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-migrations-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%88%D9%85-hozbgsghvt2w</link>
                <description>در این جلسه نیز به بررسی بیشتر migrations در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو : جلسه چهل و یک | بررسی Migrations در جنگو | پارت دومعملیات ها یا Operationsدر این بخش به بررسی تمامی عملیات هایی که میتوانند در Operations باشند و توابعی مانند RunPython خواهیم پرداخت .هر فایل migration دارای یک سری از عملیات هایی است که باید روی دیتابیس انجام بدهد . برای مثال ممکن است این عملیات ساخت یک جدول (CreateModel) باشد . این عملیات ها در متغییری با نام Operations در فایل قرار می گیرند.جنگو همچنین از Operations برای فهمیدن اینکه مدل شما در گذشته چطور بوده است و تشخیص تغییرات انجام شده روی آن استفاده می کند تا بتواند migration های بعدی را اتوماتیک بنویسید . به همین دلیل جنگو لازم نیست برای کار کردن با آنها حتما کاری روی دیتابیس انجام بدهد و صرفا می تواند آنها را در حافظه لود کند و نتایجی را از تغییرات مدل ها بدست آورد .همچنین کلاس های بیشتری از کلاس Operations وجود دارند (همان عملیات هایی که گفتیم) که برای کار های تخصصی تر مانند data migrations و دستکاری دستی دیتابیس به کار می روند . حتی می توانید در صورت لزوم خودتان یک کلاس Operations جدید بسازید (یک عملیات جدید بسازید) و سپس تغییر دلخواه را در آن ایجاد کنید و از آن بهره ببرید .اگر برای نوشتن عملیات های جدید خود به یک فایل migration خالی احتیاج دارید ,از دستور python manage.py makemigrations --empty yourappname استفاده کنید . نام اپ خود را با yourappname جایگزین کنید . اما بهتر است قبل از هر چیزی بدانید که ساخت عملیات های جدید به صورت دستی ممکن است سیستم migrations جنگو را گیج کند و عملکرد های نادرستی در دستور makemigrations ایجاد کند .تمام عملیات ها و اشیا Operations اصلی (یعنی آنهایی که شما نساختید) در مسیر django.db.migrations.operations قابل دسترسی هستند .بررسی Operations برای تغییرات در دیتابیسحال بیایید تمامی کلاس هایی که برای Operations در دسترس هستند را بررسی کنیم . تک تک موارد زیر یک عملیات را برای دیتابیس انجام می دهند . این عملیات ها مشخص می کنند که تغییرات مدل چه بوده است .1-عملیات CreateModel :سینتکس آن به صورتکد class CreateModel(name, fields, options=None, bases=None, managers=None) است . نشان دهنده ساخت یک مدل جدید است و برای شما جدولی را در دیتابیس می سازد که ویژگی های آن مدل را داشته باشد .آرگومان name در آن ,همان نام مدل شما در فایل models.py است .آرگومان fields یک لیست از تاپل هایی دو عضوی است . شکل این تاپل به صورت (field_name, field_instance) است . field_instance باید یک فیلد unbound باشد . برای مثال models.CharField .آرگومان options یک آرگومان اختیاری است . یک دیکشنری را می پذیرد که در آن مقادیری از کلاس Meta در مدل است.آرگومان bases یک لیست اختیاری است . این لیست حاوی نام مدل هایی است که مدل شما از آنها ارث بری می کند (در فرمت appname.ModelName باید نام مدل وارد شود) . اگر مقداردهی نشده باشد ,به طور پیش فرض از models.Model ارث بری خواهد کرد .آرگومان آخر یعنی managers نیز یک لیست از تاپل های دو عضوی را دریافت می کند که به شکل کد (manager_name , manager_instance) هستند . اولین عضو لیست , manager پیش فرض این مدل برای migrationها خواهد بود .2-عملیات DeleteModel :سینتکس آن به شکل class DeleteModel(name) است . به معنای این است که یک مدل حذف شده است و جدول مربوط به مدل را از دیتابیس حذف می کند . مقدار name همان نام مدل است .3-عملیات RenameModel :سینتکس آن به شکل class RenameModel(old_name, new_name) است . نام یک مدل را تغییر می دهد . آرگومان old_name نام قدیمی مدل (نام فعلی) و آرگومان new_name نامی است که می خواهید مدل به آن نام تغییر پیدا کند .اگر نام مدل و تعدادی از فیلد های آن را همزمان در یک migrate تغییر دهید ,ممکن است مجبور شوید این عملیات را به صورت دستی در فایل migration اضافه کنید . برای سیستم migrations در جنگو ,این حرکت مشخص نیست و ممکن است فکر کند که شما مدل قدیمی را حذف کردید و یک مدل جدید با نام جدید و فیلد های جدید ایجاد کند . این باعث می شود داده های جدول قدیمی از بین برود .4-عملیات AlterModelTable :سینتکس آن به شکل class AlterModelTable(name, table) است . نام جدول مربوط به مدل را عوض می کند . همان db_table که در کلاس های متا استفاده می کردیم .5-عملیات AlterModelTableComment :فقط در جنگو 4.2 وجود دارد و سینتکس آن به شکل کد AlterModelTableComment(name, table_comment) است . کامنت جدول مربوط به مدل را تغییر می دهد. همان db_table_comment که در کلاسهای متا استفاده می کردیم .6-عملیات AlterUniqueTogether :سینتکس آن به شکل کد class AlterUniqueTogether(name, unique_together) است. محدودیت های مربوط به unique بودن دیتا های مدل را تغییر می دهد . برای مثال unique_together که در کلاس های متا استفاده می کردیم .7-عملیات AlterIndexTogether :سینتکس آن به شکل class AlterIndexTogether(name, index_together) است . Index های سفارشی مدل را تغییر می دهد . برای مثال index_together که در کلاس های متا استفاده می کردیم .نکته : AlterIndexTogether به طور رسمی فقط برای فایل های migration در جنگو 4.2 پشتیبانی می شود . به دلیل اینکه نسخه های جدید باید از فایل های migration نسخه های قبل پشتیبانی کنند , API مربوط به AlterIndexTogether هنوز وجود دارد و نمی توان آن را حذف کرد . اما بهتر است که از آن در نسخه های جدید استفاده نکنید و بجای آن از AddIndex و RemoveIndex که بعدا توضیح می دهیم ,استفاده کنید .8-عملیات AlterOrderWithRespectTo :سینتکس آن به شکل کد class AlterOrderWithRespectTo(name, order_with_respect_to) است . ستون order_ موردنیاز برای متغییر order_with_respect_to را ایجاد می کند یا حذف می کند . (این ستون یکی از پیش نیاز های متغییر نامبرده است)9-عملیات AlterModelOptions :سینتکس آن به شکل class AlterModelOptions(name, options) است . تغییرات را برای متغییر های دیگر مدل (همان متغییر های کلاس متا) مانند verbose_nameها و permissions در خود ذخیره می کند . به یاد داشته باشید که این روی دیتابیس تاثیری نمی کذارد ولی این تغییرات را برای توابعی مانند RunPython در خود ذخیره می کند (با تابع RunPython کمی جلوتر آشنا می شوید). آرگومان options در آن باید یک دیکشنری باشد .10- عملیات AlterModelManagers :سینتکس آن به شکل class AlterModelManagers(name, managers) است . هر مدل می تواند چندین manager داشته باشد ولی فقط از یکی از آنها استفاده کند . این کلاس نیز , برای مدل شما manager مورد استفاده را تغییر می دهد .11-عملیات AddField :سینتکس آن به شکل class AddField(model_name, name, field, preserve_default=True) است .یک فیلد را به مدل اضافه می کند . آرگومان model_name همان نام مدل است ,آرگومان name همان نام فیلد است و آرگومان field همان شی ای از کلاس Field است . این شی همان چیزی است که برای تعریف فیلد در فایل models.py از آن استفاده می کنید . برای مثال : models.IntegerField(null=True)آرگومان preserve_default نیز دو مقدار True و False را می پذیرد . سعی کنید همیشه آن را روی دیفالت بگذارید . وظیفه این آرگومان چیزی بیش از این نیست که نشان دهد آیا مقدار Default فیلد دائمی است (True) و یا موقتی است (False) . معمولا به این دلیل استفاده می شود که migration یک فیلد non-nullable را به جدول ممکن است اضافه کند و برای آن حتما نیاز به یک مقدار پیش فرض برای جایگذاری در ردیف دارد .این آرگومان بر رفتار تنظیم پیش فرض ها در دیتابیس تاثیر مستقیم ندارد . (جنگو معمولا پیش فرض ها در ORM خود برای فیلد ها تنظیم می کند)در دیتابیس های قدیمی، افزودن یک فیلد با یک مقدار پیش فرض ممکن است باعث بازنویسی کامل جدول شود . این حتی ممکن است در اضافه شدن یک فیلد non-nullable نیز اتفاق بیافتد . برای جلوگیری از آن اقدامات زیر را مرحله به مرحله می توانید انجام بدهید :ابتدا فیلد را بدون مقدار پیش فرض اضافه کنید و دستور makemigrations را اجرا کنید . این باید یک فایل migration دارای عملیات AddField را ایجاد کند .سپس مقدار پیش فرض را به فیلد خود اضافه کنید و دستور makemigrations را اجرا کنید . این کار باید یک فایل migration دارای عملیات AlterField را ایجاد کند . (جلوتر ...)12-عملیات RemoveField :سینتکس آن به شکل class RemoveField(model_name, name) است . همانطور که از اسمش مشخص است ,یک فیلد را از مدل حذف می کند . البته هنگامی که migration را برعکس کنید (به عقب بازگردانید) ,این کلاس ,یک فیلد را به مدل اضافه می کند که همان فیلد حذف شده است . گرچه داده های درون آن دیگر بازگردانده نمی شوند . نکته مهم این است که اگر فیلد nullable نباشد یا یک مقدار پیش فرض نداشته باشد ,عملیات بازگردانی فیلد غیرممکن خواهد بود . پس باید حتما فیلد شما یا nullable باشد و یا یک مقدار پیش فرض داشته باشد .نکته : اگر تا اینجا متوجه نشده اید ,فیلد های nullable به فیلدهایی گفته می شود که null=True دارند و می توانند مقادیر خالی را نیز درون دیتابیس بپذیرند . فیلد non-nullable دقیقا بالعکس عمل می کند .13-عملیات AlterField :سینتکس آن به شکلclass AlterField(model_name, name, field, preserve_default=True) است . تغییرات ایجاد شده در یک فیلد را در دیتابیس اعمال می کند . برای مثال تغییرات در نوع آن , null , uniqueبودن و db_columnو دیگر ویژگی های یک فیلد . آرگومان model_nameهمان نام مدل است ,آرگومان nameهمان نام فیلد است و آرگومان fieldهمان شی ای از کلاس Fieldاست . این شی همان چیزی است که برای تعریف فیلد در فایل models.pyاز آن استفاده می کنید . برای مثال : models.IntegerField(null=True)آرگومان preserve_defaultنیز دو مقدار Trueو Falseرا می پذیرد . سعی کنید همیشه آن را روی دیفالت بگذارید . وظیفه این آرگومان چیزی بیش از این نیست که نشان دهد آیا مقدار Defaultفیلد دائمی است (True) و یا موقتی است (False) . معمولا به این دلیل استفاده می شود که migrationممکن است یک فیلد nullableرا به یک non-nullableتغییر بدهد و در اینجا نیاز به یک مقدار پیش فرض برای جایگذاری در ردیف دارد .این آرگومان بر رفتار تنظیم پیش فرض ها در دیتابیس تاثیر مستقیم ندارد . (جنگو معمولا پیش فرض ها در ORMخود برای فیلد ها تنظیم می کند)توجه داشته باشید همه تغییرات روی همه دیتابیس ها امکان پذیر نیست . برای مثال تغییر نوع یک فیلد TextFieldبه یک نوع عددی مانند IntegerFieldدر اکثر دیتابیس ها امکان ندارد .14-RenameField :سینتکس آن به شکل class RenameField(model_name, old_name, new_name) است . نام یک فیلد را در دیتابیس تغییر می دهد . (مگر اینکه db_columnتنظیم شده باشد)15-AddIndex :سینتکس آن به شکل class AddIndex(model_name, index)است . آرگومان model_nameدر آن نام مدل مربوطه است . این کلاس ,یک Indexرا برای جدول مربوط به مدل ایجاد می کند . Indexشی ای از کلاس Indexخواهد بود .16-RemoveIndex :سینتکس آن به شکل class RemoveIndex(model_name, name)است . یک indexرا از جدول مربوطه حذف می کند . نام مدل model_nameخواهد بود و nameنام indexای است که قرار است حذف شود .17-RenameIndex :سینتکس آن به شکلclass RenameIndex(model_name, new_name, old_name=None, old_fields=None) است . فقط در جنگو 4.1 به بعد در دسترس است . نام یک indexرا تغییر می دهد . آرگومان model_nameنام مدلی است که indexدر آن قرار دارد . فقط یکی از آرگومان های old_nameو old_fieldsمی تواند مقداردهی شود و هر دوی آنها با هم نمی توانند . old_nameنام قدیمی indexاست . old_fieldsنیز یک iterableاز رشته ها است که اغلب مربوط به فیلد های index_togetherهستند .در دیتابیس هایی که از تغییر نام indexپشتیبانی نمی کنند (مانند SQLiteو MariaDB &lt; 10.5.2) ,عملیات یا operationحذف می شود و دوباره indexایجاد می شود . گرچه این کار بسیار کار سنگینی برای دیتابیس است .18-AddConstraint :سینتکس آن به شکل class AddConstraint(model_name, constraint)است . Constraintها یا محدودیت های SQLرا این کلاس در دیتابیس ایجاد می کند . model_nameبرابر با نام مدل خواهد بود .19-RemoveConstraint :سینتکس آن به شکل class RemoveConstraint(model_name, name)است . یک محدودیت یا Constraintرا با توجه به آرگومان nameکه برابر با نام آن محدودیت خواهد بود ,حذف می کند . model_nameبرابر با نام مدل خواهد بود .بررسی Operations برای تغییرات دیگردر این بخش قرار است به بررسی توابع RunPythonو RunSQLو یک تابع دیگر بپردازیم . اینها نوع خاصی از Operationsیا عملیات ها محسوب می شوند .1-RunSQL :سینتکس آن به شکلclass RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)است . کد SQLدلخواه شما را در دیتابیس اجرا می کند (در فایل migrationباید آن را بنویسید) . این تابع برای استفاده های پیشرفته از دیتابیس که جنگو مستقیما از آنها پشتیبانی نمی کند ,ساخته شده است .آرگومان های sqlو reverse_sqlآن در صورت مقداردهی شدن ,باید رشته هایی شامل کد SQLباشند که می خواهید در دیتابیس اجرا شوند . در بیشتر دیتابیس ها (به جز PostgreSQL) جنگو قبل از اجرای کد های SQL ,آنها را به بخش های جداگانه تقسیم می کند .نکته : در PostgreSQLو SQLiteفقط از BEGINو COMMITدر فایل migration(فقط non-atomic) خود استفاده کنید تا از خراب شدن transactionهای جنگو جلوگیری کنید .همچنین می توانید به عنوان مقدار برای این تابع یک لیست از رشته ها و یا تاپل های دو عضوی ارسال کنید . از تاپل های دو عضوی برای ارسال پارامتر ها در عبارت Query خود می توان استفاده کرد (مانند cursor.execute که بعدا توضیح می دهیم) . سه عبارت زیر با یکدیگر برابر خواهند بود .migrations.RunSQL(&quot;INSERT INTO musician (name) VALUES (&#x27;Reinhardt&#x27;);&quot;)migrations.RunSQL([(&quot;INSERT INTO musician (name) VALUES (&#x27;Reinhardt&#x27;);&quot;, None)])migrations.RunSQL([(&quot;INSERT INTO musician (name) VALUES (%s);&quot;, [&quot;Reinhardt&quot;])])اگر می خواهید از علامت درصد در Queryاستفاده کنید (واقعا منظورتان درصد باشد!) ,باید هنگام ارسال پارامتر ها ,علامات را دوبار بنویسید .آرگومان reverse_sqlنیز مانند همان آرگومان sqlاست . با این تفاوت که زمانی اجرا می شود که فایل migrationدر دیتابیس اجرا نشود و یا معکوس شود . آنها قرار است کدی را اجرا کنند که عملیاتی که انجام شده را به حالت اول بازگرداند . به عنوان مثال , برای بازگردانی تغییرات کد بالا ,می توان نوشت :migrations.RunSQL(sql=[(&quot;INSERT INTO musician (name) VALUES (%s);&quot;, [&quot;Reinhardt&quot;])],reverse_sql=[(&quot;DELETE FROM musician where name=%s;&quot;, [&quot;Reinhardt&quot;])],)اگر reverse_sqlبرابر با Noneبماند ,عملیات انجام شده برگشت ناپذیر خواهد بود .آرگومان state_operationsبه شما امکان می دهد تا عملیاتی را در دیتابیس انجام بدهید که از نظر جنگو معادل SQL های از پیش آماده است . به عنوان مثال ,اگر باید به صورت دستی یک ستون را ایجاد کنید ,شما باید یک لیست حاوی کلاس AddFieldرا به آن ارسال کنید تا سیستم migrationsجنگو وضعیت به روز آن را اتوماتیک داشته باشد . اگر این کار را نکنید , دفعه بعدی که دستور makemigrationsرا اجرا کنید ,سیستم جنگو نمی تواند تشخیص دهد که یک فیلد به دیتابیس اضافه شده است و تلاش می کند تا دوباره آن را به دیتابیس اضافه کند . مثلا :migrations.RunSQL(&quot;ALTER TABLE musician ADD COLUMN name varchar(255) NOT NULL;&quot;,state_operations=[migrations.AddField(&quot;musician&quot;,&quot;name&quot;,models.CharField(max_length=255),),],)آرگومان hintsنیز یک آرگومان اختیاری است که به عنوان **hintsبه متد allow_migrate در روتر دیتابیس ارسال می شود تا به آنها در بعضی از موارد کمک کند . در مورد این قسمت بعدا بیشتر خواهیم خواند .آرگومان elidableنیز اختیاری است و تعیین می کند که آیا این عملیات یا operationدر هنگام فرایند squashingفایل های migrationباید حذف شود یا خیر .اگر می خواهید عملیات یا operationهیچ کاری را انجام ندهد نیز کافی است RunSQL.noopرا به آرگومان های sqlیا reverse_sqlارسال کنید .2-RunPython :سینتکس آن به شکلclass RunPython(code, reverse_code=None, atomic=None, hints=None, elidable=False)است . این تابع کد های پایتون دلخواه شما را اجرا خواهد کرد . آرگومان code و reverse_codeباید اشیا callableیا قابل فراخوانی باشند که دو آرگومان را بپذیرند . اولین آرگومان آنها شی از کلاس django.apps.registry.Appsباید باشد که حاوی historical models شماست و با محل اجرای عملیات یا operationsشما مطابقت دارد . دومین آرگومان نیز شی ای از کلاس SchemaEditorاست .آرگومان reverse_codeنیز مانند همان آرگومان codeاست . با این تفاوت که زمانی اجرا می شود که فایل migrationدر دیتابیس اجرا نشود یا معکوس شود . آنها قرار است کدی را اجرا کنند که عملیاتی که انجام شده را به حالت اول بازگرداند . اگر reverse_codeبرابر با Noneبماند ,عملیات انجام شده برگشت ناپذیر خواهد بود .آرگومان hintsنیز یک آرگومان اختیاری است که به عنوان **hintsبه متد allow_migrate در روتر دیتابیس ارسال می شود تا به آنها در بعضی از موارد کمک کند . در مورد این قسمت بعدا بیشتر خواهیم خواند .آرگومان elidableنیز اختیاری است و تعیین می کند که آیا این عملیات یا operationدر هنگام فرایند squashingفایل های migrationباید حذف شود یا خیر .به شما توصیه می شود که کد را به عنوان یک تابع جداگانه در بالای کلاس Migrationدر فایل migrationبنویسید و سپس آن را به تابع RunPythonارسال کنید . در اینجا مثالی برای استفاده از RunPythonبرای ایجاد و نمونه سازی چند شی از مدلی با نام Countryاورده شده است :from django.db import migrationsdef forwards_func(apps, schema_editor):# We get the model from the versioned app registry;# if we directly import it, it&#x27;ll be the wrong versionCountry = apps.get_model(&quot;myapp&quot;, &quot;Country&quot;)db_alias = schema_editor.connection.aliasCountry.objects.using(db_alias).bulk_create([Country(name=&quot;USA&quot;, code=&quot;us&quot;),Country(name=&quot;France&quot;, code=&quot;fr&quot;),])def reverse_func(apps, schema_editor):# forwards_func creates two Country instances,# so reverse_func should delete them.Country = apps.get_model(&quot;myapp&quot;, &quot;Country&quot;)db_alias = schema_editor.connection.aliasCountry.objects.using(db_alias).filter(name=&quot;USA&quot;, code=&quot;us&quot;).deleteCountry.objects.using(db_alias).filter(name=&quot;France&quot;, code=&quot;fr&quot;).deleteclass Migration(migrations.Migration):dependencies = []operations = [migrations.RunPython(forwards_func, reverse_func),]نکته : با توابعی مانند get_modelو connectionو ویژگی های دیگر بعدا بیشتر آشنا می شوید .این تابع معمولا زمانی مورد استفاده قرار می گیرد که می خواهید یک data migrationایجاد کنید و یا تغییرات دلخواه در دیتابیس یا فایل های پایتونی پروژه اعمال کنید و یا هر کار دیگری انجام دهید که نیاز به کد پایتون یا ORM جنگو دارید .دقیقا مانند RunSQL ,اطمینان حاصل کنید اگر بخشی از سیستم یا طرح دیتابیس یا پروژه را تغییر دادید , آن را در خارج از سیستم مدل های جنگو نیز (برای مثال در database triggers) تغییر دهید و یا از تابع SeparateDatabaseAndStateکه جلوتر توضیح می دهیم ,برای اضافه کردن یک عملیات یا operationاستفاده کنید تا تغییرات را در سطح های دیگر جنگو نیز اعمال کند (برای مثال در سطح مدل ها) . در غیر این صورت ممکن است ORMبه مشکل بخورد .به طور پیش فرض ,تابع RunPythonمحتویات خود را در داخل یک transactionدر دیتابیسی که از DDL transaction پشتیبانی نمی کند (MySQL و Oracle) , اجرا می کند . این کار باید ایمن باشد ,اما اگر بخواهید از schema_editorدریافت شده در این دیتابیس ها استفاده کنید ,ممکن است با مشکل مواجه شوید . در این حالت باید مقدار atomic=Falseرا به تابع RunPythonارسال کنید .در دیتابیسی که از DDL transaction پشتیبانی می کند (SQLite و PostgreSQL) ,تابع RunPythonهیچ transactionخاصی را به طور اتوماتیک در کنار یک transactionاز پیش ساخته شده ,در یک فایل migrationایجاد نمی کند . بنابراین برای مثال در PostgreSQL ,باید از ترکیب تغییرات schemaو توابع RunPythonدر یک فایل migrationخودداری کنید . در غیر این صورت ممکن است با خطاهایی مواجه شوید ,مانند :OperationalError: cannot ALTER TABLE &quot;mytable&quot; because it has pending trigger events.اگر از دیتابیس دیگری استفاده می کنید که مطمئن نیستید از DDL transactionپشتیبانی می کند یا خیر ,کافی است ویژگی django.db.connection.features.can_rollback_ddlرا بررسی کنید .اگر تابع RunPythonدر یک فایل migrationاستفاده شده است که در آن atomic=Falseاست ,تابع زمانی کار می کند که atomic=Trueبه تابع RunPythonارسال شود .نکته : تابع RunPythonبه طور شگفت انگیزی اتصال یا connectionمدل های شما را تغییر نمی دهد . یعنی هر تابع یا متدی که فراخوانی شود برای دیتابیس پیش فرض پروژه اجرا خواهد شد مگر اینکه نام مستعار یا alias nameدیتابیس را به تابع ارسال کنید (در schema_editor.connection.aliasدر دسترس خواهند بود و باید آرگومان دوم یا همان schema_editorاستفاده شوند) .اگر می خواهید تابع هیچ کاری را انجام ندهد نیز کافی است RunPython.noopرا به آرگومان های sqlیا reverse_sqlارسال کنید .3-SeparateDatabaseAndState :سینتکس آن به شکلclass SeparateDatabaseAndState(database_operations=None, state_operations=None) است . یک تابع است که امکان می دهد تا دیتابیس (تغییر ساختار آن) را با state(مراحل توسعه) ترکیب کنید و یا استفاده کنید .دو لیست از عملیات ها یا operationsرا با نام های state_operationsو database_operationsمی پذیرد . هنگامی که آن را فراخوانی کنید تا stateرا applyکند (یا وضعیتی را اعمال کند) ,از لیست state_operationsاستفاده می کند . هنگامی که آن را برای تغییرات دیتابیس فراخوانی کنید از لیست database_operationsاستفاده می کند .البته اگر ساختار دیتابیس شما و ویو ها یا سیستم های جنگو از کنترل خارج شوند و با هم هماهنگ نباشند ,استفاده از این تابع می تواند منجر به خرابی سیستم migrationجنگو و حتی از دست رفتن داده ها شود . باید در استفاده از این تابع بسیار احتیاط کنید و عملیات ها یا operationsخود را به دقت بررسی کنید . برای این کار می توانید از sqlmigrateیا dbshellاستفاده کنید . همچنین می توانید از makemigrations –dry-run(این دستور به شما نشان می دهد که یک فایل migrationچه کاری انجام خواهد داد ,البته روی دیتابیس واقعا تغییرات را انجام نمی دهد) برای بررسی عملیات های خود استفاده کنید .بعدا برای استفاده از این تابع مثالی خواهیم زد .نوشتن Operations به صورت سفارشیعملیات ها یا operations ساختار ساده ای دارند و API آنها نیز بسیار ساده طراحی شده است . در این بخش به شما یاد می دهیم تا چگونه یک عملیات برای خود بنویسید . شما می توانید حتی عملیات های دیگر را نیز به کمک این API تکمیل تر کنید . ساختار اصلی یک Operation به شکل زیر است :from django.db.migrations.operations.base import Operation

class MyCustomOperation(Operation):
    # If this is False, it means that this operation will be ignored by
    # sqlmigrate; if true, it will be run and the SQL collected for its output.
    reduces_to_sql = False
    
    # If this is False, Django will refuse to reverse past this operation.
    reversible = False

    def __init__(self, arg1, arg2):
        # Operations are usually instantiated with arguments in migration
        # files. Store the values of them on self for later use.
        pass

    def state_forwards(self, app_label, state):
        # The Operation should take the &#039;state&#039; parameter (an instance of
        # django.db.migrations.state.ProjectState) and mutate it to match
        # any schema changes that have occurred.
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        # The Operation should use schema_editor to apply any changes it
        # wants to make to the database.
        pass

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        # If reversible is True, this is called when the operation is reversed.
        pass

    def describe(self):
        # This is used to describe what the operation does in console output.
        return &amp;quotCustom Operation&amp;quot

    @property
    def migration_name_fragment(self):
        # Optional. A filename part suitable for automatically naming a
        # migration containing this operation, or None if not applicable.
        return &amp;quotcustom_operation_%s_%s&amp;quot %(self.arg1, self.arg2)شما می توانید این الگوریتم و چهارچوب را بگیرید و با آن کار کنید . اگر چه پیشنهاد می کنیم بیشتر از عملیات ها یا operations داخلی جنگو که قبل تر آنها را توضیح دادیم ,استفاده کنید . آنها از بسیاری ویژگی ها مانند ProjectState و الگوریتم هایی برای دریافت historical models و یا ModelState و الگوریتم هایی در تابع state_forwards پشتیبانی می کنند .به نکات زیر در ساخت Operation توجه داشته باشید :شما نیازی به یادگیری کامل ProjectState برای نوشتن یک فایل migration ندارید . فقط در این حد بدانید که یک ویژگی با نام apps دارد که دسترسی شما را به app registry فراهم می کند (در آنجا برای مثال می توانید get_model را فراخوانی کنید) .هر دو ویژگی های database_forwards و database_backwards دو state را می پذیرند .اگر می خواهید کلاس های مدل و یا اشیای یک مدل را از آرگومان from_state دریافت کنید و در database_forwards و یا database_backwards با آنها کار کنید , باید stateهای مختلف مدل را رندر بگیرید. این کار را با تابع clear_delayed_apps_cache انجام دهید تا مدل های مرتبط در دسترس قرار بگیرند . برای مثال :def database_forwards(self, app_label, schema_editor, from_state, to_state):
    # This operation should have access to all models. Ensure that all models are
    # reloaded in case any are delayed.
    from_state.clear_delayed_apps_cache
    ...مقدار to_state در database_backwards , state قدیمی تر خواهد بود . یعنی حالتی که پس از تمام شدن عملیات معکوس کردن migration ,وضعیت آن خواهد بود .ممکن است استفاده از references_model را در عملیات های داخلی جنگو مشاهده کنید . این بخشی از سیستم اتوماتیک تشخیص کد جنگو است و برای عملیات مهم نیست .نکته : برای عملکرد بهتر ,نمونه های کلاس Field که در ModelState.fields وجود دارند ,دوباره استفاده می شوند . بنابراین شما هرگز نباید ویژگی های این اشیا را تغییر دهید . اگر نیاز به تغییر فیلد در تابع state_forwards دارید ,باید اشیا قدیمی را از ModelState.fields حذف کنید و یک شی جدید بجای آن اضافه کنید که ویژگی های جدید دارد . همین امر برای اشیای کلاس Manager در تابع ModelState.managers نیز باید رعایت شود .به عنوان مثال بیایید یک عملیات یا Operation بسازیم که extensions از PostgreSQL(extensions همان افزونه ها هستند که ویژگی های جدید به دیتابیس اضافه می کنند) را لود می کند . از آنجایی که لازم به تغییر در مدل نیست ,فقط قرار است یک دستور را اجرا کنیم :from django.db.migrations.operations.base import Operation

class LoadExtension(Operation):
    reversible = True

    def __init__(self, name):
        self.name = name

    def state_forwards(self, app_label, state):
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute(&amp;quotCREATE EXTENSION IF NOT EXISTS %s&amp;quot % self.name)

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute(&amp;quotDROP EXTENSION %s&amp;quot % self.name)

    def describe(self):
        return &amp;quotCreates extension %s&amp;quot % self.name

    @property
    def migration_name_fragment(self):
        return &amp;quotcreate_extension_%s&amp;quot % self.nameدر این جلسه به بررسی بخشی دیگر از مفاهیم migrations در جنگو پرداختیم . در جلسه بعدی نیز بخش آخر این مفاهیم را بررسی می کنیم و از مبحث migrations خارج می شویم .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sat, 16 Dec 2023 12:39:18 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه چهل | بررسی Migrations در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%DA%86%D9%87%D9%84-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-migrations-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-iowcfes0awbu</link>
                <description>در این جلسه به چگونگی کار کردن با Migrations در جنگو میپردازیم . با ما همراه باشید . آموزش جنگو : جلسه چهل | بررسی Migrations در جنگوبررسی مفهوم Migrationsمفهوم Migrations :روشی است که جنگو استفاده می کند تا تغییراتی را که در مدل خود ایجاد می کنید (افزودن یک فیلد ,حذف آن و غیره) را بر روی ساختار جداول دیتابیس نیز اعمال کند . آنها طراحی شده اند تا به طور خودکار وظایف خود را انجام دهند ,اما بهتر است با روش کار آنها و اینکه چه زمانی از آنها استفاده کنید و یا مشکلات آنها ,آشنا باشید .دستورات Migrationsآیا دستور python3 manage.py migrate را به یاد دارید ؟ این دستور در واقع مربوط به همین مبحث بوده است . ما مدلی را ایجاد می کردیم و سپس با این دستور و یک دستور دیگر (makemigrations) آنها را روی دیتابیس پیاده سازی می کردیم . در زیر قرار است تمام دستوراتی که برای عملیات های migrations می توان استفاده کرد را بررسی کنیم :(روش استفاده از آنها به همان صورت بالا است)دستور migrate :مسئولیت وضعیت اعمال شدن یا نشدن تغییرات روی دیتابیس را هندل می کند .دستور makemigrations :مسئولیت ایجاد لیستی از تغییراتی که روی مدل دادید را هندل می کند .دستور sqlmigrate :دستورات SQL اجرا شده برای یک migrate را نشان می دهد .دستور showmigrations :مسئولیت ذخیره لیستی از migrate ها و وضعیت اعمال شدن یا نشدن آنها بر روی دیتابیس را دارد .در جنگو Migrations در اصل یک سیستم کنترلی است . این سیستم را جنگو استفاده می کند تا دقیق تر میان کدهای شما و دیتابیس ارتباط برقرار کند . همچنین یک تاریخچه نیز برای تغییرات دیتابیس محسوب می شود . در این سیستم دستور makemigrations فایل هایی با نام migration را ایجاد می کند که حاوی تغییراتی هستند که باید روی دیتابیس اعمال شود (مشابه commit) . سپس دستور migrate این فایل ها و تغییرات را بر دیتابیس اعمال می کند .فایل های ایجاد شده توسط makemigrations در هر اپ متفاوت هستند . هر اپ در دایرکتوری یا فولدر ‘migration’ آنها را ذخیره می کند . همچنین آنها به عنوان بخشی از دیتابیس و برنامه شناسایی می شوند . پس می توانید روی دستگاه خود آنها را ایجاد کنید و دوباره روی دستگاه های دیگر آنها را اجرا کنید .نکته : برای تغییر محل ذخیره سازی فایل های migration کافی است متغییر MIGRATION_MODULES را در settings.py تغییر دهید .سیستم Migrations روی پروژه های یکسان نتایج یکسان را به وجود می آورند . به زبانی دیگر می توان گفت که هر آنچه در مرحله توسعه می بینید ,دقیقا همین چیزی است که هنگام production خواهید دید .جنگو برای تغییرات کوچک (حتی آنهایی که بر دیتابیس تاثیر ندارد) نیز Migration ایجاد می کند . زیرا همانطور که گفتیم جنگو فیلد ها را دقیق بازسازی می کند و تاریخچه آنها را برای این کار نیاز دارد .نکته : در صورتی که از validator های بازنویسی شده استفاده کنید ,ممکن است برخی از Migration ها بعدا روی دیتابیس اعمال شوند .دیتابیس های قابل پشتیبانیسیستم Migrations در تمام دیتابیس های قابل پشتیبانی جنگو ,کار می کنند . همچنین بک اند های دیگر (third-party) در صورتی که از schema alteration پشتیبانی کنند نیز می توانند با Migrations کار کنند . (این کار با SchemaEditor انجام می شود)با این حال برخی از دیتابیس ها کنترل بیشتری روی فرایند کنترل دارند . در زیر آنها را توضیح دادیم :دیتابیس PostgreSQL :این دیتابیس از میان دیتابیس های جنگو ,قدرتمند ترین آنها برای پشتیبانی از Migrationsاست دیتابیس MySQL :این دیتابیس از transactionsدر بین عملیات اعمال migrationsپشتیبانی نمی کند . به زبانی ساده اگر اعمال یک migrationروی دیتابیس با شکست مواجه شود ,باید تغییراتی که در مدل ها ایجاد کرده اید را به عقب بازگردانید (بردارید) و دوباره امتحان کنید . علاوه بر این MySQLتقریبا برای هر عملیات migrateطرح تمام جدول ها را بازنویسی می کند . این کار باعث می شود تا زمان انجام عملیات به طور زیادی افزایش پیدا کند . البته که این زمان وابسته به تعداد ردیف ها و ستون ها است . در سخت افزار های کند این دیتابیس برای اعمال یک migrateکه در آن چند ستون و چند میلیون ردیف وجود داشته باشد , به بیش از ده دقیقه زمان نیاز دارد . در نهایت , MySQLدارای محدودیت هایی است که تعداد کاراکتر را برای نام ستون ها , جدول ها و indexها و حتی اندازه ستون هایی که یک indexمی تواند داشته باشد را محدود می کند (کاهش پیدا می کند) . این به این معنی است که گاهی اوقات ممکن است indexهایی که در دیتابیس های دیگر کار می کنند ,در MySQLکارایی نداشته باشند .دیتابیس SQLite :این دیتابیس پشتیبانی خیلی کمی از سیستم migrationدارد . بنابراین جنگو تلاش می کند این سیستم را با مواردی بازسازی کند . از نمونه این موارد :ایجاد جدول جدید با یک schemaجدید (schemaهمان سیستم مربوط به migrateاست که از این پس با این نام آن را صدا می کنیم) ,کپی کردن داده ها در همه ی بخش ها ,حذف جدول قدیمی ,تغییر نام جدول جدید برای مطابقت با اسم اصلی آن . این فرایند به طور کلی خوب کار می کند ,اما گاهی اوقات نیز ممکن است پر از باگ و آهسته باشد . توصیه نمی شود که SQLite را برای پروژه های واقعی استفاده کنید . طراحان جنگو توصیه می کنند که از SQLite بر روی پروژه های محلی خود استفاده کنید که نیاز به پیچیدگی کمتر دارند .محیط کارروش کار به این صورت است که جنگو برای شما اتوماتیک , Migration ها را ایجاد می کند . فقط کافی است تغییراتی که می خواهید را روی مدل اعمال کنید (برای مثال یک فیلد را حذف کنید) و دستور makemigrations را اجرا کنید . نتیجه اجرای این دستور به صورت زیر است :$ python manage.py makemigrations
Migrations for &#039;books&#039;:
    books/migrations/0003_auto.py:
        - Alter field author on bookبعد از اجرای این دستور ,مدل های شما اسکن می شوند و با فایل های migration موجود مقایسه می شوند . تغییرات جدید در فایل migration جدیدی نوشته می شود و در دایرکتوری مخصوص به خودش قرار می گیرد . یک نکته مهم این است که حتما خروجی دستور makemigrations را بخوانید تا از تغییرات مطمئن شوید . (البته خروجی ها آنقدر دقیق نیستند)هنگامی که فایل های جدید migration ایجاد شده اند ,با دستور migrate باید تغییرات را بر دیتابیس اعمال کنید . برای مثال :$ python manage.py migrate
Operations to perform:
    Apply all migrations: books
Running migrations:
    Rendering model states... DONE
    Applying books.0003_auto... OKهنگامی که این تغییرات روی دیتابیس اعمال شد ,کار تمام است ! یکی از استفاده های خوب از سیستم migration ها این است که برنامه نویسان دیگر در هر زمان می توانند هم مدل شما را مشاهده کنند و هم تغییرات اعمال شده روی آن طی زمان بررسی کنند .اگر می خواهید به جای یک نام اتوماتیک تولید شده ,یک نام معنادار برای فایل migration بسازید از makemigrations –name استفاده کنید . برای مثال :$ python manage.py makemigrations--name changed_my_model your_app_labelمعرفی version controlبه سیستم ذخیره سازی فایل های migration , version control گفته می شود . گاهی اوقات با موقعیت هایی مواجه می شوید که شما و یک برنامه نویس دیگر همزمان با هم و در یک اپ , یک migration را ایجاد می کند . این باعث می شود که برنامه شما دو فایل migration را با یک عدد داشته باشد .اگر این اتفاق افتاد ,نگران نباشید . اعدادی که برای migrationها ثبت می شود ,فقط برای خود برنامه نویسان اهمیت دارد و جنگو فقط به متفاوت بودن نام آنها اهمیت می دهد . فایل های migration به طوری طراحی شده اند که مشخص می کنند که به اجرای کدام فایل migration بستگی دارند . یعنی کدام فایل ها باید قبل از خودشان در دیتابیس اعمال شده باشد . بنابراین تشخیص اینکه در کدام اپ دو فایل migration وجود دارند که ترتیب ندارند ,کار راحتی است .وقتی این اتفاق رخ می دهد ,جنگو به شما اطلاع می دهد و گزینه هایی را در اختیار شما می گذارد . یکی از گزینه ها این است که این دو فایل به صورت اتوماتیک با هم ادغام شوند . اگر فکر می کنید که به اندازه کافی این کار ایمن است ,این گزینه را انتخاب کنید . اگر نه باید خودتان به صورت دستی آنها را تغییر دهید . در بخش “فایل های migration” این کار را توضیح داده ایم . بررسی Transactionsدر دیتابیس هایی که از DDL transactions پشتیبانی می کنند (SQLite , PostgreSQL) ,همه ی عملیات های migration به صورت پیش فرض در یک transaction رخ می دهند .نکته : transaction همان قدم های دیتابیس برای انجام عملیات ها است . در اینجا منظور این است که تمام عملیات به یکباره و در یک گام انجام خواهد شد . البته برخی از دیتابیس ها می توانند بدون transaction نیز عملیاتی را هندل کنند. (برای اطلاعات بیشتر درباره transaction تحقیق کنید)در دیتابیس هایی که از DDL transactions پشتیبانی نمی کنند (MySQL , Oracle) ,همه ی عملیات های migration به صورت پیش فرض بدون هیچ transaction انجام می گیرد .برای اینکه در هر دیتابیسی ,از انجام migration در داخل یک transaction جلوگیری کنید ,کافی است ویژگی atomic=False را تنظیم کنید. برای مثال :from django.db import migrations

class Migration(migrations.Migration):
    atomic = Falseهمچنین بعدا به این می پردازیم که چگونه فقط بخش هایی از یک migration را درون transaction وارد کنیم . بررسی dependencyدر این بخش درباره مفهوم dependency صحبت خواهیم کرد . فایل های migration گاهی اوقات پیچیده تر از آن هستند که در یک اپ پیاده سازی شوند . گاهی ممکن است روابطی را استفاده کنید که نیاز به اجرای migration های اپ های دیگر نیز وجود دارد . برای مثال ,فرض کنید یک ForeignKey را در اپ books ایجاد می کنید که به مدلی در اپ authors مرتبط شده است . فایل های migration در اپ books ,یک وابستگی یا همان dependency به فایل های migration در اپ authors دارند .این بدان معناست که وقتی دستور migrate را اجرا می کنید ,ابتدا فایل های migration مربوط به اپ authors اجرا می شوند تا جدول های آن اپ را بسازند . سپس فایل های migration را در اپ books اجرا می شوند تا فیلد ForeignKey ایجاد شود و ارتباط آن میان مدل اول و دوم برقرار شود . در آخر نیز محدودیت ها برای هر دو مدل اعمال می شوند . اگر این اتفاق رخ نمی داد , جنگو تلاش می کرد تا فیلد ForeignKey را ابتدا ایجاد کند و رابطه ای به جدولی بزند که هنوز به وجود نیامده است . این باعث ارور می شد .رفتار dependency بر روی اکثر سیستم های migration تاثیر می گذارد . محدود کردن دستورات makemigrations و migrate به اجرا در یک اپ ,توصیه نمی شود . اگر این کار را انجام دادید ,باید بدانید که اپ هایی که هنوز migration های اجرا نشده دارند ,نباید روابط (ForeignKey , ManyToManyField) با اپ هایی که تمام فایل های migration آنها اجرا شده است ,داشته باشند . البته گاهی اوقات ممکن است کار کند ولی پشتیبانی نمی شود . فایل های migrationفایل های migration در واقع به صورت on-disk هستند . یعنی فقط در ماشین محلی وجود خواهند داشت . آنها فایل های پایتون معمولی هستند که حاوی اطلاعات درباره تغییرات طرح جداول هستند .یک فایل migration ساده به صورت زیر است :from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies =[(&amp;quotmigrations&amp;quot, &amp;quot0001_initial&amp;quot)]
    operations = [
        migrations.DeleteModel(&amp;quotTribble&amp;quot),
        migrations.AddField(&amp;quotAuthor&amp;quot, &amp;quotrating&amp;quot, models.IntegerField(default=0)),
    ]چیزی که جنگو هنگام لود کردن یک migration بدنبال آن می گردد یک زیرکلاس از django.db.migrations.Migration است که نام آن Migration باشد . سپس آن را برای چهار ویژگی بررسی خواهد کرد . دو ویژگی آن اکثر اوقات استفاده می شوند :ویژگی dependencies :لیستی از فایل های migration که این فایل ابتدا به اجرا شدن آنها وابسته است .ویژگی operations :لیستی از کلاس های Operation که مشخص می کنند این فایل چکاری انجام می دهد.مهمترین قسمت ,همان operations ها هستند . جنگو آنها را اسکن می کند و طبق دستورات آنها , نقشه ای از تمام تغییرات schema (به ساختار دیتابیس schema گفته می شود) در تمام اپ ها در حافظه ذخیره می کند و از آن برای تولید کد های SQL استفاده می کند تا تغییرات را به دیتابیس اعمال کند .ساختاری که درون حافظه ذخیره شده است برای بررسی تفاوت های میان مدل شما و وضعیت فعلی migrationها نیز بکار می رود . جنگو همه تغییرات را به ترتیب انجام می دهد .جمله بالا یعنی جنگو ابتدا ساختار را درون حافظه ذخیره می کند . سپس مدلی را شکل می دهد که شکل آن مانند آخرین باری است که دستور makemigrations را روی آن اعمال کرده اید . سپس از آن مدل برای مقایسه با مدل کنونی استفاده می کند تا بفهمد چه چیزی را تغییر دادید .شما به ندرت نیاز به ویرایش فایل های migration دارید ,اما ممکن است برخی از عملیات ها آنقدر پیچیده باشند که با تشخیص اتوماتیک جنگو در فایل های migration ایجاد نشوند . شما می توانید آن تغییرات را درون فایل های migration بنویسید . بنابراین از ویرایش آنها لازم نیست بترسید . فیلد های سفارشی در migrationsاگر یک فیلد سفارشی سازی شده داشته باشید که خودتان آن را ساختید , نمی توانید تعداد آرگومان های موقعیتی (positional) را که می پذیرد را پس از انجام migrate تغییر دهید . در این صورت شما با یک TypeError مواجه می شوید زیرا migration قدیمی متد __inti__ قدیمی را اجرا می کند . بنابراین اگر یک آرگومان جدید نیاز دارید , یک آرگومان argument keyword ایجاد کنید و چیزی را مانند &#x27;argument_name‘ برای آن در نظر بگیرید . مدیر ها (manager) در migrationsشما می توانید به صورت اختیاری managerها را با serialize کردن در فایل های migration قرار دهید . پس از این آنها در کلاس RunPython در دسترس خواهند بود . این کار را با تعریف ویژگی use_in_migrations در کلاس manager انجام دهید . برای مثال :class MyManager(models.Manager):
    use_in_migrations = True

class MyModel(models.Model):
    objects = MyManagerاگر در این وضعیت در حال استفاده از تابع from_queryset هستید تا کلاس manager را به صورت داینامیک ایجاد کنید ,شما باید از کلاس ایجاد شده ارث بری کنید . بعد از آن می توانید از manager استفاده کنید . برای مثال :class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
    use_in_migrations = True

class MyModel(models.Model):
    objects = MyManager مفهوم initial migrationsدر این بخش درباره مفهوم initial migrations صحبت می کنیم . initial migrationsها ,فایل های migration هستند که اولین طرح از جدول های دیتابیس را ایجاد می کنند . در هر اپ فقط یکی از آنها وجود دارد (یعنی فقط یکبار جداول اولیه برای یک اپ ایجاد می شوند . مگه نه ؟!). اما بعضی اوقات ممکن است تعداد آنها بخاطر مفهوم dependency به دو یا بیشتر هم برسد .کلاس های migration در صورتی که ویژگی initial=True داشته باشند یک initial migrations حساب می شوند . بنابراین به راحتی قابل تشخیص هستند . اگر ویژگی initial در کلاس پیدا نشود ,فایل migration در صورتی که اولین migration در اپ باشد (یعنی برای مثال به هیچ فایل دیگری وابسته نباشد) , به عنوان initial migrations حساب خواهد شد .همچنین شما دستوری با نام migrate --fake-initial دارید . اگر این دستور را اجرا کنید برای مثال ,برای initial migrations در آن اپ ,بررسی ای توسط جنگو صورت می گیرد (فرض کنید وظیفه آن ساخت چند جدول بوده است) . جنگو بررسی می کند که آیا جداول از قبل در دیتابیس وجود دارند یا خیر . در صورتی که وجود داشته باشند ,آنها را اجرا می کند . یا برای مثال برای initial migrations که قرار باشد چند فیلد را اضافه کند ,بررسی می کند که آیا از قبل ستون های آن در دیتابیس ایجاد شده اند یا خیر و سپس migration را اجرا میکند . بدون fake-initial فایل های migration تفاوتی با فایل initial migrations نخواهند داشت. درهم ریختگی زمانی فایل هاهمانطور که گفتیم ,گاهی ممکن است نیاز باشد تا به صورت دستی دو فایل migration را با یکدیگر ادغام کنید . در حین ویرایش مقدار dependencies در فایل ,ممکن است ناخواسته یک وضعیت ناسازگار از لحاظ زمانی را ایجاد کنید که خود فایل migration اعمال شده است اما بعضی از dependencies اعمال نشده است . جنگو در این مواقع تا زمان حل مشکل از اعمال هرگونه migration یا ساخت migration جدید جلوگیری می کند .نکته : در هنگامی که از چند دیتابیس استفاده می کنید ,شما می توانید با فراخوانی متد allow_migrate از روتر دیتابیس (database router) برای کنترل اینکه دستور makemigration از کدام دیتابیس برای بررسی زمانی استفاده کند ,بهره ببرید . اضافه کردن migration در یک اپهر اپ در جنگو برای اجرای migration های جدید با دستور makemigrations آماده است . اگر اپ شما قبلا مدل ها و جدول هایی در آن وجود دارد و هنوز migration ای در آن ثبت نشده است (برای مثال نسخه جنگو خود را عوض کردید) , باید با دستور زیر migration جدیدی را برای آن آماده کنید (your_app_label با نام اپ جایگزین می شود) :$ python manage.py makemigrations your_app_labelدستور بالا برای شما اولین migration را در اپ ایجاد می کند . گرچه این واقعا اولین migration این اپ نیست ! پس کافی است دستور python manage.py migrate --fake-initial را اجرا کنید و جنگو تشخیص می دهد که شما یک initial migrations دارید که جدول های آن از قبل ممکن است وجود داشته باشند . پس از اعمال migration ,فایل به عنوان ‘اعمال شده’در نظر گرفته می شود . (بدون --fake-initial دستور با خطا مواجه می شود ,زیرا جداول آن از قبل در دیتابیس وجود دارند)توجه داشته باشید که این فرایند فقط به دو شرط کار می کند :در صورتی که مدل های خود را تغییر نداده باشید ولی جدول های آنها ایجاد کرده باشید . برای اینکه migrationها در این حالت کار کنند ,باید ابتدا initial migrations را ایجاد کنید و سپس تغییرات را در مدل انجام دهید . دلیل آن این است که جنگو تغییرات را با فایل migrationمقایسه می کند و نه با دیتابیس .در صورتی که شما دیتابیس را به صورت دستی تغییر نداده باشید . جنگو نمی تواند تشخیص دهد که دیتابیس با مدل ها مطابقت ندارد و زمانی که فایل های migrationسعی کنند تا جدول ها را در آن زمان تغییر دهند ,با ارور مواجه خواهند شد . معکوس کردن migrationهمانطور که گفته شد ,فایل های migration کاربرد زمانی نیز دارند . یعنی می توانید مراحلی را در هنگام توسعه ایجاد کنید و هر زمان که خواستید به یکی از آن مراحل بازگردید . برای معکوس کردن migrationها کافی است از migration های قبلی آن استفاده کنید . برای مثال برای معکوس کردن فرایند فایل migration شماره book.0003 کافی است بنویسید :$ python manage.py migrate books 0002
Operations to perform:
    Target specific migration: 0002_auto, from books
Running migrations:
    Rendering model states... DONE
    Unapplyingbooks.0003_auto... OKاگر می خواهید تمام migrationهایی که در یک اپ وجود دارد را معکوس کنید ,کافی است از zero استفاده کنید . برای مثال :$ python manage.py migrate books zero
Operations to perform:
    Unapply all migrations: books
Running migrations:
    Rendering model states... DONE
    Unapplyingbooks.0002_auto... OK
    Unapplyingbooks.0001_initial... OKالبته اگر فایل migration دارای عملیات هایی باشد که برگشت ناپذیر هستند ,نمی توانید آنها را معکوس کنید . تلاش برای معکوس کردن این گونه از migrationها باعث خطای IrreversibleError می شود . برای مثال :$ python manage.py migrate books 0002
Operations to perform:
    Target specific migration: 0002_auto, from books
Running migrations:
    Rendering model states... DONE
    Unapplyingbooks.0003_auto...Traceback (most recent call last):
django.db.migrations.exceptions.IrreversibleError: Operation &lt;RunSQL sql=&#039;DROP TABLE
demo_books&#039;&gt; in books.0003_auto is not reversible مفهوم historical modelsهنگامی که فایل های migration را اجرا می کنید ,جنگو از مدل هایی استفاده می کند که درون فایل های migration وجود دارند (طرح مدل ها) . این مدل ها با نام historical models شناخته می شوند . اگر از RunPython (بعدا درباره آن صحبت می کنیم) یا allow_migrate در روتر دیتابیس خود استفاده می کنید ,شما نیز باید با historical models کار کنید و نه اینکه مستقیم از مدل های اصلی استفاده کنید . ُنکته : اگر بجای استفاده از historical models ,از مدل ها مستقیما استفاده کنید ,فایل های migration ممکن است در ابتدا کار کنند اما در آینده (زمانی که مثلا بخواهید migration های قدیمی را دوباره اجرا کنید) به مشکل می خورند . معمولا این مشکل هنگامی که یک راه اندازی انجام می دهید ,رخ می دهد . یعنی هنگامی که دیتابیس را آماده کرده باشید و تمام migration ها را اعمال کرده باشید ,دیتابیس با ارور مواجه می شود . البته اگر به این گونه مشکلات برخوردید ,ایرادی ندارد تا فایل های migration را به صورت دستی برای حل مشکل ویرایش کنید .از آنجایی که برخی کد های پایتون دلخواه نمی توانند serialize شوند , historical models نمی توانند متد های شخصی سازی شده را در خود ذخیره کنند (یعنی متد هایی را که خودتان نوشته باشید) . با این حال , آنها دارای تمامی فیلد ها ,روابط , managerها (فقط آنهایی که use_in_migrations = True دارند) و گزینه های کلاس متا (Meta) هستند .نکته :این به این معنی است که هنگام دسترسی به اشیا در فایل های migration ,شما به متد save بازنویسی شده خود دسترسی نخواهید داشت . همچنین نمی توانید از متد های بازنویسی شده دیگر نیز استفاده کنید .استفاده از توابع در آرگومان های فیلد ها مانند upload_to یا limit_choices_to و یا manager(فقط آنهایی که use_in_migrations = True دارند) می تواند در فایل های migration ,سریال سازی یا serialize شود . به زبانی ساده مجاز هستید از توابع در این گزینه ها به عنوان مقدار استفاده کنید و این توابع نادیده گرفته نمی شوند . در واقع تا زمانی که یک migration به یک تابع چیزی را ارجاع کند ,توابع و کلاس ها نگه داشته می شوند . فیلد های سفارشی سازی شده نیز همینطور هستند , زیرا این فیلد ها مستقیما توسط migration استفاده می شوند .علاوه بر این اگر از ارث بری در مدل های خود استفاده می کنید , کلاس های والد تا زمانی که فایل migration شما از آن استفاده کند ,نگه داشته می شوند . برای حذف ارجاع به مدل ها از فایل migration آنها را به صورت دستی می توانید حذف کنید . حذف فیلد سفارشی در migrationهمانطور که گفتیم ,در صورتی که فیلد های سفارشی سازی شده داشته باشید که در فایل migration نیز به آنها اشاره شده باشد ,نمی توانید به سادگی آن فیلد ها را حذف کنید .برای حل این مشکل ,جنگو از فریمورک system checks استفاده می کند . این فریمورک یک سری از ویژگی ها را برای فیلد شما ارائه می دهد .برای مثال می توانید ویژگی system_check_deprecated_details را در فیلد سفارشی مقداردهی کنید :class IPAddressField(Field):
    system_check_deprecated_details = {
        &amp;quotmsg&amp;quot: (
            &amp;quotIPAddressField has been deprecated. Support for it (except &amp;quot
            &amp;quotin historical migrations) will be removed in Django 1.9.&amp;quot
        ),
        &amp;quothint&amp;quot:  &amp;quotUse GenericIPAddressFieldinstead.&amp;quot, # optional
        &amp;quotid&amp;quot:  &amp;quotfields.W900&amp;quot, # pick a unique ID for your field.
    }پس از اجرای این می توانید system_check_deprecated_details را به system_check_removed_details تغییر دهید و مقدار آن را به صورت زیر ویرایش کنید :class IPAddressField(Field):
    system_check_removed_details = {
        &amp;quotmsg&amp;quot: (
            &amp;quotIPAddressField has been removed except for support in &amp;quot
            &amp;quothistorical migrations.&amp;quot
        ),
        &amp;quothint&amp;quot:  &amp;quotUse GenericIPAddressField instead.&amp;quot,
        &amp;quotid&amp;quot: &amp;quotfields.E900&amp;quot, # pick a unique ID for your field.
    }به یاد داشته باشید تا پایان فرایند حذف فیلد سفارشی ,باید تمامی متد های موردنیاز آن مانند __init__ و deconstruct و get_internal_type را نگه دارید . زمان نگه داری این متدها تا هنگامی است که هنوز فایل migrationای وجود داشته باشد که به این فیلد اشاره کند (یا از مدلی که این فیلد را دارد ,استفاده کرده باشد) . پس از حذف تمامی migrationهایی که به این فیلد اشاره دارند ,می توانید متد ها و خود فیلد را حذف کنید . مفهوم data migrationsهمانطور که می توانید به کمک فایل های migration ساختار دیتابیس و طراحی آن را تغییر دهید ,می توانید از این فایل ها برای تغییر خود داده ها و مقادیر درون دیتابیس نیز استفاده کنید .این نوع از migrationها که داده های درون دیتابیس را تغییر می دهند با نام “data migrations” شناخته می شوند . بهتر است آنها در فایل های جدا نوشته شوند و کنار فایل های migration دیگر که ساختار دیتابیس را تغییر می دهند ,قرار داده شوند .جنگو نمی تواند برای شما به صورت اتوماتیک این نوع از migrationها را ایجاد کند . اما نوشتن آنها خیلی سخت نیست . فایل های migration اصولا با نوشتن Operations و مقداردهی آنها تعریف می شوند . در data migrations برای Operations ,باید مقدار تابع RunPython را تعریف کنید .برای شروع یک فایل migration خالی را با دستور زیر بسازید . جنگو اتوماتیک آن را محل مناسب ایجاد می کند و یک نام را برای آن پیشنهاد می دهد و سپس dependenciesها را برای شما در آن مقداردهی می کند :python manage.py makemigrations--empty yourappnameسپس فایل ایجاد شده را باز کنید . محتوای فایل باید مانند زیر باشد :# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotyourappname&amp;quot, &amp;quot0001_initial&amp;quot),
    ]
    operations = []اکنون تمام کاری که باید انجام دهید این است که یک تابع جدید بسازید و از RunPython استفاده کنید . خود تابع RunPython یک تابع دیگر را به عنوان آرگومان می پذیرد که آن هم دو آرگومان دریافت می کند .اولی یک app registry است که تمامی historical models از تمامی مدل ها در آن قرار داشته باشند تا با migrationها مطابقت داشته باشد . آرگومان دوم نیز یک SchemaEditor است . از آن برای اعمال تغییرات در ساختار دیتابیس به صورت دستی استفاده می توانید کنید . (مراقب باشید ! این کار ممکن است سیستم اتوماتیک وارانه migrations را گیج کند)بیایید یک فایل migration بنویسیم که فیلد name را با مقادیر ترکیب شده فیلد های first_name و last_name ,مقداردهی کند . تنها کاری که باید انجام بدهیم این است که مانند کد زیر از historical model خود استفاده کنیم و روی ردیف ها iterate یا پیمایش کنیم :from django.db import migrations

def combine_names(apps, schema_editor):
    # We can&#039;t import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Person = apps.get_model(&amp;quotyourappname&amp;quot, &amp;quotPerson&amp;quot)
    for person in Person.objects.all:
        person.name = f&amp;quot{person.first_name} {person.last_name}&amp;quot
        person.save

class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotyourappname&amp;quot, &amp;quot0001_initial&amp;quot),
    ]
    operations = [
        migrations.RunPython(combine_names),
    ]پس از انجام این کار می توانید به سادگی دستور migrate را اجرا کنید و تمامی تغییرات داده ای اعمال خواهند شد .می توانید یک تابع یا callable دومی نیز به تابع RunPython ارسال کنید تا هر منطقی را که می خواهید هنگام معکوس کردن اجرا کنید ,پیاده سازی کنید . اگر تابع دومی ارسال نشود ,تلاش برای معکوس کردن migration باعث ایجاد ارور می شود .دسترسی به اپ های دیگر در migrationدر هنگام نوشتن RunPython ممکن است از مدل هایی استفاده کنید که در اپ های دیگر قرار دارند . در این صورت ویژگی dependencies باید اخرین migration در اپی که می خواهید استفاده کنید را نیز شامل بشود . در غیر این صورت ممکن است با خطای &#x27;LookupError: No installed app with label myappname‘ مواجه شوید (هنگامی که در RunPython بخواهید با apps.get_model به آن مدل دسترسی پیدا کنید) .در مثال زیر ما در اپ شماره 1 (app1) ,یک فایل migration داریم که به مدلی از اپ شماره 2 (app2) نیاز دارد . بنابراین ویژگی dependencies را تغییر می دهیم و migration آخر در اپ شماره 2 را به آن اضافه می کنیم . مثال در صفحه بعدی مشخص شده است :class Migration(migrations.Migration):
    dependencies = [
        (&amp;quotapp1&amp;quot, &amp;quot0001_initial&amp;quot),
        # added dependency to enable using models from app2 in move_m1
        (&amp;quotapp2&amp;quot, &amp;quot0004_foobar&amp;quot),
    ]

    operations = [
        migrations.RunPython(move_m1),
    ]اگر به دنبال نوشتن migrationهای بیشتر و بررسی بیشتر تمامی operationsها هستید ,در بخش های آینده آنها را توضیح خواهیم داد .فشرده سازی migrationدر این بخش به مفهوم squashing خواهیم پرداخت . شما می توانید در جنگو به صورت آزادانه هزاران یا صد ها migration ایجاد کنید . آنها بدون اینکه ذره ای از سرعت جنگو کم کنند ,میتوانند در اپ وجود داشته باشند . گرچه شما می توانید برای خوانایی بیشتر و بهتر , آن صد فایل migration را در چند فایل migration خلاصه کنید . به این کار squashing می گویند .مفهوم squashing به عملی گفته می شود که در طی آن تعداد فایل های migration موجود ,به یک یا گاهی چند فایل migration کاهش پیدا می کند که همچنان همان تغییرات را نشان می دهند .جنگو این کار را با جمع آوری تمامی فایل های migration ,استخراج Operations در هر کدام از آنها و قرار دادن آنها به ترتیب انجام می دهد . بعد از اینکار یک بهینه ساز روی آنها فراخوانی می شود تا طول فایل کاهش پیدا کند . برای مثال عملیات های نقیض را حذف کند . (برای مثال بهینه ساز میداند که CreateModel و DeleteModel یکدیگر را لغو می کنند و یا اینکه می توان AddField را در CreateModel قرار داد)هنگامی که تمامی عملیات یا Operations مرتب شدند ,جنگو آنها را در یک فایل migration جدید بازنویسی می کند .نکته : کاهش طول فایل بستگی به این دارد که مدل های شما چقدر در هم تنیده هستند و آیا عملیات های RunSQL و RunPython در فایل خود دارید یا نه . (آنها در واقع بهینه سازی نمی شوند مگر اینکه تنظیمات آنها را دستکاری کنید تا قابل حذف باشند)فایل هایی که squashing روی آنها صورت گرفته است می توانند به سادگی در کنار فایل های migration قدیمی همچنان باقی بمانند . جنگو نیز به صورت اتوماتیک با توجه به تاریخ ساختار , بین آنها جا به جا می شود . البته این اتفاق فقط در صورتی رخ می دهد که اپ شما تازه این migrationها را نصب نکرده باشد . در این صورت جنگو به استفاده از فایل های قدیمی migration ادامه می دهد تا به پایان برسند . سپس به فایل جدید (فایل squashing شده) سوئیچ می شود .در زمانی که فایل های migration به تازگی وارد اپ شده باشند ,جنگو از فایل جدید استفاده می کند و از استفاده از فایل های قدیمی خودداری می کند .مفهوم squashing و نحوه عملکردش به شما امکان می دهد تا فایل های migration را در یک اپ در حال توسعه squash(همان فرایند squashing) کنید و آنها را خراب نکنید ! پیشنهاد جنگو این است که فایل های قدیمی را squash کنید و آنها را commit کنید و نسخه اولیه اپ را منتشر کنید . سپس منتظر بمانید تا همه ی سیستم های اپ بروزرسانی و تکمیل شوند و در آخر فایل های قدیمی را حذف کنید . تغییرات را commit کنید و دومین نسخه اپ را منتشر کنید .دستور squashmigrations برای شما فرایند squashing را انجام خواهد داد . این دستور یک app label دریافت می کند که نام اپ حاوی migration است و یک نام که نام فایل migration است که می خواهید تمام فایل ها در آن خلاصه شوند . برای مثال :$ ./manage.py squashmigrations myapp 0004
Will squash the following migrations:
    - 0001_initial
    - 0002_some_change
    - 0003_another_change
    - 0004_undo_something
Do you wish to proceed? [yN] y
Optimizing...
    Optimized from 12operations to 7operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001
_squashed_0004_undo_something.py
    You should commit this migration but leave the old ones inplace;
    the new migration will be used fornew installs. Once you are sure
    all instances of the codebase have applied the migrations you squashed,
    you can delete them.اگر می خواهید نام فایل migration که تمامی فایل ها در آن خلاصه می شوند را خودتان تنظیم کنید (جنگو آن را اتوماتیک تولید نکند) ,از squashmigrations --squashed-name استفاده کنید .البته لازم است که بگوییم اگر مدل های شما و dependencies هرکدام آنها بسیار پیچیده شوند ,ممکن است squashing یک migration تولید کند که اجرا نمی شود . ممکن است این فایل اشتباه بهینه شده باشد (البته این مورد را می توان با استفاده از –no-optimizeحل کرد) و یا ارور CircularDependencyError در آن وجود داشته باشد (این مورد را باید دستی حل کنید) .برای حل ارور CircularDependencyError ,یکی از ForeignKeyها را از حلقه ای که در dependencyها ایجاد شده است ,خارج کنید و آن را در یک migration دیگر بگذارید . سپس dependency را نیز به یک اپ دیگر منتقل کنید . اگر مطمئن نیستید ,ببینید makemigration در هنگام ساخت migration های جدید چگونه رفتار می کند . جنگو ,اعلام کرده است که در نسخه های بعدی دستور squashmigrations آپدیت می شود تا خودش این گونه خطاها را برطرف کند .هنگامی که migration خود را به اصطلاح squash کردید ,سپس آن را commit کنید و در کنار migration قدیمی قرار دهید (یا جایگزین کنید) . بعد از آن باید این تغییرات را روی تمامی اشیا فعال در اپ خود نیز اعمال کنید . یادتان نرود تا در انتها با migrate تغییرات را در دیتابیس نیز ثبت کنید .برای انتقال فایل migration که squash شده است به یک فایل migration معمولی مراحل زیر را دنبال کنید :حذف تمام فایل های migration که جایگزین شدند .بروزرسانی تمام فایل های migration ای که به فایل های حذف شده وابستگی دارند تا وابستگی خود را به فایل squash شده انتقال دهند .حذف ویژگی replaces از کلاس Migration در فایل squash شده . (این روشی است که جنگو تفاوت بین یک فایل squash شده و یک فایل معمولی migration را متوجه می شود)نکته : شما نمی توانید یک فایل squash شده را دوباره squash کنید (دوباره دستور squashmigrations را روی آن اجرا کنید) . تنها در صورتی می توانید این کار را انجام دهید که به طور کامل فایل را به یک فایل معمولی migration تبدیل کنید . (مراحل بالا)نکته : در جنگو 4.1 ,اگر بخواهید در آینده از نام یک فایل migration حذف شده ,برای یک فایل جدید استفاده کنید باید ارجاعات مربوط به آن در جدول migrations را حذف کنید . این کار را با اجرای دستور migrate –prune انجام بدهید .سریال سازی مقادیرسریال سازی یا serialization یک فرایند ترجمه کامپیوتری است . در آن ساختمان های داده به یک قالب تبدیل می شوند تا اطلاعات را ذخیره کنند یا بتوانند آنها را انتقال دهند . این فرایند در جنگو نیز انجام می گیرد .برای مثال ,فایل های migration در واقع فایل های پایتونی هستند که شامل ویژگی های مدل قدیمی شما می شوند . بنابراین جنگو برای نوشتن آنها باید وضعیت فعلی مدل شما را نیز داشته باشد . جنگو وضعیت کنونی مدل را بدست میاورد و برای ذخیره سازی و انتقال آن را سریال سازی می کند .البته ,یک سری از چیز ها در پایتون وجود دارند که به هیچ وجه نمی توانند سریال سازی شوند . یعنی هیچ استاندارد پایتونی برای اینکه مقادیر آنها را به یک کد بتوان تبدیل کرد ,وجود ندارد . (repr فقط برای مقادیر پایه کار می کند و import ها را مشخص نمی کند)با این حال جنگو نوع های داده ای زیر را می تواند سریال سازی کند :مقادیر int, float, bool, str, bytes, None, NoneTypeمقادیر list, set, tuple, dict, range.مقادیر datetime.date, datetime.time,  و اشیا  datetime.datetimeاشیا decimal.Decimalاشیا enum.Enum و enum.Flag(Flagفقط برای جنگو 4.1 موجود است)اشیا uuid.UUID(اینها تماما کلاس هایی در جنگو هستند)اشیا functools.partial و functools.partialmethod که مقادیر func و args و kwargs آنها نیز سریال سازی می شود .اشیا pure و concrete(اینها نوع هایی از اشیا هستند) از کتابخانه pathlib .البته نوع concrete به معادل pure خود تبدیل خواهد شد . برای مثال pathlib.PosixPath به pathlib.PurePosixPath تبدیل می شود .اشیا os.PathLike. برای مثال os.DirEntry که با استفاده از os.fspath به رشته یا بایت تبدیل می شود .اشیا LazyObjectانواع قابل شمارش ها مانند TextChoices یا IntegerChoicesهر فیلدی که در جنگو وجود دارد .هر تابع یا متد مورد استفاده مانند datetime.datetime.today که در اسکوپ سطح ماژول باشد .متد هایی که در بدنه کلاس غیرقابل استفاده باشند .هر ارجاعی به یک کلاس (در اسکوپ سطح ماژول)هرچیزی که در آن متد deconstruct باشد .جنگو موارد زیر را نمی تواند سریال سازی کند :کلاس های تودرتولامبدا ها (Lambda)اشیا کلاس های مطلق مانند MyClass(4.3 , 5.7)سریال سازی شخصی سازی شدهشما می توانید انواع داده ای دیگر را نیز سریال سازی کند . قبل از آن باید serializer را شخصی سازی کنید . برای مثال اگر جنگو مقدار داده Decimal را نمی تواند سریال سازی کند ,شما می توانید از کد زیر برای سریال سازی آن در migrationها استفاده کنید :from decimal import Decimal
from django.db.migrations.serializer import BaseSerializer
from django.db.migrations.writer import MigrationWriter

class DecimalSerializer(BaseSerializer):
    def serialize(self):
        return repr(self.value), {&amp;quotfrom decimal import Decimal&amp;quot}

MigrationWriter.register_serializer(Decimal, DecimalSerializer)اولین آرگومان در MigrationWriter.register_serializer یک نوع یا یک iterable از انواعی است که باید در سریال سازی استفاده شوند (در اینجا Decimal) .متد serialize از serializer باید یک رشته را بازگرداند . این رشته حاوی اطلاعاتی از نحوه نمایش این مقدار در migration و مجموعه ای از importهایی که برای migration نیاز دارید است .اضافه کردن متد deconstructشما می توانید با ایجاد متد deconstruct در کلاس ,نمونه های کلاس شخصی سازی شده خود را سریال سازی کنید . این متد هیچ آرگومانی را دریافت نمی کند و فقط یک تاپل حاوی سه عضو path , args , kwargs را بازمیگرداند .مقدار path :باید مسیر پایتون به کلاس باشد . در قسمت آخر مسیر نیز نام کلاس باید وجود داشته باشد . برای مثال my_app.sutom_things.MyClass. اگر کلاس شما در سطح ماژول در دسترس نیست ,نمی توانید آن را سریال سازی کنید .مقدار args :باید لیستی از positional arguments هایی باشد که قرار است به متد __init__ در کلاس ارسال شود . هر کدام از اعضای این لیست باید به خودی خود قابل سریال سازی باشند . (یعنی جزو لیستی که بالاتر گفتیم باشند)مقدار kwargs :باید دیکشنری ای از keyword arguments باشد که به متد __init__ در کلاس منتقل می شود . هر کدام از اعضای این لیست باید به خودی خود قابل سریال سازی باشند . (یعنی جزو لیستی که بالاتر گفتیم باشند)نکته : مقداری که از این متد بازمیگردد یک تاپل سه عضوی است و با متد deconstruct که در فیلد های سفارشی سازی شده استفاده می کنیم فرق دارد (آنها تاپل چهار عضوی را بازمیگردانند) .پس از تنظیم آرگومان ها ,جنگو مشابه روشی که ارجاعات به فیلد ها را می نویسید , شی ای از کلاس شما را نیز می نویسید .برای جلوگیری از ایجاد فایل های migration جدید پس از هر بار اجرای دستور makemigrations ,باید متد __eq__ را به دکوراتور (decorator) کلاس خود اضافه کنید . این تابع معمولا توسط فریمورک migration های جنگو فراخوانی می شود تا تغییرات میان مراحل توسعه را شناسایی کند .تا زمانی که آرگومان های مورد استفاده کلاس همگی قابل سریال سازی باشند , شما می توانید از deconstructible@ (مسیر آن در django.utils.deconstruct قرار دارد) برای اضافه کردن متد deconstruct استفاده کنید . برای مثال :from django.utils.deconstruct import deconstructible

@deconstructible
class MyCustomClass:
    def __init__(self, foo=1):
        self.foo = foo
        ...
    def __eq__(self, other):
        return self.foo == other.fooاین کد منطقی را برای گرفتن و نگه داری آرگومان ها در سازنده پیاده سازی می کند . سپس آن آرگومان ها را هنگامی که از deconstruct استفاده کنید ,بازمیگرداند .پشتیبانی جنگو از نسخه های مختلف migrationبه یاد داشته باشید که اگر از یک اپ third-party استفاده می کنید که سیستم migrations آن می تواند از چندین نسخه جنگو پشتیبانی کند ,همیشه از پایین ترین نسخه قابل پشتیبانی جنگو برای آن برای اجرای دستور makemigrations استفاده کنید .مانند نرم افزار های تولید محتوا ,جنگو با پشتیبانی رو به عقب سازگاری دارد . یعنی فایل های migration تولید شده در نسخه 4.1 جنگو می توانند بدون تغییر در نسخه جنگو 1.3 اجرا شوند (نسخه های بالاتر از نسخه های پایینتر پشتیبانی می کنند) . گرچه ممکن است فایل های migration تولید شده در جنگو 1.3 در جنگو نسخه 4.1 اجرا نشوند .در این جلسه در رابطه با بخشی از مباحث مربوط به migrations صحبت کردیم . در جلسه بعدی نیز به بررسی بخش های دیگری از آن خواهیم پرداخت . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sat, 16 Dec 2023 00:39:23 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و نه | بررسی API روابط دیتابیسی در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D9%86%D9%87-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-api-%D8%B1%D9%88%D8%A7%D8%A8%D8%B7-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3%DB%8C-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-yl8qt0vandoa</link>
                <description>در این جلسه api مربوط به روابط دیتابیسی را بررسی خواهیم کرد . با ما همراه باشید .آموزش جنگو : جلسه سی و نه | بررسی API روابط دیتابیسی در جنگواشیا روابطیبا اینکه از قبل توضیحاتی را درباره اشیا مرتبط (اشیایی که در آنها روابط وجود دارد) داده ایم ,اما بهتر است تا یک بخش کامل را به بررسی دوباره و تکمیلی آن بپردازیم تا بهتر در ذهن شما جا بیافتد .جنگو دارای یک کلاس با نام RelatedManager است . این کلاس شامل متد هایی است که برای ایجاد و دریافت ارتباطات میان مدل ها به کار می روند . کلاس RelatedManager یک manager نیز محسوب می شود. برای روابط one-to-many و many-to-many می تواند استفاده شود ,ولی فقط در دو صورت کار می کند :1-متد ها در آن سوی دیگر یک ForeignKey قابل دسترسی هستند . برای مثال :from django.db import models

class Blog(models.Model):
    # ...
    pass

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE, null=True)در مثال بالا ,متدها در manager ای با نام blog.entry_set قابل دسترس هستند .2-در دو سوی یک ManyToManyField قابل دسترس هستند . برای مثال :class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)در مثال بالا در چند manager با نام های topping.pizza_set و pizza.toppings متد ها , قابل دسترسی هستند .حال بیایید به بررسی متد ها بپردازیم . این متد ها می توانند از چندین manager که نام بردیم فراخوانی شوند . برای شما روی فیلد یک سری از تغییرات را اعمال می کنند و فقط در فیلد هایی وجود دارند که از نوع روابطی باشند . (دو نسخه synchronous و asynchronous آنها نیز در لیست زیر وجود دارد)1-متد add(*objs, bulk=True, through_defaults=None)یا aadd(*objs, bulk=True, through_defaults=None) : این متد اشیای دریافت شده را (objs*) به لیست اشیا درون فیلد اضافه می کند (رابطه ایجاد می کند) . برای مثال :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry.objects.get(id=234)
&gt;&gt;&gt; b.entry_set.add(e) # Associates Entry e with Blog b.در مثال بالا (حالتی که از یک ForeignKey استفاده کرده ایم) ,متد update که در مباحث QuerySet توضیح دادیم ,اجرا خواهد شد تا آپدیت جدید را انجام دهد و رابطه ای را میان شی یک و دو برقرار کند . البته باید اشیا از قبل ذخیره شده باشند .اگر از آرگومان bulk=False استفاده کنید ,می توانید مدیر مربوطه را مجبور کنید تا بجای متد QuerySet.update از save برای آپدیت استفاده کند (e.save) .به یاد داشته باشید که استفاده از add در یک رابطه many-to-many به هیچ وجه نمی گذارد تا save فراخوانی شود . این یعنی در این گونه از رابطه آرگومان bulk=False کار نمی کند . در این روابط فرایند آپدیت توسط ایجاد شدن روابط با متد QuerySet.bulk_create صورت می گیرد . اگر نیاز به شخصی سازی روش کار add برای روابط many-to-many دارید ,کافی است با سیگنال m2m_changed کار کنید . این سیگنال و روش کار با آن را بعدا توضیح می دهیم (pre_add و post_add نیز جزو مباحث کار با این سیگنال هستند) .نکته : استفاده از add در رابطه ای که از قبل وجود داشته باشد ,روابط را کپی نمی کند اما سیگنال ها را ارسال می کند.در روابط many-to-many نیز ,متد add اشیا مدل یا مقدار فیلد های آنها (معمولا primary key آنها) را در آرگومان objs* می پذیرد .آرگومان through_defaults نیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callable مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .2-متد create(through_defaults=None, **kwargs)یا acreate(through_defaults=None, **kwargs) : این متد یک شی جدید می سازد ,آن را ذخیره می کند و سپس با آن شی رابطه ایجاد می کند . نتیجه را با بازگرداندن شی ساخته شده ,نشان می دهد . برای مثال :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = b.entry_set.create(
...          headline=&amp;quotHello&amp;quot, body_text=&amp;quotHi&amp;quot, pub_date=datetime.date(2005, 1, 1)
...     )
# No need to call e.saveat this point -- it&#039;s already been saved.کد بالا را می توان به شکل زیر نیز نوشت :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry(blog=b, headline=&amp;quotHello&amp;quot, body_text=&amp;quotHi&amp;quot,
pub_date=datetime.date(2005, 1, 1))
&gt;&gt;&gt; e.save(force_insert=True)توجه داشته باشید که نیازی نیست تا kwargs** را در متد مقداردهی کنید . kwargs** برای تعیین اینکه رابطه با چه شی ای ایجاد شود به کار می رود . در مثال بالا ما پارامتر blog را برای create ارسال نکردیم . جنگو خودش متوجه می شود که باید فیلد blog در شی جدید از مدل Entry روی متغییر b(که یک شی است) تنظیم شود .آرگومان through_defaultsنیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callableها مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .3-متد remove(*objs, bulk=True) یا aremove(*objs, bulk=True) :یک شی را دریافت می کند و آن را از اشیا مرتبط آن فیلد حذف می کند (یعنی رابطه میان آنها را حذف می کند) . برای مثال :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry.objects.get(id=234)
&gt;&gt;&gt; b.entry_set.remove(e) # Disassociates Entry e from Blog b.مانند add , e.save نیز در مثال بالا برای انجام آپدیت فراخوانی شده است . با این حال استفاده از remove در یک رابطه many-to-many ,با متد QuerySet.delete اشیا مرتبط را حذف می کند که به این معنی است که هیچگاه متد save فراخوانی نمی شود . برای شخصی سازی فرایند کار متد remove ,با سیگنال m2m_changed کار کنید .آرگومان objs* , همان آرگومانی است که اشیایی که باید حذف شوند (مانند مثال بالا) را در آن قرار می دهید .برای ForeignKey ,این متد فقط زمانی می تواند مورد استفاده قرار بگیرد که null=True در فیلد تنظیم شده باشد. اگر فیلد مربوطه را نتوان روی None (Null) تنظیم کرد ,نمی توان یک شی را بدون اضافه شدن به یک رابطه دیگر , از یک رابطه حذف کرد . در مثال بالا ,حذف eاز b.entry_set معادل e.blog=None است و چون فیلد دارای null=True نیست ,اینکار انجام نمی شود .برای ForeignKey این متد یک آرگومان با نام bulk نیز دریافت می کند . اگر Trueباشد (پیش فرض) ,عملیات شما با QuerySet.update انجام می شود . اگر آن را برابر با False تنظیم کنید ,متد save روی هر شی مجزا فراخوانی می شود . گرچه قرار دادن آن روی False کمی عملکرد را سنگین تر می کند و سیگنال های pre_save و post_save را اجرا می کند .برای روابط many-to-many آرگومان bulk قابل دسترس نیست .4-متد clear(bulk=True) یا aclear(bulk=True) :این متد روابط همه ی اشیا مرتبط با فیلد را حذف می کند (دیگر با آن مدل رابطه نخواهند داشت). برای مثال :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entry_set.clearتوجه داشته باشید که این اشیا را حذف نمی کند ,فقط رابطه آنها را با مدل پاک می کند .درست مانند remove , clear نیز فقط زمانی می تواند در فیلد های ForeignKey مورد استفاده قرار بگیرد که null=True در آن فیلد تنظیم شده باشد . همچنین آرگومان bulk را نیز در این متد می توانید استفاده کنید .گرچه برای روابط many-to-many ,آرگومان bulk وجود ندارد .5-متد set(objs, bulk=True, clear=False, through_defaults=None) یا aset(objs, bulk=True, clear=False, through_defaults=None) : این متد برای شما اشیا مرتبط را با لیستی از اشیا جدید که به آن می دهید ,جایگزین می کند . برای مثال :&gt;&gt;&gt; new_list = [obj1, obj2, obj3]
&gt;&gt;&gt; e.related_set.set(new_list)متد set , یک آرگومان با نام clear دارد . اگر برابر با False باشد (پیش فرض) ,اشیا گمشده از لیست ورودی ,به وسیله متد remove حذف می شوند و فقط اشیا جدید اضافه می شوند . اگر clear برابر با False باشد ,متد clear بجای آن فراخوانی می شود و کل مجموعه اشیا به یکباره اضافه می شوند .برای فیلدهای ForeignKey ,آرگومان bulk نیز وجود دارد که مقادیر آن به add یا remove در حال استفاده ارسال می شود .برای روابط many-to-many آرگومان bulk غیر قابل دسترسی است .به یاد داشته باشید که استفاده از set باعث ایجاد شرایط race می شود . برای مثال ممکن است بین فراخوانی clear و add ,یک سری از اشیا جدید نیز به دیتابیس اضافه بشوند .آرگومان through_defaults نیز برای تعیین مقادیر و مقداردهی به اشیا مدل های میانی جدید استفاده می شود. این آرگومان مقدار خود را به صورت دیکشنری می پذیرد . البته می توانید از callable مانند توابع نیز به عنوان value در دیکشنری ورودی استفاده کنید . آنها قبل از ایجاد اشیا یکبار فراخوانی خواهند شد .نکته : تمام متد های add , aadd , create , acreate, remove , aremove , clear , aclear , set , aset همگی تغییرات دیتابیس را بلافاصله اعمال می کنند و نیازی به فراخوانی save برای هر دو طرف رابطه نیست .در ضمن اگر از متد prefetch_related استفاده کنید ,متد های add و aadd و remove و aremove و clear و aclear و set و aset کش ذخیره شده را حذف می کنند .در این جلسه در رابطه با چگونگی کار کردن روابط دیتابیسی در جنگو صحبت کردیم . در جلسه بعدی در رابطه با migrations صحبت خواهیم کرد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 20:11:54 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و هشت | بررسی Lookup API در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D9%87%D8%B4%D8%AA-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-lookup-api-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-xyjdojhimzsu</link>
                <description>در این جلسه به بررسی lookup api در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو : جلسه سی و هشت | بررسی Lookup API در جنگوکار کردن با 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 روی یک فیلد این کلاس استفاده می شود . (مثل exact در field_name__exact)بخش Transform :برای انجام عملیات transform روی یک فیلد از این کلاس استفاده می شود .بیایید به بررسی Lookup بپردازیم . هر عبارت lookup (مثال field_name__exact) که می نویسید ,دارای سه بخش است .بخش فیلد ها که ممکن است بخاطر روابط حتی به چند فیلد هم برسید (برای مثال Book.objects.filter(author__best_friends__first_name...))بخش transform که ممکن است حذف شود . این بخش برای تبدیل داده به کار می رود . برای مثال می تواند داده را به Lowercase تبدیل کند . (برای مثال lower__first3chars__reversed__)بخش lookup که به صورت پیش فرض برابر با exact است . (مثال دیگر icontains__)کار کردن با Registration APIمفهوم 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(&amp;quotdate_joined&amp;quot).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 Expression APIدر این بخش قرار است تمام متد هایی را بررسی کنیم که به وسیله آنها جنگو می تواند 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 باشد .کار کردن با TransformTransformیک کلاس 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است .کار کردن با LookupLookupیک کلاس genericاست که برای پیاده سازی lookupها به کار می رود . lookupیک عبارت است که در Queryقرار می گیرد و یک lookup_nameدارد که برای مقایسه از نوع Trueیا Falseبین lhs(سمت چپ Query) و rhs(سمت راست Query) به کار می رود . شکل کلی آن به صورت &lt;lhs&gt;__&lt;lookup_name&gt;=&lt;rhs&gt;است .Lookupها همچنین می تواند مستقیما در QuerySetو متد filterقرار بگیرند . برای مثال :Book.objects.filter(LessThan(F(&quot;word_count&quot;), 7500))یا حتی در متد annotateنیز استفاده شود .Book.objects.annotate(is_short_story=LessThan(F(&quot;word_count&quot;), 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 مربوط به آنها را نیز بررسی خواهیم کرد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 19:55:03 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و هفت | بررسی Field API در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D9%87%D9%81%D8%AA-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-field-api-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-bmsljyprcb4g</link>
                <description>در این جلسه در رابطه با api مربوط به فیلد ها در جنگو صحبت خواهیم کرد و چگونگی عملکرد فیلد ها در جنگو را بررسی می کنیم . با ما همراه باشید . آموزش جنگو : جلسه سی و هفت | بررسی Field API در جنگوبررسی API فیلداین بخش درباره API مربوط به فیلد ها است . یعنی قرار است متد های داخلی و فرایند عملکرد یک فیلد را با هم بررسی کنیم . همچنین بررسی می کنیم که یک فیلد دقیقا چیست . این مباحث مربوط به امور پیشرفته جنگو است .  پس اگر هنوز مرحله مقدماتی هستید ,نیازی به بررسی این بخش نخواهید داشت .در بخش های پیشین انواع فیلد ها را بررسی کرده بودیم . فیلد یک کلاس نوع abstract است که ستون های جدول دیتابیس را نشان می دهد . جنگو از فیلد ها (متد های درون کلاس آنها) برای ایجاد ساختار جدول دیتابیس (متد db_type) ,برای تبدیل نوع داده ای پایتون به نوع های درون دیتابیس (متد get_prep_value) و بالعکس (متد from_db_value) استفاده می کند .بنابراین فیلد یک قطعه اساسی است که از API های مختلف جنگو بهره برده است .در مدل ها یک فیلد به عنوان یک attribute یا ویژگی در کلاس تعریف می شود و در دیتابیس یک ستون خاص را نشون میدهد . همچنین مقادیری مانند null یا unique یا متد هایی را قبول می کند که باعث خاص شدن رفتار های فیلد (مثلا بتواند مقادیر خالی را بپذیرد) می شوند .یک فیلد یک زیر کلاس از کلاس RegisterLookupMixin است . این علتی است که شما می توانید Lookup و Transform را روی آن پیاده سازی کنید (برای مثال field_name__exact=&quot;foo&quot;) .همچنین علاوه بر فیلد های پیش فرض جنگو (مثلا JSONField یا CharField) می توانید بر اساس نیازتان فیلد های جدیدی را بسازید و از آنها استفاده کنید . بعدا درباره این کار توضیحاتی را ارائه خواهیم کرد . فعلا به بررسی کلاس ها و متد های درون یک فیلد خواهیم پرداخت . (از اینها بعدا در ساخت فیلد های custom استفاده می توانید کنید)1-آرگومان description :یک توضیح مفصل درباره فیلد را می توانید در آن تعریف کنید . برای مثال در django.contrib.admindocs استفاده می شود .یک Description می تواند به شکل زیر تعریف شود :description = _(&amp;quotString (up to %(max_length)s)&amp;quot)آرگومان های کد بالا نیز از __dict__ در فیلد پیدا و جایگذاری خواهند شد (مثل ویژگی max_length) .2-آرگومان descriptor_class :کلاسی که descriptor protocol(از مباحث پایتون است) را برای کلاس شما تنظیم می کند . این کلاس نمونه سازی (instantiated) می شود و به ویژگی های شی افزوده می شود . البته سازنده یک آرگومان که شی Field است را باید قبول کند . بازنویسی این ویژگی می تواند به بازنویسی رفتار های get و set کلاس کمک کند .جنگو برای تبدیل نوع های داده ای میان پایتون و دیتابیس چندین متد را ارائه می کند :3-متد get_internal_type :این متد رشته ای را می سازد که فیلد شما را برای عملکرد های پشت صحنه (داخلی) جنگو نامگذاری کرده است . به طور پیش فرض نام کلاس را بازمی گرداند . (در مبحث شبیه سازی فیلد های پیش فرض برای استفاده در فیلد های سفارشی این مورد را استفاده می کنیم)4-متد db_type(connection) :این متد با در نظر گرفتن connection یا بهتر بگوییم وابسته به دیتابیسی که استفاده می کنید , نوع داده ای ستون دیتابیس (فیلد) را بازمیگرداند . (در مبحث انواع داده های خاص برای فیلد های سفارشی این مورد را استفاده می کنیم)5-متد rel_db_type(connection) : این متد با در نظر گرفتن connection یا بهتر بگوییم وابسته به دیتابیسی که استفاده می کنید , نوع داده ای فیلد را (فیلدهایی مانند OneToOneField یا ForeignKey) بازمیگرداند .(در مبحث انواع داده های خاص برای فیلد های سفارشی این مورد را استفاده می کنیم) .در اصل سه موقعیت اصلی وجود دارد که جنگو نیاز دارد تا ارتباطی را میان فیلد ها و دیتابیس برقرار کند . یعنی داده ها را میان آنها جا به جا کند تا اتفاقی رخ دهد . این سه موقعیت عبارتند از :هنگامی که یک Query اجرا می کند (داده ها از پایتون به دیتابیس ارسال می شوند)هنگامی که از دیتابیس داده ای را بازمیگرداند (داده ها از دیتابیس به پایتون ارسال می شوند)هنگامی که چیزی را در دیتابیس ذخیره می کند (داده ها از پایتون به دیتابیس ارسال می شوند)در هنگام اجرای Query دو متد get_db_prep_value و get_prep_value استفاده می شوند .6-متد get_prep_value(value) :تعریف value در اینجا مقدار کنونی آن ویژگی مدل (فیلد) است و این متد داده ها را در قالبی که برای استفاده در Query (به عنوان پارامتر) آماده شده اند ,بازمیگرداند . (در مبحث تبدیل اشیا پایتونی به مقادیر Query این مورد را استفاده می کنیم) .نکته : همگی مباحث جلوتر توضیح استفاده خواهند شد و از این متد ها برای ساخت فیلد های شخصی سازی شده یا custom استفاده می کنیم .7-متد get_db_prep_value(value , connection , prepared=False) :نوع مقادیر یک Query با نوع مقادیر درون دیتابیس ها تفاوت دارند . این در صورتی است که مقدار های آنها کاملا یکسان است ! این متد نوع مقدار یک Query را به نوع مقداری که در دیتابیس وجود دارد تبدیل می کند . به طور پیش فرض اگر prepared=True باشد ,خود value یا مقدار را بازمیگرداند و در غیر این صورت متد get_prep_value را بازمیگرداند . (در مبحث تبدیل مقدار Query به مقادیر دیتابیس این مورد را استفاده می کنیم) .هنگام لود کردن داده ها از دیتابیس متد from_db_value استفاده می شود .8-متد from_db_value(value, expression, connection) :این متد مقداری که از دیتابیس لود شده است را تبدیل به یک شی پایتون می کند . دقیقا برعکس کاری که get_prep_value انجام می دهد . درباره چندی از این متدها قبلتر صحبت کردیم . (در مبحث تبدیل مقادیر دیتابیس به مقادیر پایتون این مورد را استفاده می کنیم) .البته این متد در بیشتر فیلد های داخلی (فیلد هایی که خود جنگو دارد) استفاده نمی شود زیرا خود موتور دیتابیس نوع صحیح داده ای برای پایتون را بازمیگرداند .و اینکه آرگومان expression در واقع همان self است .نکته : به دلایل عملکردی ، from_db_value در فیلدهایی که به آن نیاز ندارند (همه فیلدهای جنگو) استفاده نشده است . در نتیجه شما ممکن است در تعریف خود super را فراخوانی نکنید . در ذخیره شدن اشیا یا رکورد ها در دیتابیس نیز متد های pre_save و get_db_prep_save استفاده می شوند .9-متد get_db_prep_save(value, connection) : این متد مانند get_db_prep_value است ,اما فقط زمانی فراخوانی می شود که داده ها نیاز به سیو شدن در دیتابیس دارند (اگر یادتان باشد سیو و ایجاد در جنگو با یک روش انجام می شوند) . به طور پیش فرض get_db_prep_value را بازمیگرداند .10-متد pre_save(model_instance, add) :در این قسمت درباره متد pre_save صحبت خواهیم کرد و باید مراقب باشید که متد را با با سیگنال آن اشتباه نگیرید . این متد قبل از get_db_prep_save فراخوانی می شود تا مقدار یا value را برای ذخیره شدن آماده کند (برای مثال زمان اتوماتیک در جنگو که با DateField.auto_now تعریف می شود با این متد مقداردهی می شود) .آرگومان model_instance در واقع شی ای را می پذیرد که فیلدی که باید مقداردهی شود , در آن قرار گرفته است. آرگومان add نیز نشان می دهد که آیا این شی برای اولین بار در دیتابیس قرار است ذخیره شود یا خیر .این متد باید مقدار مناسب را با توجه به ویژگی های شی و مقدار model_instance برای فیلد بازگرداند . نام ویژگی به طور خودکار توسط Field اضافه خواهد شد و در self.attname قابل دسترسی است . (در مبحث پردازش داده قبل از ذخیره شدن این متد را استفاده می کنیم)فیلد ها گاهی اوقات مقادیر خود را به نوع های متفاوتی دریافت می کنند . برای مثال از طریق فرم ها یا serialization (از serialization در یکی از فریمورک های مربوط به جنگو استفاده می شود) . حال بیایید متد های مربوط به این مباحث را نیز بررسی کنیم .11-متد to_python(value) :یک value را به یک شی پایتونی تبدیل می کند . این دقیقا برعکس عملکرد متد value_to_string است و همچنین در تابع clean نیز از آن استفاده شده است . (در مبحث تبدیل مقادیر به اشیا پایتون از این متد استفاده می کنیم)علاوه بر فرم ها و نحوه ذخیره سازی داده ,یک فیلد باید بداند که چگونه داده ها را serialize کند .نکته : serialize مبحثی مربوط به فریمورک rest جنگو است . اگر هنوز آشنایی با این مباحث ندارید ,از این بخش رد شوید .12-متد value_from_object(obj) :مقدار فیلد را با توجه به شی دریافت شده (آرگومان obj) بازمیگرداند . این متد اغلب توسط value_to_string استفاده می شود .13-متد value_to_string : شی دریافت شده را به رشته تبدیل می کند . این فرایند برای serialization بسیار کاربردی است . (در مبحث تبدیل داده های فیلد برای serialization این مورد را استفاده خواهیم کرد)هنگام استفاده از مدل فرم ها ,باید فیلد بداند که از کدام form field استفاده کند . برای این کار یک متد وجود دارد :14- متد formfield(form_class=None, choices_form_class=None, **kwargs) : فیلد پیش فرض مناسب برای ModelForm را از django.forms.Field بازمیگرداند .به طور پیش فرض اگر form_class و choices_form_class برابر با None باشد ,فیلد CharField را بازمیگرداند.اگر هم فیلد دارای choices و choices_form_class باشد که مقداردهی نشده ,از یک فیلد TypedChoiceField استفاده می کند . (در مبحث تعیین فرم فیلد برای یک مدل فرم این مورد را استفاده خواهیم کرد)15-متد deconstruct :یک تاپل حاوی 4 اطلاعات مهم برای ساختن دوباره فیلد را بازمیگرداند :نام فیلد در مدلمسیری که نوع فیلد در آن قرار دارد (برای مثال django.db.models.IntegerField)یک لیست حاوی آرگومان های positionalیک دیکشنری از آرگومان های keywordاین متد باید به فیلد های قبل از 1.7 اضافه شود تا داده های آن با استفاده از Migrations منتقل شود .ویژگی های فیلد (Field attributes)هر فیلد علاوه بر متد هایی که دارد ,دارای یک سری از ویژگی های خاص نیز می باشد . این ویژگی ها امکان بررسی درونی رفتار آن را فراهم می کنند . زمانی که نیاز به نوشتن کدی دارید که به عملکرد یک فیلد بستگی دارد ، به جای بررسی شی از این ویژگی ها استفاده کنید . این ویژگی ها را می توان همراه با Model._meta API  برای محدود کردن فیلد ها و یا گسترش آنها استفاده کرد . (یعنی فیلد های سفارشی جدید ایجاد کرد)1-ویژگی Field.auto_created : یک Boolean که نشان می دهد آیا فیلد به طور خودکار ایجاد شده است یا خیر. فیلدهایی مانند OneToOneField ایجاد شده توسط وراثت مدل مقدار True را برای این ویژگی دارند .2-ویژگی Field.concrete :یک Boolean که نشان می دهد آیا ستونی در جدول دیتابیس با این فیلد مرتبط است (رابطه ایجاد شده است ,برای مثال رابطه OneToOne) یا خیر .3-ویژگی Field.hidden :یک Boolean که نشان می دهد آیا از یک فیلد در عملکرد یک فیلد دیگر (که non-hidden است) استفاده می شود یا خیر . برای مثال ,فیلد های content_type و object_id که یک GenericForeignKey را تشکیل می دهند . این ویژگی برای تشخیص زیرمجموعه ای از فیلدها نیز به کار می رود.نکته : متد Options.get_fields به طور پیش فرض فیلدهای hidden را exclude می کند . برای اینکه بتوانید فیلد های hidden را نیز در این متد استفاده کنید , include_hidden=True را درون متد استفاده کنید .4-ویژگی Field.is_relation :یک Boolean است که نشان می دهد آیا یک فیلد حاوی ارجاعات روابطی به فیلد یا مدل های دیگر است یا خیر . برای مثال حاوی ForeignKey یا ManyToManyField یا OneToOneField یا …5-ویژگی Field.model :مدلی را بازمیگرداند که حاوی فیلد شماست . اگر فیلد در یک superclass از مدل ساخته شده باشد,این ویژگی به superclass اشاره خواهد کرد و نه به کلاسی که شی دارد .علاوه بر تمام اینها هر فیلد ویژگی هایی را نیز برای روابط خود دارد . این ویژگی ها برای ذخیره جزییات یک رابطه وجود دارند . این ویژگی ها در تمام فیلد ها نیز وجود دارد . اگر فیلد از نوع رابطه ای باشد (مانند OneToOneField) آنها فقط مقادیر Boolean را بجای None خواهند داشت .1-ویژگی Field.many_to_many :اگر فیلد دارای یک رابطه چند به چند (many to many) باشد ,برابر با True خواهد بود . در غیر این صورت برابر با False خواهد بود . تنها فیلد موجود در جنگو که در آن این ویژگی برابر با True است ManyToManyField است .2-ویژگی Field.many_to_one : اگر فیلد دارای یک رابطه چند به یک (many to one) باشد ,برابر با Trueخواهد بود . در غیر این صورت برابر با False خواهد بود . تنها فیلد موجود در جنگو که در آن این ویژگی برابر با True  است ForeignKey است.3-ویژگی Field.one_to_many :اگر فیلد دارای یک رابطه یک به چند (one to many) باشد ,برابر با True خواهد بود . در غیر این صورت برابر با False خواهد بود . تنها فیلدهای موجود در جنگو که در آن این ویژگی برابر با True  است ForeignKey معکوس یا GenericRelation است.4-ویژگی Field.one_to_one :  اگر فیلد دارای یک رابطه یک به یک (one to one) باشد ,برابر با True خواهد بود. در غیر این صورت برابر با False خواهد بود . تنها فیلد موجود در جنگو که در آن این ویژگی برابر با True است OneToOneField است .5-ویژگی Field.related_model :به مدلی اشاره می کند که فیلد به آن رابطه ای ایجاد کرده است . برای مثال مدل Author در ForeignKey(Author, on_delete=models.CASCADE) . همچنین همیشه برای یک GenericForeignKey ,مقدار related_model برابر با None است .6-ویژگی Field.is_relation :این ویژگی نشان می دهد که آیا یک فیلد از نوع رابطه ای است یا خیر . فیلد های رابطه ای شامل ForeignKey , ManyToManyFieldو OneToOneField هستند . اگر فیلد از نوع رابطه ای باشد برابر با Trueقرار داده می شود .در این جلسه نحوه عملکرد و مدیریت یک فیلد در جنگو را بررسی کردیم و تمامی ویژگی های یک فیلد در بک اند جنگو را دیدیم . در جلسه بعدی به بررسی api لوکاپ ها خواهیم پرداخت . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 19:14:42 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و شش | بررسی Model API در جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D8%B4%D8%B4-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-model-api-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-oww1jlnmbpiv</link>
                <description>در این جلسه در رابطه با نحوه عملکرد مدل ها در جنگو صحبت می کنیم و api مربوط به آنها را بررسی می کنیم. با ما همراه باشید . آموزش جنگو : جلسه سی و شش | بررسی Model API در جنگوقبل از اینکه وارد این مبحث شویم , بیایید یک متد دیگر که در سری قبلی مقالات توضیحات کامل آن نبود , را بررسی کنیم .  توابع Aggregationمتد aggregate را به یاد داشته باشید . سینتکس استفاده از آن به شکل زیر بود .&gt;&gt;&gt; q = Blog.objects.aggregate(Count(&#039;entry&#039;))این متد برای شما مقدارهایی از یک فیلد در آن مدل را جمع آوری می کرد و برای شما آن را محاسبه می کرد . این محاسبه میتواند میانگین آنها ,جمع آنها یا تعداد آنها (مانند مثال بالا) یا بزرگترین و کوچکترین مقادیر در میان آنها باشد . برای استفاده از هر کدام کافی است از توابع آن استفاده کنیم . در کد بالا از تابع مربوط به شمارش (Count) استفاده کردیم .نکته : در SQLite شما نمی توانید aggregate را روی فیلد های DateTimeField و هر فیلد مربوط به تاریخ انجام دهید . زیرا این دیتابیس اصلا از تاریخ پشتیبانی نمی کند و فیلد های مربوط به تاریخ توسط جنگو به متن تبدیل می شوند و سپس در دیتابیس ذخیره می شوند . تلاش برای استفاده از aggregate روی این نوع فیلد های منجر به ارور می شود .توجه داشته باشید که اگر یک QuerySet خالی باشد (درون آن شی ای وجود نداشته باشد) متد aggregate مقدار None را بازمیگرداند .اکنون توابع دیگر که می توانیم به شکل بالا از آنها برای محاسبات مقادیر استفاده کنیم را بررسی می کنیم .-مقدار Avg :مقدار میانگین از مقادیر فیلد ها را بازمیگرداند . خروجی به صورت float خواهد بود .-مقدار Count :تعداد اشیا و مقادیر را برای شما بازمیگرداند .-مقدار Max :بالاترین مقدار را پیدا می کند و برای شما بازمیگرداند .-مقدار Min :کمترین مقدار را پیدا می کند و برای شما بازمیگرداند .-مقدار Sum : جمع مقادیر را برای شما بازمیگرداند .-مقدار variance :اختلاف مقادیر را برای شما بازمیگرداند .نکته : برای مثال اگر شما از یک QuerySet خالی استفاده کنید ,تابع Sum به شما None را بازمیگرداند . ولی یک استثنا درباره تابع Count وجود دارد . برعکس دیگر توابع , Count عدد 0 را بجای None بازمیگرداند .البته اینجا مبحث aggregation را خلاصه باز کردیم و بعدا بیشتر به بحث درباره آن خواهیم پرداخت .اشیا در یک مدلدر این بخش ,به بررسی API مدل ها می پردازیم و توضیح می دهیم که جنگو کارهای خود را در دیتابیس چگونه پیش می برد . دانستن این رویکرد ها به شما کمک می کند تا جنگو را شخصی سازی کنید . تمام مثال های این بخش نیز مربوط به کد های بخش قبل است که نوشته ایم . (کد های مربوط به مدل های Entry و غیره)ساخت اشیابرای ساخت یا ایجاد یک شی در جنگو ,فقط کافی است کلاس مدل را نمونه سازی یا instantiate کنید . بعد از فراخوانی متد save از آن کلاس ,شی شما در دیتابیس ثبت خواهد شد و یک رکورد جدید ساخته خواهد شد .Class Model(**kwargs) :کد بالا را نگاه کنید , آرگومان ها و kwargs در هر مدل نام فیلد هایی است که در آن تعریف کردید . توجه داشته باشید نمونه سازی از مدل به تنهایی باعث ذخیره چیزی در دیتابیس نمی شود و این عملیات فقط بعد از save اتفاق میافتد .نکته :هر کلاس در پایتون یک تابع __init__ همراه خود دارد . شاید بخواهید تا برای شخصی سازی مدل , این تابع را override کنید . اگر اینکار را انجام دادید مراقب باشید تا آسیبی به مدل وارد نشود . تغییر در فرایند کارکرد مدل ممکن است باعث ذخیره نشدن نمونه ها در دیتابیس شود . به هر حال ,بهتر است بجای override کردن __init__ سعی کنید از روش های زیر استفاده کنید .1-یک classmethod به کلاس خود اضافه کنید . (cls همیشه باید به عنوان آرگومان ارسال شود)from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    @classmethod
    def create(cls, title):
        book = cls(title=title)
       # do something with the book
       return book

book = Book.create(&amp;quotPride and Prejudice&amp;quot)در کد بالا ما یک تابع سفارشی (شخصی سازی) نوشتیم و از آن استفاده کردیم .2-تابع سفارشی را در یک manager بنویسید . (این روش توصیه می شود)class BookManager(models.Manager):
    def create_book(self, title):
        book = self.create(title=title)
       # do something with the book
        return book
    
class Book(models.Model):
    title = models.CharField(max_length=100)
    objects = BookManager
    book = Book.objects.create_book(&amp;quotPride and Prejudice&amp;quot)البته که راه حل دوم نیاز به آشنایی با چگونگی ساخت manager ها دارد و در ادامه با آن بیشتر آشنا می شویم . به طور خلاصه کافی است یک کلاس که از models.Manager ارث بری می کند را به عنوان manager تعریف کنید و سپس در مدل اصلی objects را با کلاس manager مقداردهی کنید .لود شدن اشیا از دیتابیسجنگو در هر مدل متدی با نام from_db نیز دارد . در واقع هنگامی که جنگو داده ها را از دیتابیس دریافت می کند . داده ها به شکل خام هستند . بر فرض مثال که داده ها به این صورت باشند :رکورد اول : {&#x27;id&#x27;: 14, &#x27;name&#x27;: &#x27;foo&#x27;}رکورد دوم : {&#x27;id&#x27;: 25, &#x27;name&#x27;: &#x27;bar&#x27;}جنگو این داده های خام را به کمک متد from_db تبدیل به model obejcts می کند . در واقع اطلاعات خام به اشیا تبدیل می شوند و به شما تحویل داده می شوند . هر بار که یک QuerySet اجرا می شود این متد داده های دیتابیس را به اشیا تبدیل خواهد کرد . رکورد های بالا به شکل زیر به شما تحویل داده می شوند (همان نتیجه QuerySet ها) :SomeModel.from_db(&#039;db-alias&#039;, [&#039;id&#039;, &#039;name&#039;], [14, &#039;foo&#039;])
SomeModel.from_db(&#039;db-alias&#039;, [&#039;id&#039;, &#039;name&#039;], [25, &#039;bar&#039;])این متد قابل شخصی سازی شدن است و میتواند override شود .متد from_db سه آرگومان دریافت می کند . آنها به ترتیب db , field_names و values نام دارند .آرگومان db همان نام مستعار دیتابیس یا db-alias است . دیتابیسی که قرار است داده ها از آن بارگیری شوند . آرگومان field_names شامل نام تمامی فیلد های بارگذاری شده است . در آخر آرگومان values نیز شامل مقادیر فیلد هایی است که در field_names وجود دارند . همه ی مقادیر به ترتیب قرار گرفتن فیلد ها در field_names قرار می گیرند . به همین علت تضمین می شود که مقادیر به ترتیبی باشند که __init__ آنها را لازم دارد . این دلیلی است که می توانید با cls(*values) یک نمونه ایجاد کرد (cls مخفف class است) .اگر روی هر کدام از فیلد ها defer فراخوانی شود ,در field_names نام آن فیلد قرار نخواهد گرفت . در آن صورت مقدار django.db.models.DEFERRED به هر کدام از آن فیلد ها اختصاص خواهد یافت .علاوه بر این متد from_db کارایی های دیگری نیز برای تنظیم state_ دارد . در صفحه بعد مثالی وجود دارد که نشان می دهد چگونه این متد مقادیر فیلد ها را از دیتابیس دریافت می کند و سپس تبدیل به مقادیر خروجی (model objects) می کند .from django.db.models import DEFERRED

@classmethod
def from_db(cls, db, field_names, values):
    # Default implementation of from_db(subject to change and could
    # be replaced with super).
    if len(values) != len(cls._meta.concrete_fields):
        values = list(values)
        values.reverse
        values = [
            values.pop if f.attname in field_names else DEFERRED
            or f in cls._meta.concrete_fields
        ]
    instance = cls(*values)
    instance._state.adding = False
    instance._state.db = db
    # customization to store the original field values on the instance
    instance._loaded_values = dict(
        zip(field_names, (value for value in values if value is not DEFERRED))
    )
    return instance

def save(self, *args, **kwargs):
    # Check how the current values differ from ._loaded_values. For example,
    # prevent changing the creator_id of the model. (This example doesn&#039;t
    # support cases where &#039;creator_id&#039; is deferred).
    if not self._state.adding and (
            self.creator_id != self._loaded_values[&#039;creator_id&#039;]):
        raise ValueError(&amp;quotUpdating the value of creator isn&#039;t allowed&amp;quot)
    super.save(*args, **kwargs)مثال بالا یک پیاده سازی کامل از متد from_db انجام می دهد تا چگونگی کارکرد آن را توضیح دهد . شما می توانید برای ساده سازی از super نیز استفاده کنید .ریلود شدن اشیااگر یک فیلد از یک شی را حذف کنید ,دسترسی مجدد به آن باعث می شود تا آن مقدار از دیتابیس دوباره بارگیری شود (ریلود شود) . برای مثال :&gt;&gt;&gt; obj = MyModel.objects.first
&gt;&gt;&gt; del obj.field
&gt;&gt;&gt; obj.field # Loads the field from the databaseجنگو برای ریلود کردن مقادیر درون یک شی ,متدی با نام refresh_form_db دارد . البته این متد ها فقط درون کلاس وجود دارند و برای override کردن آنها ,این موضوعات را توضیح می دهیم . با تنظیم دوباره این متد ها میتوانید مدل خود را شخصی سازی کنید تا طبق انتظار شما کار کند .هنگامی که این متد از یک کلاس فراخوانی می شود ,فرایند های زیر اتفاق میافتد :1-تمام فیلد هایی که در defer قرار نگرفته باشند ,مقادیر خود را ریلود و بروزرسانی می کنند .2-هر رابطه (فیلد های روابطی) که کش شده باشد ,از شی پاک می شود .فقط فیلد های مدل شما ,از دیتابیس ریلود و بارگذاری مجدد می شوند . سایر مقادیر مانند فیلد هایی که در annotations هستند ,دوباره بارگذاری نمی شوند . در ضمن ,هیچ کدام از ویژگی و attribute های @cached_property نیز پاک نمی شود .این متد دو آرگومان را می پذیرد . آرگومان اولی using نام دارد . این آرگومان مقدار نام دیتابیس مورد استفاده برای بارگیری مجدد یا ریلود داده ها را دریافت می کند و باعث می شود ریلود شدن شی از آن دیتابیس اتفاق بیافتد . به طور پیش فرض بارگیری مجدد از دیتابیسی که شی از آن دریافت شده ,انجام می شود .آرگومان بعدی fields است . این آرگومان مجموعه ای از نام فیلد ها را دریافت می کند که فقط آنها را لازم است ریلود کند.به عنوان مثال برای نوشتن یک تست که صحیح بودن فراخوانی update را در کد تایید کند ,تست زیر را می توانید بنویسید . (فعلا به نحوه استفاده از refresh_from_db نگاه کنید . بعدا درباره تست نویسی مفصل صحبت می کنیم)def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F(&#039;val&#039;) +1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object&#039;s updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db
    self.assertEqual(obj.val, 2)وقتی که به یک فیلد که در داخل متد defer قرار گرفته بود دسترسی پیدا کنید ,لود شدن مقدار آن فیلد از طریق همین متد انجام می شود . بنابراین امکان سفارشی کردن نحوه لود شدن مقادیر فیلد هایی که در متد defer قرار گرفته شده اند ,وجود دارد . برای مثال کد زیر نشان می دهد که چگونه تمام فیلدهای یک شی را هنگام بارگیری مجدد یک فیلد به تاخیر افتاده (فیلدی که در متد defer باشد) لود کنیم :class ExampleModel(models.Model):
    def refresh_from_db(self, using=None, fields=None, **kwargs):
        # fields contains the name of the deferred field to be
        # loaded.
        if fields is not None:
            fields = set(fields)
            deferred_fields = self.get_deferred_fields
            # If any deferred field is going to be loaded
            if fields.intersection(deferred_fields):
            # then load all of them
            fields = fields.union(deferred_fields)
        super.refresh_from_db(using, fields, **kwargs)در کد بالا از یک متد دیگر با نام get_deferred_fields استفاده کردیم . این متد یک متد کمکی است که یک ست حاوی تمام attribute های تمام فیلد های به تاخیر افتاده را بازمیگرداند .اعتبارسنجی اشیاچهار مرحله برای اعتبارسنجی یک مدل وجود دارد . اعتبارسنجی یعنی درستی و صحیح بودن مقدار های فیلد و مدل را بررسی می کنید .-متد Model.clean_fields تمام فیلد های مدل را اعتبارسنجی می کند .-متد Model.clean تمام یک مدل را اعتبارسنجی می کند .-متد Model.validate_unique , unique و یکتا بودن مقادیر در فیلد ها را اعتبارسنجی می کند .-متد Model.validate_constraints برای محدودیت های اعمال شده بر مدل ,مدل را اعتبارسنجی می کند .هر چهار متد زمانی با هم فراخوانی می شوند و کل مدل و فیلد ها را اعتبارسنجی می کنند که متد full_clean را از مدل فراخوانی کنید .به یاد داشته باشید که در هنگام استفاده از ModelForm و فراخوانی is_valid در آنها (قبلا در فصل اول این ها را بررسی کردیم) ,تمام این متد ها برای اعتبارسنجی داده ها و فیلد ها فراخوانی می شوند . فقط در صورتی از متد full_clean استفاده کنید که می خواهید خطاها و ارور های اعتبارسنجی فیلد ها را خودتان مدیریت کنید یا فیلد هایی را که به اعتبارسنجی نیاز دارند را از ModelForm حذف کردید (در واقع آنها را در exclude قرار دادید) .نکته : به یاد داشته باشید بعضی از فیلد ها مانند JSONField ممکن است هیچ اروری از طرف اعتبارسنجی جنگو به شما بازنگردانند . زیرا بعضی از ارور های مربوط به key , index و path transforms در خود دیتابیس اتفاق میافتند و فقط در لاگ ها (log =گزارش های ارورها) قابل مشاهده هستند .از این رو حتما بررسی کنید که هیچ پیام خطایی در logger جنگو (در مسیر django.db.models قرار دارد) وجود نداشته باشد .قبل تر درباره متدی با نام full_clean صحبت کردیم . قبل از بررسی آن به شکل کلی این متد نگاهی بیندازید:Model.full_clean(exclude=None, validate_unique=True, validate_constraints=True)این متد برای مدل شما تمامی متد های مربوط به اعتبار سنجی را (همه ی 4 مورد) اجرا می کند . به ترتیب clean_fields ,سپس clean و بعد validate_unique (اگر validate_unique برابر با True باشد) و در آخر validate_constraints (اگر validate_constraints برابر با Trueباشد) اجرا می شوند .در صورت فراخوانی تمامی متد ها و وجود ارور یک ValidationError بازگردانده می شود که حاوی یک message_dict است . این تمامی خطاها و ارور ها را در هر چهار مرحله درون خود ذخیره می کند .این متد و تابع یک آرگومان با نام exclude را نیز قبول می کند . البته این آرگومان اختیاری است . در واقع یک set حاوی نام فیلد ها را قبول می کند و آنها را exclude یا جدا می کند (آنها را اعتبارسنجی نمی کند) . از این ویژگی در ModelForm نیز استفاده می شود تا فیلد هایی که در فرم نیستند ,اعتبارسنجی نشوند . دلیل اعتبارسنجی نشدن آن فیلدها این است که اگر خطایی داشته باشند توسط کاربر نمی تواند اصلاح شود (اصلا آن فیلد ها در فرم نیستند که کاربر آنها را اصلاح کند!) .به یاد داشته باشید که با فراخوانی متد save هیچ اعتبارسنجی صورت نمی گیرد . در صورتی که نیاز داشتید باید خودتان مدل را اعتبارسنجی کنید . البته به شکل زیر :from django.core.exceptions import ValidationError

try:
    article.full_clean
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    passالبته در ModelForm خود این مراحل اتوماتیک انجام می شود . بعدا با فرم ها بیشتر آشنا خواهید شد . حال بهتر است تک تک 4 متد فراخوانی شده را به صورت جداگانه مورد بررسی قرار دهیم .-متد clean_fields(exclude=None) :این متد تمامی فیلد های مدل را (به جز آنهایی که exclude یا جداسازی شده اند) اعتبارسنجی می کند .در اعتبارسنجی فیلد ها بررسی می شود که مقادیر مخرب یا خارج از فرمت فیلد (int , str , …) ,وارد فیلد نشده باشد .-متد Model.clean :این متد را فقط زمانی بازنویسی کنید که نیاز دارید اعتبارسنجی بر مدل شما انجام شود . این یعنی برای مثال یک فیلد را در هنگام اعتبارسنجی قبل از save بطور اتوماتیک مقداردهی کنید . یا برای مثال چندین فیلد را بر اساس مقادیر یکدیگر اعتبارسنجی کنید . به مثال زیر نگاه کنید که چگونه این کار را انجام دادیم:import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

class Article(models.Model):
    ...
    def clean(self):
    # Don&#039;t allow draft entries to have a pub_date.
    if self.status == &#039;draft&#039; and self.pub_date is not None:
        raise ValidationError(_(&#039;Draft entries may not have a publication date.&#039;))
        # Set the pub_date for published items if it hasn&#039;t been set already.
    if self.status == &#039;published&#039; and self.pub_date is None:
        self.pub_date = datetime.date.todayبا این حال توجه داشته باشید که قبل از save باید خودتان به صورت دستی متد clean را فراخوانی کنید و با فراخوانی save به تنهایی متد clean اجرا نمی شود .به یاد دارید که گفتیم هر ارور به شکل یک دیکشنری به شما نشان داده می شود ؟ در کد بالا نیز اگر مقادیر اشتباه وارد شود ارور ValidationError به شما بازگشت داده می شود که درون یک str ذخیره شده است . در دیکشنری , key آن ارور برابر با NON_FIELD_RRORS است . شما نیز می توانید از آن استفاده کنید . برای مثال :from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

try:
    article.full_clean
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]کد بالا از مدل شما ایراد خواهد گرفت و به هیچ فیلدی اشاره نخواهد کرد . برای حل این مشکل کافی است ValidationError را به شکل یک دیکشنری بسازید و key را در آن برابر با اسم فیلد ها قرار دهید . می توانیم مثال بالا را بازسازی کنیم تا ارور به فیلد pub_date اختصاص داده شود . (یعنی در پیغام خروجی به فیلد pub_date اشاره کند)class Article(models.Model):
    ...
    def clean(self):
        # Don&#039;t allow draft entries to have a pub_date.
        if self.status == &#039;draft&#039; and self.pub_date is not None:
            raise ValidationError({&#039;pub_date&#039;: _(&#039;Draft entries may not have a publication date.&#039;)})
    ...اگر ارور ها مربوط به چند فیلد باشند می توانید از ValidationError به شکل یک دیکشنری استفاده کنید . Keyها در آن نام فیلد و value در آن ارور مربوط به آن فیلد هستند . برای مثال :raise ValidationError({
    &#039;title&#039;: ValidationError(_(&#039;Missing title.&#039;), code=&#039;required&#039;),
    &#039;pub_date&#039;: ValidationError(_(&#039;Invalid date.&#039;), code=&#039;invalid&#039;),
})بعد از این متد ,متد full_clean برای شما محدودیت های unique را بررسی می کند . قبل از آن ,این نکته را بخوانید.نکته : به نظر شما اگر فیلد هایی دچار ارور شده باشند که در ModelForm نمایش داده نمی شوند ,چگونه باید آنها را نشان دهیم؟در واقع در متد Model.clean شما نمی توانید ارورها را برای فیلد هایی که قابل نمایش نیستند , raise کنید (فرم ممکن است فیلد های خود را به وسیله Meta.fields و یا Meta.exclude محدود کند) . انجام این کار یک ValueError ایجاد می کند .برای حل این مشکل ,کافی است متد Model.clean_fields را بازنویسی کنید و ارور ها را در آن ایجاد کنید . زیرا این متد حتی به فیلد هایی که exclude (جدا شده اند و نمایش داده نمی شوند) شده اند نیز دسترسی دارد . برای مثال :class Article(models.Model):
    ...
    def clean_fields(self, exclude=None):
        super.clean_fields(exclude=exclude)
    if self.status == &#039;draft&#039; and self.pub_date is not None:
        if exclude and &#039;status&#039; in exclude:
            raise ValidationError(
            _(&#039;Draft entries may not have a publication date.&#039;) )
        else:
            raise ValidationError({ &#039;status&#039;: _(
            &#039;Set status to draft if there is not a &#039; &#039;publication date.&#039; ),
            })-متد Model.validate_unique(exclude=None) :این متد شباهت زیادی به متد clean_fields دارد . اما در واقع محدودیت های فیلد ها را در زمینه یکتا یا unique بودن مقادیر آنها بررسی می کند . شما می توانید از طریق Field,unique , Field.unique_for_date , Field.unique_for_month و Field.unique_for_year یا Meta.unique_together یک مدل یا فیلد را unqiue کنید . این یعنی مقادیر تکراری در آن نمی تواند ثبت شود .این متد نیز بررسی می کند و اجازه ثبت مقادیر تکراری در یک جدول در دیتابیس را به شما نمی دهد . هر مقدار باید یکبار در ردیف تکرار شود .آرگومان exclude نیز می تواند تعدادی نام فیلد را قبول کند . فیلد های قرار گرفته در این آرگومان , مورد بررسی قرار نخواهند گرفت .اگر هر کدام از فیلد ها نیز در اعتبار سنجی با ارور مواجه شود (برای مثال مقدار تکراری در آن وارد شود) ,ارور ValidationError ایجاد می شود .البته توجه داشته باشید که فقط محدودیت های مربوط به unqiue بودن در این متد بررسی می شود و محدودیت های دیگر مروبط به مدل در متد دیگری با نام Model.validate_constraints بررسی خواهد شد .در نهایت متد full_clean ,متد Model_validate_constraints را برای بررسی محدودیت های دیگر مدل اجرا خواهد کرد .-متد Model.validate_constraints(exclude=None) : این متد به تازگی در جنگو 4.1 اضافه شد . وظیفه این متد بررسی محدودیت های مدل است که در گزینه های متا و در متغییر constraints تعریف شده است . آرگومان exclude نیز نام فیلد هایی را قبول می کند و روی آنها بررسی برای محدودیت را انجام نمی دهد . در آخر ,اگر یکی از محدودیت ها رعایت نشده باشد ,دچار ارور ValidationError می شوید .ذخیره سازی (سیو کردن) اشیابرای ذخیره کردن یک شی در دیتابیس کافی است متد save را از شی فراخوانی کنید . سینتکس کلی دستور save به شکل زیر است :Model.save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)کمی جلوتر درباره force_insert و دیگر آرگومان ها بیشتر صحبت می کنیم . همچنین این متد می تواند بازنویسی شود تا طبق انتظار شما رفتار کند . در بخش زیر درباره ریزه کاری های کار با save صحبت خواهیم کرد .تعیین خودکار primarykeyیکی از کارهایی که متد save وظیفه انجام آن را بر عهده دارد ,تعیین مقدار primarykey به صورت اتوماتیک است (id) .در واقع اگر مدل شما یک AutoField داشته باشد (مانند فیلد id) ,مقدار آن به صورت خودکار محاسبه خواهد شد و اولین باری که متد save را روی یک شی فراخوانی می کنید ,آن مقدار را در فیلد مربوطه وارد خواهد کرد . مقادیر همیشه افزایش پیدا خواهند کرد . برای مثال 1 و 2 و 3 و 4 و ...برای مثال :&gt;&gt;&gt; b2 = Blog(name=&#039;Cheddar Talk&#039;, tagline=&#039;Thoughts on cheese.&#039;)
&gt;&gt;&gt; b2.id   # Returns None, because b2 doesn&#039;t have an ID yet.
&gt;&gt;&gt; b2.save
&gt;&gt;&gt; b2.id  # Returns the ID of your new object.به صورت پیش فرض در یک مدل همیشه فیلد id وجود دارد . نوع این فیلد یک AutoField است . تنها در صورتی این فیلد ایجاد نخواهد شد که در یکی از فیلد ها primary_key را برابر با True قرار دهید .نکته : راهی برای تعیین مقدار یک id هنگام فراخوانی save وجود ندارد . زیرا مقدار آن توسط دیتابیس محاسبه می شود و نه توسط جنگو .همچنین یک ویژگی با نام pk برای primarykey در جنگو ایجاد شده است . فرقی ندارد که فیلد primarykey را خودتان بسازید (به یک فیلد primary_key=True اختصاص دهید) یا بگذارید جنگو اتوماتیک آن را ایجاد کند (فیلد id) , در هر دو حالت شما به یک مقدار با نام pk دسترسی خواهید داشت که به primarykey آن مدل (هر فیلدی که باشد) اشاره می کند . این مقدار در حالت پیش فرض برابر با فیلد id خواهد بود .تعیین مقادیر primarykey به صورت دستیاگر یک مدل دارای AutoField باشد ,می توانید به صورت دستی id را در هنگام ساخت شی ,تعیین کنید . برای مثال :&gt;&gt;&gt; b3 = Blog(id=3, name=&#039;Cheddar Talk&#039;, tagline=&#039;Thoughts on cheese.&#039;)
&gt;&gt;&gt; b3.id  # Returns 3.
&gt;&gt;&gt; b3.save
&gt;&gt;&gt; b3.id  # Returns 3.اگر مقادیر primarykey را به صورت دستی (مانند بالا) تعیین کنید ,باید مطمئن باشید مقداری که می خواهید به آن اختصاص دهید ,تکراری نباشد . در واقع تمامی فیلد های primarykey ,ویژگی unique=True دارند . اگر شما تلاش کنید یک مقدار قبلا استفاده شده را به فیلد اختصاص دهید , جنگو فکر می کند که می خواهید رکورد حاوی مقدار را (رکورد قدیمی) بازنویسی کنید .با توجه به مثال Cheddar Talk در بالا , مثال زیر رکورد قبلی را در دیتابیس بازنویسی می کند :b4 = Blog(id=3, name=&#039;Not Cheddar&#039;, tagline=&#039;Anything but cheese.&#039;)
b4.save # Overrides the previous blog with ID=3!تعیین دستی مقدار primarykey زمانی مفید است که بخواهید اشیایی را به صورت انبوه بسازید . در آن صورت مطمئن خواهید بود که primarykey تکراری نخواهند بود .فرایند عملکرد متد saveدر این بخش درباره این صحبت خواهیم کرد که وقتی متد save را روی یک شی فراخوانی می کنید ,دقیقا چه مراحلی طی می شود تا شی در دیتابیس ذخیره شود . 5 مرحله در هنگام فراخوانی این مت اتفاق میافتد :1-یک سیگنال pre-save ارسال می شود . درباره سیگنال ها بعدا صحبت خواهیم کرد اما در این حد بدانید که می توانید برای مثال با استفاده از سیگنال یک تابع را در زمان ذخیره شدن یک شی , اجرا کند .2-سپس داده ها پردازش می شوند . در این مرحله متد pre_save روی هر فیلد فراخوانی می شود . وظیفه این متد تغییر داده های درون فیلدها در صورت نیاز است . برای مثال اگر فیلد شما datefield باشد و auto_now_add = True باشد ,این متد برای شما مقدار کنونی زمان را در فیلد وارد می کند .3-در این مرحله داده ها برای دیتابیس آماده می شوند . متد get_db_prep_svae در این مرحله برای هر فیلد اجرا می شود . این متد از هر فیلد می خواهد تا مقدار فعلی خود را در نوع داده ای (int , str , json , …) که می تواند در دیتابیس ذخیره شود ,ارائه دهد .اکثر فیلد ها به این مرحله نیازی ندارند . انواع داده ای مانند str یا int آماده ثبت شدن در دیتابیس هستند . با این حال برخی از انواع داده های پیچیده تر , نیاز به این مرحله ویژه خواهند داشت .به عنوان مثال ,فیلد های DateField از شی datetime در پایتون برای ذخیره سازی تاریخ در خود استفاده می کنند . دیتابیس ها نمی توانند اشیا پایتونی را در خود ذخیره کنند پس باید تاریخ شما تبدیل به یک رشته مطابق با ISO بشود . این مرحله برای شما اینکار را انجام می دهد .4-داده های شما در قالب یک دستور SQL به دیتابیس وارد می شوند و ذخیره می شوند .5-یک سیگنال post-save ارسال می شود .نکته : سیگنال های مربوط به save دو دسته هستند . Pre-save (قبل از ذخیره شدن شی) و post-save (بعد از ذخیره شدن شی) که هر بار در زمان خود ارسال می شوند . با توجه به این سیگنال ها می توانید در بعد یا قبل از ذخیره شدن یک شی یک عملیات را با استفاده از یک تابع انجام دهید . برای مثال قبل از ذخیره شدن یک شی , یک شی دیگر در دیتابیس را به صورت اتوماتیک حذف کنید ! در جلوتر آن را بیشتر بررسی می کنیم .نکته : متد های get_db_prep_save و pre_save مربوط به فیلد ها هستند و از خود هر فیلد فراخوانی می شوند.تشخیص اتوماتیک تفاوت UPDATE و INSERT در جنگوتا الان باید متوجه شده باشید که جنگو چه برای آپدیت و چه برای ایجاد شی از یک متد استفاده می کند (متد save) . در واقع جنگو دستورات INSERT و UPDATE در SQL را در یک متد خلاصه کرده است . به طور کلی زمانی که یک defaultبرای primary key شی تعیین نشده باشد و متد saveفراخوانی شود ,جنگو از الگوریتم زیر پیروی می کند :اگر primary key مقداردهی شود و مقدار آن None یا رشته خالی نباشد ,جنگو یک UPDATE را اعمال می کند .اگر primary key مقداردهی نشود و یا UPDATE شی ای را آپدیت نکند (برای مثال primary key روی مقداری تنظیم شود که در دیتابیس نیست) ,جنگو دستور INSERT را اجرا می کند .در صورتی که یک default برای primary key در متد save تعیین شود ,جنگو همین فرایند بالا را انجام می دهد و در صورتی که مقدار primary key در دیتابیس وجود داشته باشد ,دستور UPDATE و در غیر این صورت دستور INSERT را اعمال می کند .یک نکته مهم این است که حتما باید primary key یک مقدار بگیرد که در دیتابیس استفاده نشده است . در صورتی که از این بابت مطمئن نیستید ,اجازه بدهید جنگو خودش برای شما primary key را اتوماتیک مقدار دهی کند (مثل فیلد id) .در نسخه های 1.5 و قدیمی تر جنگو ,ابتدا یک دستور SELECT روی primary key انجام می شد . اگر این عملیات یک ردیف را پیدا می کرد , جنگو یک UPDATE انجام می داد و در غیر این صورت یک INSERT انجام می داد . الگوریتم قدیمی دارای خطاهایی بود و علاوه بر آن باعث می شد تا یک Query اضافه نیز تولید شود . موارد نادری باعث ایجاد خطا می شدند و دیتابیس ممکن بود در آنها اصلا گزارش ندهد که یک ردیف آپدیت شده است (برای مثالPostgreSQL ON UPDATE trigger) ! گرچه استفاده از این الگوریتم توصیه نمی شود اما برای استفاده از آن کافی است select_on_save را روی True قرار دهید . بعدا درباره این صحبت خواهیم کرد .بررسی force_update و force_insertدر برخی از شرایط بسیار نادر ,لازم است متد save را مجبور کنیم که حتما یک دستور INSERT یا حتما یک دستور UPDATE انجام دهد (در واقع نمی خواهیم جنگو تصمیم بگیرد از کدام استفاده کند) . برای مجبور کردن آن برای دستور INSERT از force_insert=True و برای UPDATE از force_update=True استفاده می کنیم . هر دو پارامتر را با هم نمی توانید استفاده کنید , زیرا نمی توان همزمان هم آپدیت کرد و هم یک شی را به دیتابیس وارد کرد .این پارامتر ها فقط برای استفاده پیشرفته مناسب است . همچنین عملکرد متد update_fields و پارامتر force_update تقریبا شبیه یکدیگر است .آپدیت کردن یک فیلد بر اساس فیلدی دیگرقبل از بررسی عنوان این بخش به این مورد توجه داشته باشید . گاهی از وقت ها نیاز دارید تا یک عملیات حسابی (مانند حمع یا تفریق یا ضرب) روی یکی از فیلد ها انجام دهید و مقدار جدید را در فیلد وارد کنید (آپدیت کنید) . این کار را با سینتکس ساده پایتون انجام می دهید :&gt;&gt;&gt; product = Product.objects.get(name=&#039;Venezuelan Beaver Cheese&#039;)
&gt;&gt;&gt; product.number_sold +=1
&gt;&gt;&gt; product.saveاگر مقدار قبلی number_sold برابر با 10 باشد ,تبدیل به عدد 11 می شود و مقدار 11 در دیتابیس ذخیره می شود .مثال بالا می تواند سریعتر و قوی تر نیز نوشته شود . کافی است برای این کار از عبارات F استفاده کنید . با استفاده از F می توانید مثال بالا را به شکل زیر بنویسید (توصیه می شود) :&gt;&gt;&gt; from django.db.models import F
&gt;&gt;&gt; product = Product.objects.get(name=&#039;Venezuelan Beaver Cheese&#039;)
&gt;&gt;&gt; product.number_sold = F(&#039;number_sold&#039;) +1
&gt;&gt;&gt; product.saveتعیین فیلد ها برای سیو شدندر این بخش می خواهیم درباره این صحبت کنیم که چگونه به متد save بفهمانیم که فقط کدام فیلد ها را سیو کند (عملیات سیو را روی آنها انجام دهد) . برای مثال ,اگر در متد save آرگومان update_fields را مقداردهی کنید و به آن یک لیست از نام فیلد هایی خاص را بدهید , فقط همان فیلد ها آپدیت خواهند شد .در مواردی که بخواهید فقط چند فیلد بخصوص آپدیت شوند ,این آرگومان مهم است . با جلوگیری از بروزرسانی تمامی فیلد های یک مدل و استفاده از update_fields یک عملکرد جزئی بهتر دریافت می کنید :product.name = &#039;Name changed again&#039;
product.save(update_fields=[&#039;name&#039;])مقداری که به این آرگومان ارسال می کنید باید یک iterable حاوی رشته ها (همان نام فیلدها) باشد . یک iterable خالی باعث می شود مرحله سیو شدن رد شود و ارسال مقدار None به این آرگومان به معنی آپدیت همه فیلد ها است .همچنین استفاده از این آرگومان به این معنی است که آپدیت اجباری انجام خواهد شد و دستور INSERT قرار نیست مورد استفاده قرار بگیرد . (مانند force_update)نکته دیگر این است که اگر یک فیلد در defer قرار گرفته باشد یا به اصطلاح به تعویق افتاده باشد و شما مقدار آن را تغییر دهید یا بروز کنید ,آن فیلد به update_fields در پشت صحنه اضافه می شود . در اصل اگر تغییری در مقدار فیلد انجام ندهید , فقط فیلد هایی که قبلا در defer قرار نگرفته اند ,آپدیت می شوند (یعنی اگر هیچ کاری با فیلد های به تعویق افتاده نداشته باشید) .نکته :به یاد داشته باشید اگر از آرگومان update_fields استفاده کنید ,جنگو به صورت اتوماتیک متد pre_save را فقط برای فیلد های نام برده درون آرگومان فراخوانی می کند . این یعنی اگر یک فیلد DateField داشته باشید که auto_now=True باشد ,آپدیت نمی شود (auto_now کار نمی کند) مگر اینکه نام فیلد درون آرگومان update_fields باشد .حذف اشیابرای حذف اشیا یا رکورد ها از دیتابیس کافی است از متد delete روی شی استفاده کنید . ساختار کلی متد به صورت زیر است :Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False)به مثال زیر نگاه کنید که چگونه یک شی را پیدا می کنیم و آن را حذف می کنیم :instance = SomeModel.objects.get(id=id)
instance.deleteهمچنین می توانید آن را به صورت مستقیم حذف کنید .SomeModel.objects.filter(id=id).deleteدر اصل یک دستور DELETE در SQL ایجاد می کند و شی را حذف می کند . این متد فقط شی (رکورد) را از دیتابیس حذف می کند و این یعنی شی ای که در پایتون وجود دارد (در حافظه است) , هنوز وجود دارد ! شما می توانید به فیلد های آن دسترسی پیدا کنید . گرچه فیلد primary key آن روی None تنظیم خواهد شد .پس از استفاده از این متد تعداد اشیا حذف شده و تعداد حذف ها در هر نوع شی در قالب یک دیکشنری به شما بازگردانده می شود . که البته در قبل تر راجب آن مفصل صحبت کردیم .اگر می خواهید رفتار حذف شدن یک رکورد یا شی را توسط این متد تغییر دهید , کافی است آن را در مدل مربوطه بازنویسی کنید .آرگومان using در این متد تعیین می کند که از کدام دیتابیس استفاده کند . (درباره چند دیتابیسی و این آرگومان بعدا نیز توضیح می دهیم)آرگومان دیگر keep_parents است که اگر برابر با True قرار داده شود ,در ارث بری های multi-table یا چند جدولی باعث می شود فقط داده های مدل فرزند حذف شود و داده های مدل والد حفظ شود .متد های دیگر اشیامتد های دیگری نیز در یک شی وجود دارند که باید درباره آنها صحبتی داشته باشیم . متد های پیشین باعث می شدند تا با فراخوانی آنها عملیاتی روی شی انجام شود . حال این متد ها را نگاه کنید :1-متد __str__ :این متد زمانی اجرا می شود که شما str را روی یک شی فراخوانی کنید . در واقع کاری که انجام می دهد تعیین یک نام برای همان شی است . (در ادمین پنل نام شی ها در لیست آنها به وسیله همین متد نمایش داده می شوند) به علت اینکه جنگو از این متد در نمایش نام اشیا در ادمین پنل استفاده می کند ,همیشه برای آن یک مقدار قابل خواندن برای انسان و قابل فهم ایجاد کنید .برای مثال :(باید معمولا این متد در هر مدل بازنویسی شود)from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    def __str__(self):
        return &#039;%s %s&#039; % (self.first_name, self.last_name)نکته : در حالت عادی این متد مقدار نام مدل + object + یک عدد (مانند id آن) بازمیگرداند . مثلا Person object12-متد __eq__ :این متد در اشیا عادی پایتون نیز وجود دارد . وظیفه آن overload کردن عملگر == برای اشیا است تا بتونید بررسی کنید که آیا دو شی با یکدیگر مساوی هستند یا خیر . در جنگو نیز این متد وجود دارد . در جنگو این متد اشیایی که مقدار primary key آنها برابر باشد و از یک کلاس اصلی باشند ,برابر در نظر می گیرد . با این تفاوت که اگر primary key برابر با None باشد ,به جز خودش برابر با شی دیگری نیست .برای مدل هایی که ارث بری نوع proxy دارند ,کلاس اصلی برابر با اولین والد غیر پروکسی است . در بقیه مدل ها , کلاس اصلی برابر با کلاس مدل است . در کد زیر نحوه کارکرد این متد را می بینید :(نکته مهم اینکه این متد و چند متد دیگر تنها برای درک کردن چگونگی کار جنگو توضیح داده می شوند و لازم به بازنویسی ندارند)from django.db import models

class MyModel(models.Model):
    id = models.AutoField(primary_key=True)

class MyProxyModel(MyModel):
    class Meta:
        proxy = True

class MultitableInherited(MyModel):
    pass
    # Primary keys compared

MyModel(id=1) == MyModel(id=1)
MyModel(id=1) != MyModel(id=2)
# Primary keys are None
MyModel(id=None) != MyModel(id=None)
# Same instance
instance = MyModel(id=None)
instance == instance
# Proxy model
MyModel(id=1) == MyProxyModel(id=1)
# Multi-table inheritance
MyModel(id=1) != MultitableInherited(id=1)3-متد __hash__ :در اشیا عادی پایتون نیز این متد وجود دارد . وظیفه آن در فرایند هش شدن شی تاثیر دارد . در واقع تعیین می کند کدام ویژگی از شی باید هش بشود . در جنگو نیز این متد قرار داده شده است . به صورت عادی مقدار فیلدی که primary key باشد در هش شدن استفاده خواهد شد (یعنی hash(obj.pk)) . اگر primary key مقدار دهی نشده باشد یک TypeError بازگردانده می شود .متد __hash__ مقادیر مختلفی را قبل و بعد از سیو شدن شی برمی گرداند، اما تغییر مقدار __hash__ یک شی در پایتون ممنوع است.4-متد get_absolute_url :این متد در اشیا عادی پایتون وجود ندارد و مخصوص به جنگو است . همچنین بسیار پرکاربرد است و بهتر است آن را خوب یاد بگیرید . فرض کنید یک مدل مربوط به پست های یک وبلاگ را ساختید . هر شی در این مدل در واقع یک پست از وبلاگ شماست . این متد استفاده می شود تا هر شی از مدل (در اینجا هر پست در وبلاگ) یک url داشته باشد . یعنی وقتی این متد را فراخوانی کردید url یا آدرس صفحه مربوط به آن شی بازگردانده شود.منظور ما از url آن شی ,در واقع آدرس مربوط به ویویی است که می خواهید . بر فرض مثال اگر این متد را بر اساس id اشیا تنظیم کرده باشید و در آدرسی با نام post ,ویو مربوط به آن را نوشته باشید , url مربوط به شی با id 12 برابر با post/12/ خواهد بود .با نوشتن این متد به جنگو خواهید فهماند که چطور url مربوط به هر شی محاسبه شود . هنگامی که رشته ای که حاوی url است بازگردانده می شود ,از طریق HTTP می تواند کاربر را به یک ویو یا صفحه از جنگو هدایت کند . برای مثال :def get_absolute_url(self):
    return &amp;quot/people/%i/&amp;quot % self.idاگر چه کد بالا بسیار واضح و ساده است و کار می کند ,اما بهترین راه استفاده از تابع reverse است .5-متد Reverse :این تابع برای شما url تولید می کند . در واقع name مربوط به url شما را (در فایل urls.py) دریافت می کند و در ادامه kwargs ارسال شده را درون url جایگذاری می کند . (بعدا به بررسی کامل آن می پردازیم)برای مثال :def get_absolute_url(self):
    from django.urls import reverse
    return reverse(&#039;people-detail&#039;, kwargs={&#039;pk&#039; : self.pk})از کاربرد های متعدد این متد می توان به ادمین پنل اشاره کرد . اگر این متد برای یک مدل تعریف شود ,در صفحه لیست اشیا آن مدل (در ادمین پنل) شما یک لینک View on site را مشاهده خواهید کرد . این لینک همان url تولید شده در این متد است و شما را مستقیما به آن آدرس هدایت خواهد کرد .همچنین بسیاری از کاربرد های دیگر نیز برای این متد وجود دارد . برای مثال در هنگام استفاده از syndication feed framework باید متد get_absolute_url تعریف شود . اگر منطقی است که نمونه های مدل شما هر کدام یک url  منحصر به فرد داشته باشند، باید get_absolute_url را تعریف کنید .نکته : شما باید از ساخت url در این متد بر اساس ورودی کاربر خودداری کنید . در این صورت یک هکر می تواند به راحتی url های مخرب در سایت شما ایجاد کند . برای مثال اگر متد زیر را نوشته باشید :def get_absolute_url(self):
    return &#039;/%s/&#039; % self.nameاگر self.name برابر با /example.com باشد (یعنی برای مثال این مقدار را از کاربر بگیرد) , این متد مقدار //example.com را بازمیگرداند که باز هم به نوبه خود معتبر است اما اگر برای مثال مقدار /%2Fexample.com/ بازگردانده شود ,اصلا آدرس معتبری نیست .همچنین استفاده از get_absolute_url در تمپلیت ها (templates) توصیه می شود . برای مثال کد زیر یک کد بد در تمپلیت است :&lt;!-- BAD template code. Avoid! --&gt;
&lt;a href=&amp;quot/people/{{ object.id }}/&amp;quot&gt;{{ object.name }}&lt;/a&gt;برای اصلاح این کد می توانید از متد get_absolute_url استفاده کنید :&lt;a href=&amp;quot{{ object.get_absolute_url }}&amp;quot&gt;{{ object.name }}&lt;/a&gt;دلیل بهینه بودن این روش این است که اگر بخواهید یک تغییر کوچک در url خود بدهید ,می توانید بدون اینکه انجام تغییر در همه تمپلیت هایی که از آن استفاده کردید , آن را تغییر دهید . یعنی یکبار url را ایجاد می کنید و هر چندبار که خواستید از آن استفاده می کنید .نکته : در رشته ای که متد get_absolute_url بازمیگرداند فقط کاراکتر های ASCII مجاز هستند . یا اینکه اگر نیاز است می توانند encode شوند . (در این لینک درباره کاراکتر های مجاز در url بیشتر توضیح داده شده است : https://datatracker.ietf.org/doc/html/rfc2396.html#section-2)کدهایی که متد get_absolute_url را فراخوانی می کنند باید بدون نیاز به پردازش شدن ,بتوانند از نتیجه استفاده کنند . اگر نیز در urlشما کاراکتر هایی به غیر از محدوده ASCII بود ,شاید نیاز باشد از تابع django.utils.encoding.iri_to_uri استفاده کنید . (در اینترنت درباره آن بیشتر جستجو کنید)6-متد get_FOO_display :این متد فقط برای اشیایی است که در آنها یک فیلد حاوی choices تعریف شده باشد . در این متد FOO با نام فیلد حاوی choices جایگذاری می شود . قسمت خوانا توسط کاربر یا human-readable مجموعه ی choices با استفاده از این متد بازگردانده می شود . به مثال زیر نگاه کنید .from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        (&#039;S&#039;, &#039;Small&#039;),
        (&#039;M&#039;, &#039;Medium&#039;),
        (&#039;L&#039;, &#039;Large&#039;),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)&gt;&gt;&gt; p = Person(name=&amp;quotFred Flintstone&amp;quot, shirt_size=&amp;quotL&amp;quot)
&gt;&gt;&gt; p.save
&gt;&gt;&gt; p.shirt_size
&#039;L&#039;
&gt;&gt;&gt; p.get_shirt_size_display
&#039;Large&#039;7-متد get_next_by_FOO(**kwargs) : این متد یک ورژن دیگر با نام get_previous_by_FOO نیز دارد . همچنین فقط برای مدل هایی کار می کند که فیلد DateField یا DateTimeField در آنها تعریف شده است . توجه کنید که نباید null=True در آن نوع از فیلد ها وجود داشته باشد . FOO برابر با نام فیلد در نظر گرفته می شود (فیلد DateField یا DateTimeField ) . همانطور که از نام آن مشخص است متد get_next_by_FOO شی بعدی و get_previous_by_FOO شی قبلی را (که بر اساس تاریخ مرتب شده اند) بازمیگرداند . در صورت خطا یک ارور DoesNotExist بازگردانده می شود .هر دوی این متد ها عملیات هایشان را با manager پیش فرض انجام می دهند . اگر نیاز داشته باشید می توانید از lookup در kwargs** استفاده کنید تا جستجو های خود را دقیق تر کنید .اگر دو شی با تاریخ برابر وجود داشته باشند ,متد ها از primary key آنها برای مقایسه استفاده می کنند . این یعنی هیچ شی ای رد یا تکراری نمی شود . همچنین این یعنی نمی توانید این متد ها را برای اشیایی که هنوز save روی آنها فراخوانی نشده استفاده کنید .نکته : در بیشتر موارد بازنویسی متد های get_FOO_display , get_next_by_FOO , get_previous_by_FOO مشکلی ندارد و کار می کند . ولی از آنجایی که این متد ها توسط کلاس Meta اضافه می شوند ,باید در بعضی از شرایط متد Field.contribute_to_class بازنویسی شود تا بتوانید به متد های دیگر دسترسی داشته باشید .8مقدار state_ : این در واقع یک ویژگی در شی شماست . به یک شی ModelState (یک کلاس در جنگو است که اشیا آن وضعیت مدل را زیر نظر می گیرند) اشاره می کند که وضعیت کنونی شی را نشان می دهد .هر شی ModelState دو ویژگی درون خود دارد . یک flag با نام adding که اگر هنوز شی شما در دیتابیس ذخیره نشده باشد برابر با True خواهد بود . و یک db که یک رشته حاوی database alias یا نام مستعار دیتابیسی است که شی از آن بارگیری می شود .برای مثال اشیایی که تازه ساخته شده اند دارای adding=True و db=None هستند . در صورتی که اشیایی که ذخیره شده باشند دارای adding=False و db که مقدار آن database alias است ,هستند .در این جلسه نحوه مدیریت مدل ها و عملیات های آنها توسط جنگو را بررسی کردیم . در جلسه بعدی api مربوط به فیلد ها را بررسی می کنیم و نحوه کارکردن آنها را نیز بررسی می کنیم .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 18:12:51 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و پنج | بررسی تکمیلی لوکاپ ها (Field lookups)</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D9%BE%D9%86%D8%AC-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%AA%DA%A9%D9%85%DB%8C%D9%84%DB%8C-%D9%84%D9%88%DA%A9%D8%A7%D9%BE-%D9%87%D8%A7-field-lookups-aovcvwyhecw8</link>
                <description>در این جلسه به بررسی Field lookups در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو : جلسه سی و پنج | بررسی تکمیلی لوکاپ ها (Field lookups)بررسی لوکاپ ها (Field lookups)بسیار خب ,در قسمت پیشین بیشتر متد هایی که میشد از آنها استفاده کرد را معرفی کردیم . اکنون می خواهیم درباره lookup های یک فیلد صحبت کنیم . Lookup ها به زبانی ساده همان کلمات اضافه ای هستند که با __ به نام فیلد اضافه می شوند (برای مثال field__exact) و به شما امکان می دهند تا جستجوی پیشرفته تری را روی دیتابیس انجام دهید . در واقع آنها چگونگی ایجاد دستور WHERE در SQL را تغییر می دهند . درباره آنها قبلا مقداری صحبت کردیم . حال میخواهیم تک تک آنها را بررسی کنیم .نکته : lookup ها به صورت پیش فرض روی exact هستند . یعنی اگر lookup را استفاده نکنید (Entry.objects.get(id=1))exact برای شما فعال می شود .-لوکاپ exact : به معنای همخوانی دقیق است . یعنی دقیقا مقداری که وارد کرده اید برای مقایسه و فیلتر استفاده می شود . برای مثال اگر مقدار وارد شده ‘hello’ باشد ,دقیقا بدنبال ‘hello’ در دیتابیس خواهد گشت . به یاد داشته باشید که این lookup , Case-sensitive است . یعنی به حروف بزرگ و کوچک حساس است .اگر مقدار None را برای آن تنظیم کنید بدنبال مقادیر NULL در دیتابیس خواهد بود . به مثال های زیر نگاه کنید . مثال های اول شکل پایتونی کد و مثال های دوم کد SQL تولید شده برای آن است .Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)SELECT ... WHERE id =14;
SELECT ... WHERE id IS NULL;-لوکاپ iexact : همان exact است اما به حروف بزرگ و کوچک حساسیت ندارد . این یعنی مقادیری مانند Hello و یا heLlo نیز پذیرفته خواهند شد .  اگر مقدار None را تنظیم کنید بدنبال مقادیر NULL در دیتابیس خواهد گشت . برای مثال :Blog.objects.get(name__iexact=&#039;beatlesblog&#039;)
Blog.objects.get(name__iexact=None)SELECT ... WHERE name ILIKE &#039;beatlesblog&#039;;
SELECT ... WHERE name IS NULL;نکته : در SQLite مراقب باشید . این دیتابیس تطبیق غیر حساس به حروف بزرگ و کوچک را برای رشته های غیر ASCII انجام نمی دهد .-لوکاپ contains :مقادیری را بازمیگرداند که در آنها پارامتر وارد شده وجود داشته باشد . به حروف بزرگ و کوچک نیز حساس است . برای مثال :Entry.objects.get(headline__contains=&#039;Lennon&#039;)SELECT ... WHERE headline LIKE &#039;%Lennon%&#039;;برای مثال مقادیر Lennon is good یا Lennon is okay چون کلمه Lennon در آنها وجود دارد ,پذیرفته خواهند شد .نکته : در SQLite , contains مانند icontains رفتار می کند (از لحاظ حساسیت به حروف بزرگ و کوچک) .-لوکاپ icontains :همان contains است اما به حروف بزرگ و کوچک حساس نیست . برای مثال :Entry.objects.get(headline__icontains=&#039;Lennon&#039;)SELECT ... WHERE headline ILIKE &#039;%Lennon%&#039;;نکته : در SQLite مراقب باشید . این دیتابیس تطبیق غیر حساس به حروف بزرگ و کوچک را برای رشته های غیر ASCII انجام نمی دهد . (برای تمامی lookup های جلوتر نیز این قانون وجود دارد)-لوکاپ in : in برای بدست آوردن مقادیری است که در آن مقدار فیلد یکی از مقادیر لیست , رشته ,تاپل یا QuerySet وارد شده است (هر چیزی که iterable باشد پذیرفته می شود) . برای مثال :Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in=&#039;abc&#039;)SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN (&#039;a&#039;, &#039;b&#039;, &#039;c&#039;);همچنین می توانید از یک QuerySet برای پارامتر استفاده کنید . به این صورت برنامه شما داینامیک خواهد بود و هر لحظه می توانند نتایج تغییرات داشته باشند . برای مثال :inner_qs = Blog.objects.filter(name__contains=&#039;Cheddar&#039;)
entries = Entry.objects.filter(blog__in=inner_qs)SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE &#039;%Cheddar%&#039;)توجه داشته باشید اگر یک QuerySet را که روی آن values یا list_values فراخوانی شده است , به __inارسال کنید ,باید مطمئن شوید که فقط و فقط یک فیلد از آن QuerySet خارج می شود . برای مثال :inner_qs = Blog.objects.filter(name__contains=&#039;Ch&#039;).values(&#039;name&#039;)
entries = Entry.objects.filter(blog__name__in=inner_qs)برعکس کد بالا ,کد زیر بخاطر خارج شدن مقادیر دو فیلد از QuerySet دچار ارور خواهد شد .# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains=&#039;Ch&#039;).values(&#039;name&#039;, &#039;id&#039;)
entries = Entry.objects.filter(blog__name__in=inner_qs)برخی از دیتابیس ها (مانند MySQL) بدرستی از QuerySet های تودرتو پشتیبانی نمی کنند . کد های بالا مثال هایی از QuerySet های تودرتو هستند . در این دیتابیس ها ,استخراج یک لیست از QuerySet اول و ارسال آن لیست به QuerySet دوم کارآمد تر خواهد بود . یعنی به جای اجرای یک Query ,دو Query را اجرا کنید. برای مثال :values = Blog.objects.filter(
    name__contains=&#039;Cheddar&#039;).values_list(&#039;pk&#039;, flat=True)
entries = Entry.objects.filter(blog__in=list(values))در کد بالا قبل از Blog , list را فراخوانی کردیم تا QuerySet اول را مجبور به اجرا در دیتابیس کنیم . بدون آن یک QuerySet تودرتو اجرا خواهد شد .-لوکاپ gt : به معنای مقایسه برای بزرگتر از یا بیشتر از است . برای مثال :Entry.objects.filter(id__gt=4)SELECT ... WHERE id &gt;4;کد بالا یعنی مقادیری را پیدا کن که id آنها از 4 بیشتر باشند .-لوکاپ gte : به معنای مقایسه برای بزرگتر یا مساوی است .-لوکاپ lt : به معنای کوچک تر یا کمتر از است .-لوکاپ lte : به معنای مقایسه برای کوچکتر یا مساوی است .-لوکاپ startswith :مقادیری را بازمیگرداند که ابتدای آنها با پارامتر ارسالی شروع شده باشد . به حروف بزرگ و کوچک نیز حساس است . برای مثال :Entry.objects.filter(headline__startswith=&#039;Lennon&#039;)SELECT ... WHERE headline LIKE &#039;Lennon%&#039;;کد بالا مقادیری که ابتدای آنها با Lennon شروع شده باشد را بازمیگرداند .نکته : در SQLite , startswith مانند istartswith عمل خواهد کرد . زیرا این دیتابیس دستور های LIKE را نمی توانند به صورت Case-sentisive هندل کند .-لوکاپ istartswith :همان startswith است ولی به حروف بزرگ و کوچک حساس نیست . برای مثال :Entry.objects.filter(headline__istartswith=&#039;Lennon&#039;)SELECT ... WHERE headline ILIKE &#039;Lennon%&#039;;-لوکاپ endswith :مقادیری را بازمیگرداند که انتهای آنها با پارامتر ارسالی تمام شده باشد . به حروف بزرگ و کوچک نیز حساس است . برای مثال :Entry.objects.filter(headline__endswith=&#039;Lennon&#039;)SELECT ... WHERE headline LIKE &#039;%Lennon&#039;;کد بالا مقادیری که انتهای آنها با Lennon تمام شده باشد را بازمیگرداند .نکته : در SQLite , endswith مانند iendswith عمل خواهد کرد . زیرا این دیتابیس دستور های LIKE را نمی توانند به صورت Case-sentisive هندل کند .-لوکاپ iendswith :همان endswith است اما به حروف بزرگ و کوچک حساس نیست . برای مثال :Entry.objects.get(headline__iendswith=&#039;Lennon&#039;)SELECT ... WHERE headline ILIKE &#039;%Lennon‘;-لوکاپ range : این looup مقادیری را بازمیگرداند که میان دو چیز قرار دارند . مقادیر میتوانند عدد , حروف و یا تاریخ ها باشند . برای مثال :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))SELECT ... WHERE pub_date BETWEEN &#039;2005-01-01&#039; and &#039;2005-03-31&#039;;نکته : به طور کلی شما نمی توانید dates و datetimes را با هم ترکیب کنید . بنابر این اگر pub_date یک DateTimeField بود ,کد بالا به صورت SQL زیر درمیامد :SELECT ... WHERE pub_date BETWEEN &#x27;2005-01-01 00:00:00&#x27; and &#x27;2005-03-31 00:00:00&#x27;;-لوکاپ date :مقدار داخل دیتابیس را به صورت تاریخ در میاورد . برای فیلد های datetime کار می کند و راهی است برای مقایسه تاریخ های درون یک شی .پارامتر ارسالی باید یک شی از datetime.date باشد . برای مثال :Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))بدلیل متفاوت بودن کارکرد موتور های دیتابیسی در این مورد , هیچ کد SQL ای ارائه نشده است .نکته : همچنین وقتی USE_TZ در settings.py روی Trueباشد ,مقادیر فیلد ها قبل از فیلتر شدن به منطقه زمانی کنونی تبدیل می شود . (برای تمامی lookupها این مطلب درست است)-لوکاپ year : برای فیلد های datefield و datetimefield است . مقدار سال فیلد را با پارامتر ارسالی تطبیق می دهد و در صورت تطابق آن را بازمیگرداند . برای مثال :Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)SELECT ... WHERE pub_date BETWEEN &#039;2005-01-01&#039; AND &#039;2005-12-31&#039;;
SELECT ... WHERE pub_date &gt;= &#039;2005-01-01&#039;;البته سینتکس دقیق کد SQL بالا برای هر دیتابیس متفاوت خواهد بود .-لوکاپ iso_year :برای فیلد های datefield و datetimefield است . مقدار سال فیلد را با پارامتر ارسالی تطبیق می دهد و در صورت تطابق آن را بازمیگرداند (البته به صورت استاندارد جهانی ISO 8601) . برای مثال :Entry.objects.filter(pub_date__iso_year=2005)
Entry.objects.filter(pub_date__iso_year__gte=2005)-لوکاپ month : برای فیلد های datefield و datetimefield است . مقدار ماه فیلد را با پارامتر ارسالی تطبیق می دهد و در صورت تطابق آن را بازمیگرداند . پارامتر شما باید یک عدد از 1 تا 12 باشد (ماه های سال) . برای مثال :Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)SELECT ... WHERE EXTRACT(&#039;month&#039; FROM pub_date) = &#039;12&#039;;
SELECT ... WHERE EXTRACT(&#039;month&#039; FROM pub_date) &gt;= &#039;6&#039;;-لوکاپ day : برای فیلد های datefield و datetimefield است . مقدار روز فیلد را با پارامتر ارسالی تطبیق می دهد و در صورت تطابق آن را بازمیگرداند . برای مثال :Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)SELECT ... WHERE EXTRACT(&#039;day&#039; FROM pub_date) = &#039;3&#039;;
SELECT ... WHERE EXTRACT(&#039;day&#039; FROM pub_date) &gt;= &#039;3&#039;;توجه داشته باشید هر رکورد که تاریخ آن در روز سوم ماه باشد (برای مثال 3 ژانویه یا 3 ژوئیه) مطابقت پیدا خواهد کرد.-لوکاپ week : بر اساس استاندارد ISO 8601 برای شما شماره هفته فیلد را (1-52 یا 53) با پارامتر وارد شده مقایسه می کند . به یاد داشته باشید هفته ها از روز دوشنبه شروع می شوند . برای مثال :Entry.objects.filter(pub_date__week=52)
Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)به یاد داشته باشید که اگر USE_TZ در settings.py برابر با True باشد ,زمان ها به زمان محلی شما تبدیل می شوند .-لوکاپ week_day :برای شما روز هفته فیلد را (برای مثال دوشنبه) با پارامتر وارد شده مقایسه می کند . یک عدد را به عنوان پارامتر میگیرد که نشان دهنده روز هفته از 1(یکشنبه) تا 7(شنبه) است . برای مثال :Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)کد های بالا بدون توجه به ماه یا سال هر تاریخی که مربوط به روز دوشنبه باشد را بازمیگردانند .-لوکاپ iso_week_day :در واقع همان week_day است . اما عملیات را بر اساس استاندارد ISO 8601 انجام می دهد . پارامتر های ارسالی باید یک عدد بین 1 (دوشنبه) تا 7(یکشنبه) باشند . برای مثال :Entry.objects.filter(pub_date__iso_week_day=1)
Entry.objects.filter(pub_date__iso_week_day__gte=1)کد های بالا بدون توجه به ماه یا سال هر تاریخی که مربوط به روز دوشنبه باشد را بازمیگردانند .-لوکاپ quarter :یک سال به 4 قسمت تبدیل می شود . در واقع هر سه ماه یک قسمت است . این lookup برای شما قسمتی که تاریخ فیلد در آن قرار دارد را با پارامتر ورودی مقایسه می کند . پارامتر ورودی باید یک عدد بین 1تا 4 باشد که برای مثال مقدار 1 نشان دهنده سه ماه اول سال است .برای دریافت اشیا مدل Entry که در سه ماهه دوم سال هستند (از 1 آپریل تا 30 ژوئن) کد زیر را مینویسید .Entry.objects.filter(pub_date__quarter=2)-لوکاپ time : برای datetimefield بکار می رود . مقدار فیلد را با پارامتر وارد شده مقایسه می کند . پارامتر وارد شده باید یک شی از datetime.time باشد . برای مثال :Entry.objects.filter(pub_date__time=datetime.time(14, 30))
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))-لوکاپ hour : برای شما ساعت فیلد را با پارامتر وارد شده مقایسه می کند . یک عدد را به عنوان پارامتر میگیرد که نشان دهنده ساعت از 0 تا 23 است . برای مثال :Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)SELECT ... WHERE EXTRACT(&#039;hour&#039; FROM timestamp) = &#039;23&#039;;
SELECT ... WHERE EXTRACT(&#039;hour&#039; FROM time) = &#039;5&#039;;
SELECT ... WHERE EXTRACT(&#039;hour&#039; FROM timestamp) &gt;= &#039;12&#039;;-لوکاپ minute : برای شما دقیقه فیلد را با پارامتر وارد شده مقایسه می کند . یک عدد را به عنوان پارامتر میگیرد که نشان دهنده دقیقه از 0 تا 59 است . برای مثال :Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)SELECT ... WHERE EXTRACT(&#039;minute&#039; FROM timestamp) = &#039;29&#039;;
SELECT ... WHERE EXTRACT(&#039;minute&#039; FROM time) = &#039;46&#039;;
SELECT ... WHERE EXTRACT(&#039;minute&#039; FROM timestamp) &gt;= &#039;29&#039;;-لوکاپ second : برای شما ثانیه فیلد را با پارامتر وارد شده مقایسه می کند . یک عدد را به عنوان پارامتر میگیرد که نشان دهنده ثانیه از 0 تا 59 است . برای مثال :Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)SELECT ... WHERE EXTRACT(&#039;second&#039; FROM timestamp) = &#039;31&#039;;
SELECT ... WHERE EXTRACT(&#039;second&#039; FROM time) = &#039;2&#039;;
SELECT ... WHERE EXTRACT(&#039;second&#039; FROM timestamp) &gt;= &#039;31&#039;;-لوکاپ is_null :دو مقدار True و False را قبول می کند که به ترتیب کد های IS NULL و IS NOT NULL را ایجاد می کند . در واقع بررسی می کند که آیا یک مقدار Null است یا خیر . برای مثال :Entry.objects.filter(pub_date__isnull=True)SELECT ... WHERE pub_date IS NULL;-لوکاپ regex : یک یا چند regular expression یا عبارات منظم را به عنوان پارامتر می پذیرد و مقدار فیلد را با آن عبارت منظم تطابق می دهد (در مورد آنها باید خودتان بخوانید زیرا سرفصل بزرگی است) .در هر دیتابیس نحوه کارکرد عبارات منظم متفاوت است . در مورد SQLite که هیچ پشتیبانی داخلی ای از عبارات منظم ندارد ,این ویژگی توسط یک تابع REGEXP هندل می شود . بنابراین شبیه ماژول re در پایتون عمل می کند.برای مثال :Entry.objects.get(title__regex=r&#039;^(An?|The) +&#039;)SELECT ... WHERE title REGEXP BINARY &#039;^(An?|The) +&#039;; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, &#039;^(An?|The) +&#039;, &#039;c&#039;); -- Oracle
SELECT ... WHERE title ~ &#039;^(An?|The) +&#039;; -- PostgreSQL
SELECT ... WHERE title REGEXP &#039;^(An?|The) +&#039;; -- SQLiteاستفاده از رشته های خالی مانند r‘foo’ بجای ‘foo’ برای pass یا رد شدن از عبارت منظم توصیه می شود .-لوکاپ iregex : همان regex است اما به حروف بزرگ و کوچک حساس نیست .برای مثال :Entry.objects.get(title__iregex=r&#039;^(an?|the) +&#039;)SELECT ... WHERE title REGEXP &#039;^(an?|the) +&#039;; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, &#039;^(an?|the) +&#039;, &#039;i&#039;); -- Oracle
SELECT ... WHERE title ~* &#039;^(an?|the) +&#039;; -- PostgreSQL
SELECT ... WHERE title REGEXP &#039;(?i)^(an?|the) +&#039;; -- SQLiteدر این جلسه در رابطه با تمامی لوکاپ های قابل استفاده در جنگو صحبت کردیم . در جلسه بعدی درباره api مدل ها در جنگو صحبت می کنیم و مباحث تخصصی ما شروع خواهد شد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 15:20:23 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و چهار | آشنایی با تمامی متد های جنگو | پارت دوم</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%DA%86%D9%87%D8%A7%D8%B1-%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%AA%D9%85%D8%A7%D9%85%DB%8C-%D9%85%D8%AA%D8%AF-%D9%87%D8%A7%DB%8C-%D8%AC%D9%86%DA%AF%D9%88-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%88%D9%85-jop6d2ykok11</link>
                <description>در این جلسه به بررسی برخی دیگر از متد های قابل استفاده (نوع دوم) خواهیم پرداخت . بهتر است مقاله قبلی را قبل از این مقاله مطالعه کرده باشید . با ما همراه باشید .آموزش جنگو : جلسه سی و چهار | آشنایی با تمامی متد های جنگو | پارت دوممتد هایی که QuerySet جدید بازنمیگردانند !در جلسه قبلی گفتیم متدها در جنگو به دو نوع تقسیم می شوند . در این جلسه به بررسی نوع دوم می پردازیم . در نوع دوم شما Queryset جدیدی را به عنوان پاسخ دریافت نمی کنید و مستقیما خود شی یا اشیا را دریافت خواهید کرد . متد هایی که در ادامه بررسی می کنیم ,لیست متد هایی است که QuerySet را بازنمیگردانند و بجای آن یک چیز دیگر مانند یک شی یا نمونه از دیتابیس یا یک کلاس یا حتی یک عدد را بازمیگردانند .این متد ها از کش استفاده نمی کنند (درباره کش ها قبلا صحبت کرده ایم) . آنها هر بار که فراخوانی می شوند ,روی دیتابیس عملیاتی انجام می دهند .این متد ها به صورت عادی در نسخه ی synchronous برنامه استفاده می شوند . اگر شما یک برنامه از نوع asynchronous دارید ,باید ورژن دیگری از این متدها را فراخوانی کنید . برای مثال بجای استفاده از ()get باید از ()await get استفاده کنید . هر دو ورژن متدها (synchronousو asynchronous) را برای هر کدام از آنها در لیست زیر نوشته ایم .معمولا ورژن های این متدها با یکدیگر تفاوتی در عملکرد ندارند , اما اگر تفاوتی وجود داشته باشد ,آن را در توضیحات متد بررسی می کنیم .نکته : جنگو 4.1 فقط دارای ورژن های asynchronous متد های زیر است . تنها تفاوت در میان آنها پیشوند a برای متد های استفاده شده در asynchronous است .متد get | aget : یک شی را بازمیگرداند که با پارامتر های ورودی و lookup شما همخوانی داشته باشد . شما قبلا با این متد آشنا شده اید . همچنین می توانید آن را به صورت ترکیبی نیز استفاده کنید . (برای مثال استفاده از شی Q)Entry.objects.get(id=1)
Entry.objects.get(Q(blog=blog) &amp; Q(entry_number=1))اگر مطمئن هستید که QuerySet شما فقط یک شی را قرار است بازگرداند ,میتوانید برای دسترسی مستقیم به شی از get بدون ارسال هیچ پارامتری استفاده کنید .Entry.objects.filter(pk=1).get()در مثال بالا ما مطمئن هستیم که در سطر مورد نظر فقط یک شی با pk=1 قرار دارد (PrimaryKey نمی تواند دو مقدار یکسان را در یک جدول داشته باشد) . بنابراین از get برای دسترسی مستقیم به همان شی استفاده می کنیم .اگر هیچ شی ای با پارامتر یا lookup های شما همخوانی نداشته باشد ,این متد ارور DoesNotExsist را بازمیگرداند .Entry.objects.get(id=-999) # raises Entry.DoesNotExistمتد get فقط و فقط در زمانی استفاده می شود که مطمئن باشید یک شی با مشخصات شما وجود دارد . اگر این متد چند شی را که با پارامتر های شما همخوانی دارند , پیدا کند باعث ایجاد ارور MultipleObjectsReturned می شود . (در مواقعی که چندین شی دارید باید از filter استفاده کنید)Entry.objects.get(name=&#039;A Duplicated Name&#039;) # raises Entry.MultipleObjectsReturnedگرچه اگر خواستید یا نیاز داشتید می توانید ارور ها را وسیله try و except هندل کنید . ابتدا کافی است ارور های بالا را از Django.core.exceptions وارد کنید . هر ارور در واقع کلاس مربوط به خود را در این بخش دارد . سپس می توانید ارور ها را مدیریت کنید . برای مثال در کد زیر ارور DoesNotExsist را هندل کرده ایم . در واقع برای این ارور از کلاس ObjectDoesNotExsist استفاده کردیم .from django.core.exceptions import ObjectDoesNotExist

try:
    blog = Blog.objects.get(id=1)
    entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
    print(&amp;quotEither the blog or entry doesn&#039;t exist.&amp;quot)متد create | acreate :همانطور که قبلا با این متد آشنا شده اید ,این متد برای شما یک شی یا رکورد را درون دیتابیس ایجاد می کند و آن را سیو می کند . در استفاده از آن نیازی به فراخوانی save ندارید . همچنین پارامتر های آن فیلد های مدل هستند که باید به هر کدام از آنها یک مقدار اختصاص داد . (استثنای آنهایی که null,blank=True هستند)p = Person.objects.create(first_name=&amp;quotBruce&amp;quot, last_name=&amp;quotSpringsteen&amp;quot)کد زیر نیز می تواند برای ساخت اشیا استفاده شود . البته که create بهتر از روش زیر (نمونه سازی از مدل) است.p = Person(first_name=&amp;quotBruce&amp;quot, last_name=&amp;quotSpringsteen&amp;quot)
p.save(force_insert=True)نکته : اگر شما یک primarykey دستی برای مدل تنظیم کرده باشید و از قبل آن را مقداردهی کرده باشید , وارد کردن دوباره مقدار آن در create باعث ارور IntegrityError می شود . دلیل آن این است که primarykey باید منحصر به فرد باشد . (همچنین force_insert را بعدها توضیح خواهیم داد)متد get_or_create | aget_or_create : یک متد که ابتدا بررسی می کند که آیا شی یا رکوردی با پارامتر و lookup های شما همخوانی دارد یا خیر و سپس اگر آن را پیدا کند ,شی را بازمیگرداند و اگر نه ,آن را ایجاد می کند. (برای ایجاد یک شی به مقداردهی همه فیلد ها نیاز دارد)نتیجه این متد به صورت یک تاپل است . شکل این تاپل به صورت (object , created) است . در این تاپل object شی یافته شده و یا ایجاد شده است و created یک Boolean است که نشان می دهد آیا یک شی جدید ایجاد شده است یا خیر .این متد به کار می رود تا از ایجاد اشیا یکسان در یک لحظه توسط چندین درخواست (request) جلوگیری شود . یعنی فرض کنید که چندین درخواست به صورت همزمان و موازی درخواست ایجاد یک شی یکسان داشته باشند . این متد از وقوع این اتفاق جلوگیری می کند . برای مثال :try:
    obj = Person.objects.get(first_name=&#039;John&#039;, last_name=&#039;Lennon&#039;)
except Person.DoesNotExist:
    obj = Person(first_name=&#039;John&#039;, last_name=&#039;Lennon&#039;, birthday=date(1940, 10, 9))
    obj.save()در کد بالا ممکن است با چند درخواست همزمان ,چندین تلاش برای ذخیره یک Person با پارامتر های یکسان صورت بگیرد . برای حل این مشکل باید از get_or_create استفاده کرد .obj, created = Person.objects.get_or_create(
    first_name=&#039;John&#039;,
    last_name=&#039;Lennon&#039;,
    defaults={&#039;birthday&#039;: date(1940, 10, 9)},
)کد بالا نتیجه را به صورت یک تاپل حاوی object که شی ایجاد شده یا یافت شده است و created باز میگرداند .نکته : این روش نیاز دارد تا فیلد های دیتابیس شما unique باشند . این به نفع خودتان است زیرا فراخوانی همزمان این متد ممکن است باعث ایجاد چندین شی با پارامتر های یکسان شود .همچنین شما می توانید شرایط پیچیده تر را با زنجیره ای کردن get_or_create با متدهای filter و یا شی Q مدیریت کنید . برای مثال ,برای دریافت Robert یا Bob Marley در صورتی که اولی وجود داشته باشد و دومی ساخته بشود :from django.db.models import Q

obj, created = Person.objects.filter(
    Q(first_name=&#039;Bob&#039;) | Q(first_name=&#039;Robert&#039;),
).get_or_create(last_name=&#039;Marley&#039;, defaults={&#039;first_name&#039;: &#039;Bob&#039;})اگر چندین شی را این متد شناسایی کند (که با پارامتر های شما همخوانی داشته باشند) ارور MultipleObjectsReturned به شما بازگردانده می شود . اگر هم شی ای پیدا نشود این متد آن را برای شما ایجاد می کند . وبسایت رسمی جنگو الگوریتم ایجاد شی توسط این متد را به شکل زیر توصیف می کند :params ={k: v for k, v in kwargs.items() if &#039;__&#039; not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()به زبانی ساده این متد ابتدا هر آرگومان که defaults نباشد و دارای ‘__’ نباشد را پیدا می کند (که نشان دهنده non-exact بودن فیلد است) . سپس محتویات defaults را نیز اضافه می کند . در صورتی که آرگومان ها و محتویات آرگومان defaultsمفایرت داشته باشند آنها را بازنویسی یا override می کند و در آخر از نتیجه برای یافتن شی استفاده می کند . در آخر نیز اگر وجود نداشته باشد آن را ایجاد خواهند کرد . این ساده شده الگوریتم اصلی است .اگر یک فیلد با نام defaults دارید که می خواهید از آن در get_or_create استفاده کنید ,از defaults__exact استفاده کنید . برای مثال :Foo.objects.get_or_create(defaults__exact=&#039;bar&#039;, defaults={&#039;defaults&#039;: &#039;baz&#039;})متد get_or_create یک ارور مشابه با متد create دارد . زمانی که شما از primarykey های دستی استفاده کنید . اگر یک شی در حال ایجاد شدن باشد و Key در دیتابیس از قبل مشخص شده باشد ,یک ارور IntegrityError به شما بازگردانده می شود .در نهایت جنگو پیشنهاد می کند که از این متد فقط در ویو هایی که با درخواست های POST (POST requests) کار می کنند استفاده کنید . درخواست های GET نباید روی دیتا و داده های شما تاثیری ایجاد کنند . در عوض از POST استفاده کنید تا داده ها و دیتاها بتوانند اثر جانبی را پذیرش کنند . درباره دلیل آن بیشتر صحبت خواهیم کرد .نکته : شما می توانید از فیلد ManyToManyField و روابط معکوس یا reverse نیز در get_or_create استفاده کنید.البته فقط به صورت محدود و از خود فیلد ManyToManyField و رابطه میتوانید از متد استفاده کنید . در صورت های دیگر دچار ارور خواهید شد .برای فهم بیشتر فرض کنید مدل زیر را داریم .class Chapter(models.Model):
    title = models.CharField(max_length=255, unique=True)

class Book(models.Model):
    title = models.CharField(max_length=256)
    chapters = models.ManyToManyField(Chapter)شما فقط میتوانید از get_or_create در فیلد chapters مدل Book استفاده کنید اما فقط در درون محتوای Book متد شما کار می کند . برای مثال :&gt;&gt;&gt; book = Book.objects.create(title=&amp;quotUlysses&amp;quot)
&gt;&gt;&gt; book.chapters.get_or_create(title=&amp;quotTelemachus&amp;quot)
(&lt;Chapter: Telemachus&gt;, True)
&gt;&gt;&gt; book.chapters.get_or_create(title=&amp;quotTelemachus&amp;quot)
(&lt;Chapter: Telemachus&gt;, False)
&gt;&gt;&gt; Chapter.objects.create(title=&amp;quotChapter 1&amp;quot)
&lt;Chapter: Chapter 1&gt;
&gt;&gt;&gt; book.chapters.get_or_create(title=&amp;quotChapter 1&amp;quot)
# Raises IntegrityErrorارور خط آخر به این دلیل رخ داد که جنگو تلاش کرد تا get_or_create را روی Chapter1 فراخوانی کند (البته از طریق شی book که title آن برابر با Ulysses است) . ولی نمی تواند اینکار را انجام دهد . این رابطه نمی تواند chapter را دریافت کند زیرا به book مربوط نیست . برای ایجاد آن نیز باید فیلد title , unique باشد.متد update_or_create | aupdate_or_create : این متد همانطور که از نام آن مشخص است ,روش راحتی است برای آپدیت مقادیر یک شی و یا ایجاد یک شی جدید در صورت نیاز .مقادیری که باید آپدیت شوند در یک دیکشنری با نام defaults به صورت (field , value) قرار می گیرند .مقدار Field همان فیلد انتخابی شما و value مقدار برای آن فیلد است که باید آپدیت شود . همچنین می توانید بجای value یک callable نیز قرار دهید (callable مانند توابع) .نتیجه این متد به صورت یک تاپل به شکل (object , created) است . در این تاپل object شی آپدیت شده و یا ایجاد شده است و created یک Boolean است که نشان می دهد آیا یک شی جدید ایجاد شده است یا خیر .متد update_or_create در واقع سعی می کند که شی را از دیتابیس بر اساس پارامتر و lookup های داده شده پیدا کند . وقتی آن را پیدا کرد فیلد های درون defaults را بر اساس مقادیر آنها آپدیت می کند .به کد صفحه بعد نگاه کنید که بدون استفاده از این متد ,عملکرد مشابه آن را نوشته ایم .defaults = {&#039;first_name&#039;: &#039;Bob&#039;}

try:
    obj = Person.objects.get(first_name=&#039;John&#039;, last_name=&#039;Lennon&#039;)
    for key, value in defaults.items():
        setattr(obj, key, value) obj.save()
except Person.DoesNotExist:
    new_values = {&#039;first_name&#039;: &#039;John&#039;, &#039;last_name&#039;: &#039;Lennon&#039;}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()اگر تعداد فیلد هایی که باید آپدیت شوند زیاد شود این روش ناکارآمد است . برای حل این مشکل از متد update_or_create استفاده می کنیم :obj, created = Person.objects.update_or_create(
    first_name=&#039;John&#039;, last_name=&#039;Lennon&#039;,
    defaults={&#039;first_name&#039;: &#039;Bob&#039;},
)نکته : این متد نیز نیاز به unique بودن فیلد و مقدار ها دارد . اگر این شرایط رعایت نشود , ممکن است در دیتابیس چندین شی به وسیله درخواست های همزمان با پارامتر های یکسان آپدیت یا ساخته شوند .مانند متد های get_or_create و متد create ,زمانی که شما از primarykey های دستی استفاده کنید و یک شی در حال ایجاد شدن باشد و Key در دیتابیس از قبل مشخص شده باشد ,یک ارور IntegrityError به شما بازگردانده می شود .متد bulk_create | abulk_create : این متد لیستی از اشیا را دریافت می کند و آنها را در دیتابیس وارد می کند. برتری این متد این است که همه ی اشیا را فقط در یک عملیات و Query در دیتابیس وارد می کند . سپس به عنوان نتیجه اشیا ایجاد شده را در یک لیست به همان ترتیب داده شده بازمیگرداند . برای مثال :&gt;&gt;&gt; objs = Entry.objects.bulk_create([
...           Entry(headline=&#039;This is a test&#039;),
...           Entry(headline=&#039;This is only a test&#039;),
...  ])اگرچه روی این متد یک سری از محدودیت ها اعمال می شود :-متد save مدل شما فراخوانی نمی شود و سیگنال های pre_save و post_save ارسال نمی شوند .-با مدل های فرزند در ارث بری نوع multi-table inheritance کار نمی کند .-اگر primarykey شما یک AutoField باشد ,فقط روی دیتابیس های PostgreSQL و MariaDB 10.5 و SQLite 3.35 کار می کند و میتوانید آن را دریافت کنید . در باقی آنها فیلد تنظیم و مقداردهی نمی شود .-با روابط ManyToManyFields کار نمی کند .در دیتابیس هایی که پشتیبانی می کنند (همه آنها به جز Oracle) یک پارامتر با نام ignore_conflicts دارید . دو مقدار True و False را قبول می کند . تنظیم آن روی Trueبه جنگو می گوید که ارور هایی که در سطح ردیف ها به وجود میایند (مانند ارور تکرار مقدار یکسان در فیلد unique) را نادیده بگیرد .نکته :در MySQL و MariaDB تنظیم پارامتر ignore_conflicts روی True انواع خاصی از خطاها را میتواند به هشدار یا warning تبدیل کند . برای مثال : invalid values و یا non-nullable violationsمتد bulk_update | abulk_update: این متد به صورت کارآمد فیلد های داده شده برای یک شی از مدل را آپدیت می کند . برتری این متد این است که همه ی این عملیات را برای تمام اشیا در یک Query انجام می دهد . در آخر نیز تعداد اشیا آپدیت شده بازگردانده می شود .&gt;&gt;&gt; objs = [
...            Entry.objects.create(headline=&#039;Entry 1&#039;),
...            Entry.objects.create(headline=&#039;Entry 2&#039;),
...  ]
&gt;&gt;&gt; objs[0].headline = &#039;This is entry 1&#039;
&gt;&gt;&gt; objs[1].headline = &#039;This is entry 2&#039;
&gt;&gt;&gt; Entry.objects.bulk_update(objs, [&#039;headline&#039;])
2استفاده از متد دیگر که QuerySet.update نامیده می شود روش دیگری برای آپدیت اشیا است . این متد اتوماتیک شی را آپدیت می کند و سیو می کند . این کار بهتر از پیمایش روی اشیا و فراخوانی save برای هرکدام از آنها است . اما نکاتی وجود دارند که باید در استفاده از اینها توجه داشته باشید . برای مثال شما نمی توانید مقدار primarykey را آپدیت کنید .-متد save مدل شما فراخوانی نمی شود و سیگنال های pre_save و post_save ارسال نمی شوند .-اگر شما در حال آپدیت تعداد زیادی از اشیا باشید ,کد SQL تولید شده بسیار بزرگ و طولانی خواهد شد . برای جلوگیری از آن کافیست یک batch_size را تعریف کنید . (batch_size یک پارامتر در متد است که یک عدد را به عنوان محدودیت تعداد اشیا آپدیت شده می پذیرد . به زبانی دیگر تعداد اشیا آپدیت شده را در هر بار فراخوانی متد محدود می کند . برای مثال 100 شی)-تعدادی که این متد به شما بازمیگرداند ممکن است کمتر از تعداد اشیایی باشد که ارسال کرده اید . دلیل آن این است که ممکن است بعضی از اشیا به صورت تکراری ارسال شده باشند .نکته : SQLite و Oracle محدودیت هایی در تعداد متغییر مورد استفاده برای Query دارند . پس در آنها مراقب به استفاده از batch_size باشید .متد count | acount : یک عدد (integer) را بازمیگرداند که نشان دهنده تعداد اشیا در یک QuerySet است . برای مثال :# Returns the total number of entries in the database.
Entry.objects.count()
# Returns the number of entries whose headline contains &#039;Lennon&#039;
Entry.objects.filter(headline__contains=&#039;Lennon&#039;).count()فراخوانی count یک کد (*) SELECT COUNT را ایجاد می کند . بنابراین همیشه از count باید بجای لود کردن تمام اشیا و فراخوانی len روی آنها استفاده کنید . (گرچه اگر قرار است تمام اشیا را نیاز داشته باشید و بخواهید آنها را در حافظه لود کنید , متد len سریعتر خواهد بود)اگر قبلا Query به طور کامل از دیتابیس بارگذاری یا لود شده باشد , count از طول آن استفاده می کند تا Query اضافی روی دیتابیس اعمال نکند .متد in_bulk | ain_bulk : این متد یک لیست از مقادیر فیلد (برای مثال لیست idها) و نام فیلد ها را برای آن مقادیر دریافت می کند . سپس یک دیکشنری را که اشیا با هر value مطابقت داده شده اند را بازمیگرداند . برای مثال :&gt;&gt;&gt; Blog.objects.in_bulk([1])
{1: &lt;Blog: Beatles Blog&gt;}
&gt;&gt;&gt; Blog.objects.in_bulk([1, 2])
{1: &lt;Blog: Beatles Blog&gt;, 2: &lt;Blog: Cheddar Talk&gt;}
&gt;&gt;&gt; Blog.objects.in_bulk([])
{}
&gt;&gt;&gt; Blog.objects.in_bulk()
{1: &lt;Blog: Beatles Blog&gt;, 2: &lt;Blog: Cheddar Talk&gt;, 3: &lt;Blog: Django Weblog&gt;}
&gt;&gt;&gt; Blog.objects.in_bulk([&#039;beatles_blog&#039;], field_name=&#039;slug&#039;)
{&#039;beatles_blog&#039;: &lt;Blog: Beatles Blog&gt;}
&gt;&gt;&gt; Blog.objects.distinct(&#039;name&#039;).in_bulk(field_name=&#039;name&#039;)
{&#039;Beatles Blog&#039;: &lt;Blog: Beatles Blog&gt;, &#039;Cheddar Talk&#039;: &lt;Blog: Cheddar Talk&gt;,
&#039;Django Weblog&#039;: &lt;Blog: Django Weblog&gt;}این متد هیچگاه ارور django.core.exceptions.ObjectDoesNotExist را به شما بازنمیگرداند . یعنی هر id که با هیچ نمونه ای مطابقت نداشته باشد به سادگی نادیده گرفته می شود . اگر هیچ value ای به متد ارسال نشود ,تمام اشیا بازگردانده می شوند . همچنین نام فیلد ها باید unique باشد . به صورت پیش فرض نیز نام فیلد ها روی primarykey تنظیم می شود (id) .اگر یک لیست خالی به این متد ارسال کنید یک دیکشنری خالی دریافت خواهید کرد .متد iterator | aiterator:یک QuerySet را در دیتابیس اجرا می کند و یک iterator روی نتایج بازمیگرداند . همچنین اگر از aiterator استفاده کنید یک asynchronous iterator بازگشت داده می شود .یک QuerySet معمولا نتایج خود را در کش ذخیره می کند . اما استفاده از این متد باعث می شود تا هیچ فرایندی برای کش کردن QuerySet صورت نگیرد . Iterator نتایج را مستقیما به شما می دهد . این فرایند زمانی مفید است که تعداد اشیا زیادی دارید که فقط یکبار به آنها نیاز دارید . استفاده از این متد در آن زمان باعث افزایش عملکرد حافظه می شود .توجه داشته باشید فراخوانی iterator روی یک QuerySet که قبلا در دیتابیس اجرا شده است ,باعث می شود تا دوباره در دیتابیس اجرا شود .یک Iterator با فراخوانی های متد prefetch_related در قبل از آن فقط تا زمانی سازگار است که پارامتر chunk_size تعیین شود .نکته : aiterator هیچگاه با فراخوانی متد prefetch_related در قبل از آن سازگار نیست .در برخی از دیتابیس ها مانند Oracle و SQLite حداکثر تعداد عبارات در یک SQL IN محدود است . از این رو باید مراقب باشید تا مقادیر و تعداد عبارات زیر حد مشخص شده باشند . (البته بعدا با chunk_size آشنا می شوید که به شما در این مورد کمک می کند)نکته : تا زمانی که QuerySet به هیچ شی ارجاع داده شده یا رابطه ای برنخورد , chunk_size برابر با 2000 در نظر گرفته می شود .وابسته به دیتابیس بک اندی که استفاده می کنید , Query ممکن است کل نتایج را به یکباره لود و دریافت کند و یا ممکن است نتایج را با server-side cursors , استریم کند . در قسمت زیر آنها را بررسی می کنیم .نوع اول دیتابیس های دارای server-side cursors هستند . آنها شامل PostgreSQL و Orcale می شوند . این نوع از دیتابیس ها نتایج را در حافظه لازم نیست لود کنند و بجای آن نتایج را استریم (stream) خواهند کرد .درایور های Oracle همیشه از این ویژگی استفاده می کنند . با server-side cursors پارامتر chunk_size تعداد نتایجی را که باید در حافظه (در سطح درایور) ذخیره شوند را مشخص می کند . هرچقدر این عدد بیشتر باشد تعداد رفت و برگشت داده میان درایور و دیتابیس کاهش پیدا می کند و باعث از دست دادن مقدار بیشتری از رم یا حافظه می شود .در PostgreSQL نیز فقط زمانی از این ویژگی استفاده می شود که متغییر DISABLE_SERVER_SIDE_CURSORS در settings.py برابر با False باشد .نوع دوم دیتابیس های بدون server-side cursors هستند . آنها شامل MySQL و SQLite هستند . در آنها درایور نتایج را تماما در حافظه بارگذاری می کند . سپس نتایج به وسیله database adapter (و متد fethcmany) به اشیا پایتون تبدیل می شوند . (البته این رفتار برای MySQL است ,در SQLite فقط از متد fetchmany استفاده می شود)پارامتر chunk_size در این نوع از دیتابیس ها اندازه دسته های داده که جنگو از درایور دریافت می کند را کنترل می کنند . هر چقدر بیشتر باشد عملیات دیتابیس سنگین تر ولی مصرف حافظه کمتر خواهد بود .تا زمانی که QuerySet هیچ شی ارتباطی (related object) را دریافت نکند , chunk_size به صورت پیش فرض روی 2000 قرار خواهد گرفت .نکته :با فرض اینکه شما 10 الی 20 row یا سطر در جدول داشته باشید عدد 2000 داده ای کمتر از 100 کیلوبایت را میتواند جا به جا کند .متد latest | alatest :آخرین شی را در جدول بر اساس فیلد داده شده بازمیگرداند .این مثال آخرین شی Entry در جدول را با توجه به فیلد pub_date بازمیگرداند . (قدیمی ترین تاریخ)Entry.objects.latest(&#039;pub_date&#039;)همچنین می توانید بر اساس چندین فیلد آخرین شی را انتخاب کنید . به عنوان مثال برای دریافت شی Entry با آخرین زمان فیلد expire_date در زمانی که دو شی دارای pub_date یکسان هستند .Entry.objects.latest(&#039;pub_date&#039;, &#039;-expire_date&#039;)علامت منفی (-) در پشت expire_date به معنای ترتیب نزولی expire_date است . از آنجایی که متد latest آخرین شی را دریافت می کند ,نتیجه شی ای از مدل Entry با جدیدترین تاریخ در فیلد expire_date است .اگر در کلاس متای مدل خود get_latest_by را مشخص کنید ,می توانید آرگومان های درون متد را حذف کنید . فیلد های مشخص شده در آن گزینه به صورت پیش فرض استفاده خواهند شد .اگر نیز شی ای با مشخصات داده شده پیدا نشود (فیلد نامعتبر) , ارور DoesNotExist را دریافت می کنید .متد earliest| aearliest : جدیدترین شی را در جدول بر اساس فیلد داده شده بازمیگرداند .قوانین برای متد قبلی برای این متد نیز اعمال می شوند .متد first | afirst : این متد اولین شی از QuerySet را به شما بازمیگرداند . اگر شی ای را نیز پیدا نکند ,مقدار None را بازمیگرداند . به یاد داشته باشید اگر ordering مشخص نشده باشد اشیا بر اساس primarykey مرتب می شوند .برای مثال :p = Article.objects.order_by(&#039;title&#039;, &#039;pub_date&#039;).first()توجه داشته باشید این متد برای ساده سازی کد به کار می رود . مثال زیر برابر با کد بالا است . (الگوریتم مثال بالا به شکل زیر خواهد بود)try:
    p = Article.objects.order_by(&#039;title&#039;, &#039;pub_date&#039;)[0]
except IndexError:
    p = Noneمتد last | alast :این متد آخرین شی از QuerySet را به شما بازمیگرداند .همچنین قوانین مشابه با متد first دارد .متد aggregate | aaggregate : این متد برای محاسبه مقادیر در کل یک QuerySet به کار می رود . برای مثال مقادیری مانند جمع یا میانگین مقادیر یک فیلد در کل QuerySet . همچنین می توانید این متد را با متد های دیگر نیز به کار ببرید . نتیجه این متد به صورت یک دیکشنری به شما بازگشت داده خواهد شد .آرگومانی که در این متد ارسال می کنید می تواند به دو صورت باشد . هر دو آنها را بررسی خواهیم کرد .برای مثال برای به دست آوردن تعداد اشیا Author که در Blog__entry مشارکت دارند ,کد زیر را مینویسیم . در این نوع از آرگومان نویسی , Key در دیکشنری خروجی خودکار تولید می شود .&gt;&gt;&gt; from django.db.models import Count
&gt;&gt;&gt; q = Blog.objects.aggregate(Count(&#039;entry&#039;))
{&#039;entry__count&#039;: 16}در مثال دوم همان عملیات بالا را انجام می دهیم ,اما اینبار آرگومان ارسال شده را به شکل زیر مینویسیم . در این نوع Key در دیکشنری خروجی نام مشخص شده ای خواهد داشت .&gt;&gt;&gt; q = Blog.objects.aggregate(number_of_entries=Count(&#039;entry&#039;))
{&#039;number_of_entries&#039;: 16}گرچه بعدا در مورد این متد و فرایند Aggregation بیشتر صحبت خواهیم کرد . در واقع یک تاپیک کامل درباره این موضوع وجود دارد !متد exists | aexists : این متد اگر QuerySet دارای شی باشد (نتیجه داشته باشد) True و اگر شی ای درون آن نباشد مقدار False را بازمیگرداند . این متد تلاش می کند این کار را به سریعترین روش ممکن انجام دهد اما تقریبا QuerySet را به طور کامل در دیتابیس اجرا می کند (باعث ایجاد فرایند در دیتابیس می شود).برای مثال ,برای اینکه بفهمید که آیا یک QuerySet نتیجه ای داشته یا خیر :if some_queryset.exists():
    print(&amp;quotThere is at least one object in some_queryset&amp;quot)که بخاطر استفاده از این متد سریعتر از روش زیر است :if some_queryset:
    print(&amp;quotThere is at least one object in some_queryset&amp;quot)البته لازم است بگوییم سرعت آن در QuerySet های بزرگتر چشم گیر است .علاوه بر این اگر some_queryset هنوز در دیتابیس اجرا نشده باشد ,اما قرار باشد زمانی اجرا بشود , some_queryset.exists مجبور به انجام فرایند ها و Query های بیشتر می شود . پس بهتر است در آن زمان از روش دوم استفاده کنید .متد contains | acontains : شما به این متد یک obj یا شی ارسال می کنید و برای شما بررسی می کند که آیا این شی درون QuerySet وجود دارد یا خیر . اگر وجود داشته باشد به شما True و اگر نه False را بازمیگرداند . این متد این کار را به سریعترین شکل ممکن انجام می دهد .برای مثال برای اینکه بفهمید آیا یک شی درون QuerySet وجود دارد از متد contains استفاده کنید (با فرض اینکه شی درون یک متغییر با نام obj قرار گرفته باشد) :if some_queryset.contains(obj):
    print(&#039;Entry contained in queryset&#039;)کد بالا سریعتر از کد زیر خواهد بود . زیرا کد زیر مجبور است یکبار کل QuerySet را اجرا کند و سپس روی آن پیمایش کند .if obj in some_queryset:
    print(&#039;Entry contained in queryset&#039;)مانند متد exists این متد نیز اگر some_queryset هنوز در دیتابیس اجرا نشده باشد ,اما قرار باشد زمانی اجرا بشود , some_queryset.contains مجبور به انجام فرایند ها و Query های بیشتر می شود . پس بهتر است در آن زمان از روش دوم استفاده کنید .متد update | aupdate : این متد برای بروزرسانی مقادیر فیلد های مشخص شده بکار می رود. در واقع یک دستور UPDATE در SQL ایجاد می‌کند. در آخر برای نتیجه تعداد ردیف هایی که با فیلد های شما مطابقت داشتند را بازمی‌گرداند. ممکن است این عدد با ردیف هایی که آپدیت شده اند مساوی نباشد، زیرا ممکن است برخی از آنها در آن زمان مقادیر جدید داشته باشند. برای مثال برای قرار دادن مقدار False برای فیلد comments_on در اشیا Entry که مربوط به سال 2010 هستند باید کد زیر را بنویسید.&gt;&gt;&gt; Entry.objects.filter(pub_date__year=2010).update(comments_on=False)البته در مثال بالا فرض میکنیم مدل Entry دارای فیلد های comments_on و pub_date است.همچنین شما می توانید چندین فیلد را آپدیت کنید. جنگو محدودیتی برای تعداد فیلد ها اعمال نمی کند. برای مثال در کد زیر ما فیلد های headline و comments_on را آپدیت کردیم.&gt;&gt;&gt; Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline=&#039;This is old&#039;)به یاد داشته باشید این متد فورا تغییرات را اعمال می‌کند و تنها یک محدودیت دارد. این متد نمی‌تواند روی مدل های مرتبط یا مدل های ارجاع داده شده تغییری اعمال کند. برای مثال شما نمی‌توانید این کد را بنویسید :&gt;&gt;&gt; Entry.objects.update(blog__name=&#039;foo&#039;) # Won&#039;t work!هرچند فرایند فیلتر کردن بر اساس مدل های ارجاع داده شده یا مرتبط هنوز امکان پذیر است. برای مثال :&gt;&gt;&gt; Entry.objects.filter(blog__id=1).update(comments_on=True)البته توجه کنید که شما نمی‌توانید متد update را روی QuerySet هایی که slice داده شده اند و یا نمی‌توانند فیلتر شوند، فراخوانی کنید. متد update تحت اشیایی که تحت تاثیر قرار گرفته شده اند را بازمی‌گرداند که ممکن است با تعداد اشیا آپدیت شده برابر نباشند.&gt;&gt;&gt; Entry.objects.filter(id=64).update(comments_on=True)
1
&gt;&gt;&gt; Entry.objects.filter(slug=&#039;nonexistent-slug&#039;).update(comments_on=True)
0
&gt;&gt;&gt; Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132البته توجه داشته باشید متد update مخصوص زمانی است که فقط می خواهید یک شی را آپدیت کنید و نمی خواهید به داده های آن شی دسترسی داشته باشید (برای مثال مقدار یک فیلد از آن را دریافت کنید) . پس برای مثال بجای نوشتن کد زیر که ابتدا شی را در حافظه بارگذاری می کند .e = Entry.objects.get(id=10)e.comments_on = Falsee.save()می توانید کد زیر را بنویسید که لازم به بارگذاری شی در حافظه ندارد .Entry.objects.filter(id=10).update(comments_on=False)همچنین استفاده از این متد باعث می شود تا از امکان تغییر در داده های دیتابیس در مدت زمان میان بارگذاری شی و فراخوانی save جلوگیری شود .در نهایت باید بدانید که update در سطح SQL تغییرات را اعمال می کند و متد save دیتابیس شما را یا سیگنال های pre_save و post_save را فراخوانی نمی کند . اگر می خواهید تعداد زیادی از رکورد ها را برای مدلی که متد save سفارشی دارد ,آپدیت کنید ,می توانید به شکل زیر اینکار را انجام دهید :for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()نکته خاص این است که زنجیر کردن یا اتصال متد های order_by و update فقط در دیتابیس های MariaDB و MySQL پشتیبانی می شود (برای مثال ()Entry.objects.oreder_by(‘title’).update) . در دیتابیس های دیگر این اتصال نادیده گرفته می شود . کد زیر را نگاه کنید :Entry.objects.order_by(&#039;-number&#039;).update(number=F(&#039;number&#039;) +1)نکته : البته متد order_by در صورتی که حاوی annotations و یا فیلد های وراثتی و یا related lookups باشد ,نادیده گرفته می شود .متد delete| adelete : این متد برای شما اشیا را از دیتابیس حذف می کند و اشیا حذف شده را به شکل یک دیکشنری به همراه تعداد اشیا حذف شده و تعداد حذف شده ها در هر نوع اشیا را بازمیگرداند . در ادامه بهتر این موضوع را درک می کنید .این متد سریعا بر دیتابیس اعمال می شود و روی QuerySet هایی که slice شده اند یا نمی توانید متد filter را روی آنها اعمال کنید ,کار نمی کند .برای مثال برای حذف تمام اشیا مدل Entry که فیلد blog آنها یک شی خاص است ,کد زیر را مینویسید . (نتیجه به شکل یک دیکشنری که تعداد حذف ها را نشان می دهد خواهد بود . برای مثال در اینجا دو شی از blog و دو شی از authors حذف شده است)&gt;&gt;&gt; b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
&gt;&gt;&gt; Entry.objects.filter(blog=b).delete()
(4, {&#039;blog.Entry&#039;: 2, &#039;blog.Entry_authors&#039;: 2})به طور پیش فرض ,فیلد ForeignKey نوع ON DELETE CASCADE را تقیلد می کند . یعنی اگر شی ای با ForeignKeyب ه شی دیگری اشاره کند (رابطه داشته باشد) و شی دوم حذف شود , شی اول نیز حذف می شود . برای مثال :&gt;&gt;&gt; blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
&gt;&gt;&gt; blogs.delete()
(5, {&#039;blog.Blog&#039;: 1, &#039;blog.Entry&#039;: 2, &#039;blog.Entry_authors&#039;: 2})البته از طریق on_delete در فیلد ForeignKey مربوطه میتوانید این رفتار را تغییر دهید . درباره تمام آنها قبلا صحبت کرده ایم .متد delete یک حذف انبوه انجام می دهد و متد delete را از مدل شما فراخوانی نمی کند . اما با این حال سیگنال های pre_delete و post_delete را ارسال می کند .نکته :اگر نمی دانید سیگنال های چه هستند ,نگران نباشید . بعدا درباره آنها صحبت می کنیم . ابتدا روی مطالبی که میفهمید تمرکز کنید .جنگو برای ارسال سیگنال ها و مدیریت CASCADE نیاز به بارگذاری اشیا در حافطه خواهد داشت . در شرایطی که به سیگنال ها و on_delete نوع CASCADE نیازی ندارید ,جنگو ممکن است بدون بارگذاری اشیا در حافظه اقدام به حذف آنها کند . در حذف اشیا به تعداد بالا این فرایند می تواند به افزایش سرعت حافظه کمک کند . یعنی باید on_delete را روی چیزی غیر از CASCADE و سیگنال ها را غیرفعال کنید . (البته نه همیشه)مقدار DO_NOTHING در on_delete فیلد های ForeignKey مانع در افزایش سرعت حافظه نمی شوند .متد explain | aexplain : رشته ای را بازمیگرداند که نحوه اجرای QuerySet را توضیح می دهد . جزئیات آن شامل join و index هایی است که در SQL آن QuerySet به کار رفته است . دانستن این جزئیات ممکن است برای بهبود عملکرد برنامه به شما کمک کند . (تغییری را روی QuerySet انجام نمی دهد)به عنوان مثال با فرض اینکه دیتابیس شما PostgreSQL باشد :&gt;&gt;&gt; print(Blog.objects.filter(title=&#039;My Blog&#039;).explain())
SeqScan on blog (cost=0.00..35.50 rows=10 width=12)
    Filter: (title = &#039;My Blog&#039;::bpchar)خروجی در میان هر دیتابیس متفاوت خواهد بود . در واقع هر دیتابیس نحوه اجرای کد را به شکل های مختلف ارائه می کند .متد explain در تمامی دیتابیس ها به جز Oracle پشتیبانی می شود .این متد همچنین یک پارامتر با نام format نیز دریافت می کند . در واقع این پارامتر فرمت متن خروجی است . تنوع فرمت ها وابسته به دیتابیس و پشتبیبانی آن متفاوت است . به صورت پیش فرض نیز همه ی آنها به صورت text هستند.دیتابیس PostgreSQL :فرمت های XML و TEXT و JSON و YAML را پشتیبانی می کند .دیتابیس MariaDB و MySQL :فرمت TEXT و JSON را پشتیبانی می کند .دیتابیس MySQL +8.0.16 :علاوه بر TEXTو JSON از TREE نیز پشتیبانی می کند که مشابه خروجی TEXT در PostgreSQL است .برای استفاده از هر فرمت کافی است آن را در پارامتر وارد کنید (format=‘TEXT’) .در بعضی از دیتابیس ها مانند PostgreSQL می توانید از flag استفاده کنید . آنها باعث می شوند تا اطلاعات بیشتری درباره QuerySet به شما نمایش داده شود . برای مثال :&gt;&gt;&gt; print(Blog.objects.filter(title=&#039;My Blog&#039;).explain(verbose=True, analyze=True))
SeqScan on public.blog(cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004
rows=10 loops=1)
    Output: id, title
    Filter: (blog.title = &#039;My Blog&#039;::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 msدر برخی از دیتابیس ها استفاده از بعضی flagباعث ایجاد اثرات مخرب روی دیتابیس می شود . برای مثال ANALYZE که در MariaDB و MySQL 8.0.18 و PostgreSQL پشتیبانی می شود ,در صورتی که توابعی در QuerySet وجود داشته باشند باعث تغییرات در داده های دیتابیس شود .در این جلسه برخی دیگر از متد های جنگو را بررسی کردیم و این مبحث را در همین نقطه خواهیم بست . در جلسه بعدی به بررسی کامل تمامی لوکاپ ها (field lookup) خواهیم پرداخت .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 15 Dec 2023 13:06:49 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و سه | آشنایی با تمامی متد های جنگو | پارت اول</title>
                <link>https://virgool.io/codenevis/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D8%B3%D9%87-%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D9%85%D8%AA%D8%AF-%D9%87%D8%A7%DB%8C-queryset-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%A7%D9%88%D9%84-t3zg4krxu5g4</link>
                <description>در این جلسه انواع متد ها و نحوه عملکرد آنها را در جنگو بررسی خواهیم کرد . با ما همراه باشید .آموزش جنگو : جلسه سی و سه | آشنایی با متد های Queryset | پارت اولبررسی متد هایی که QuerySet جدید بازمیگردانندمتد ها از نظر انواع به دو دسته تقسیم می شوند . متد هایی که یک QuerySet جدید بازمیگردانند و متد هایی که نتایج دیتابیس را به صورت مستقیم بازمیگردانند . جنگو طیف وسیعی از روش های بازگرداندن QuerySet را ارائه می دهد . در بخش های پیشین گفتیم که می خواهیم در قسمت های بعدی به طور کامل درباره متد هایی که یک QuerySet جدید بازمیگردانند صحبت کنیم . دانستن این متد ها به شما کمک می کند تا بفهمید جنگو چطور کار می کند و چطور بهتر از آن استفاده کنید .این متد ها سریعا در دیتابیس اجرا نمی شوند و برای استفاده در محیط asynchronous ایمن هستند .متد filter :درباره این متد صحبت هایی کرده بودیم . این متد یک QuerySet جدید را بازمیگرداند که حاوی اشیایی است که با پارامتر های داده شده مطابقت دارند . پارامتر ها با دستور AND به هم اتصال داده می شوند و کد SQL مورد نظر را می سازند . همچنین برای اجرای جستجوی های پیچیده تر می توانید از Q (قبلا صحبت کرده ایم) در پارامتر ها استفاده کنید .متد exclude :این متد یک QuerySet جدید را بازمیگرداند که حاوی اشیایی است که با پارامتر های داده شده مطابقت ندارند . دستور SQL آن به صورتی تولید می شود که پارامتر ها با AND اتصال داده شدند و کل دستور در ()NOT محصور شده است .به عنوان مثال , کد زیر تمام اشیایی را پیدا می کند که date_pub آنها برای سال 2005-1-3 و بعد از آن نیست و headline آنها برابر با Hello نیست .Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline=&#039;Hello&#039;)کد SQL تولید شده برای کد بالا به صورت زیر است :SELECT ... WHERE NOT (pub_date &gt; &#039;2005-1-3&#039; AND headline = &#039;Hello&#039;)مثال زیر ابتدا تمام اشیایی را پیدا می کند که date_pub آنها بزرگتر از تاریخ 2005-1-3 نباشد . سپس از میان آنها اشیایی را بازمیگرداند که headline آنها برابر با Hello نباشد .Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline=&#039;Hello&#039;)در SQL به صورت زیر تبدیل خواهد شد :SELECT ... WHERE NOT pub_date &gt; &#039;2005-1-3&#039; AND NOT headline = &#039;Helloهمانطور که مشخص است , مثال دوم قرار است تعداد محدود تری از نتایج را به ما نشان دهد .متد annotate : یک لیست حاوی از اشیا را به شکل QuerySet بازمیگرداند که بر اساس آرگومان ارسالی شما مرتب شده اند . آرگومان شما برای مقایسه می تواند یک مقدار ساده , ارجاع به یک فیلد از همان مدل یا روابط و یا یک عبارت مقایسه ای (مانند averages یا sums) باشد .هر آرگومان در این متد یک مقایسه است که به هر شی در QuerySet بازگشت داده شده به شما اضافه شده است .متدهایی که برای میانگین گیری و aggregation استفاده می شوند , در مثال های بعدی توضیح خواهند داده شد .برای استفاده از Annonation خود , باید از کلمات کلیدی ای به عنوان نام مستعار (نامی که آن را صدا می زنید) استفاده کنید . بطور پیش فرض و برای زمانی که نمی خواهید نامی را به متد خود اختصاص دهید , از ترکیب نام تابع و فیلد مورد استفاده , بهره می برید . البته به یاد داشته باشید این کار را فقط برای متد هایی می توانید انجام دهید که به یک فیلد منفرد اشاره کنند و برای هر چیز دیگری باید یک آرگومان کلمه کلیدی را اختصاص دهید .برای مثال فرض کنید که می خواهید به یک فهرست از وبالگ های خود دسترسی پیدا کنید تا مشخص کنید در هر blog چند entry ساخته شده است : (در اینجا از آرگومان کلمه کلیدی استفاده نکردیم زیرا به یک فیلد منفرد اشاره کرده ایم)&gt;&gt;&gt; from django.db.models import Count 
&gt;&gt;&gt; q = Blog.objects.annotate(Count(&#039;entry&#039;)) 
# The name of the first blog 
&gt;&gt;&gt; q[0].name &#039;Blogasaurus&#039; 
# The number of entries on the first blog 
&gt;&gt;&gt; q[0].entry__count 
42اما در صورتی که فیلد های شما در ورودی منفرد نبودند , باید یک آرگومان کلمه کلیدی را تعریف کنید و سپس به وسیله آن , کنترل کردن عملیات ها را انجام بدهید .&gt;&gt;&gt; q = Blog.objects.annotate(number_of_entries=Count(&#039;entry&#039;)) 
# The number of entries on the first blog, using the name provided 
&gt;&gt;&gt; q[0].number_of_entries
42متد alias : کارایی یکسانی با متد annotate دارد . اما بجای اینکه اشیا را در QuerySet مرتب کند , آنها را در QuerySet با متد های مختلف در خودش با ترتیب ذخیره می کند . این برای زمانی مفید است که بخواهید نتایج را بجای استفاده کردن , فیلتر کنید یا به عنوان بخشی از یک جستجو از آن استفاده کنید همچنین استفاده از این متد باعث می شود کار دیتابیس سبک تر بوده و برنامه بهتری را ارائه دهید .برای مثال فرض کنید شما می خواهید شی های Blog را پیدا کنید که بیشتر از 5 عدد entry را درون خود جای داده اند (یعنی با 5 entry رابطه دارند) ولی به جزییات و تعداد دقیق entry نیاز ندارید . کد زیر را می توانید برای این کار بنویسید :&gt;&gt;&gt; from django.db.models import Count 
&gt;&gt;&gt; blogs = Blog.objects.alias(entries=Count(&#039;entry&#039;)).filter(entries__gt=5)متد ()alias را می توان همراه با ()annotate،() exclude،() filter،() by_order و ()update استفاده کرد. برای استفاده از عبارت پیچیده با متد های دیگر باید آن را با یک Annonation ترکیب کنید .Blog.objects.alias(entries=Count(&#039;entry&#039;)).annotate( 
    entries=F(&#039;entries&#039;),
).aggregate(Sum(&#039;entries&#039;))متد order_by :به صورت پیش فرض ,مرتب کردن اشیا در جنگو با استفاده از تاپلی که درون گزینه های متا (Meta) تعریف شده است ,صورت می گیرد . گرچه شما می توانید این فرایند را در یک QuerySet با متد order_by بازنویسی کنید . یعنی گزینه های متا را نادیده بگیرید و بر اساس آرگومان های درون این متد مرتب سازی اشیا را انجام دهید .مثال :Entry.objects.filter(pub_date__year=2005).order_by(&#039;-pub_date&#039;, &#039;headline&#039;)نتایج بالا بر اساس فیلد pub_date بر اساس نزولی و سپس بر اساس فیلد headline با ترتیب صعودی مرتب می شوند. علامت منفی پشت فیلد (به عنوان مثال pub_date-) نشان دهنده نزولی بودن ترتیب فیلد است . همچنین برای تصادفی کردن مرتب سازی از ? استفاده می شود .Entry.objects.order_by(&#039;?&#039;)نکته : فرایند استفاده از ? وابسته به دیتابیسی که استفاده می کنید ممکن است سنگین باشد .برای مرتب سازی بر اساس یک فیلد از یک مدل متفاوت ,از همان روشی استفاده کنید که در جست و جو ها استفاده می کنید . یعنی نام فیلد مرتبط و به دنبال آن یک زیر خط دوجفتی __ و به دنبال آن نام فیلد در مدل جدید . برای مثال :Entry.objects.order_by(&#039;blog__name&#039;, &#039;headline&#039;)اگر بخواهید بر اساس فیلدی که مربوط به مدل دیگری است مرتب سازی را انجام دهید , جنگو از ترتیب پیش فرض تعیین شده در Meta در آن مدل استفاده می کند . اگر این گزینه مشخص نشده باشد , از طریق primary key در آن مدل مرتب سازی را انجام می دهد . پس از آنجایی که مدل Blog هیچ گزینه ordering خاصی را در متای خود مشخص نکرده است :Entry.objects.order_by(&#039;blog&#039;)مثال بالا ,مساوی است با مثال زیر :Entry.objects.order_by(&#039;blog__id&#039;)اگر مدل Blog دارای ordering=[“name”] در گزینه های متای خود بود آنگاه مثال بالا به شکل زیر تغییر میافت :Entry.objects.order_by(&#039;blog__name&#039;)شما همچنین می توانید با استفاده از متد های desc و asc ,مرتب سازی را با query expressions نیز انجام دهید :Entry.objects.order_by(Coalesce(&#039;summary&#039;, &#039;headline&#039;).desc())درباره asc و desc قبلا توضیح دادیم . آنها برای نزولی کردن یا صعودی کردن ترتیب به کار می روند . همچنین دارای آرگومان های null_last و nulls_first هست که نحوه مرتب سازی مقادیر null را کنترل می کند . (اولی null را در آخر مرتب می کند و دومی در ابتدای لیست خروجی)نکته : شما می توانید همچنین یک فیلد multi-value یا فیلدی مانند یک ForeignKey یا ManyToManyField (که چند مقدار را درون خود دارند) را به عنوان آرگومان برای مرتب سازی به متد ارسال کنید . برای مثال :class Event(Model):
    parent = models.ForeignKey(
        &#039;self&#039;,
        on_delete=models.CASCADE,
        related_name=&#039;children&#039;,
    )
    date = models.DateField()
  
Event.objects.order_by(&#039;children__date&#039;)شما باید در استفاده از اینگونه فیلد ها در order_by مراقب باشید . برای نمونه ,در مثال بالا هر شی از مدل Event با چند شی از همان مدل رابطه داشته باشد ,بنابراین بر اثر این تکرار ممکن است چرخه ای به وجود بیاید و QuerySet شما تعداد اشیایی بیشتر از حتی خود اشیا مدل بازگرداند ! این عملیات اشتباه است و اصلا فایده ای ندارد .بنابراین فقط اطمینان حاصل کنید که هر شی یکبار در QuerySet تکرار شده است و رویکرد شما مشکلی ندارد .شما هیچ راهی ندارید تا اشیا را بر اساس بزرگ و کوچکی حروف مرتب کنید . گرچه خود جنگو به کوچک یا بزرگ بودن حساسیت نشان می دهد و نتایج را صحیح بازمیگرداند .می‌توانید بر اساس فیلدی که با Lower به حروف کوچک تبدیل شده است سفارش دهید که به ترتیب مطابق با حروف بزرگ می‌رسد :Entry.objects.order_by(Lower(&#039;headline&#039;).desc())نکته :اگر نمی خواهید روی QuerySet شما هیچ مرتب سازی ای صورت بگیرد ,حتی مرتب سازی پیش فرض (در ordering کلاس متا مدل) ,فقط کافی است تا متد order_by را بدون هیچ آرگومانی فراخوانی کنید . همچنین اگر می خواهید بفهمید که آیا یک QuerySet مرتب سازی شده است یا خیر از ویژگی QuerySet.ordered استفاده کنید که اگر برابر با True باشد ,یعنی QuerySet شما از قبل مرتب سازی شده است .هر بار متد های order_by متد قبلی خود را نادیده می گیرند . یعنی در مثال زیر اشیا بر اساس pub_date مرتب می شوند و نه headline :Entry.objects.order_by(&#039;headline&#039;).order_by(&#039;pub_date&#039;)نکته : مرتب سازی یک عملیات ساده بر روی دیتابیس نیست و هر بار که اشیایی را مرتب می کنید ,فرایند سنگینی بر روی دیتابیس اعمال می شود .علاوه بر این اگر یک ordering را مشخص نکنید ,اشیا با ترتیب مشخصی به شما بازگردانده نمی شوند و این ممکن است برای شما کمی دردسر ساز باشد . یک ترتیب خاص تنها زمانی تضمین می شود که یک مرتب سازی بر روی فیلدی منحصر به فرد از آن انجام پذیرد . (دلیل آن این است که اگر دو فیلد مقادیر یکسان داشته باشند ,متد order_by معلوم نیست که هر بار کدام یک از آنها را ابتدا مرتب می کند)متد reverse :از متد reverse برای معکوس کردن ترتیب اشیای یک Query استفاده می شود . این یعنی ترتیب  نتایج را دقیقا برعکس می کند . فراخوانی این متد برای بار دوم روی یک Query ترتیب را به حالت اولیه بازمیگرداند .برای مثال ,برای دریافت 5 شی آخر در یک Query به شکل زیر اقدام کنید :my_queryset.reverse()[:5]توجه کنید که کد بالا دقیقا مانند یک برش لیست یا slice کردن یک لیست در پایتون نیست . در واقع مثال بالا ,ابتدا آخرین شی ,سپس شی ماقبل آن و در غیره را بازمیگرداند . اما اگر یک slice داشتیم ابتدا شی پنجم را میدیدیم . جنگو از slice به شکل reverse پشتیبانی نمی کند زیرا در SQL امکان پذیر نیست .همچنین ,توجه داشته باشید که reverse باید بر روی QuerySet ای اجرا شود که دارای ترتیب است (ترتیب پیش فرض در کلاس متا یا استفاده از متد order_by) . در غیر این صورت فراخوانی متد reverse روی آن تاثیری نخواهد داشت .متد distinct : یک QuerySet جدید را برمی‌گرداند که از SELECT DISTINCT در کد SQL خود استفاده می‌کند. این کار ردیف های (row) تکراری را از نتایج QuerySet حذف می کند .به طور پیش فرض، یک QuerySet ردیف های تکراری را حذف نمی کند . در عمل ، این به ندرت مشکل ساز است، زیرا QuerySet های ساده مانند Blog.objects.all امکان تکرار ردیف های نتیجه را ندارند . با این حال، اگر QuerySet شما از چندین جدول استفاده می کند ، زمانی که یک QuerySet اجرا می شود، ممکن است نتایج تکراری دریافت کنید. در آن زمان است که از distinct استفاده می کنید.نکته : در دیتابیس PostgreSQL میتوانید به متد distinct آرگومان هایی را ارسال کنید (نام فیلدها) . این آرگومان ها نام فیلد هایی هستند که دستور DISTINCT فقط برای آنها باید اعمال شود . در حالت عادی این دستور در SQL تمام سطر ها یا row را جستجو می کند و آیتم های تکراری را در هر کدام حذف می کند , اما با استفاده از آرگومان ها ,دستور SQL شما به شکل SELECT DISTICNT ON درخواهد آمد و روی فیلد های انتخاب شده شما اجرا خواهد شد . (یعنی فقط در همان سطر ها به دنبال آیتم های تکراری خواهد بود)نکته : وقتی نام فیلد ها را در متد مشخص می کنید باید متد order_by را نیز برای QuerySet ارائه دهید . همچنین ترتیب شروع نام فیلد ها در هر دو متد باید یکسان باشند . برای مثال :&gt;&gt;&gt; Author.objects.distinct()
[...]
&gt;&gt;&gt; Entry.objects.order_by(&#039;pub_date&#039;).distinct(&#039;pub_date&#039;)
[...]
&gt;&gt;&gt; Entry.objects.order_by(&#039;blog&#039;).distinct(&#039;blog&#039;)
[...]
&gt;&gt;&gt; Entry.objects.order_by(&#039;author&#039;, &#039;pub_date&#039;).distinct(&#039;author&#039;, &#039;pub_date&#039;)
[...]
&gt;&gt;&gt; Entry.objects.order_by(&#039;blog__name&#039;, &#039;mod_date&#039;).distinct(&#039;blog__name&#039;, &#039;mod_date&#039;)
[...]
&gt;&gt;&gt; Entry.objects.order_by(&#039;author&#039;, &#039;pub_date&#039;).distinct(&#039;author&#039;)
[...]توجه داشته باشید که در فیلد های روابطی ,فراخوانی آنها در متد order_by باعث می شود تا از فیلدی برای آرگومان استفاده کنند که در گزینه های متا و ordering آنها تعریف شده است . پس ممکن است استفاده از دو متد order_by و distinct با هم باعث ایجاد مشکلاتی شود . این مشکلات زمانی ایجاد می شوند که ordering و نام فیلد مورداستفاده از مدل ارجاع داده شده ,یکسان نباشد . برای مثال اگر مدل Blog یک ordering را بر اساس فیلد name خود تعریف کند :Entry.objects.order_by(&#039;blog&#039;).distinct(&#039;blog&#039;)کد بالا با شرط بالا کار نخواهد کرد . زیرا در متد order_by شما از فیلد name برای مرتب سازی استفاده می کنید و در متد distinct با فیلد id کار می کنید . برای حل این مشکل می توانید به صورت زیر , خیلی صریح به فیلد های مدل ارجاع داده شده اشاره کنید .Entry.objects.order_by(&#039;blog__id&#039;).distinct(&#039;blog__id&#039;)متد values :یک QuerySet را در صورت استفاده به صورت یک iterable بازمیگرداند که به جای اشیا یک مدل ,دیکشنری هایی را دارا است که هر کدام یک شی از همان مدل هستند .آنها به صورت Key-value هستند و هر Key یک فیلد را از شی مشخص می کند که مقدار آن نیز در value مشخص شده است .مثال زیر تفاوت استفاده از values را در یک QuerySet با استفاده نکردن از آن نشان می دهد .# This list contains a Blog object.
&gt;&gt;&gt; Blog.objects.filter(name__startswith=&#039;Beatles&#039;)
&lt;QuerySet[&lt;Blog: Beatles Blog&gt;]&gt;
# This list contains a dictionary.
&gt;&gt;&gt; Blog.objects.filter(name__startswith=&#039;Beatles&#039;).values()
&lt;QuerySet[{&#039;id&#039;: 1, &#039;name&#039;: &#039;Beatles Blog&#039;, &#039;tagline&#039;: &#039;All the latest Beatles news.&#039;}]&gt;همچنین این متد آرگومان هایی را تحت عنوان fields* دریافت می کند که نام فیلد هایی از مدل هستند و تعیین میکنند که دستور SELECT روی کدام فیلد ها اجرا شود . در واقع پس از تعیین فیلد ها , در دیکشنری های بازگشت داده شده فقط فیلد هایی که انتخاب کردید , وجود دارند (و مقدارهایشان) . اگر این آرگومان را مشخص نکنید , جنگو تمام فیلد ها را در خروجی نمایش می دهد .برای مثال :&gt;&gt;&gt; Blog.objects.values()
&lt;QuerySet[{&#039;id&#039;: 1, &#039;name&#039;: &#039;Beatles Blog&#039;, &#039;tagline&#039;: &#039;All the latest Beatles news.&#039;}]&gt;
&gt;&gt;&gt; Blog.objects.values(&#039;id&#039;, &#039;name&#039;)
&lt;QuerySet[{&#039;id&#039;: 1, &#039;name&#039;: &#039;Beatles Blog&#039;}]&gt;همچنین شما می توانید مقادیر keyword argument را برای متد مشخص کنید که به عنوان annotate در متد اجرا می شوند . برای مثال :&gt;&gt;&gt; from django.db.models.functions import Lower
&gt;&gt;&gt; Blog.objects.values(lower_name=Lower(&#039;name&#039;))
&lt;QuerySet[{&#039;lower_name&#039;: &#039;beatlesblog&#039;}]&gt;نکته :اگر شما بر فرض مثال یک فیلد ForeignKey داشته باشید که نام آن foo باشد ,استفاده از متد values برای آن باعث می شود تا مقدار foo_id در خروجی نشان داده شود . این در صورتی قابل تغییر است که در ارگومان fields* ذکر کنید دقیقا کدام فیلد از مدل ارجاع داده شده را نیاز دارید . (مثال foo__name)به مثال صفحه بعد خوب دقت کنید . می بینید که استفاده از نام فیلد بدون id_ دوباره به شما فیلد id را از مدل ارجاع داده شده بازمیگرداند این یک پیش فرض برای جنگو است :&gt;&gt;&gt; Entry.objects.values()
&lt;QuerySet[{&#039;blog_id&#039;: 1, &#039;headline&#039;: &#039;First Entry&#039;, ...}, ...]&gt;
&gt;&gt;&gt; Entry.objects.values(&#039;blog&#039;)
&lt;QuerySet[{&#039;blog&#039;: 1}, ...]&gt;
&gt;&gt;&gt; Entry.objects.values(&#039;blog_id&#039;)
&lt;QuerySet[{&#039;blog_id&#039;: 1}, ...]&gt;نکته :به یاد داشته باشید شما همچنان می توانید بعد از فراخوانی values زنجیره ای را ایجاد کنید . (یعنی از متد هایی مانند order_by یا filter استفاده کنید) . برای مثال دو کد زیر با هم نتایج یکسانی دارند :Blog.objects.values().order_by(&#039;id&#039;)Blog.objects.order_by(&#x27;id&#x27;).values()نکته : همچنین به این نکته توجه داشته باشید که در دیتابیس SQLite و فیلد های JSONField شما به علت دلایلی مانند نحوه عملکرد JSON_TYPE و یا JSON_EXTRACT (دستور های SQL) ,مقادیر True و False و None را بجای “true” , “false”و “null”در صورت استفاده از values دریافت خواهید کرد .متد values_list :همان values است که به جای بازگرداندن دیکشنری ,به جای هر شی یک تاپل حاوی مقادیر فیلد های انتخاب شده توسط شما بازمیگرداند . نکته این است که مقادیر داخل تاپل به ترتیب فیلد های انتخاب شده شما هستند که در آرگومان متد مشخص می کنید . برای مثال :&gt;&gt;&gt; Entry.objects.values_list(&#039;id&#039;, &#039;headline&#039;)
&lt;QuerySet[(1, &#039;First entry&#039;), ...]&gt;
&gt;&gt;&gt; from django.db.models.functions import Lower
&gt;&gt;&gt; Entry.objects.values_list(&#039;id&#039;, Lower(&#039;headline&#039;))
&lt;QuerySet[(1, &#039;first entry&#039;), ...]&gt;نکته : در صورتی که یک فیلد را در آرگومان ها استفاده کرده اید , می توانید مقدار flat را در آرگومان مشخص کنید . دو مقدار False و True را قبول می کند که اگر برابر با True باشد مقادیر را بجای ریختن درون تاپل به صورت اصلی خودشان بازمیگرداند . هر مقدار در واقع اشاره به فیلد یک شی دارد . برای مثال :&gt;&gt;&gt; Entry.objects.values_list(&#039;id&#039;).order_by(&#039;id&#039;)
&lt;QuerySet[(1,), (2,), (3,), ...]&gt;
&gt;&gt;&gt; Entry.objects.values_list(&#039;id&#039;, flat=True).order_by(&#039;id&#039;)
&lt;QuerySet[1, 2, 3, ...]&gt;اگر هنگام ارسال چند فیلد آرگومان flat را مشخص کنید ,با ارور مواجه خواهید شد .شما همچنین می توانید آرگومان named=True را مشخص کنید و نتایج را به صورت namedtuple (مباحث پایتون 3.11) دریافت کنید . برای مثال :&gt;&gt;&gt; Entry.objects.values_list(&#039;id&#039;, &#039;headline&#039;, named=True)
&lt;QuerySet[Row(id=1, headline=&#039;First entry&#039;), ...]&gt;استفاده از namedtuple می تواند خواندن اطلاعات را از دیتابیس برای شما آسان تر کند . همچنین این فرایند کمتر دیتابیس را درگیر می کند .اگر هیچ مقداری را درون values_list ارسال نکنید ,این متد تمام فیلد های مدل را به ترتیب تعریف شدن آنها در خود مدل به شما بازمیگرداند .یک عملیات متداول این است که مقادیر را دریافت کنید و مقداری را از یک فیلد بخواهید که از یک شی خاص برگرفته شده است . برای پیدا کردن این شی و مقدار فیلد آن به صورت زیر از متد get پس از متد values_list استفاده کنید :&gt;&gt;&gt; Entry.objects.values_list(&#039;headline&#039;, flat=True).get(pk=1)
&#039;First entry&#039;سازندگان جنگو می گویند که هدف اصلی از طراحی متد های values و values_list این بوده که بدون فراخوانی و ایجاد یک نمونه از کلاس مدل ,بتوانید به اطلاعات درون آن دسترسی پیدا کنید . اما این هدف و قانون برای استفاده از فیلد های روابطی در جنگو نقض می شود زیرا قانونی با نام اینکه در هر ردیف یک شی باشد در این شرایط وجود ندارد .برای مثال ,در کد زیر ما فیلد ManyToManyField را به صورت آرگومان فرا خوانده ایم و مقدار فیلد headline را از آن برداشته ایم :&gt;&gt;&gt; Author.objects.values_list(&#039;name&#039;, &#039;entry__headline&#039;)
&lt;QuerySet[(&#039;Noam Chomsky&#039;, &#039;Impressions of Gaza&#039;),
    (&#039;George Orwell&#039;, &#039;Why Socialists Do Not Believe in Fun&#039;),
    (&#039;George Orwell&#039;, &#039;In Defenceof English Cooking&#039;),
    (&#039;Don Quixote&#039;, None)]&gt;اشیا مدل Author که با چندین شی از entry در رابطه هستند ,چندین بار نمایش داده می شوند ولی آنهایی که با هیچ کدام در رابطه نیستند ,مقدار None را دارند .به همین ترتیب هنگام query زدن برای یک ForeignKey به صورت معکوس ,شی های entry که با هیچ Author در ارتباط نیستند ,مقدار None را دارند .برای مثال :&gt;&gt;&gt; Entry.objects.values_list(&#039;authors&#039;)
 &lt;QuerySet[(&#039;Noam Chomsky&#039;,), (&#039;George Orwell&#039;,), (None,)]&gt;نکته : همچنین به این نکته توجه داشته باشید که در دیتابیس SQLite و فیلد های JSONField شما به علت دلایلی مانند نحوه عملکرد JSON_TYPE و یا JSON_EXTRACT (دستور های SQL) ,مقادیر Trueو False و None را بجای “true” , “false” و “null” در صورت استفاده از values_list دریافت خواهید کرد .متد dates :یک QuerySet بازمی گرداند که حاوی فهرستی از اشیا است که تاریخ های موجود در محتویات QuerySet را به بخش های خاص (سال و ماه و روز و هفته) تقسیم می کند و شما تعیین می کنید که کدام یک از بخش ها به شما نمایش داده شود .فیلد ارسال شده در آرگومان این متد باید یک DateField باشد و نوعی که تعیین می کنید باید یکی از year (سال) , month (ماه) , week (هفته) , day (روز) باشد . هر کدام را که تعیین کنید ,شی ها همان بخش ها را از تاریخ بازمیگردانند . البته توجه داشته باشید که فرمت های متفاوتی برای هر نوع در نظر گرفته شده است .نوع Year :یک لیست از اشیایی تهیه می کند که سال های آن متفاوت است . (برای مثال اگر 2 شی با سال 2005 داشته باشید ,فقط یکبار سال 2005 را نمایش می دهد)نوع Month : یک لیست از اشیایی تهیه می کند که ماه های آن متفاوت است . (برای مثال اگر 2 شی با ماه 2 داشته باشید , فقط یکبار ماه 2 را نمایش می دهد)نوع Week :به شکل بالایی است ولی برای هفته . این را در نظر بگیرید که هفته های آنها بر اساس روز دوشنبه تعیین می شود.نوع Day :یک لیست از اشیایی که روز های آنها متفاوت است . به زبانی دیگر تمام تاریخ های درون اشیا مدل .در آخر شما می توانید order یا ترتیب آنها را بر اساس ASC یا DESC مرتب کنید . (نزولی و صعودی)برای مثال :&gt;&gt;&gt; Entry.objects.dates(&#039;pub_date&#039;, &#039;year&#039;)
[datetime.date(2005, 1, 1)]
&gt;&gt;&gt; Entry.objects.dates(&#039;pub_date&#039;, &#039;month&#039;)
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
&gt;&gt;&gt; Entry.objects.dates(&#039;pub_date&#039;, &#039;week&#039;)
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
&gt;&gt;&gt; Entry.objects.dates(&#039;pub_date&#039;, &#039;day&#039;)
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
&gt;&gt;&gt; Entry.objects.dates(&#039;pub_date&#039;, &#039;day&#039;, order=&#039;DESC&#039;)
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
&gt;&gt;&gt; Entry.objects.filter(headline__contains=&#039;Lennon&#039;).dates(&#039;pub_date&#039;, &#039;day&#039;)
[datetime.date(2005, 3, 20)]متد datetimes :یک QuerySet را بازمیگرداند که حاوی اشیایی از کلاس datetime.datetime است و همه تاریخ های موجود از یک نوع خاص را در یک مدل نمایش می دهد .یک آرگومان اجباری field_name دارد که باید نام یک DateTimeField از مدل شما باشد .نوع انتخابی نیز باید یکی از year (سال) , month (ماه) , week (هفته) , day (روز) ,ساعت (hour) ,دقیقه (minute) , ثانیه (second) باشد . هر شی و تاریخ آن با توجه به نوع انتخابی شما ,تفسیر و بازگردانده می شود .همچنین ترتیب آن به طور پیش فرض روی ASC است . دو مقدار ASC و DESC برای این QuerySet موجود هستند . (نزولی و صعودی)متد none : این متد زمانی استفاده می شود که می خواهید یک QuerySet خالی داشته باشید . در واقع این QuerySet هیچ شی ای را به شما بازنمیگرداند و هنگام دسترسی به آن روی دیتابیس اجرا نمی شود . همچنین QuerySet شما و یا qs.none یک شی از کلاس EmptyQuerySet خواهد شد .برای مثال :&gt;&gt;&gt; Entry.objects.none()
&lt;QuerySet[]&gt;
&gt;&gt;&gt; from django.db.models.query import EmptyQuerySet
&gt;&gt;&gt; isinstance(Entry.objects.none(), EmptyQuerySet)
Trueمتد all : یک کپی از QuerySet فعلی را بازمیگرداند . همچنین تمام اشیا یک مدل را به شما بازمیگرداند . این زمانی مفید است که یا همه آنها را نیاز داشته باشید و یا بخواهید در یک کپی از QuerySet اصلی به وسیله manager فیلتر کردن اشیا را انجام بدهید .نکته :هنگامی که یک QuerySet در دیتابیس اجرا می شود ,معمولا نتایج خود را در کش ذخیره خواهد کرد . اگر اطلاعات دیتابیس از زمان ذخیره سازی کش تغییر کرده باشند ,می توانید با فراخوانی all آنها رو دوباره به روز کنید.متد union :از دستور UNION در SQL استفاده می کند تا دو QuerySet را با هم ترکیب کند . برای مثال اگر نام دو QuerySet ما qs2 و qs3 باشد ,داریم :&gt;&gt;&gt; qs1.union(qs2, qs3)عملگر UNION به طور پیش فرض فقط اشیا متمایز را بازمیگرداند (یعنی اشیا تکراری را حذف می کند) . برای بازگرداندن مقادیر تکراری از آرگومان all=True استفاده کنید .متد intersection : از INTERSECT در SQL استفاده می کند تا اشیا مشترک بین دو QuerySet را بازگرداند . برای مثال :&gt;&gt;&gt; qs1.intersection(qs2, qs3)متد difference :از دستور EXCEPT در SQL استفاده می کند تا اشیایی که در QuerySet شما وجود دارند ولی در QuerySet هایی که در آرگومان ارسال شده اند وجود ندارند را بازگرداند . برای مثال :&gt;&gt;&gt; qs1.difference(qs2, qs3)در کد بالا qs1 , QuerySet اولی حساب می شود و اشیایی که در qs1 وجود دارند ولی در qs2 و qs3 وجود ندارند را بازمیگرداند .متد select_related : فیلدی را از نوع فیلد های روابطی به آن می دهید (مثال ForeignKey) و سپس اطلاعات داخل شی های مدل ارجاع داده شده را ذخیره می کند . کاربرد این متد این است که در حالت ساده استفاده از روابط دیتابیسی ,احتمالا چندین بار روی دیتابیس عملیاتی انجام بدهد و کار دیتابیس را سنگین کند . اما در صورت استفاده از این متد فقط یکبار دیتابیس عملیات جستجو را انجام می دهد و تمام روابط درون جنگو ذخیره می شوند .در اینجا تفاوت حالت ساده و متد select_related را می بینید . ابتدا نگاهی به استفاده ساده بیاندازید . در حالت اول دو بار روی دیتابیس عملیات صورت میگیرد :# Hits the database.
e = Entry.objects.get(id=5)
# Hits the database again to get the related Blog object.
b = e.blogدر حالت دوم از متد select_related استفاده می کنیم . در اینجا فقط یکبار دیتابیس عملیات جستجو را انجام می دهد :# Hits the database.
e = Entry.objects.select_related(&#039;blog&#039;).get(id=5)
# Doesn&#039;t hit the database, because e.bloghas been prepopulated
# in the previous query.
b = e.blogهمچنین شما می توانید روی هر نوع QuerySet این متد را استفاده کنید . برای مثال در کد زیر ,یک set را با نام blogs تعریف کرده ایم . در این متغییر قرار است اطلاعات شی blog آنها در صورتی که pub_date آنها مربوط به آینده باشد ,قرار بگیرد :from django.utils import timezone
# Find all the blogs with entries scheduled to be published in the future.
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related(&#039;blog&#039;):
    # Without select_related(), this would make a database query for each
    # loop iteration in order to fetch the related blog for each entry.
    blogs.add(e.blog)همچنین تفاوتی ندارد که ابتدا کدام را قرار می دهید (filter یا select_related) بنابراین دو کد زیر با هم نتیجه یکسانی دارند :Entry.objects.filter(pub_date__gt=timezone.now()).select_related(&#039;blog&#039;)
Entry.objects.select_related(&#039;blog&#039;).filter(pub_date__gt=timezone.now())البته نکته ای قابل توجه باقی مانده است . فرض کنید که مدل های زیر را نوشته اید :from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)حال کد Book.objects.select_related(‘author__hometown’).get(id=4) را بنویسید . این کد برای شما اشیایی از مدل های Person و City را که با آنها رابطه دارید ,در حافظه کش ذخیره می کند . بنابراین شما می توانید به هر دوی آنها دسترسی داشته باشید و آنها روی دیتابیس دوباره عملیاتی را انجام نمی دهند . (منظور این است که تودرتو بودن باعث نمی شود تا بعضی از بخش ها را نتواند کش کند)# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related(&#039;author__hometown&#039;).get(id=4)
p = b.author # Doesn&#039;t hit the database.
c = p.hometown # Doesn&#039;t hit the database.

# Without select_related()...
b = Book.objects.get(id=4) # Hits the database.
p = b.author # Hits the database.
c = p.hometown # Hits the database.شما می توانید به هر فیلد ForeignKey یا OneToOneField که در لیست پارامتر های ارسالی به متد select_related است ,مراجعه کنید (یعنی از آن استفاده گنید مانند فیلد hometown و author) .همچنین می توانید به شکل reverse یا بالعکس به یک OneToOneField دسترسی داشته باشید (OneToOneField که در لیست پارامتر های متد باشد) . یعنی می توانید از شی ارجاع داده شده به شی ای که فیلد OneToOneField در آن تعریف شده است ,دسترسی پیدا کنید . برای این کار ,به جای تعیین نام فیلد ,از related_name برای فیلد شی ارجاع داده شده استفاده کنید . (فقط OneToOneField)البته در بعضی از شرایط ممکن است بخواهید select_related را برای اشیا زیادی فراخوانی کنید یا همه روابط مورد نیاز را ندانید . در این شرایط میتوانید select_related را بدون هیچ پارامتری فراخوانی کنید . این کار تمام ForeignKey هایی که null نباشند را پیدا می کند و آنها را ذخیره می کند . البته این کار توصیه نمی شود زیرا در هنگام استفاده از اطلاعات پیچیدگی زیادی دارد و داده های بیشتری را از آنچه مورد نیاز است ممکن است بازگرداند .اگر می خواهید اطلاعات کش شده را که با Query قبلی در QuerySet ذخیره شده است ,پاک کنید فقط کافی است None را به عنوان پارامتر به متد ارسال کنید :&gt;&gt;&gt; without_relations = queryset.select_related(None)متد prefetch_related : این متد نیز همان کار select_related را انجام می دهد اما به روشی متفاوت . متد select_related جداول دیتابیس که با هم رابطه دارند را به وسیله SQL به هم متصل نگه می دارد و روی ManyToManyField(بخاطر سنگینی و پیچیده شدن روابط) کار نمی کند . اما متد prefetch_related جداول را با پایتون به یکدیگر اتصال می دهد و همین باعث می شود تا روی ManyToManyField کارایی داشته باشد .بسیار خب , فرض کنید مدل های زیر را نوشته اید .from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):
        return &amp;quot%s (%s)&amp;quot %(
            self.name,
            &amp;quot, &amp;quot.join(topping.name for topping in self.toppings.all()),
        )حال اگر کد زیر را برای مدل های بالا اجرا کنیم :&gt;&gt;&gt; Pizza.objects.all()
[&amp;quotHawaiian (ham, pineapple)&amp;quot, &amp;quotSeafood (prawns, smoked salmon)&amp;quot...مشکل این است که هر بار که ()__Pizza.__str برای بازگرداندن رشته اش کد self.toppings.all را اجرا می کند ,پایگاه داده مورد استفاده قرار می گیرد . این باعث می شود تا هر شی در Pizza.objects.all یکبار روی دیتابیس عملیات اضافه انجام بدهد . (کار دیتابیس سنگین می شود)شما می توانید با استفاده از prefetch_related این عملیات را به دو query کاهش بدهید :&gt;&gt;&gt; Pizza.objects.prefetch_related(&#039;toppings&#039;)این کد برای شما مقدار self.toppings.all را و تمام روابط آن را کش می کند و هر بار که کد self.toppings.all اجرا می شود ,بجای انجام عملیات روی دیتابیس ,اشیا را از کش حافظه خود پیدا می کند .همچنین هر Query اضافه برای این متد ,بعد از اینکه Query در دیتابیس اجرا شد و نتایج آن بازگشت اجرا می شود .توجه کنید که استفاده از این متد باعث می شود تا کش QuerySet به طور کامل در حافظه کامپیوتر بارگذاری شود . این رفتار برای یک QuerySet عادی صورت نمی گیرد زیرا آنها سعی میکنند تا از بارگذاری نتایج قبل از نیاز به استفاده در حافظه جلوگیری کنند .اجرا کردن یک QuerySet با متد prefetch_related باعث نمی شود تا QuerySet های بعدی که Query متفاوتی دارند ,نتایج خود را از کش بارگذاری کنند . در واقع اگر شما دو خط کد زیر را در نظر بگیرید :&gt;&gt;&gt; pizzas = Pizza.objects.prefetch_related(&#039;toppings&#039;)
&gt;&gt;&gt; [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]در اینجا Pizza.objects.prefetch_related در واقع برای شما نتایج را کش می کند ولی در خط بعدی pizza.toppings.filter شما یک Query جدید ساخته اید و این باعث می شود تا نتواند از نتایج کش شده استفاده کند. به همین علت دوباره روی دیتابیس عملیات جستجو انجام می دهد .همچنین استفاده از متد های add , remove , clear و set و از این قبیل باعث می شود تا کش حافظه prefetch_related خالی شود . (زیرا نتایج باید آپدیت بشوند)شما همچنین می توانید از سینتکس های ساده Join نیز برای دسترسی به related objects از طریق related objects انجام بدهید . برای مثال فرض کنید در مدل های بالا , مدل زیر نیز اضافه شده است :class Restaurant(models.Model):
    pizzas = models.ManyToManyField(Pizza, related_name=&#039;restaurants&#039;)
    best_pizza = models.ForeignKey(Pizza, related_name=&#039;championed_by&#039; 
    on_delete=models.CASCADE)کد زیر را می توانید برای آن بنویسید :&gt;&gt;&gt; Restaurant.objects.prefetch_related(&#039;pizzas__toppings&#039;)در کد بالا تمام اشیا pizza که متعلق به مدل Restaurant هستند (با آن رابطه دارند) + تمام فیلد های topping از تک تک اشیا pizza را کش خواهد کرد . در واقع نتایج حاصل سه Query هستند ,یکی برای Restaurant ,یکی برای pizza و یکی برای تمام فیلد های topping .حال کد زیر را ببینید :&gt;&gt;&gt; Restaurant.objects.prefetch_related(&#039;best_pizza__toppings&#039;)کد بالا نیز تمام best_pizza و تمام فیلد های topping برای آن در هر شی از Restuarant را کش می کند . این فرایند در سه Query انجام خواهد شد . یکی برای اشیا Restuarant و یکی برای اشیا best_pizza و دیگری برای فیلد های topping .همچنین برای سبک تر کردن کار دیتابیس و رساندن Query به تعداد دو Query ,میتوانید برای best_pizza از select_related به شکل زیر استفاده کنید .&gt;&gt;&gt;Restaurant.objects.select_related(&#039;best_pizza&#039;).prefetch_related(&#039;best_pizza__toppings&#039;)از آنجایی که ما قبل از prefetch_related از select_reated استفاده کرده ایم , prefetch_related می تواند تشخیص بدهد که اشیا best_pizza از قبل کش شده اند و دوباره آنها را کش نخواهد کرد .اگر شما این متد ها را زنجیره کرده باشید و بخواهید آثار متد های قبلی را پاک کنید , فقط کافی است تا None را به عنوان پارامتر برای prefetch ارسال کنید .&gt;&gt;&gt; non_prefetched = qs.prefetch_related(None)نکته : یکی از نکاتی که هنگام استفاده از prefetch_related باید به آن توجه کرد این است که اشیا یک مدل ممکن است میان اشیایی که با آنها رابطه دارند ,تکرار بشوند . در واقع این متد از تکرار اشیا پرهیز نمی کند و اشیا ممکن است دو بار در میان QuerySet بازگشتی دیده شوند .نکته : همچنین کد prefetch_related در SQL با عملگر IN در واقع ترکیب می شود و عملیات جستجو را انجام می دهد . این به این معنی است که می توان برای یک QuerySet بزرگ یک عبارت IN بزرگ نیز تعریف کرد که ممکن است این کار باعث ایجاد مشکلات در عملکرد متد شود .می توانید از شی Prefetch برای کنترل بیشتر این متد استفاده کنید (Django.db.models.Prefetch) . در ساده ترین حالت استفاده خالی از Prefetch به معنای جستجو مبنا بر یک رشته است . (همان نام فیلد ها)&gt;&gt;&gt; from django.db.models import Prefetch
&gt;&gt;&gt; Restaurant.objects.prefetch_related(Prefetch(&#039;pizzas__toppings&#039;))شما می توانید یک QuerySet را به عنوان پارامتر به شی Pretetch بدهید . برای مثال می توانید از این روش برای تغییر ترتیب پیش فرض QuerySet اصلی استفاده کنید . (یعنی روی QuerySet اصلی شما اعمال می شود و باید با آن هماهنگ باشد)&gt;&gt;&gt; Restaurant.objects.prefetch_related(
...                       Prefetch(&#039;pizzas__toppings&#039;, queryset=Toppings.objects.order_by(&#039;name&#039;)))یا می توانید select_related را به عنوان پارامتر ارسال کنید تا جستجو را روی QuerySet اصلی محدود تر کنید .&gt;&gt;&gt; Pizza.objects.prefetch_related(
...            Prefetch(&#039;restaurants&#039;, queryset=Restaurant.objects.select_related(&#039;best_pizza&#039;)))شما می توانید همچنین از یک آرگومان با نام to_attr استفاده کنید . این آرگومان اختیاری است و باعث می شود تا نتایج QuerySet همان لحظه در لیستی با نام متغییری که به عنوان پارامتر برای این ارگومان ارسال کرده اید ,ذخیره شوند .این به شما اجازه می دهد تا چندین بار یک QuerySet را به اصطلاح fetch کنید و هر بار پارامتر های ارسالی را تغییر دهید . برای مثال :&gt;&gt;&gt; vegetarian_pizzas = Pizza.objects.filter(vegetarian=True)
&gt;&gt;&gt; Restaurant.objects.prefetch_related(
...                    Prefetch(&#039;pizzas&#039;, to_attr=&#039;menu&#039;),
...                    Prefetch(&#039;pizzas&#039;, queryset=vegetarian_pizzas, to_attr=&#039;vegetarian_menu&#039;))علاوه بر این توصیه جنگو این است که استفاده از to_attr بهتر از تبدیل نتایج فیلترشده به لیست و پیمایش آنها است . زیرا عملیات دوم باعث می شود تا کش QuerySet کمی درهم ریخته شود . برای مثال :&gt;&gt;&gt; queryset = Pizza.objects.filter(vegetarian=True)

&gt;&gt;&gt; # Recommended:
&gt;&gt;&gt; restaurants = Restaurant.objects.prefetch_related(
...                 Prefetch(&#039;pizzas&#039;, queryset=queryset, to_attr=&#039;vegetarian_pizzas&#039;))
&gt;&gt;&gt; vegetarian_pizzas = restaurants[0].vegetarian_pizzas

&gt;&gt;&gt; # Not recommended:
&gt;&gt;&gt; restaurants = Restaurant.objects.prefetch_related(
...                   Prefetch(&#039;pizzas&#039;, queryset=queryset))
&gt;&gt;&gt; vegetarian_pizzas = restaurants[0].pizzas.all()معمولا برای فیلد های OneToOneField و ForeignKey از select_related استفاده می شود . اما شرایطی وجود دارد که باعث می شود بخواهید از prefetch_related برای این فیلد ها استفاده کنید :شما می خواهید به فیلد های روابطی بیشتری دسترسی داشته باشید .شما می خواهید فقط به زیرمجموعه ای محدود از روابط در نتایج دسترسی داشته باشید .شما می خواهید از تکنیک های بهینه سازی مانند deferred fields (بعدا درباره این صحبت خواهیم کرد) استفاده کنید.متد prefetch_related از چندین دیتابیسی در جنگو پشتیبانی می کند و در صورتی که Query شما تعیین نکند که از کدام دیتابیس باید استفاده شود ,از دیتابیس تعیین شده در بیرون از Query استفاده می کند . به همین علت تمام مثال های زیر معتبر هستند و کار می کنند :(مبحث چند دیتابیسی در بعد ها اشاره شده است و اکنون زمان مناسبی برای شرح آن نیست)&gt;&gt;&gt; # Both inner and outer queries will use the &#039;replica&#039; database
&gt;&gt;&gt; Restaurant.objects.prefetch_related(&#039;pizzas__toppings&#039;).using(&#039;replica&#039;)
&gt;&gt;&gt; Restaurant.objects.prefetch_related(
...              Prefetch(&#039;pizzas__toppings&#039;),
...              ).using(&#039;replica&#039;)

&gt;&gt;&gt; # Inner will use the &#039;replica&#039; database; outer will use &#039;default&#039; database
&gt;&gt;&gt; Restaurant.objects.prefetch_related(
...            Prefetch(&#039;pizzas__toppings&#039;, queryset=Toppings.objects.using(&#039;replica&#039;)),
...            )

&gt;&gt;&gt; # Inner will use &#039;replica&#039; database; outer will use &#039;cold-storage&#039; database
&gt;&gt;&gt; Restaurant.objects.prefetch_related(
...           Prefetch(&#039;pizzas__toppings&#039;, queryset=Toppings.objects.using(&#039;replica&#039;)),
...           ).using(&#039;cold-storage&#039;)ترتیب lookup شما نیز مهم است . برای مثال کد های زیر را نگاه کنید :&gt;&gt;&gt; prefetch_related(&#039;pizzas__toppings&#039;, &#039;pizzas&#039;)این کد حتی اگر ترتیبی برای آن تعریف نشود باز نیز کار می کند . زیرا آرگومان اول pizzas__toppings از قبل حاوی تمام اطلاعات مورد نیاز است . بنابراین آرگومان دوم اضافی است .&gt;&gt;&gt; prefetch_related(&#039;pizzas__toppings&#039;, Prefetch(&#039;pizzas&#039;,
queryset=Pizza.objects.all()))کد بالا یک valueError را ایجاد می کند که بخاطر تلاش برای تعریف دوباره query از lookup قبلی است . توجه داشته باشید که این QuerySet برای این ساخته شده است تا اشیا pizzas را در جستجوی قبلی خود pizzas__toppings پیدا کند .&gt;&gt;&gt; prefetch_related(&#039;pizza_list__toppings&#039;, Prefetch(&#039;pizzas&#039;, to_attr=&#039;pizza_list&#039;))کد بالا خطای AttributeError را خواهد داد زیرا pizza_list هنوز وجود ندارد (یعنی هنوز pizza_list__toppings در حال پردازش و اجرا است)راه حل برای تمام این مشکلات استفاده درست و بجا از Prefetch است . همچنین باید عملیات ها را به طوری ترتیب داد که باعث ایجاد Query اضافه نشوند .متد extra : گاهی اوقات سینتکس های جنگو برای Query زدن ,نمی تواند یک Query پیچیده را که از دستور WHERE استفاده می کند را روی دیتابیس اجرا کند . (نمی تواند آن را هندل کند) در این مواقع شما از extra برای اضافه کردن کد های SQL خودتان به کد های SQL تولید شده توسط QuerySet استفاده خواهید کرد . (به کدهای SQL جستجوی خود ,مواردی را دستی اضافه می کنید)نکته امنیتی : در هنگام استفاده از extra بسیار مراقب باشید . در واقع هر بار که از آن استفاده می کنید ممکن است بخاطر تغییر دادن سینتکس SQL به صورت دستی باگ SQL Injection را ایجاد کنید . (درباره این باگ در اینترنت بخوانید)همچنین نباید از از متغییر ها در این متد استفاده کنید . برای مثال کد SQL زیر آسیب پذیر است زیرا از ‘در اطراف %s استفاده کرده است :SELECT col FROM sometable WHERE othercol = &#039;%s&#039; # unsafe!نکته : لازمه یادگیری این متد مسلط بودن به SQL است !همچنین اینکه چند دیتابیس داشته باشید و از این متد استفاده کنید ممکن است قابل انجام نباشد (زیرا شما به صراحت در حال نوشتن کد SQL هستید) . به همین علت باید تا حداکثر توان خود از نوشتن آنها اجتناب کنید .در این متد باید حداقل یکی از params , select , where یا tables را استفاده کنید و مقداردهی کنید . در ادامه به بررسی هر کدام از اینها خواهیم پرداخت . (یادتان باشد می توانید همه آنها را با هم نیز استفاده کنید)استفاده از Select :این دستور برای اضافه کردن فیلد های اضافی به سینتکس SELECT در SQL به کار می رود . مقدار آن باید یک دیکشنری باشد . دیکشنری ای ک حاوی نام فیلد و مقدار مورد نیاز برای آن است . برای مثال :Entry.objects.extra(select={&#039;is_recent&#039;: &amp;quotpub_date &gt; &#039;2006-01-01&#039;&amp;quot})نتیجه کد بالا این خواهد بود که به هر شی Entry یک attribute یا ویژگی اضافه می شود . نام آن is_recent است . این ویژگی در واقع یک Boolean است که در صورتی که pub_date شی , از 1 ژانویه 2006 بیشتر باشد , True خواهد بود.جنگو قطعه کد SQL ورودی را مستقیما در دستور SELECT وارد خواهد کرد . بنابراین کد بالا به شکل زیر ترجمه می شود :SELECT blog_entry.*, (pub_date &gt; &#039;2006-01-01&#039;) AS is_recent
FROM blog_entry;کد بعدی کمی پیچیده تر خواهد بود ,کد زیر یک جستجوی فرعی (subquery) انجام می دهد که به هر شی Blog یک ویژگی با نام entry_count اضافه می کند . این ویژگی یک int است که تعداد روابط شی با اشیا Entry را نشان می دهد .Blog.objects.extra(
    select={
        &#039;entry_count&#039;: &#039;SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id =
        blog_blog.id&#039;
    },
)در این مورد خاص , Query شما از قبل حاوی جدول blog_blog در دستور FROM خود است (یعنی دستوری که خود جنگو ایجاد کرده است این ویژگی را دارد ) . کد بالا به صورت زیر در SQL ترجمه می شود :SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
FROM blog_blog;نکته : در مورد subquery در SQL خودتان تحقیق کنید . این مبحث از مباحث SQL است و در آموزش های جنگو نمیگنجد .توجه داشته باشید که پرانتز های یک subquery برای بعضی از موتور های دیتابیسی لازم نیست (در مثال بالا دستور دوم SELECT را نگاه کنید ,این بخش می گوید که لازم نیست پرانتز های این قسمت حتما گذاشته شوند). همچنین توجه داشته باشید که در بعضی از موتور های دیتابیسی مانند چند نسخه از MySql از subquery پشتیبانی نمی شود .در برخی موارد نادر نیز ممکن است بخواهید پارامتر ها را به قطعات SQL به extra ارسال کنید . برای این کار از پارامتر select_params استفاده کنید . برای مثال :Blog.objects.extra(
    select={&#039;a&#039;: &#039;%s&#039;, &#039;b&#039;: &#039;%s&#039;},
    select_params=(&#039;one&#039;, &#039;two&#039;),
)در صورتی که می خواهید از خود %s در دستور SELECT خود استفاده کنید ,از %%s بهره ببرید .استفاده از Where / tables :با استفاده از این دو تا می توانید دستور های WHERE و FROM را به صورت مستقیم در کد وارد کنید (به ترتیب) . همچنین هر دوی آنها لیستی از str را دریافت می کنند که به صورت مستقیم وارد کد می شود .نکته بعدی این است که تمام پارامتر های where با AND به یکدیگر متصل هستند . برای مثال :Entry.objects.extra(where=[&amp;quotfoo=&#039;a&#039; OR bar = &#039;a&#039;&amp;quot, &amp;quotbaz = &#039;a&#039;&amp;quot])کد بالا به صورت کد SQL زیر ترجمه می شود .SELECT * FROM blog_entry WHERE (foo=&#039;a&#039; OR bar=&#039;a&#039;) AND (baz=&#039;a&#039;)هنگامی که می خواهید از پارامتر tables استفاده کنید ,مراقب باشید . گاهی اوقات ممکن است جدول هایی را اضافه کنید که از قبل در Query استفاده شده اند . هنگامی که این کار را انجام بدهید و از قبل جدول شما در Query استفاده شده باشد ,جنگو برای آن جدول یک نام مستعار در نظر می گیرد . چون در SQL اگر یک جدول دو بار تکرار شود ,جستجو های دوم و سوم آن باید از همان نام مستعار استفاده کنند . این در بعضی از مواقع باعث ایجاد خطا می شود .معمولا باید از جداولی استفاده کنید که در Query استفاده نشده اند . اما اگر مشکل بالا برای شما رخ داد ,چندین راه حل وجود دارد . ابتدا بررسی کنید که می توانید بدون استفاده از جدول تکراری این مشکل را حل کنید یا نه . اگر جواب نداد فقط کافی است در کد داخل extra از نام مستعار جدول استفاده کنید . (نام مستعار جدول هرگر تغییر نخواهد کرد )نکته : درباره نام مستعار جداول در SQL بخوانید .استفاده از order_by : اگر می خواهید تا نتایج QuerySet را بر اساس یک فیلد یا جدول جدید که از طریق extra اضافه کرده اید مرتب کنید ,از order_by استفاده کنید و رشته ای از مقادیر را به آن پاس دهید (ارسال کنید) . این مقادیر باید نام فیلد های مدل باشند . می توانند در فرمت table_name.column_name باشند یا نام مستعار ستونی که در extra ارسال کرده اید .مثال :q = Entry.objects.extra(select={&#039;is_recent&#039;: &amp;quotpub_date &gt; &#039;2006-01-01&#039;&amp;quot})
q = q.extra(order_by = [&#039;-is_recent&#039;])کد بالا تمام اشیا را که شرط is_recent آنها برابر True است (pub_date آنها از 2006 بیشتر باشد) را در ابتدای نتایج مرتب می کند . (مرتب سازی True قبل از False صورت می گیرد و ترتیب آن نزولی است)همچنین این کد نشان می دهد که می توانید روی یک QuerySet چندین بار extra را فراخوانی کنید و هر بار محدودیت های جدید را اضافه کنید .استفاده از Params : پارامتر Where توضیح داده شده در بالا ممکن است از متغییر های استاندارد پایتون برای دیتابیس استفاده کند . برای مثال %s .آرگومان params لیستی از پارامتر های اضافی است که باید جایگزین شوند .برای مثال :Entry.objects.extra(where=[&#039;headline=%s&#039;], params=[&#039;Lennon&#039;])همیشه به جای جاگذاری مستقیم در دستور where از params استفاده کنید . زیرا این کار باعث می شود تا جنگو اطمینان حاصل کند که مقادیر به درستی در متغییر ها جایگذاری می شوند . به عنوان مثال کد زیر جالب نیست و ممکن است خطا ایجاد کند .Entry.objects.extra(where=[&amp;quotheadline=&#039;Lennon&#039;&amp;quot])بهتر است به جای کد بالا از کد زیر استفاده کنید .Entry.objects.extra(where=[&#039;headline=%s&#039;], params=[&#039;Lennon&#039;])متد defer :برخی مواقع ممکن است یک مدل حاوی فیلد های زیادی باشد . دریافت اطلاعات و مقادیر این فیلد ها ممکن است کار دیتابیس را سنگین کند و شما ممکن است اصلا نیازی به همه ی آن فیلد ها نداشته باشید . در این مواقع از متد defer استفاده می کنید و به آن نام فیلد ها را ارسال می کنید . این کار باعث می شود آن فیلد های به خصوص از دیتابیس بارگذاری نشوند .برای مثال در کد زیر فیلد های headline و body در هر شی بارگذاری نخواهند شد و کار دیتابیس سبک تر می شود .Entry.objects.defer(&amp;quotheadline&amp;quot, &amp;quotbody&amp;quot)نکته : در برنامه نویسی synchronous شما می توانید به شکل lazy-load فیلد هایی که دریافت نشده اند (deferred fields) را دریافت کنید (البته یکی از آنها در هر زمان و نه همه ی آنها با هم) . در برنامه نویسی asynchronous این کار امکان پذیر نیست و در صورت تلاش برای انجام آن با ارور SynchronousOnlyOperation مواجه خواهید شد .همچنین شما می توانید این متد را به صورت زنجیره ای استفاده کنید یا فیلد های جدیدی را به این متد اضافه کنید .# Defers both the body and headline fields.
Entry.objects.defer(&amp;quotbody&amp;quot).filter(rating=5).defer(&amp;quotheadline&amp;quot)به یاد داشته باشید که ترتیب استفاده از فیلد ها در defer اهمیت خاصی ندارد و حتی اگر یک فیلد دو بار در این متد استفاده شود هیچ اروری را ایجاد نمی کند .این نیز مهم است که در هر صورت استفاده از select_related یا فیلد های روابطی و برای اشاره به فیلد های ارجاع داده شده از آنها کافی است مانند روال عادی جنگو از __ استفاده کنید . برای مثال :Blog.objects.select_related().defer(&amp;quotentry__headline&amp;quot, &amp;quotentry__body&amp;quot)اگر می خواهید تا سابقه فیلد های استفاده شده در این متد را پاک کنید نیز کافی است مقدار None را به عنوان پارامتر به این متد بدهید .# Load all fields immediately.
my_queryset.defer(None)برخی از فیلد ها در یک مدل حتی اگر بخواهید هم defer روی آنها تاثیری نخواهد داشت . مثال های آن primarykey هستند . هنگامی که از select_related استفاده می کنید نیز نباید defer را روی فیلدی استفاده کنید که مدل ها را به یکدیگر اتصال داده است (فیلد رابطه ای).  این کار موجب ایجاد ارور می شود .نکته : متد defer و only که در ادامه آن را خواهید خواند ,فقط برای زمانی مفید هستند که نیاز به چند فیلد ندارید و تمام جستجو های خود در دیتابیس را به طور دقیق بررسی کردید .حتی اگر در حالت بالا هستید و بررسی را به طور دقیق انجام داده اید ,پیشنهاد می شود تا از متد defer زمانی استفاده کنید که مطمئن هستید به فیلد های اضافی نیازی ندارید . اگر پروژه شما آنقدر پیشرفته نیست ,فقط کافی است اطلاعات مورد نیاز در یک مدل و اطلاعات اضافی را در یک مدل جداگانه قرار بدهید (اگر یادتان باشد درباره ارث بری ها صحبت کرده ایم) . اگر ستون های دیتابیس باید در یک جدول قرار بگیرند ,مدلی با Meta.managed = False ایجاد کنید و فیلد های موردنیاز را در آن قرار بدهید . این باعث می شود تا دیتابیس سریع تر باشد و حافظه کمتری نسبت به استفاده از defer اشغال بشود .برای مثال در کد زیر هر دو مدل ها از یک جدول استفاده می کنند :class CommonlyUsedModel(models.Model):
    f1 = models.CharField(max_length=10)

    class Meta:
        managed = False
        db_table = &#039;app_largetable&#039;

class ManagedModel(models.Model):
    f1 = models.CharField(max_length=10)
    f2 = models.CharField(max_length=10)
    
    class Meta:
        db_table = &#039;app_largetable&#039;

# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.defer(&#039;f2&#039;)اگر تعداد فیلد هایی که نیاز دارید نیز زیاد است ,فقط کافی است تا یک مدل abstract ایجاد کنید که آن را در مباحث ارث بری به طور کامل توضیح دادیم و سپس مدل managed=False را و مدل دیگر را فرزندان آن قرار بدهید .نکته : هنگامی که از save بعد از defer استفاده کنید ,فقط فیلد هایی که از آنها استفاده کرده اید سیو می شوند.متد only : متد only اساسا برعکس متد defer است. فیلد هایی که از قبل در متد defer قرار نگرفته باشند و در متد only مورد استفاده قرار بگیرند، از دیتابیس بارگذاری خواهند شد. اگر مدلی دارید که اکثریت فیلد ها باید defer شوند، می‌توانید از only استفاده کنید و فقط فیلد هایی که نیاز دارید را دریافت کنید. فرض کنید یک مدل با نام Person دارید که دارای فیلد های name ، age  و biography است . دو QuerySet زیر از نظر عملکرد و نتیجه یکسان هستند :Person.objects.defer(&amp;quotage&amp;quot, &amp;quotbiography&amp;quot)
Person.objects.only(&amp;quotname&amp;quot)همچنین فراخوانی پی در پی only باعث می شود تا فقط فیلدی که در آخرین متد فراخوانی شده استفاده کرده اید، از دیتابیس دریافت شود. برای مثال، در کد زیر فقط فیلد headline از دیتابیس دریافت می شود.# This will defer all fields except the headline.
Entry.objects.only(&amp;quotbody&amp;quot, &amp;quotrating&amp;quot).only(&amp;quotheadline&amp;quot)همچنین می‌توانید از defer و only همزمان استفاده کنید تا رفتار های منطقی ای را شکل بدهید. کد زیر را نگاه کنید که در کامنت های آن نتیجه هر QuerySet نوشته شده است.# Final result is that everything except &amp;quotheadline&amp;quot is deferred.
Entry.objects.only(&amp;quotheadline&amp;quot, &amp;quotbody&amp;quot).defer(&amp;quotbody&amp;quot)
# Final result loads headline immediately.
Entry.objects.defer(&amp;quotbody&amp;quot).only(&amp;quotheadline&amp;quot, &amp;quotbody&amp;quot)نکته: تمام نکاتی که برای متد defer نوشته شده بود برای متد only نیز کار می کند و باید با احتیاط زیاد از این متد استفاده شود.همچنین به یاد داشته باشید مانند متد defer نمی‌توانید انتظار داشته باشید که در کد asynchronous فیلد های لود نشده، لود شوند و شما به آنها دسترسی داشته باشید.نکته : هنگامی که از defer بعد از only استفاده می‌کنید، فقط فیلد هایی که در هر دو متد استفاده شده اند و در deferموجود هستند، بازنویسی می شود.متد using : این متد برای زمانی استفاده می شود که بیش از چند دیتابیس دارید. این متد باعث می شود تا QuerySet را کنترل کنید که با کدام دیتابیس اجرا بشود. تنها آرگومانی که دریافت می‌کند نیز نام دیتابیس مورد استفاده است. همان نامی که در متغییر DATABASES در settings.py تعریف کرده اید. در مورد چند دیتابیسی بعدا صحبت خواهیم کرد.به مثال زیر نگاه کنید :# queries the database with the &#039;default&#039; alias.
&gt;&gt;&gt; Entry.objects.all()
# queries the database with the &#039;backup&#039; alias
&gt;&gt;&gt; Entry.objects.using(&#039;backup&#039;)متد select_for_update: یک دستور SELECT … FOR UPDATE در SQL ایجاد می کند و ردیف های انتخاب شده را تا پایان انجام شدن transactions یا عملیات ها قفل می کند . (باعث می شود تا نتوانید آنها را تغییر دهید یا دریافت کنید)برای مثال :from django.db import transaction
entries = Entry.objects.select_for_update().filter(author=request.user)

with transaction.atomic():
    for entry in entries:
        ...نکته : به یاد داشته باشید که این متد فقط در دیتابیس هایی که از آن پشتیبانی کنند کار می کند.در کد بالا هنگامی که QuerySet شما روی دیتابیس اجرا می شود، تمام ردیف های مطابق با Query شما قفل می شوند. در اینجا ردیف های قفل شده همان entry in entries  هستند. به این معنی که این ردیف ها تا پایان بلوک تراکنش یا عملیات شما قابل تغییر و دستکاری نیستند.نکته مهم این است که اگر تراکنش یا عملیات دیگری در حال انجام روی ردیف های قفل شده باشد ، QuerySet  شما مسدود یا block می شود و به شما اجازه دریافت اطلاعات را تا زمانی که قفل آزاد نشود ، نمی‌دهد. اگر این رفتار را نمی‌خواهید فقط کافیست بنویسید select_for_update(nowait=True) . این باعث می شود تا QuerySet مسدود نشود. اگر یک قفل متضاد نیز بر روی ردیف ها اعمال شود شما دچار DatabaseError میشوید. برای نادیده گرفتن قفل ردیف ها و حل این ارور میتوانید از کد select_for_update(skip_locked=True) استفاده کنید . به یاد داشته باشید که نمی‌توانید هر دوی skip_locked و nowait را با هم استفاده کنید. این کار موجب ایجاد ValueError می شود.نکته : اگر شما از وراثت multi-table inheritance استفاده می‌کنید، برای قفل کردن مدل های والد فقط کافی است تا فیلد های parent link را به عنوان آرگومان برای of ارسال کنید. به طور پیش فرض آنها به صورت &lt;parent_model_name_&lt;ptr نامگذاری شده اند.برای مثال :Restaurant.objects.select_for_update(of=(&#039;self&#039;, &#039;place_ptr&#039;))فقط در دیتابیس PostgreSQL میتوانید آرگومان no_key=True را ارسال کنید تا قفل شدن ردیف ها را ضعیف تر کنید. این ویژگی اجازه می دهد تا ردیف هایی را ایجاد کنید که می‌توانند به ردیف های قفل شده ارجاع پیدا کنند یا با آن برای مثال با یک ForeignKey مرتبط شوند.همچنین نمی‌توانید از این متد برای روابط nullable استفاده کنید (دچار ارور خواهید شد) :&gt;&gt;&gt; Person.objects.select_related(&#039;hometown&#039;).select_for_update()
Traceback(most recent call last):
     ...
    django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullableside
    of an outer joinبرای حل این مشکل فقط کافی است اگر نیازی به مقادیر null ندارید، آنها را جدا کنید.&gt;&gt;&gt;Person.objects.select_related(&#039;hometown&#039;).select_for_update().exclude(hometown=None)&lt;QuerySet[&lt;Person: ...)&gt;, ...]&gt;دیتابیس های PostgreSQL ،oracle  و mysql از آرگومان ها برای این متد پشتیبانی می‌کنند. گرچه Mariadb  فقط از آرگومان nowait پشتیبانی می‌کند. نسخه 10.6Mariadb از skip_locked نیز پشتیبانی می‌کند .Mysql 8.0.1 نیز از آرگومان های skip_locked و nowait و of پشتیبانی می‌کند . no_key فقط در PostgreSQL  پشتیبانی می شود.استفاده از این آرگومان ها در هر دیتابیسی که از آن پشتیبانی نکند باعث ارور می شود.استفاده از select_for_update در حالت autocommit mode و در دیتابیس هایی که ازکد خاصی مانند SELECT ... FOR  UPDATE پشتیبانی می‌کنند باعث ارور TransactionManagementError می‌شود زیرا ردیف ها در آن حالت قفل نمی شوند . اگر اینکار مجاز باشد باعث می شود تا داده ها به راحتی دچار خرابی شوند.استفاده از select_for_update  در یک backend که از SELECT ... FOR UPDATE پشتیبانی نمی کنند (مانند SQLite) بیهوده است و عمل نمی کند .متد raw : در آن یک Query به زبان SQL را به صورت خام می نویسید و آن را برای شما اجرا می کند . نتیجه این Query یک شی از کلاس dajngo.db.models.query.RawQuerySet است . این شی RawQuerySet می تواند درست مانند QuerySet معمولی پیمایش شود (iterate) . بعدا در این باره بیشتر صحبت خواهیم کرد . ابتدا به مثال زیر نگاه کنید که چگونه یک کد SQL خام را به صورت دستی با این متد اجرا کرده ایم .&gt;&gt;&gt; from Django.db.models import RawQuery
&gt;&gt;&gt; Person.objects.raw(‘SELECT * FROM Person’)همچنین به یاد داشته باشید raw تمام فیلتر های زنجیره ای قبل از خودش را نادیده میگیرد . به همین علت معمولا آن را از یک Manager یا یک QuerySet جدید دست نخورده فراخوانی می کنند .عملگر هایی که QuerySet جدید بازمیگردانند !در این بخش درباره عملگر یا operator که یک QuerySet جدید را بازمیگردانند صحبت می کنیم . به یاد داشته باشید که Query استفاده شده باید از یک مدل باشد (متعلق به یک مدل باشند) . عملگر ها نیز همان علاماتی هستند که می توانید به وسیله آنها دو Query را با یکدیگر ترکیب کنید (عملگر AND , ORو XOR) .عملگر AND(&amp;) :دو QuerySet را با یکدیگر ترکیب می کند . در واقع از دستور AND در SQL استفاده می کند .دو کد زیر با یکدیگر نتایج یکسان دارند .Model.objects.filter(x=1) &amp; Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)

from django.db.models import Q
Model.objects.filter(Q(x=1) &amp; Q(y=2))هر دو کد بالا ,کد SQL زیر را تولید می کنند :SELECT ... WHERE x=1 AND y=2عملگر OR(|) : دو QuerySet را با یکدیگر ترکیب می کند . در واقع از دستور OR در SQL استفاده می کند . این دستور به معنی “یا” است و باعث می شود یکی از QuerySetها اجرا شود و نتیجه ای نمایش دهد .دو کد زیر نتایج یکسان تولید می کنند :Model.objects.filter(x=1) | Model.objects.filter(y=2)

from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))کد SQL زیر برای مثال های بالا تولید می شود :SELECT ... WHERE x=1 OR y=2نکته :این عملگر ممکن است Query های مساوی اما متفاوت در هر بار تولید کند . (یعنی نتایج یکسان است اما کد متفاوت خواهد بود)عملگر XOR(^) : دو QuerySet را با استفاده از دستور XOR ترکیب می کند . (این دستور در جنگو 4.1 پشتیبانی میشود)مثال های زیر نتایج یکسانی را دارند :Model.objects.filter(x=1) ^ Model.objects.filter(y=2)
Model.objects.filter(Q(x=1) ^ Q(y=2))کد SQL زیر برای مثال های بالا تولید خواهد شد :(درباره عملگر XOR کمی تحقیق کنید)SELECT ... WHERE x=1 XOR y=2عملگر XOR به صورت native در MariaDB و MySQL پشتیبانی می شود . در دیتابیس های دیگر x ^ y ^ … ^ z به صورت یک معادل درمیاید . برای مثال :(x OR y OR ... OR z) AND
1=(
    (CASE WHEN x THEN 1 ELSE 0 END) +
    (CASE WHEN y THEN 1 ELSE 0 END) +
    ...
    (CASE WHEN z THEN 1 ELSE 0 END) +
)در این جلسه در رابطه با تمامی متد هایی که یک queryset جدید را بازمیگردانند صحبت شد . در جلسه بعدی در رابطه با متد هایی صحبت خواهیم کرد که queryset جدیدی را به ما بازنمیگردانند . در جلسات آینده تمامی متد های دیتابیس ها به طور کامل بررسی خواهد شد و این مبحث تمام می شود . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Thu, 14 Dec 2023 16:16:57 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و دو | نحوه عملکرد QuerySet و استفاده از pickle</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%D8%AF%D9%88-%D9%86%D8%AD%D9%88%D9%87-%D8%B9%D9%85%D9%84%DA%A9%D8%B1%D8%AF-queryset-%D9%88-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-pickle-ndka5via5sv8</link>
                <description>در این جلسه نحوه کارکرد یک Queryset و استفاده از pickle ( خیارشور :)) ) را در جنگو بررسی خواهیم کرد . با ما همراه باشید . آموزش جنگو : جلسه سی و دو | کار با QuerySet و نحوه عملکرد آنکار با QuerySetدر این بخش به بررسی نحوه کار QuerySet می پردازیم . ابتدا به یاد داشته باشید که query با queryset تفاوت اساسی دارد . در واقع هر کدی که برای جستجو در دیتابیس می نویسید , یک QuerySet است . هنگامی که در دیتابیس اجرا می شود تبدیل به Query خواهد شد .همچنین از مثال Entry و Blog و Author در این بخش دوباره استفاده خواهیم کرد (در مقالات قبلتر وجود دارد).اجرای QuerySet در دیتابیس (تبدیل به query)به صورت کلی ,یک QuerySet را می توان ساخت ,فیلتر کرد ,برش داد و کارهای دیگری انجام داد بدون اینکه واقعا روی دیتابیس عملیاتی انجام بدهد . در واقع تا زمانی که با QuerySet کاری انجام ندهید که روی دیتابیس اجرا شود (مثال print کردن آن برای اولین بار) در واقع هیچ فعالیتی روی دیتابیس انجام نمی شود .لیست فعالیت هایی که باعث می شوند تا QuerySet در دیتابیس اجرا شود ,در زیر آمده است :یک-iteration :یک QuerySet در واقع iterable است . این یعنی می توانید روی آن پیمایش کنید و اعضای آن را ببینید.توجه داشته باشید که زمانی که اولین بار است که اعضای آن را پیمایش می کنید , روی دیتابیس اجرا می شود و در باقی مواقع از کش استفاده خواهد کرد . برای مثال کد زیر , تمام مقادیر فیلد های headline از اشیا مدل Entry را بازمیگرداند .for e in Entry.objects.all():
    print(e.headline)البته اگر می خواهید از این کد برای این استفاده کنید که بفهمید آیا حداقل یک شی با مشخصات شما وجود دارد یا خیر بهتر است از ()exists استفاده کنید .دو-Asynchronous iteration :یک QuerySet همچنین می توانید به صورت async نیز پیمایش شود . برای مثال:async for e in Entry.objects.all():
    results.append(e)هر دو نوع synchronous و asynchronous حافظه کش یکسانی را به اشتراک می گذارند .سه-slicing : همانطور که توضیح داده شد یک QuerySet می تواند با استفاده از روش Array-slicing در پایتون, slice یا برش داده شود . Slice کردن یک QuerySet اجرا نشده در دیتابیس معمولا یک QuerySet اجرا نشده دیگر را بازمیگرداند . البته اگر از پارامتر step استفاده کنید ,جنگو QuerySet را دیتابیس اجرا می کند و نتیجه را به صورت لیست به شما بازمی گرداند . Slice کردن یک querySet که اجرا شده است نیز یک لیست بازمیگرداند .همچنین به یاد داشته باشید که اگر یک QuerySet که slice روی آن اعمال شده است و در دیتابیس هنوز اجرا نشده ,یک QuerySet دیگر را که روی دیتابیس اجرا نشده بازگرداند , افزودن اصلاحات بیشتر (مانند فیلتر کردن آن) مجاز نیست . زیرا موتور دیتابیس های جنگو نمی توانند آن را به SQL به درستی ترجمه کند .چهار-pickle : یکی از موارد دیگر که باعث اجرای QuerySet روی دیتابیس می شود استفاده از کتابخانه pickle و یا کش کردن QuerySet است . (درباره pickle در گوگل جستجو کنید زیرا مبحث بعدی ما درباره همین است)پنج-متد ()repr :اگر متد ()repr روی یک QuerySet فراخوانی شود ,روی دیتابیس اجرا خواهد شد . دلیل آن ایجاد قابلیت تعاملی بودن است . برای مثال هنگام استفاده از API دیتابیس در ترمینال می توانید با اینکار نتایج QuerySet را به صورت زنده تماشا کنید .شش-متد ()len :هنگامی که این متد را برای شمارش اعضای لیست نتیجه فراخوانی کنید , QuerySet روی دیتابیس اجرا می شود .نکته : اگر شما فقط به تعداد اشیا نیاز دارید و نه به خود آنها (یعنی فقط تعداد آنها را نیاز دارید و کاری با خود آنها ندارید) استفاده از دستور (*)SELECT COUNT در SQL راحت تر است . برای همین جنگو اجازه استفاده از متد ()count را به شما می دهد .هفت-()list :زمانی که این متد را برای تبدیل QuerySet به یک list استفاده کنید , QuerySet شما در دیتابیس اجرا می شود . برای مثال :entry_list = list(Entry.objects.all())هشت-()bool :استفاده از QuerySet در یک محیط منطقی مانند ()bool یا دستور if (هر دستوری که در آن True یا False تعیین شده باشد) باعث می شود QuerySet اجرا شود . اگر حداقل یک شی درون نتیجه وجود داشته باشد به شما True و در غیر این صورت False را بازمیگرداند . مثلا :if Entry.objects.filter(headline=&amp;quotTest&amp;quot):
    print(&amp;quotThere is at least one Entry with the headline Test&amp;quot)نکته : اگر شما فقط به تعداد اشیا نیاز دارید و نه به خود آنها (یعنی فقط تعداد آنها را نیاز دارید و کاری با خود آنها ندارید) استفاده از ()exists مناسب تر است .استفاده از pickleنکته : این بخش نیاز به آشنایی اولیه با کتابخانه pickle دارد . پس لطفا قبل از خواندن این بخش حتما در مورد آن بخوانید .شما در جنگو می توانید یک QuerySet را pickle کنید . با اینکار تمام نتایج QuerySet شما در حافظه بارگذاری خواهند شد . این روش کارایی بالاتری برای زمان هایی که نیاز به دسترسی سریع به اطلاعات دارید ,دارد . البته بهتر است توجه داشته باشید زمانی که شما به اطلاعات ذخیره شده دسترسی پیدا می کنید ,اطلاعات برای زمانی هستند که شما آنها را pickle کردید و آپدیت نمی شوند .اگر فقط می خواهید اطلاعات ضروری از QuerySet را ذخیره کنید ,باید مقدار query را از QuerySet به شکل زیر pickle یا ذخیره کنید . (در این روش نتایج هیچگاه بارگذاری نمی شود و فقط query شما ذخیره می شود)&gt;&gt;&gt; import pickle
&gt;&gt;&gt; query = pickle.loads(s) # Assuming &#039;s&#039; is the pickled string.
&gt;&gt;&gt; qs = MyModel.objects.all()
&gt;&gt;&gt; qs.query = query # Restore the original &#039;query&#039;.البته باید بدانید که query در اینجا محو است و فقط ساختار داخلی یک query را نشان می دهد . این باعث می شود تا بخشی از API خود جنگو نباشد . با این حال pickle و unpickle کردن به این صورت (یعنی محتوای query) کاملا پشتیبانی می شود .نکته : به یاد داشته باشید نمی توانید با ورژن n از کتابخانه یک QuerySet را pickle کنید و با ورژن دیگری از کتابخانه آن را unpickle کنید . در آن صورت با ارور مواجه خواهید شد . به همین علت هیچگاه pickle کردن برای ذخیره داده به مدت طولانی مناسب نیست .در این جلسه در رابطه با Queryset و نحوه استفاده از کتابخانه pickle پرداختیم . در جلسه بعدی در رابطه با متد های قابل استفاده روی Queryset و انواع آن (دو نوع) صحبت خواهیم کرد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Wed, 13 Dec 2023 16:02:27 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی و یک | بررسی روابط دیتابیسی در نمونه های جنگو</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D9%88-%DB%8C%DA%A9-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B1%D9%88%D8%A7%D8%A8%D8%B7-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3%DB%8C-%D8%AF%D8%B1-%D9%86%D9%85%D9%88%D9%86%D9%87-%D9%87%D8%A7%DB%8C-%D8%AC%D9%86%DA%AF%D9%88-pcua9ma2cdw5</link>
                <description>در این جلسه در رابطه با انواع روابط دیتابیسی توضیح میدهیم و آنها را به طور تخصصی و نحوه استفاده از آنها را در query بررسی می کنیم . با ما همراه باشید .آموزش جنگو : جلسه سی و یک | بررسی روابط دیتابیسی در نمونه های جنگوشی های ارجاع داده شدهدر این بخش به بررسی ساده ی نحوه استفاده از API فیلد های روابطی مانند ForeignKey و OneToOneField یا ManyToManyField خواهیم داشت .با استفاده از مثال های اول این مقالات ,برای مثال در مدل Entry می توانید با دسترسی به ویژگی blog (اگر شی Entry با عنوان e تعریف شود : e.blog) شی Blog مرتبط را دریافت کنید .البته شما این مباحث را قبلا بررسی کردید . ما در اینجا برای افراد کنجکاوتر این مباحث را با جزییات توضیح می دهیم .همچنین جنگو یک API دیگر برای طرف دیگر رابطه ایجاد می کند . پیوندی از مدل ارجاع داده شده به مدلی که رابطه را تعریف کرده است . برای مثال اگر یک شی Blog با نام b تعریف شده باشد ,از طریق entry_set می تواند به لیستی از تمامی شی های مدل Entry دسترسی یابد که به شی b رابطه دارند (دقیقا برعکس مثال بالایی). برای مثال : ()b.entry_set.allتمام مثال های این بخش را با مثال های مدل Blog و Author و Entry پیش خواهیم برد . (در سری مقالات پیش آنها را ایجاد کردیم . می توانید آنها را بررسی کنید)روابط One-to-many (نوع Forward)همانطور که می دانید اگر برای مثال یک ForeignKey داشته باشید (جزو روابط یک به چند حساب می شود) شما می توانید از طریق نام فیلد ForeignKey مدل ,به شی ارجاع داده شده (شی ای از مدل Blog که به آن رابطه زدیم) دسترسی پیدا کنید .برای مثال :&gt;&gt;&gt; e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog # Returns the related Blog object.همچنین شما از این روش می توانید حتی برای ست کردن (تنظیم شی جدید برای فیلد ForeignKey) استفاده کنید . فقط حواستان باشد که تا متد ()save را فراخوانی نکنید تغییرات روی دیتابیس اعمال نمی شوند . برای مثال :&gt;&gt;&gt; e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog = some_blog
&gt;&gt;&gt; e.save()اگر فیلد شما از null=True پشتیبانی کند (یعنی مقادیر NULL را بپذیرد) شما می توانید با تنظیم فیلد روی None رابطه را با آن شی حذف کنید . برای مثال :&gt;&gt;&gt; e = Entry.objects.get(id=2)
&gt;&gt;&gt; e.blog = None
&gt;&gt;&gt; e.save() # &amp;quotUPDATE blog_entry SET blog_id = NULL ...;&amp;quotدسترسی های شما به فیلد ForeignKey برای اولین بار در کش ذخیره می شوند و دفعات بعدی دسترسی , از همان کش استفاده می کنند . برای مثال :&gt;&gt;&gt; e = Entry.objects.get(id=2)
&gt;&gt;&gt; print(e.blog) # Hits the database to retrieve the associated Blog.
&gt;&gt;&gt; print(e.blog) # Doesn&#039;t hit the database; uses cached version.توجه داشته باشید که استفاده از متد ()select_related تمام کش را از قبل پر می کند (درباره این متد جلوتر صحبت خواهیم کرد) . برای مثال :&gt;&gt;&gt; e = Entry.objects.select_related().get(id=2)
&gt;&gt;&gt; print(e.blog) # Doesn&#039;t hit the database; uses cached version.
&gt;&gt;&gt; print(e.blog) # Doesn&#039;t hit the database; uses cached version.نکته : نوع های رابطه ای که رو به جلو هستند (یعنی از مدل حاوی رابطه به شی ارجاع داده شده) Forward نامیده می شوند . بالعکس نوع های رابطه بازگشتی (یعنی از شی ارجاع داده شده به مدل حاوی رابطه) Backward نامیده می شوند . مثال های بالا تماما از نوع Forward بودند .بررسی روابط backwardهنگامی که شما در یک مدل رابطه ForeignKey ایجاد می کنید ,اشیا مدل ارجاع داده شده به یک مدیر یا manager دسترسی دارند که همه ی اشیا مدل اول یا منبع را (مدل حاوی رابطه) بازخواهد گرداند . به طور پیش فرض این مدیر FOO_set نام دارد . در آن FOO نام مدل منبع با حروف کوچک است . این مدیر ,یک QuerySet را بازمیگرداند . پس شما می توانید روی نتایج آن فیلتر انجام دهید (همانطور که در بخش قبلی توضیح داده شد).برای مثال :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entry_set.all() # Returns all Entry objects related to Blog.
# b.entry_set is a Manager that returns QuerySets.
&gt;&gt;&gt; b.entry_set.filter(headline__contains=&#039;Lennon&#039;)
&gt;&gt;&gt; b.entry_set.count()شما می توانید FOO_set را با استفاده از تعریف related_name در ForeignKey بازنویسی کنید . برای مثال اگر کد را به شکل blog = ForeignKey(Blog , on_delete=models.CASCADE , related_name=‘entries’) بنویسید , مثال بالا به شکل زیر خواهد بود :&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entries.all() # Returns all Entry objects related to Blog.
# b.entries is a Manager that returns QuerySets.
&gt;&gt;&gt; b.entries.filter(headline__contains=&#039;Lennon&#039;)
&gt;&gt;&gt; b.entries.count()استفاده از یک reverse manager سفارشیبه طور پیش فرض مدیر یا manager مورد استفاده برای روابط معکوس یا بازگشتی یک زیر کلاس از مدل پیش فرض مدل است . اگر می خواهید برای یک query از مدیری سفارشی استفاده کنید از روش زیر استفاده می کنید:from django.db import models

class Entry(models.Model):
    #...
    objects = models.Manager() # Default Manager
    entries = EntryManager() # Custom Manager

b = Blog.objects.get(id=1)
b.entry_set(manager=&#039;entries&#039;).all()اگر EntryManager فیلتر پیش فرض را در متد ()get_aueryset اعمال میکرد ,این عملیات برای متد ()all و همگی اشیا اجرا می شد .تعیین یک مدیر سفارشی همچنین به شما این امکان را می دهد تا از متد ها سفارشی سازی شده آن نیز استفاده کنید :b.entry_set(manager=&#039;entries&#039;).is_published()نکته : در قسمت های بعدی به شما توضیح می دهیم که چگونه می توانید یک مدیر سفارشی بنویسید .متدهای پرکاربرد در related objectsعلاوه بر تمام متد هایی که در بالا گفته شد یک ForeignKey دارای متد های دیگری نیز است که در اینجا نگاهی اجمالی به آنها انداخته ایم . خلاصه ای از هر یک در زیر آمده است و جزییات کامل را بعدا در فصل related obejcts خواهید خواند .متد add(obj1 , obj2) :شی های داده شده را به ForeignKey اضافه می کند .متد create(**kwargs) :یک شی جدید را ایجاد می کند و آن را به ForeignKey اضافه می کند . همچنین شی جدید را باز میگرداند .متد remove(obj1 , obj2) :شی های داده شده را از ForeignKey حذف می کند .متد ()clear :تمام اشیا را از فیلد شما پاک می کند .متد set(objs) :یک مجموعه از اشیا را جایگزین می کند .بعدا درباره تمام این متد ها بیشتر صحبت خواهیم کرد .روابط Many-to_manyدر این نوع از رابطه ,هر دو طرف رابطه یک API برای دسترسی به طرفین دیگر خود می گیرند . API مشابه عملگرد backward در one_to_many هست . گرچه دارای تفاوت هایی نیز هست .یک تفاوت در نامگذاری ویژگی ها (attribute) است . مدلی که ManyToManyField را درون خود تعریف می کند ,از نام متغییر حاوی فیلد برای آن استفاده می کند گرچه در رابطه reverse یا بازگشتی خود از نام مدل حاوی فیلد به صورت حروف کوچک به علاوه set_ استفاده می کند .برای مثال :e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains=&#039;John&#039;)
a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.مانند روابط ForeignKey ,روابط ManyToMany نیز می توانند related_name را مشخص کنند . در مثال بالا, اگر فیلد ManyToManyField در مدل Entry ,یک ویژگی “related_name = entries” را مشخص کنید , هر شی از مدل Author به جای یک entry_set یک entries خواهد داشت .یک تفاوت دیگر ManyToMany با OneToMany این است که نوع اول می توانند در متد های ()add() , set و ()remove مقدار های primary (مانند لیست) دریافت کنند . برای مثال اگر e1 و e2 شی هایی از مدل Entry باشند ,آنگاه متد ()set می تواند به صورت زیر عمل کند .a = Author.objects.get(id=5)
a.entry_set.set([e1, e2])
a.entry_set.set([e1.pk, e2.pk])روابط One-to-oneاستفاده از روابط One-to-one بسیار شبیه استفاده از Many-to-one است . برای مثال اگر شما یک فیلد OneToOneField در مدل خود داشته باشید ,شما می توانید با استفاده از نام متغییر حاوی فیلد به آن ویژگی دسترسی پیدا کنید :class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.تفاوت اصلی در میان این دو نوع از روابط در آن است که در OneToOneField برای استفاده از رابطه بازگشتی شما مانند هر رابطه دیگری به یک manager و  API آن دسترسی دارید ,اما manager شما بجای بازگرداندن یک مجموعه از اشیا یک شی واحد را بازمیگرداند . (به دلیل اینکه رابطه شما فقط با یک شی می تواند باشد) برای مثال :e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail objectاگر هیچ شی ای هنوز به این فیلد اختصاص داده نشده باشد یک خطای DoesNotExist بازگردانده می شود . همچنین به یاد داشته باشید که می توانید در روابط بازگشتی One-to-one به همان روش عادی فیلد را مقداردهی کنید . (دقت کنید که گفتیم رابطه بازگشتی را!)e.entrydetail = edنکته : روابط بازگشتی در جنگو به وسیله تنظیمات INSTALLED_APPS شناسایی و ایجاد می شوند . پس مراقب باشید زیرا اگر اپ حاوی مدل را به درستی در جنگو وارد نکنید ,باعث می شود تا بعضی از روابط بازگشتی به درستی کار نکنند .(این یکی از ویژگی های خوب جنگو است زیرا بقیه فریموورک ها همواره نیاز دارند تا رابطه های بازگشتی را خودتان دستی تعریف کنید ولی جنگو این کار را اتوماتیک انجام می دهد)در این جلسه در رابطه با انواع روابط دیتابیسی جنگو و کاربرد آنها صحبت کردیم . در جلسه بعدی به بررسی پیشرفته query و چگونگی عملکرد آن می پردازیم . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Mon, 11 Dec 2023 15:05:37 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه سی | بررسی کوئری های ساده و پیچیده با Q در جنگو</title>
                <link>https://virgool.io/codenevis/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%B3%DB%8C-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D9%87%D8%A7%DB%8C-%D8%B3%D8%A7%D8%AF%D9%87-%D9%88-%D9%BE%DB%8C%DA%86%DB%8C%D8%AF%D9%87-%D8%A8%D8%A7-q-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-bw0fy1owoltu</link>
                <description>در این جلسه به بررسی Q در جنگو و چندین مبحث دیگر درباره چگونگی کار با اشیا در جنگو خواهیم پرداخت . آموزش جنگو : جلسه سی | بررسی کوئری های ساده و پیچیده با Q در جنگوایجاد query های پیچیده با Qجستجو هایی مانند ()filter و غیره همگی آرگومان هایی را به صورت Keyword دریافت می کنند و آنها را به صورت AND اجرا می کنند . این یعنی هر کدام از رکورد ها در نتیجه باید همزمان تمام مقادیر درون جستجو را داشته باشد . اگر نیاز داشته باشید نوع های پیچیده تری از query را بنویسید که برای مثال به صورت OR کار کنند (یعنی یا مقدار اول را دارا باشند یا مقدار دوم) می توانید از Q استفاده کنید .کلاس Q , یک کلاس است که از آن نمونه سازی می شود . آن را می توانید از django.db.models.Q داشته باشید . شما با این کلاس می توانید queryset خود را به همراه lookup درونش , کپسوله سازی کنید .برای مثال شی کلاس Q زیر , یک LIKE را کپسوله سازی می کند . (lookup و query درون آن قرار دارند)from django.db.models import Q
Q(question__startswith=&#039;What&#039;)اشیا Q را می توان با عملگر های ^ و &amp; و | ترکیب نمود . وقتی یک عملگر روی دو شی Q استفاده می شود ,یک شی Q جدید را ایجاد می کند . (عملگر ها می توانند شی های Q را با هم ترکیب کنند)به عنوان مثال ,این عبارت یک شی Q را می سازد که OR را بر روی دو query حاوی question__startswith ,اجرا می کند . (عملگر ‘یا’ استفاده شده است پس هر کدام از شروط زیر برقرار باشد , رکورد برگردانده می شود)Q(question__startswith=&#039;Who&#039;) | Q(question__startswith=&#039;What&#039;)کد بالا معادل دستور SQL زیر است :WHERE question LIKE &#039;Who%&#039; OR question LIKE &#039;What%&#039;می توانید با ترکیب عملگر های مختلف ,عبارات جستجوی پیچیده تری بنویسید . همچنین می توانید از ~ در قبل شی Q استفاده کنید تا دستور NOT اجرا شود و شرط شما نفی شود . برای مثال :Q(question__startswith=&#039;Who&#039;) | ~Q(pub_date__year=2005)هر نوع از lookup functions و یا همان متد های جستجو (query) که از شما Keword دریافت می کنند ,مانند ()filter و یا ()exclude همچنین می توانند یک یا چند نمونه Q را به عنوان آرگومان دریافت کنند . اگر چندین نمونه Q را با هم به تابع یا متد ارسال کنید ,دستور AND روی آنها فراخوانی خواهد شد . مثلا :Poll.objects.get(
    Q(question__startswith=&#039;Who&#039;),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)کد بالا به صورت کد SQL زیر خواهد بود :SELECT * from polls WHERE question LIKE &#039;Who%&#039;
AND (pub_date = &#039;2005-05-02&#039; OR pub_date = &#039;2005-05-06&#039;)شما می توانید همچنین بجای استفاده از دو نمونه Q , از یک lookup Keyword و یک Q استفاده کنید و نتایج هنوز درست هستند . با این حال به یاد داشته باشید که همه ی آنها دستور AND را خواهند داشت (یعنی همه شروط و آرگومان ها در خروجی باید رعایت شوند) . برای مثال :Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith=&#039;Who&#039;,
)نکته :به یاد داشته باشید اگر کد شما به شکل بالا بود و همزمان دارای آرگومان های Keyword و Q بود ,نمونه های Q ابتدا قرار می گیرند . برای مثال کد بالا درست است . اما کد زیر یک کد نامعبتر است زیرا این دستور در آن رعایت نشده است .# INVALID QUERY
Poll.objects.get(
    question__startswith=&#039;Who&#039;,
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)مقایسه شی هابرای مقایسه دو شی با هم از یک مدل ,از عملگر استاندارد پایتون (==) استفاده کنید . در پشت صحنه ,جنگو مقدار primary key هر دو شی را با یکدیگر مقایسه خواهد کرد .پس با توجه به این ,دو عبارت زیر برابر خواهند بود :&gt;&gt;&gt; some_entry == other_entry
&gt;&gt;&gt; some_entry.id == other_entry.idاگر فیلد id شما primary key نباشد و شما آن را تغییر داده باشید ,مشکلی نیست . هر فیلدی که primary key مدل شما باشد , برای مقایسه استفاده خواهد شد . برای مثال اگر فیلد primary key شما name باشد ,دو عبارت زیر برابر هستند :&gt;&gt;&gt; some_obj == other_obj
&gt;&gt;&gt; some_obj.name == other_obj.nameحذف شی هامتد ()delete برای حذف یک شی به کار می رود . این متد بلافاصله شی را حذف می کند و سپس تعداد اشیا حذف شده را به همراه یک دیکشنری و تعداد حذفیات در هر نوع شی درون آن باز میگرداند .&gt;&gt;&gt; e.delete()
(1, {&#039;blog.Entry&#039;: 1})متد ()delete علاوه بر این می تواند به صورت گروهی نیز اشیا را حذف کند . هر QuerySet یک متد ()delete دارد که تمام اعضای آن QuerySet را برای شما حذف خواهد کرد .برای مثال ,کد زیر تمام اشیا مدل Entry را که فیلد pub_date آنها برابر با 2005 است را حذف می کند :&gt;&gt;&gt; Entry.objects.filter(pub_date__year=2005).delete()
(5, {&#039;webapp.Entry&#039;: 5})به خاطر داشته باشید که فراخوانی متد ()delete بر روی نمونه های منفرد یا همان شی ها به صورت تکی ,منتظر نمی ماند تا فرایند به طور کامل اجرا شود و همان لحظه روی دیتابیس اجرا خواهد شد . پس اگر می خواهید از فراخوانی درست این متد اطمینان حاصل کنید می توانید شی ها را به صورت دستی و یکی پس از دیگری حذف کنید . (مثلا با تکرار QuerySet و فرخوانی ()delete روی هر کدام از شی ها به صورت جدا) این کار را بجای حذف کلی و فراخوانی متد ()delete روی کل QuerySet می توان انجام داد .وقتی جنگو یک شی را حذف می کند رفتار ON DELETE CASCADE را در SQL شبیه سازی می کند . برای مثال :b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()در مثال بالا هر شی که به وسیله ForeignKey به سمت این شی رابطه داشته باشد ,نیز حذف خواهد شد . این رفتار از طریق آرگومان on_delete می تواند تغییر پیدا کند .به یاد داشته باشید که دلیل وجود نداشتن متد ()delete روی manager و اینکه کد ها به شکل ()Entry.obejcts.delete وجود ندارند این است که جنگو می خواهد از تصادفی حذف کردن تمام اشیا به طور اشتباهی جلوگیری کند . پس برای حذف یک دسته از اشیا , باید ابتدا آنها را از دیتابیس دریافت کنید و سپس متد ()delete را از QuerySet فراخوانی کنید .کپی کردن اشیااگر چه جنگو روش خاصی را برای کپی کردن یک شی و ایجاد یک شی جدید از همان ندارد ولی , شما می توانید به راحتی فیلد pk را روی None و state.adding_ را روی True تنظیم کنید و شی جدیدی را با مقادیر فیلد های شی قدیمی ایجاد کنید . (کپی می شود) برای مثال :blog = Blog(name=&#039;My blog&#039;, tagline=&#039;Blogging is easy&#039;)
blog.save() # blog.pk == 1
blog.pk = None
blog._state.adding = True
blog.save() # blog.pk == 2گرچه اوضاع هنگامی که از ارث بری استفاده کنید ,پیچیده تر می شود . برای مثال اگر مدل زیر زیرکلاسی از Blog باشد :class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)
    django_blog = ThemeBlog(name=&#039;Django&#039;, tagline=&#039;Django is easy&#039;, theme=&#039;python&#039;)
     django_blog.save() # django_blog.pk == 3با توجه به اینکه وراثت شما چگونه کار می کند باید pk و id را  به صورت همزمان روی None و state.adding_ را روی True قرار دهید . به مثال زیر نگاه کنید :django_blog.pk = None
django_blog.id = None
django_blog._state.adding = True
django_blog.save() # django_blog.pk == 4در طول فرایند کپی کردن ,فیلد های روابطی کپی نخواهند شد . پس شما نیاز دارید تا آنها را جدا کپی و وصل کنید . برای مثال Entry یک ManyToManyField دارد که با Author رابطه پیدا کرده است . بعد از تعریف کردن entry در کد زیر ,شما باید آن را برای شی جدید تنظیم کنید :entry = Entry.objects.all()[0]
# some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry._state.adding = True
entry.save()
entry.authors.set(old_authors)البته توجه داشته باشید که برای OneToOneField این قانون به طور عادی اجرا نمی شود (به دلیل اینکه شما نمی توانید دو شی یکسان برای یک فیلد OneToOne داشته باشید) . برای مثال اگر entry تعریف شده در کد بالا را در مثال زیر نیز داشته باشیم , به صورت زیر باید عمل کنیم :(باید به صورت دستی خودمان شی ای را برای فیلد تنظیم کنیم)detail = EntryDetail.objects.all()[0]
detail.pk = None
detail._state.adding = True
detail.entry = entry
detail.save()آپدیت کردن چند فیلد همزمان (به یکباره)گاهی می خواهید یک مقدار خاص را برای یک فیلد خاص ,در تمامی اشیا یک QuerySet تنظیم کنید . برای این کار از متد ()update استفاده می شود :# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline=&#039;Everything is the same&#039;)با استفاده از این روش فقط می توانید فیلد های غیر روابطی و ForeignKey را آپدیت کنید . برای آپدیت فیلد های غیر رابطه ای فقط کافیست مقدار جدید را به عنوان مقدار ثابت تنظیم کنید . برای آپدیت فیلد های ForeignKey نیز کافی است تا شی جدیدی که می خواهید به آن اشاره کنید را به عنوان مقدار تنظیم کنید .برای مثال :&gt;&gt;&gt; b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
&gt;&gt;&gt; Entry.objects.update(blog=b)متد ()update فورا بر دیتابیس اعمال می شود و تعداد سطرهایی را که با query منطبق هستند را بازمیگرداند (که ممکن است برابر با تعداد ستون های آپدیت شده نباشد زیرا بعضی از ستون ها از قبل مقدار جدید دارند) . تنها محدودیتی که در اینجا دارید این است که شما فقط روی جدول اصلی دیتابیس می توانید این عملیات ها را انجام دهید . این یعنی از فیلد های روابطی نمی توانید به طور reverse استفاده کنید . برای مثال :&gt;&gt;&gt; b = Blog.objects.get(pk=1)
# Update all the headlines belonging to this Blog.
&gt;&gt;&gt; Entry.objects.filter(blog=b).update(headline=&#039;Everything is the same&#039;)توجه کنید که استفاده از ()update مستقیما یک دستور SQL را تولید می کند , پس هیچ عملیات ()save یا سیگنال های pre_save و post_save را فراخوانی نمی کند (یا auto_now را نادیده می گیرد) . پس اگر می خواهید از ذخیره شدن عملیات بر روی دیتابیس مطمین شوید ,فقط کافیست روی QuerySet خود یک حلقه بزنید و آنها را تک به تک ()save کنید . برای مثال :for item in my_queryset:
    item.save()همچنین می توانید روی یک F expression نیز متد ()update را فراخوانی کنید . این زمانی مفید است که بخواهید مقدار یک فیلد را بر اساس یک فیلد دیگر از همان مدل به روز رسانی کنید . برای مثال برای افزایش تعداد pingback به ازای هر شی entry در مدل Blog :&gt;&gt;&gt; Entry.objects.update(number_of_pingbacks=F(&#039;number_of_pingbacks&#039;) +1)نکته : اگر تلاش کنید از join (استفاده از _ برای اتصال به فیلد هایی از شی ارجاع داده شده در فیلد رابطه ای) در توابع ()F در زمان آپدیت استفاده کنید ,یک FieldError دریافت خواهید کرد :# This will raise a FieldError
&gt;&gt;&gt; Entry.objects.update(headline=F(&#039;blog__name&#039;))در این جلسه در رابطه با اشیا Q , نحوه آپدیت و انجام بعضی از عملیات ها روی اشیا در جنگو صحبت کردیم . در جلسه بعدی در مورد اشیا ارجاع داده شده و روابط دیتابیسی و نحوه جستجو در میان آنها صحبت خواهیم کرد و آنها را به صورت پیشرفته بررسی می کنیم . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Sun, 10 Dec 2023 01:06:15 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو : جلسه بیست و نه | transform , Asynchronous , caching در جنگو</title>
                <link>https://virgool.io/codenevis/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-%D8%AC%D9%84%D8%B3%D9%87-%D8%A8%DB%8C%D8%B3%D8%AA-%D9%88-%D9%86%D9%87-transform-asynchronous-caching-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88-jghwnyf6o366</link>
                <description>در این جلسه در رابطه با transform در فیلتر ها , برخی از میانبر ها یا شورتکات ها , مبحث کش ها و Asynchronous صحبت خواهیم کرد . با ما همراه باشید . آموزش جنگو : جلسه بیست و نه | transform , Asynchronous , caching در جنگوبررسی transform در فیلتر هاجنگو از transform یا همان تبدیل نیز در lookup پشتیبانی می کند .به عنوان مثال برای دریافت تمام رکورد های Entry که سال pub_date آنها با سال mod_date آنها یکسان باشد (یعنی سال انتشار آنها با سال آخرین تغییر یافتن آنها یکسان باشد) :&gt;&gt;&gt; from django.db.models import F
&gt;&gt;&gt; Entry.objects.filter(pub_date__year=F(&#039;mod_date__year&#039;))برای دریافت اولین سالی که (قدیمی ترین) یک Entry منتشر شده است ,از کد زیر استفاده می کنیم . (بعدا بیشتر درباره این کد ها صحبت می کنیم)&gt;&gt;&gt; from django.db.models import Min
&gt;&gt;&gt; Entry.objects.aggregate(first_published_year=Min(&#039;pub_date__year&#039;))کد زیر نیز بالاترین امتیاز (فیلد rate) که یک رکورد Entry دارد را به علاوه جمع تعداد comment از هر سال و تمام entry ,پیدا می کند و مقدار آن را محاسبه می کند . (در مورد تابع های ناشناخته داخل کد بیشتر صحبت می کنیم)&gt;&gt;&gt; from django.db.models import OuterRef, Subquery, Sum
&gt;&gt;&gt; Entry.objects.values(&#039;pub_date__year&#039;).annotate(
..   top_rating=Subquery(
...  Entry.objects.filter(
...  pub_date__year=OuterRef(&#039;pub_date__year&#039;),
...  ).order_by(&#039;-rating&#039;).values(&#039;rating&#039;)[:1]
...   ),
...  total_comments=Sum(&#039;number_of_comments&#039;),
...  )بررسی میانبر pk lookupنکته : تفاوت primary key با id در آن است که اولی می تواند از پیش فرض که روی id است ,روی فیلد دیگری قرار بگیرد و از این پس در lookup استفاده از pk (primary key) از همان فیلد استفاده می شود .برای راحتی، جنگو میانبر lookup pk را ارائه می دهد که مخفف “primary key&quot; است. برای مثال در مدل Blog، کلید اصلی یا همان  , primary key فیلد id است، بنابراین این سه عبارت یکسان هستند و یک کار را انجام می دهند :&gt;&gt;&gt; Blog.objects.get(id__exact=14) # Explicit form
&gt;&gt;&gt; Blog.objects.get(id=14) # __exact is implied
&gt;&gt;&gt; Blog.objects.get(pk=14) # pk implies id__exactاستفاده از pk محدود به exact نیست و می توانید هر query و با هر نوع lookup را با pk ترکیب کنید تا یک query روی مدل اصلی انجام بدهید .# Get blogs entries with id 1, 4 and 7
&gt;&gt;&gt; Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id &gt; 14
&gt;&gt;&gt; Blog.objects.filter(pk__gt=14)همچنین آنها بدون استفاده از pk و با نام بردن از فیلد primary key نیز می توانند کار خود را انجام دهند . سه عبارت زیر برابر هستند :&gt;&gt;&gt; Entry.objects.filter(blog__id__exact=3) # Explicit form
&gt;&gt;&gt; Entry.objects.filter(blog__id=3) # __exact is implied
&gt;&gt;&gt; Entry.objects.filter(blog__pk=3) # __pk implies __id__exactاستفاده از علامت درصد و _ در LIKEنکته : برای خواندن این بحث باید کمی به SQL مسلط باشید !جستجوی ها یا همان query در جنگو هنگامی که از دستور LIKE استفاده می کنند (مثال iexact , contains , icontains , startwith , istartwith , endwith , iendswith) به طور خودکار علامات خاص (%و _) را در دستور جای گذاری می کنند . در یک عبارت LIKE ,علامت درصد و علامت _ برای مطابقت ها به کار می رود .بنابراین شاید فکر کنید که نمی توان علامت درصد را در پارامتر ها استفاده کرد ولی اینطور نیست . جنگو به طور مستقیم کار می کند و آنها را هندل می کند . برای مثال , برای دریافت تمام رکورد هایی که در آنها علامت درصد به کار رفته است , کد زیر را بنویسید :&gt;&gt;&gt; Entry.objects.filter(headline__contains=&#039;%&#039;)جنگو کد بالا را به صورت زیر درمیاورد و آن را می تواند بسیار ساده در دیتابیس اجرا کند .SELECT ... WHERE headline LIKE &#039;%\%%&#039;;همین مراحل برای علامت _ نیز تکرار می شوند . پس لازم نیست نگران آن باشید !بررسی Querysets و cachingهر QuerySet دارای یک cache یا کش یا همان حافظه نهان است تا اطلاعات را در آن ذخیره کند و میزان تعدد دسترسی به دیتابیس را به حداقل برساند . درک نحوه کار این قابلیت به شما امکان می دهد تا کد کارآمدتری بنویسید .در یک QuerySet که تازه ایجاد شده باشد ,کش خالی است . هنگامی که برای اولین بار یک QuerySet اجرا می شود ,یک Query در دیتابیس اجرا می شود و جنگو نتایج آن را در حافظه پنهان QuerySet شما ذخیره می کند و سپس نتایج را به شما باز میگرداند (و اگر همان QuerySet دوباره تکرار شود از کش استفاده می کند) .این رفتار ذخیره سازی را در یاد داشته باشید زیرا اگر آن را درست مدیریت نکنید , ممکن است برای شما مشکلاتی را ایجاد کند ! به عنوان مثال موارد زیر دو QuerySet را ایجاد می کند ,آنها را در دیتابیس اجرا می کند و در آخر آنها را دور میندازد .&gt;&gt;&gt; print([e.headline for e in Entry.objects.all()])
&gt;&gt;&gt; print([e.pub_date for e in Entry.objects.all()])کد بالا دو بار Query را در دیتابیس اجرا می کند (از کش آن استفاده نمی کند) و باعث می شود کار دیتابیس سنگین تر باشد . این همچنین باعث می شود تا رکورد های بازگشتی یکسان نباشند , زیرا ممکن است یک رکورد در هنگام اجرای دو Query بین آنها تقسیم شود و حذف شود !برای جلوگیری از این مشکل باید فقط یک QuerySet را ایجاد کرده و هر بار روی آن پیمایش کنید . برای مثال :&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; print([p.headline for p in queryset]) # Evaluate the query set.
&gt;&gt;&gt; print([p.pub_date for p in queryset]) # Reuse the cache from the evaluation.نکته مهم : به یاد داشته باشید که QuerySet همیشه نتایج خود را کش نمی کنند . هنگام اجرای QuerySet تنها بخشی از جستجو ,کش بررسی میشود تا پر نشده باشد ! پس مواردی که در Query بعدی برگردانده می شود ,کش شده نیستند . این به معنی آن است که هنگامی که شما نتایج بازگشتی query را محدود کنید (مثلا با ایندکس های آرایه ها یا slice دادن) , query شما هیچگاه در کش ذخیره نخواهد شد .به عنوان مثال کد زیر یک slice آرایه ای را روی QuerySet شما اعمال کرده است پس در هر خط به جای استفاده از کش , جنگو آن را دوباره در دیتابیس اجرا می کند .&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; print(queryset[5]) # Queries the database
&gt;&gt;&gt; print(queryset[5]) # Queries the database againولی ,اگر QuerySet قبلا در دیتابیس اجرا شده باشد ,این بار حافظه کش آن بررسی می شود و این بهتر است !&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; [entry for entry in queryset] # Queries the database
&gt;&gt;&gt; print(queryset[5]) # Uses cache
&gt;&gt;&gt; print(queryset[5]) # Uses cacheدر اینجا یک مثال دیگر نیز آورده ایم که در آن QuerySet ابتدا در دیتابیس اجرا می شود و در استفاده های بعدی از کش آن استفاده می شود .&gt;&gt;&gt; [entry for entry in queryset]
&gt;&gt;&gt; bool(queryset)
&gt;&gt;&gt; entry in queryset
&gt;&gt;&gt; list(queryset)نکته : استفاده از دستور ()print کش را پر نمی کند !بررسی مفهوم جستجو های Asynchronousنکته : قبل از مطالعه این بخش و بخش های بعدی ابتدا با مفهوم synchronous (اجرای کدها به صورت همزمان) و asynchronous (اجرای کد ها به صورت ناهمزمان) در پایتون آشنا شوید!اگر در حال نوشتن کدها و ویو های asynchronous هستید ,شما نمی توانید از ORM برای query به روشی که در بالا توضیح دادیم ,استفاده کنید . زیرا نمی توان یک کد synchronous را از داخل یک قطعه کد asynchronous مسدود یا block کرد (متوقف) . به احتمال زیاد نیز جنگو در این مواقع یک خطای SynchronousOnlyOperation برای جلوگیری از این کار ایجاد خواهد کرد .خوشبختانه جنگو راه های زیادی پیش پای شما گذاشته است تا بتوانید به صورت asynchronous کد های خود را بنویسید . هر متدی که باعث می شود تا کد های شما block شوند (مانند ()get یا ()delete) یک ورژن دیگر نیز دارد که برای نوشتن کدهای asynchronous به کار می روند (برای مثال ()aget و ()adelete) و هنگامی که از آنها استفاده کنید , میتوانید روی نتایج بازگشتی از آنها به صورت asynchronous پیمایش کنید !در صفحه بعد تا حدودی با این روش ها آشنا خواهید شد .پیمایش روی queryجنگو به صورت پیش فرض پیمایش یا همان iteration روی synchronous انجام می دهد . به همین علت هنگامی که بخواهید به صورت معمولی و در خود ویو با حلقه for پیمایشی را روی یک query انجام دهید ,جنگو نتایج را block و متوقف می کند . این به این علت است که جنگو همزمان با پیمایش ,نتایج را نیز لود می کند (شما قطعا این را نمی خواهید) . برای درست کردن این مشکل فقط کافی است آن را به صورت async اجرا کنید .نکته : شما این مشکل را فقط زمانی دارید که در ویو خود بخواهید روی query پیمایش کنید و for زدن در تمپلیت های جنگو باعث ایجاد این مشکل نمی شود (یعنی اگر ابتدا query را به تمپلیت بفرستید و با تمپلیت تگ ها روی آن پیمایش کنید)async for entry in Authors.objects.filter(name__startswith=&amp;quotA&amp;quot):
    ...توجه داشته باشید که در حالت async نمی توانید کار های دیگری مثل تبدیل کردن query به لیست انجام دهید یا هر کاری که موجب شود QuerySet شما دوباره روی دیتابیس اجرا شود .دلیل اینکه می توانیم از متد های ()filter یا ()exclude استفاده کنیم آن است که این متد ها در واقع فقط query را ایجاد می کنند و آن را تا زمانی که از آن استفاده نکنید روی دیتابیس اجرا نمی کنند . پس شما می توانید آزادانه هر زمانی که نیاز داشتید در حالت async استفاده کنید . بعدا بیشتر درباره انواع متد هایی که این قابلیت را دارند صحبت خواهیم کرد .بررسی QuerySet و متد های managerزمانی که بخواهید کد ها را به صورت asynchronous بنویسید ,باید توجه کنید که برخی از متد ها مانند ()get و ()first اجرای query ساخته شده را اجباری می کنند و به همین علت همان لحظه توسط جنگو متوقف می شوند. این در حالی است که برخی دیگر از متد ها مانند ()filter و ()exclude اجرای در لحظه query را اجباری نمی کنند و می توانند در asynchronous به کار بروند . اما چگونه باید تفاوت میان آنها را تشخیص بدهید ؟یک راه این است که به اول آنها نگاه کنید . برای متد هایی که باعث متوقف شدن کد خواهند شد یک ورژن از آنها در نظر گرفته شده است که این را اصلاح می کند . برای مثال ()get و ()aget . ولی شما هیچگاه کدی به شکل ()afilter نخواهید دید ! گرچه جنگو پیشنهاد بهتری دارد و فقط کافی است در صفحه زیر بررسی کنید که با چه نوعی از متدها طرف هستید ؟(https://docs.djangoproject.com/en/4.1/ref/models/querysets/)در این صفحه شما دو گروه از متد ها را مشاهده خواهید کرد :1-متد هایی که یک QuerySet جدید را بازمیگردانند (قبلا در مورد چگونگی آن صحبت کرده ایم) . این ها همان متد هایی هستند که متوقف نمی شوند و نسخه های asynchronous ندارند . شما می توانید در هر شرایطی از آنها استفاده کنید .2-متد هایی که QuerySet جدیدی را بازمیگردانند . این متد ها توسط جنگو در زمان asynchronous بودن کد متوقف می شوند و برای آنها نسخه های async در نظر گرفته شده است تا در آن حالت بتوانید استفاده کنید .برای استفاده از نسخه async گروه دوم فقط کافی است پیشوند a را به اول اسم آنها اضافه کنید . با استفاده از تمایز ها می توانید بفهمید چگونه کد های asynchronous بنویسید . مثال زیر یک کد asynchronous معتبر است :user = await User.objects.filter(username=my_input).afirst()در کد بالا  , ()filter یک queryset جدید را بازمیگرداند پس شما می توانید آن را بدون هیچ مشکلی در محیط asynchronous استفاده کنید . ولی متد ()first مجبور است تا query را در دیتابیس اجرا کند و رکرود اول آن را به شما بازگرداند پس شما لازم است برای استفاده از آن در محیط asynchronous از ورژن ()afirst آن استفاده کنید . در آخر نیز باید کد را به صورت await به شکل بالا قرار دهید تا همه چیز درست کار کند . (مباحث wait و await جزو دروس ما نیستند)بررسی Transactionsدر asyncدر محیط asynchronous شما نمی توانید از transactions استفاده کنید . این به این دلیل است که جنگو هنوز به درستی از آنها در این محیط پشتیبانی نمی کند . اگر شما این کار را انجام بدهید , با خطای SynchronousOnlyOperation برخورد می کنید .اگر شما نیاز به استفاده از Transactions در این محیط دارید ,پیشنهاد می شود آن را به صورت ORM و به صورت یک تابع synchronous بنویسید و سپس آن را با استفاده از sync_to_async فراخوانی کنید .بررسی query در JSONFieldجستجو در میان JSONField متفاوت است (به دلیل وجود Key Transformations) . برای بررسی این موضوع در ادامه از مثال زیر استفاده خواهیم کرد .from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = models.JSONField(null=True)
    
    def __str__(self):
        return self.nameبررسی None در JSONFieldهمانطور که می دانید ذخیره کردن نوع داده ای None به عنوان مقدار یک فیلد باعث می شود تا در دیتابیس مقدار آن به صورت SQL NULL ذخیره شود . با اینکه توصیه نمی شود ولی شما می توانید نوع داده ای null (به صورت JSON) را بجای SQL NULL وارد دیتابیس کنید . فقط کافی است از Value(‘null’) استفاده کنید .البته که هنگامی که داده ها از دیتابیس دریافت می شوند ,پایتون جفت SQL NULL و JSON null را به صورت None یا خالی نمایش می دهد . پس تمایز بین آنها کمی دشوار است .البته باید گفت که اگر None درون یک لیست یا دیکشنری باشد همیشه به عنوان JSON null در نظر گرفته می شود . همچنین هنگام query و جستجو مقدار None همیشه به عنوان JSON null تفسیر می شود . برای جستجو و query با SQL NULL از isnull استفاده کنید .&gt;&gt;&gt; Dog.objects.create(name=&#039;Max&#039;, data=None) # SQL NULL.
&lt;Dog: Max&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Archie&#039;, data=Value(&#039;null&#039;)) # JSON null.
&lt;Dog: Archie&gt;
&gt;&gt;&gt; Dog.objects.filter(data=None)
&lt;QuerySet [&lt;Dog: Archie&gt;]&gt;
&gt;&gt;&gt; Dog.objects.filter(data=Value(&#039;null&#039;))
&lt;QuerySet [&lt;Dog: Archie&gt;]&gt;
&gt;&gt;&gt; Dog.objects.filter(data__isnull=True)
&lt;QuerySet [&lt;Dog: Max&gt;]&gt;
&gt;&gt;&gt; Dog.objects.filter(data__isnull=False)
&lt;QuerySet [&lt;Dog: Archie&gt;]&gt;نکته : اگر مطمئن نیستید که قرار است با مقادیر null کار داشته باشید بهتر است null را در فیلد روی False قرار داده و مقدار پیش فرضی را برای فیلد های خالی به صورت default=dict تنظیم کنید .نکته : ذخیره کردن JSON null ,مقدار null=False را نقض نمی کند .مفهوم Key , index و path در transformsمقدار JSON در پایتون مانند یک دیکشنری است . پس برای استفاده از آن فقط کافی است تا Key را به عنوان نام lookup استفاده کنید . برای مثال به مثال زیر نگاه کنید :&gt;&gt;&gt; Dog.objects.create(name=&#039;Rufus&#039;, data={
...  &#039;breed&#039;: &#039;labrador&#039;,
...  &#039;owner&#039;: {
...  &#039;name&#039;: &#039;Bob&#039;,
...  &#039;other_pets&#039;: [{
...  &#039;name&#039;: &#039;Fishy&#039;,
...  }],  },  })
&lt;Dog: Rufus&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Meg&#039;, data={&#039;breed&#039;: &#039;collie&#039;, &#039;owner&#039;: None})
&lt;Dog: Meg&gt;
&gt;&gt;&gt; Dog.objects.filter(data__breed=&#039;collie&#039;)
&lt;QuerySet [&lt;Dog: Meg&gt;]&gt;می توانید همچنین چندین Key را به هم متصل کنید تا lookup دقیق تری شکل بگیرد .&gt;&gt;&gt; Dog.objects.filter(data__owner__name=&#039;Bob&#039;)
&lt;QuerySet [&lt;Dog: Rufus&gt;]&gt;اگر به عنوان Key یک عدد را بدهید , جنگو آن را به عنوان ایندکس در نظر خواهد گرفت .&gt;&gt;&gt; Dog.objects.filter(data__owner__other_pets__0__name=&#039;Fishy&#039;)
&lt;QuerySet [&lt;Dog: Rufus&gt;]&gt;اگر Key شما با نام lookup دیگری در تضاد باشد ,میتوانید به جای آن از contains استفاده کنید .برای جستجو missing Keys یا کلید های گمشده از isnull استفاده کنید :&gt;&gt;&gt; Dog.objects.create(name=&#039;Shep&#039;, data={&#039;breed&#039;: &#039;collie&#039;})
&lt;Dog: Shep&gt;
&gt;&gt;&gt; Dog.objects.filter(data__owner__isnull=True)
&lt;QuerySet [&lt;Dog: Shep&gt;]&gt;نکته : شما همچنین می توانید Key را با lookup هایی مانند icontains, endswith, iendswith, iexact, regex, iregex, startswith, istartswith, lt, lte, gt نیز متصل کنید .نکته : بخاطر روشی که Key-path استفاده می کند ,متد های ()filter و ()exclude ضمانت نمی شوند که بتوانند رکورد های بازگشتی را درست برگردانند . اگر مسیر Key مشخص نیست ,بهتر است از isnull استفاده کنید .نکته : در دیتابیس های Oracle و MariaDB استفاده از متد ()order_by رکورد ها را بر اساس str مقدار ها مرتب می کند . دلیل آن این است که این دو ,تابعی درون خود ندارند که بتواند مقادیر JSON را به SQL تبدیل کند .مفهوم Key lookups و containmentدر این بخش درباره اتصال lookup و Key صحبتی خواهیم داشت . (چند lookup اضافه نیز برای JSONField در نظر گرفته شده است .)یک-contains :این متد روی JSONField به صورت متفاوتی کار می کند و بازنویسی شده است . آنها به عنوان مقدار lookup یک دیکشنری را دریافت می کنند تا آن را مقادیر درون فیلد مطابقت بدهند و اگر مقدار داخل فیلد حاوی آنها بود ,رکورد را بازمیگردانند . برای مثال :&gt;&gt;&gt; Dog.objects.create(name=&#039;Rufus&#039;, data={&#039;breed&#039;: &#039;labrador&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Rufus&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Meg&#039;, data={&#039;breed&#039;: &#039;collie&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Meg&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Fred&#039;, data={})
&lt;Dog: Fred&gt;
&gt;&gt;&gt; Dog.objects.filter(data__contains={&#039;owner&#039;: &#039;Bob&#039;})
&lt;QuerySet [&lt;Dog: Rufus&gt;, &lt;Dog: Meg&gt;]&gt;
&gt;&gt;&gt; Dog.objects.filter(data__contains={&#039;breed&#039;: &#039;collie&#039;})
&lt;QuerySet [&lt;Dog: Meg&gt;]&gt;نکته : contains در دیتابیس های Oracle و SQLite پشتیبانی نمی شود .دو-contained_by :این دقیقا برعکس contains است . یعنی رکورد هایی را بازمیگرداند که با مقدار ارسال شده توسط شما یا دقیقا مطابقت داشته باشد و یا خالی باشد ! به مثال زیر نگاه کنید :&gt;&gt;&gt; Dog.objects.create(name=&#039;Rufus&#039;, data={&#039;breed&#039;: &#039;labrador&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Rufus&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Meg&#039;, data={&#039;breed&#039;: &#039;collie&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Meg&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Fred&#039;, data={})
&lt;Dog: Fred&gt;
&gt;&gt;&gt; Dog.objects.filter(data__contained_by={&#039;breed&#039;: &#039;collie&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;QuerySet [&lt;Dog: Meg&gt;, &lt;Dog: Fred&gt;]&gt;
&gt;&gt;&gt; Dog.objects.filter(data__contained_by={&#039;breed&#039;: &#039;collie&#039;})
&lt;QuerySet [&lt;Dog: Fred&gt;]&gt;نکته : contained_by در دیتابیس های Oracle و SQLite پشتیبانی نمی شود .سه-has_key :رکورد هایی را باز میگرداند که حاوی Key داده شده هستند . برای مثال :&gt;&gt;&gt; Dog.objects.create(name=&#039;Rufus&#039;, data={&#039;breed&#039;: &#039;labrador&#039;})
&lt;Dog: Rufus&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Meg&#039;, data={&#039;breed&#039;: &#039;collie&#039;, &#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Meg&gt;
&gt;&gt;&gt; Dog.objects.filter(data__has_key=&#039;owner&#039;)
&lt;QuerySet [&lt;Dog: Meg&gt;]&gt;چهار-has_any_keys :رکورد هایی را باز میگرداند که هرکدام ار پارامتر های داده شده به عنوان Key در آنها وجود داشته باشند (همان قبلی است ولی چند Key را میتواند بپذیرد) . برای مثال :&gt;&gt;&gt; Dog.objects.create(name=&#039;Rufus&#039;, data={&#039;breed&#039;: &#039;labrador&#039;})
&lt;Dog: Rufus&gt;
&gt;&gt;&gt; Dog.objects.create(name=&#039;Meg&#039;, data={&#039;owner&#039;: &#039;Bob&#039;})
&lt;Dog: Meg&gt;
&gt;&gt;&gt; Dog.objects.filter(data__has_any_keys=[&#039;owner&#039;, &#039;breed&#039;])
&lt;QuerySet [&lt;Dog: Rufus&gt;, &lt;Dog: Meg&gt;]&gt;در این جلسه به بررسی چند مفهوم به صورت کوتاه در جنگو پرداختیم . در جلسه بعدی در رابطه با مفهوم متد Q و چندین متد و روش دیگر در جنگو صحبت خواهیم کرد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Thu, 07 Dec 2023 14:54:36 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو (Basic) : جلسه بیست و هشت | لوکاپ ها (field lookups)</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-basic-%D8%AC%D9%84%D8%B3%D9%87-%D8%A8%DB%8C%D8%B3%D8%AA-%D9%88-%D9%87%D8%B4%D8%AA-%D9%84%D9%88%DA%A9%D8%A7%D9%BE-%D9%87%D8%A7-field-lookups-j0toyqxo4njb</link>
                <description>در این جلسه در رابطه با field lookup صحبت خواهیم کرد . با ما همراه باشید .آموزش جنگو (Basic) : جلسه بیست و هشت | لوکاپ ها (field lookups)بررسی Field Lookupsمقادیر Field lookups در واقع روشی هستند که مقدار ورودی به WHERE در SQL را با آنها مشخص می کنید . آنها به عنوان کلمات کلیدی ()filter و ()exclude و ()get به صورت آرگومان وجود دارند .در واقع انواع مختلف دارند و با هر کدام یک نوع از عملیات های مقایسه ای در فیلد ورودی انجام می شود . شکل کلی استفاده از آنها به صورت field__(loouktype)=value است . برای مثال :&gt;&gt;&gt; Entry.objects.filter(pub_date__lte=&#039;2006-01-01&#039;)دستورات SQL زیر برای کد بالا تولید خواهند شد .SELECT * FROM blog_entry WHERE pub_date &lt;= &#039;2006-01-01&#039;;فیلد مشخص شده در Field lookup باید یکی از فیلد های مدل شما باشد (مدلی که manager آن را فراخوانی کرده اید) . البته یک استثنا وجود دارد . زمانی که فیلد شما یک ForeignKey باشد ,شما می توانید نام آن را به علاوه id_ بنویسید . در این زمان انتظار می رود که مقدار پارامتر شما برابر با مقدار id شی ارجاع داده شده به فیلد ForeignKey باشد (یعنی id شی ای که ForeignKey شما به آن ربط دارد ) . برای مثال :&gt;&gt;&gt; Entry.objects.filter(blog_id=4)اگر یک آرگومان کلمه کلیدی نامعتبر ارسال کنید ,جنگو به شما ارور TypeErrorرا بازمیگرداند .بک اند و API دیتایس های جنگو مقدار زیادی از این گونه lookupها را پشتیبانی می کند . بعدها تمام آنها را بررسی خواهیم کرد .اما ابتدا برای آشنایی بیشتر بیایید به پرکاربرد ترین آنها نگاهی بیندازیم .1-اولین از لیست ما exact است . به معنای تطابق دقیق است . یعنی فیلد باید دقیقا همان مقدار ورودی را داشته باشد (نه کمتر و نه بیشتر) . برای مثال :&gt;&gt;&gt; Entry.objects.get(headline__exact=&amp;quotCat bites dog&amp;quot)کد بالا مقدار SQL زیر را تولید می کند .SELECT ... WHERE headline = &#039;Cat bites dog&#039;;اگر شما هیچ نوع lookup را ارائه نکنید و فقط نام فیلد خالی را بنویسید ,به صورت پیش فرض نوع lookup شما exact فرض می کند .به عنوان مثال ,دو عبارت زیر معادل هستند .&gt;&gt;&gt; Blog.objects.get(id__exact=14) # Explicit form
&gt;&gt;&gt; Blog.objects.get(id=14) # __exact is impliedدر حالی که exact به حروف بزرگ و کوچک نیز حساس است ,ولی نوع دیگری وجود دارد که تنها تفاوتش با exact در همین است .2-دومین از لیست ما iexact است . این همان exact است با این تفاوت که به حروف بزرگ و کوچک حساس نیست . برای مثال :&gt;&gt;&gt; Blog.objects.get(name__iexact=&amp;quotbeatles blog&amp;quot)کد بالا با مقدار Beatles Blog یا beatles blog یا حتی BeAtLeS BlOg مطابقت پیدا می کند .3-نوع بعدی contains است . مقدار هایی با این نوع از lookup مطابقت پیدا می کنند که حاوی عبارت ورودی هستند (حساس به حروف بزرگ و کوچک) . برای مثال :Entry.objects.get(headline__contains=&#039;Lennon&#039;)کد بالا مقدار SQL زیر را تولید می کند .SELECT ... WHERE headline LIKE &#039;%Lennon%&#039;;توجه داشته باشید ,کد بالا با مقدار Today Lennon honored مطابقت دارد ولی با مقدار today lennon honored مطابقت نخواهد داشت .همچنین ورژن غیرحساس به حروف بزرگ و کوچک نیز وجود دارد که نام آن icontains است .نوع های startswith و endswith نیز وجود دارند که بررسی می کنند آیا ابتدا یا پایان مقدار , با عبارت شما همخوانی دارد یا خیر . ورژن غیرحساس به حروف بزرگ و کوچک آنها برابر با istartswith و iendswith است .لوکاپ (lookup) های مربوط به روابط دیتابیسیجنگو یک راه قدرتمند و ساده را برای هندل کردن روابط دیتابیسی (مانند ForeignKey) در lookup ,برای شما قرار داده است که در پشت صحنه اتوماتیک SQL JOIN را هندل می کند . برای دسترسی به مقدار یک فیلد از شی ارجاع داده شده در lookup خود ,از نام فیلدی که ارتباط را میان دو مدل برقرار کرده است (همان نام فیلد ForeignKey) به علاوه ‘__’ و سپس فیلد داخل مدلی که به آن ارجاع داده شده ایم استفاده کنید .مثال زیر تمام رکورد های مدل Blog را دریافت می کند که حداقل به یک رکورد Entryکه فیلد headlineدر آن رکورد ,مقدار Lennonرا داشته باشد ,مرتبط باشند .قدار Beatles Blog است .&gt;&gt;&gt; Entry.objects.filter(blog__name=&#039;Beatles Blog&#039;)این جستجو هرچقدر که بخواهید می تواند تودرتو و عمیق باشد .البته معکوس هم کار می کند و همچنین می توان آن را سفارشی کرد . به طور پیش فرض شما در lookup به وسیله نام کوچک مدل ارجاع داده شده ,مقدار reverse name را تعیین می کنید .مثال زیر تمام رکورد های مدل Blog را دریافت می کند که حداقل به یک رکورد Entry که فیلد headline در آن رکورد ,مقدار Lennon را داشته باشد ,مرتبط باشند .&gt;&gt;&gt; Blog.objects.filter(entry__headline__contains=&#039;Lennon&#039;)فرض کنید روابط شما پیچیده هستند و می خواهید بین چند رابطه فیلتر را ایجاد کنید , اگر یکی از جدول های میانی شما ,با فیلتر های شما مطابقت پیدا نکند ,جنگو با آن به صورتی برخورد می کند که انگار یک رکورد خالی (مقادیر فیلد ها NULL هستند) اما معتبر در جدول وجود دارد . همه این ها به این معنی است که هیچ خطا یا اروری به شما در این شرایط بازگردانده نمی شود . برای مثال در این فیلتر :Blog.objects.filter(entry__authors__name=&#039;Lennon&#039;)اگر برای مثال در کد بالا شما به مدل 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=&#039;Lennon&#039;, entry__pub_date__year=2008)کد بالا تمام رکورد هایی را باز میگرداند که دو شرط را به صورت همزمان دارا هستند . برای ساخت یک فیلتر سهلگیرانه تر که صرفا مقدار Lennon را در فیلد headline داشته باشد و یا تاریخ آن مربوط به 2008 باشد (یا اولی یا دومی یا با هم ,اکنون فرقی نمی کند) کد زیر را بنویسید :Blog.objects.filter(entry__headline__contains=&#039;Lennon&#039;).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 ارجاع داده شده است و سپس از آن برای مقایسه استفاده می کنیم .&gt;&gt;&gt; from django.db.models import F
&gt;&gt;&gt; Entry.objects.filter(number_of_comments__gt=F(&#039;number_of_pingbacks&#039;))نکته : ابتدا ()F را import کنید . (django.db.models)همچنین جنگو جمع ,تفریق ,ضرب و توان و تقسیم و مدول را بر روی ثابت ها (مانند رشته ها) و نمونه های ()F پشتیبانی می کند . برای یافتن رکورد هایی از Entry که مقدار comments آنها دو برابر مقدار فیلد pingbacks باشد ,کد زیر را می نویسیم :&gt;&gt;&gt; Entry.objects.filter(number_of_comments__gt=F(&#039;number_of_pingbacks&#039;) *2)برای دریافت تمام رکوردهای Entry که مقدار فیلد rating در آنها کمتر از جمع مقادیر فیلد های pingbacks و comments آنها است ,کد زیر را بنویسید .&gt;&gt;&gt; Entry.objects.filter(rating__lt=F(&#039;number_of_comments&#039;) + F(&#039;number_of_pingbacks&#039;))شما همچنین می توانید از __ نیز برای گسترش و بسط دادن روابط دیتابیسی در نمونه ی ()F استفاده کنید . این دقیقا مانند همان فیلتر هایی است که استفاده کردیم (سرفصل های قبلی). برای مثال برای دریافت تمام رکورد های Entry که فیلد name در قسمت authors آنها برابر با مقدار name در قسمت blog آنها است ,کد زیر را استفاده می کنید .&gt;&gt;&gt; Entry.objects.filter(authors__name=F(&#039;blog__name&#039;))برای فیلد های تاریخ و زمان می توانید یک شی timedelta را اضافه یا کم کنید . برای مثال ,کد زیر تمام رکورد هایی را باز می گرداند که 3 روز پس از انتشار ,ویرایش شده اند (از فیلد های pub_date و mod_date استفاده می کنیم).&gt;&gt;&gt; from datetime import timedelta
&gt;&gt;&gt; Entry.objects.filter(mod_date__gt=F(&#039;pub_date&#039;) + timedelta(days=3))نمونه های ()F همچنین از bitwise نیز پشتیبانی می کنند . برای آنها توابع bitand , bitor , bitxor   bitrightshift , bitleftshift قرار داده شده است . برای مثال :&gt;&gt;&gt; F(&#039;somefield&#039;).bitand(16)نکته :دیتابیس oracle از bitwise XOR پشتیبانی نمی کند .در این جلسه به بررسی lookup و انواع مقایسه مقادیر در فیلد ها پرداختیم . در جلسه بعدی به بررسی مفهوم transform در جنگو خواهیم پرداخت .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 17 Nov 2023 13:11:26 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو (Basic) : جلسه بیست و هفت | ایجاد کوئری (Query)</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-basic-%D8%AC%D9%84%D8%B3%D9%87-%D8%A8%DB%8C%D8%B3%D8%AA-%D9%88-%D9%87%D9%81%D8%AA-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-query-kfeg33xsimna</link>
                <description>در این جلسه به بررسی کوئری ها در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو : جلسه بیست و هفت | ایجاد کوئری (Query)ایجاد queryهنگامی که یک مدل را ایجاد می کنید ,جنگو به صورت اتوماتیک به شما یک مجموعه از API ها را می دهد که به وسیله آنها می توانید جدول دیتابیس خود را کنترل کنید . برای مثال رکورد ایجاد کنید ,حذف کنید ,آپدیت کنید و یا دریافت کنید . در این بخش قرار است درباره چگونگی کار با این API صحبت کنیم .نکته :مدل های زیر را در نظر بگیرید . تمام این بخش قرار است با مدل های زیر کار کنیم (یعنی کد ها به این مدل ها اشاره دارند) مدل های زیر مدل های یک وبلاگ ساده هستند :from datetime import date
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()
    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField(default=date.today)
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField(default=0)
    number_of_pingbacks = models.IntegerField(default=0)
    rating = models.IntegerField(default=5)
    def __str__(self):
        return self.headlineایجاد رکورد (یا همان object یا شی)جنگو برای نمایش و ذخیره داده های شما در دیتابیس از یک سیستم ساده استفاده می کند . یک کلاس مدل ,یک جدول پایگاه داده را نشان می دهد و هر شی آن کلاس یک رکورد خاص را دیتابیس نشان می دهد .برای ایجاد یک شی (رکورد) ,آن را با استفاده از آرگومان های خود کلاس (ویژگی های آن) نمونه سازی کنید . سپس ()save را فراخوانی کنید تا در دیتابیس ذخیره شود .با فرض اینکه مدل های شما در mysute/blog/models.py وجود دارند ,مثال در اینجا آمده است .&gt;&gt;&gt; from blog.models import Blog
&gt;&gt;&gt; b = Blog(name=&#039;Beatles Blog&#039;, tagline=&#039;All the latest Beatles news.&#039;)
&gt;&gt;&gt; b.save()این کد در پشت صحنه یک دستور INSERT را روی دیتابیس اعمال می کند . توجه داشته باشید که تغییرات شما تا زمانی که متد ()save فراخوانی نشود ,بر روی دیتابیس اعمال نخواهند شد .نکته : متد ()save هیچ مقدار بازگشتی ای ندارد !همچنین می توانید از متد ()create استفاده کنید تا بدون نیاز به نمونه سازی و فراخوانی متد ()save تمام کار ها را انجام دهید . (خودش ()save را فراخوانی می کند)آپدیت رکوردبرای ذخیره تغییرات روی یک شی که از قبل در دیتابیس وجود دارد , از ()save استفاده کنید .برای مثال فرض کنید نمونه با نام b5 دارید که قبلا در دیتابیس وجود دارد . این مثال نام آن را تغییر داده و رکورد آن را در دیتابیس به روز رسانی می کند .&gt;&gt;&gt; b5.name = &#039;New name&#039;
&gt;&gt;&gt; b5.save()این مثال در پشت صحنه یک دستور UPDATE را اعمال می کند . جنگو تا زمانی که متد ()save را فراخوانی نکنید, تغییرات را روی دیتابیس اعمال نمی کند .ذخیره فیلد های روابطیبه روز رسانی یک ForeignKey دقیقا مانند ذخیره سازی یک فیلد معمولی عمل می کند . یک شی از نوع مناسب به فیلد موردنظر اختصاص می دهید و آن را ()save می کنید . مثال زیر ,مقدار فیلد blog در مدل Entry را به روز رسانی می کند و به ان یک شی از مدل Blog را اختصاص می دهد . (توجه داشته باشید رکورد های ما در دیتابیس از قبل ایجاد شده اند !)&gt;&gt;&gt; from blog.models import Blog, Entry
&gt;&gt;&gt; entry = Entry.objects.get(pk=1)
&gt;&gt;&gt; cheese_blog = Blog.objects.get(name=&amp;quotCheddar Talk&amp;quot)
&gt;&gt;&gt; entry.blog = cheese_blog
&gt;&gt;&gt; entry.save()در مورد ManyToManyField کمی راهکار متفاوت است . در اینجا باید از متد ()add در فیلد برای افزودن رکورد به رابطه استفاده کنید . این مثال Joe را که نمونه ای از مدل Author است را به فیلد ManyToManyField ما اضافه می کند .&gt;&gt;&gt; from blog.models import Author
&gt;&gt;&gt; joe = Author.objects.create(name=&amp;quotJoe&amp;quot)
&gt;&gt;&gt; entry.authors.add(joe)برای افزودن چندین رکورد به فیلد به صورت یکجا تمام آنها را در ()add وارد کنید .&gt;&gt;&gt; john = Author.objects.create(name=&amp;quotJohn&amp;quot)
&gt;&gt;&gt; paul = Author.objects.create(name=&amp;quotPaul&amp;quot)
&gt;&gt;&gt; george = Author.objects.create(name=&amp;quotGeorge&amp;quot)
&gt;&gt;&gt; ringo = Author.objects.create(name=&amp;quotRingo&amp;quot)
&gt;&gt;&gt; entry.authors.add(john, paul, george, ringo)اگر از نوع غیر شی تلاش کنید به فیلد اضافه کنید ,جنگو ارور خواهد داد .دریافت و دسترسی به رکوردبرای دسترسی و دریافت اشیا یا رکورد ها باید یک QuerySet را با manager مدل خود ایجاد کنید .یک QuerySet در واقع مجموعه ای از اشیا یا رکورد های خاص هست . آنها می توانند یک شی یا بیشتر یا حتی صفر شی درون خود داشته باشند . آنها را با filter می توانید ایجاد کنید تا فقط به مجموعه خاصی از اشیا که با پارامتر های شما همخوانی داشته باشند دسترسی داشته باشد . این روش دستورات SELECT را اعمال می کند . علاوه بر آن ارسال پارامتر های filter دستورات WHERE و LIMIT را اجرا خواهد کرد .یک QuerySet را میتوانید به وسیله manager ایجاد کنید . هر مدل حداقل یک manager دارد که به صورت پیش فرض نام آن objects است . از مدل خود به صورت مستقیم می توانید به manager دسترسی پیدا کنید :&gt;&gt;&gt; Blog.objects
&lt;django.db.models.manager.Manager object at ...&gt;
&gt;&gt;&gt; b = Blog(name=&#039;Foo&#039;, tagline=&#039;Bar&#039;)
&gt;&gt;&gt; b.objects
Traceback:
...
AttributeError: &amp;quotManager isn&#039;t accessible via Blog instances.&amp;quotنکته : ارور بالا حاصل از آن است که manager فقط از طریق خود مدل قابل دسترسی هستند و از طریق نمونه های مدل نمی توان به آنها دسترسی یافت !متد های مختلفی در QuerySet ها وجود دارند که از طریق manager قابل اجرا هستند . برای مثال متد ()Blog.objects.all تمامی شی و رکورد ها را در مدل Blog به شما باز میگرداند .دریافت تمامی رکورد هابرای دریافت تمامی رکورد های یک جدول یا دریافت تمامی اشیا یک مدل فقط کافی است از متد ()all در manager استفاده کنید .&gt;&gt;&gt; all_entries = Entry.objects.all()فیلتر کردن رکورد هامتد قبلی شما تمامی اشیا را در دیتابیس باز میگرداند . معمولا شما این را نمی خواهید و فقط رکورد هایی را نیاز دارید که بر اساس پارامتر های خاصی فیلتر شده باشند . در این زمان از ()filter استفاده می شود .فیلترگرچه filter معمول ترین متد مورد استفاده است اما در واقع دو متد برای انجام چنین کار هایی مورد استفاده است .شماره 1-filter(**kwargs) :تمام رکورد هایی که با آرگومان های داده شده هم خوانی داشته باشند را باز میگرداند .شماره 2-exclude(**kwargs) :تمام رکورد هایی را باز میگرداند که با آرگومان های داده شده هم خوانی نداشته باشد !تمامی **kwargs شما همان نام فیلد ها هستند که بر اساس مقادیر آنها , آنها را فیلتر می کنید . برای مثال برای دریافت تمامی رکورد هایی از Blog که فیلد pub_date آنها و قسمت سال یا year آنها برابر با 2006 باشد :Entry.objects.filter(pub_date__year=2006)شما همچنین می توانید از روش زیر نیز استفاده کنید . (گرچه توصیه نمی شود اما گاهی باید تمام اشیا را ابتدا بگیرید و سپس آنها را فیلتر کنید)Entry.objects.all().filter(pub_date__year=2006)فیلتر زنجیرینتیجه ای که جنگو از یک QuerySet بازمیگرداند خودش یک QuerySet است . پس این امکان دارد تا بتوانید آنها را به یکدیگر زنجیر کنید و فیلتر های دو الی سه مرحله ای ایجاد کنید :&gt;&gt;&gt; Entry.objects.filter(
...  headline__startswith=&#039;What‘
... ).exclude(
...  pub_date__gte=datetime.date.today()
... ).filter(
...  pub_date__gte=datetime.date(2005, 1, 30)
... )این کد , QuerySet ابتدایی را می گیرد و روی آن فیلتر را انجام می دهد . سپس روی نتیجه اش یک exclude انجام می دهد و روی نتیجه آن دوباره یک فیلتر را انجام می دهد . این QuerySet حاوی تمام اشیایی است که فیلد headline آنها با what شروع شده باشد و محدوده تاریخ فیلد pub_date آنها از 30 ژوئن 2005 تا امروز باشد (ابتدا بررسی می کنیم که امروز نباشد و سپس بررسی می کنیم که از چه تاریخی بیشتر باشد) .نکته : هر QuerySet در واقع unique یا منحصر به فرد است . هر بار که شما یک QuerySet را مجددا استفاده می کنید , شما یک QuerySet کاملا جدید و جدا از قبلی دریافت خواهید کرد .هر بار استفاده مجدد از یک QuerySet یک QuerySet جدید ایجاد می کند که آن نیز می تواند دوباره استفاده بشود . برای مثال کد بالا را می توان به صورت زیر نوشت :&gt;&gt;&gt; q1 = Entry.objects.filter(headline__startswith=&amp;quotWhat&amp;quot)
&gt;&gt;&gt; q2 = q1.exclude(pub_date__gte=datetime.date.today())
&gt;&gt;&gt; q3 = q1.filter(pub_date__gte=datetime.date.today())برای مثال q1 در اینجا یک QuerySet حاوی تمام رکورد هایی است که با what شروع می شوند . بعد از آن از q2 وجود دارد که زیر مجموعه ای از q1 است که تمام رکورد هایی که pub_date آنها برابر با امروز یا آینده است را حذف می کند .بعد از آن نیز q3 وجود دارد که زیر مجموعه ای از q1است که تمام رکورد هایی که pub_date آنها برابر با امروز یا آینده است را فیلتر می کند و ذخیره می کند . توجه داشته باشید که در طی کل این فرایند ها q1 تغییر نخواهد کرد .یک نکته این است که QuerySet ها معمولا lazy هستند . این یعنی شما می توانید تمام روز QuerySet ایجاد کنید و جنگو تا زمانی که از آن استفاده نکنید , QuerySet شما را در دیتابیس اجرا نمی کند . به مثال زیر نگاه کنید.&gt;&gt;&gt; q = Entry.objects.filter(headline__startswith=&amp;quotWhat&amp;quot)
&gt;&gt;&gt; q = q.filter(pub_date__lte=datetime.date.today())
&gt;&gt;&gt; q = q.exclude(body_text__icontains=&amp;quotfood&amp;quot)
&gt;&gt;&gt; print(q)در کد بالا به نظر می رسد که جنگو سه بار در دیتابیس عملیات اجرا کرده است و QuerySet ها شما را ایجاد کرده است اما در حقیقت فقط یک بار جنگو QuerySet را اجرا کرده است . فقط زمانی که شما از آن استفاده کردید (در خط آخر و دستور print) . به طور کلی QuerySet شما فقط زمانی در دیتابیس اجرا خواهد شد که شما نتیجه آن را بخواهید .متد ()getبه یاد داشته باشید که متد filter حتی اگر فقط یک رکورد با پارامتر های شما همخوانی داشته باشد , آن را به صورت یک QuerySet بازمیگرداند . در اینجا , QuerySet شما فقط یک رکورد را خواهد داشت .اگر می دانید که فقط یک رکورد در دیتابیس شما با پارامتر های شما همخوانی دارد , می توانید از متد ()get از manager مدل خود استفاده کنید تا آن را به صورت مستقیم بگیرید . برای مثال :&gt;&gt;&gt; one_entry = Entry.objects.get(pk=1)استفاده از این متد نیز مانند استفاده از ()filter ساده است . فقط کافی است تا پارامتر های خود را به همراه نام فیلد به آنها بدهید .توجه داشته باشید که اگر رکوردی با پارامتر های شما همخوانی نکند , متد ()filter فقط یک QuerySet خالی به شما باز میگرداند . اما متد ()get به شما یک ارور DoesNotExist باز خواهد گرداند . بنابر این در کد بالا اگر رکوردی با این مشخصات یافت نشود , شما ارور دریافت خواهید کرد .همچنین اگر بیشتر از یک رکورد با مشخصات شما در متد ()get همخوانی کند ,شما به ارور MultipleObejctReturned برخواهید خورد .متدهای queryدر جنگو معمولا از متد های filter , all و یا get و exclude استفاده می شود . اما جنگو متد های بیشتری نیز درون خود جاسازی کرده است . بعدها به بررسی آنها خواهیم پرداخت . جستجوی دیتابیس :محدود کردن QuerySetبرای محدود کردن نتایج بازگشتی از QuerySet (یعنی تعیین محدودیت تعداد رکورد های بازگشتی) فقط کافی است به روش list slice در پایتون ,محدوده را مشخص کنید . این معادل دستور LIMIT و OFFSE در SQL است.به عنوان مثال ,کد زیر 5 رکورد اول جدول را باز میگرداند . (LIMIT 5)&gt;&gt;&gt; Entry.objects.all()[:5]کد زیر نیز رکورد های شماره 6 الی 10 ابتدایی را باز خواهد گرداند . (OFFSET 5 LIMIT 5)&gt;&gt;&gt; Entry.objects.all()[5:10]ایندکس های منفی (یعنی Entry.objects.all()[-1]) پشتیبانی نمی شود .به طور کلی , slice کردن یک QuerySet باعث ایجاد یک QuerySet جدید می شود . این باعث نمی شود تا query شما ,روی دیتابیس اجرا شود . البته یک استثنا وجود دارد ;زمانی که شما از پارامتر step در سینتکس slice کردن خود استفاده کنید . برای مثال کد زیر ,لیستی از رکورد های شماره زوج یعنی هر دو رکورد یکی را از 10 مورد اولی باز میگرداند .&gt;&gt;&gt; Entry.objects.all()[:10:2]نکته مهم این است که به دلیل مبهم بودن ماهیت کارکرد آن لیست , شما روی آن نمی توانید فیلتر یا دستورات QuerySet دیگری را اجرا کنید .اگر می خواهید برای مثال اولین رکورد را به جای یک لیست از رکورد ها دریافت کنید (SELECT foo FROM bar LIMIT 1) به جای برش لیست ,از یک index استفاده کنید . برای مثال کد زیر پس از مرتب کردن رکورد ها بر اساس حروف الفبا بر اساس فیلد headline ,اولین رکورد آن را باز میگرداند .&gt;&gt;&gt; Entry.objects.order_by(&#039;headline&#039;)[0]این تقریبا معادل است با :&gt;&gt;&gt; Entry.objects.order_by(&#039;headline&#039;)[0:1].get()البته به یاد داشته باشید که اولین کد اگر هیچ رکوردی با آن مطابقت نکند , ارور IndexError به شما باز خواهد گرداند ولی کد دوم اگر هیچ رکوردی را مطابق با پارامتر هایش پیدا نکند ,ارور DoesNotExist را باز خواهد گرداند.در این جلسه به بررسی چندین متد در جنگو پرداختیم که برای جستجو در دیتابیس به کار میرفتند . در جلسه بعدی درباره lookup صحبت خواهیم کرد . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Fri, 27 Oct 2023 12:39:24 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو (Basic) : جلسه بیست و شش | کلاس متا (meta)</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-basic-%D8%AC%D9%84%D8%B3%D9%87-%D8%A8%DB%8C%D8%B3%D8%AA-%D9%88-%D8%B4%D8%B4-%DA%A9%D9%84%D8%A7%D8%B3-%D9%85%D8%AA%D8%A7-meta-asjyiszjgaxk</link>
                <description>در این جلسه به بررسی کلاس های متا در جنگو خواهیم پرداخت . با ما همراه باشید . آموزش جنگو  : جلسه بیست و شش | کلاس متا (meta)کلاس های Metaدر این قسمت تمامی گزینه هایی (متغییرها) که می توان در کلاس Meta برای یک مدل تعریف کرد را بررسی خواهیم کرد .کلاس Meta زیرکلاسی است که در مدل ایجاد می شود تا اطلاعات و تنظیمات اضافه ای را برای مدل به وسیله آن تعریف کنید .متغییر های قابل تعریف در کلاس های Meta1-متغییر اول که می خواهیم معرفی کنیم ,متغییر abstract است که دو مقدار True و False را قبول می کند . در گذشته استفاده از آن را توضیح دادیم و اشاره کردیم که استفاده از آن فقط برای ارث بری مدل ها است . (حتما به صورت کامل مباحث شی گرایی مدل ها را بخوانید)2-متغییر دوم شما app_label است . اگر مدل شما خارج از اپی که در INSTALLED_APPS وجود دارد ,ساخته شده است , باید مشخص کنید که این مدل به کدام اپ تعلق دارد . (نام اپ را به صورت رشته به عنوان مقدار می پذیرد)app_label = &#039;myapp&#039;فایده این روش این است که فرمت فراخوانی مدل شما به صورت app_label.object_name یا app_label.model_name باشد ,شما می توانید در مواقع موردنیاز از فرمت های model._meta.label یا model._meta.label_lower استفاده کنید .3-متغییر سوم base_manager_name است . اگر به یاد داشته باشید گفتیم که در ()model.objects.all یا ()model.objects.filter نام objects در واقع به manager مدل شما اشاره دارد . این متغییر یک رشته را قبول می کند که نام همان objects است . در واقع نام base_manager شما در این متغییر تعریف می شود و به صورت پیش فرض objects است .نکته : اگر آن را مثلا به things تغییر دهید ,باید از این پس بنویسید : ()model.things.filter یا ()model.things.all 4-متغییر چهارم db_table است . این متغییر یک رشته را قبول می کند . در واقع نام جدول شماست که به صورت پیش فرض از روی نام مدل برداشته می شود . برای تنظیم نام جدول به music_album به صورت زیر در کلاس Meta بنویسید :db_table = &#039;music_album&#039;نکته :برای صرفه جویی در زمان جنگو نام جدول ها را خودش از روی app_label و نام مدل شما ایجاد می کند . به این صورت که نام یک جدول دیتابیس تشکیل شده است از : ابتدا app_label و سپس نامی که برا ساخت اپ حاوی مدل در دستور manage.py startapp استفاده کردید با یک زیرخط یا underscore یا _ میان آنها ساخته می شود .برای مثال اگر یک اپ با نام bookstore داشته باشید و یک مدل با نام Book داشته باشید ,شما جدولی با نام bookstore_book خواهید داشت .برای بازنویسی نام جدول از db_table استفاده کنید .نکته جالب این است که اگر نام جدول پیشنهادی شما دارای کلمات رزرو شده SQL یا کلماتی است که در پایتون مجاز نیست  , عیبی ندارد . جنگو آنها را در پشت صحنه پردازش خواهد کرد .بهتر است که از حروف کوچک برای MySQL و MariaDB استفاده کنید . همچنین در Oracle شما محدودیت 30 کاراکتری را برای نام جدول دیتابیس خواهید داشت . جنگو در این مواقع معمولا طول آنها را کوچک خواهد کرد و همه ی آنها را به حروف بزرگ تبدیل خواهد کرد . برای جلوگیری از این اتفاق تنها کافیست به صورت زیر عمل کنید : (آنها را میان “” , ‘’ قرار دهید)db_table = &#039;&amp;quotname_left_in_lowercase&amp;quot&#039;5-متغییر پنجم شما db_tablespace است . (مبحث db_tablespace را در مورد دیتابیس ها بخوانید) این متغییر یک رشته را قبول می کند و در واقع نام tablespace شما است . اگر آن را تعیین نکرده باشید (که معمولا نمی کنند) , جنگو آن را از تنظیم DEFAULT_TABLESPACE در settings.py بر میدارد . اگر دیتابیس شما از db_tablespace پشتیبانی نکند ,این متغییر نادیده گرفته می شود .6-متغییر ششم default_manager_name است . به یاد داشته باشید که base_manager با defaut_manager تفاوت دارد . Base_manager از همان ابتدا که مدل شما ایجاد می شود ,وجود دارد . Default_manager نیز در واقع مدیری است که جنگو از آن برای مدل استفاده می کند . این متغییر یک رشته را قبول می کند و در واقع همان نام default_manager شما است .به طور خلاصه از default_manager فقط در زمانی استفاده می شود که شما یک manager را خودتان ایجاد کرده باشید.7-متغییر هفتم default_related_name است . این متغییر یک رشته را قبول می کند و همانطرو که از اسمش مشخص است , نام related_nameهایی که رابطه بازگشتی آنها به این مدل است را تعیین می کند . (یعنی رابطه فیلد های آنها با این مدل است) . به صورت پیش فرض model_name_set است . (بجای model_name نام مدل قرار می گیرد)همچنین مقدار این متغییر برای related_query_name نیز استفاده خواهد شد .شما می دانید که related_name های شما باید unique و منحصر به فرد باشند .8-متغییر هشتم get_latest_by است . متد latest و earliest را بعدا خواهید خواند . توضیح ابتدایی برای آنها این است که فراخوانی این دو متد باعث می شوند که اشیای شما به ترتیبی مرتب شوند . یعنی ترتیب جدیدترین و قدیمی ترین . شما با استفاده از این متغییر به آنها می گویید تا بر اساس چه فیلدی جدیدترین و قدیمی ترین را مرتب کنند . نام فیلد مورد نظر را در رشته بنویسید یا اگر فیلدهایی دارد باید آنها را درون یک لیست مرتب کنید. معمولا فیلد های مورد استفاده برای این متد ها DateField , DateTimeField یا IntegerField است .برای مثال :# Latest by ascending order_date.
get_latest_by = &amp;quotorder_date&amp;quot
# Latest by priority descending, order_date ascending.
get_latest_by = [&#039;-priority&#039;, &#039;order_date&#039;]9-متغییر بعدی managed است . به صورت پیش فرض True است . بهتر است که آن را تغییر ندهید . این متغییر روی True قرار گرفته است و معنای آن این است که جنگو به صورت اتوماتیک migrateها را برای مدل شما می سازد و با دستور flush (بعدا آشنا می شوید) آنها را برای شما حذف می کند . وقتی این متغییر روی True باشد ,جنگو سیستم migrateها بر روی مدل را کنترل خواهد کرد . (چیزی است که شما معمولا می خواهید)اگر برابر با False قرار داده شود ,جنگو هیچ عملیات ایجاد جدول ,اصلاح یا حذف جدولی را بر روی این مدل انجام نمی دهد . این در صورتی مفید است که جدول از قبل به وسیله های دیگری (مانند خود برنامه نویس) ایجاد شده باشد . این تنها تفاوت برای managed=False است و تمام جنبه های دیگر از کار کردن با این مدل مانند روال عادی است . این یعنی :-اگر فیلد primary key وجود نداشته باشد , جنگو آن را به صورت خودکار در مدل ایجاد می کند . برای جلوگیری از سردرگمی هنگام استفاده از جدول های مدیریت نشده توسط جنگو ,بهتر است تمامی فیلد های جدول خود را در مدل تعریف کنید (ایجاد کنید).-اگر شما یک ManyToManyField در مدلی که managed=False است داشته باشید که به مدل دیگری اشاره می کند که آن مدل نیز ویژگی managed=False را دارا است ,جدول میانی به صورت اتوماتیک برای آنها ایجاد نمی شود .نکته : با این حال اگر جدول میانی بین دو مدل که فقط یکی از آنها managed=False را دارا است باشد ,جدول میانی را خود جنگو مانند روال عادی ایجاد خواهد کرد .نکته : اگر نیاز دارید تا دو مدل مدیریت نشده (managed=False) را به یکدیگر از ManyToManyField متصل کنید,فقط کافی است جدول میانی را دستی ایجاد کنید و در فیلد خود از through برای ربط دادن به مدل سفارشی استفاده کنید .برای ایجاد تست ها روی جداول مدیریت نشده نیز به شما بستگی دارد تا آنها را در تست به کار ببرید و یا جداول را به صورت صحیح از مدل خود ایجاد کنید .اگر نیز می خواهید رفتار مدل را در سطح خود پایتون تغییر دهید می توانید یک مدل مدیریت نشده ایجاد کنید و سپس از آن یک کپی نیز ایجاد کنید . البته راهکار بهتر استفاده از مدل های نوع proxy است .10-متغییر بعدی order_with_respect_to است . این متغییر یک رشته را به عنوان مقدار می پذیرد , که نام یکی از فیلد های مدل است . این گزینه ,شی های مدل را با توجه به آن فیلد مرتب می کند . بر فرض مثال که شما دو مدل با نام های Answer (جواب) و Question (سوال) دارید . اگر هر سوال چند جواب داشته باشد ولی هر جواب فقط متعلق به یک سوال باشد و ترتیب جواب ها برای شما اهمیت داشته باشد ,کد زیر را خواهید نوشت .from django.db import models

class Question(models.Model):
    text = models.TextField()
    # ...

class Answer(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    # ...
    class Meta:
        order_with_respect_to = &#039;question&#039;هنگامی که این متغییر تنظیم می شود ,دو متد برای دریافت و تنظیم اشیا مرتبط اضافه خواهد شد . ()get_RELATED_order و ()set_RELATED_order متد های شما هستند که در آنها RELATED با نام مدل به صورت حروف کوچک جایگزین می شود . به عنوان مثال , با فرض اینکه شی ای از Question دارای چندین پاسخ یا همان Answer های مرتبط باشد ,لیستی که از این متد دریافت خواهد شد ,حاوی id های اشیا Answer به صورت ترتیبی است.&gt;&gt;&gt; question = Question.objects.get(id=1)
&gt;&gt;&gt; question.get_answer_order()
[1, 2, 3]حال که فهمیدید چگونه می توانید آنها را دریافت کنید ,بهتر است نگاهی بیاندازیم به اینکه چطور ترتیب آنها را تنظیم کنیم . شما به وسیله کد زیر و فراخوانی این متد می توانید لیستی حاوی ایدی Answer های مرتبط با Question را به این متد بدهید تا آنها را به ترتیب خودتان مرتب کند .&gt;&gt;&gt; question.set_answer_order([3, 1, 2])حال که فهمیدید چگونه می توان به طور کلی اشیا Answer مربوط به هر شی Question را دریافت کرد یا تنظیم نمود ,باید بدانید که هر کدام از Answer ها نیز دارای دو متد خواهند شد . یکی از آنها ()get_next_in_order است (شی بعدی را در ترتیب نشان می دهد) و بعدی ()get_previous_in_order است (شی قبلی را در ترتیب نشان می دهد) . از آنها برای دسترسی به Answer ها در ترتیب خودشان استفاده می توانید کنید . با فرض اینکه Answer های شما بر اساس فیلد id مرتب شده اند :&gt;&gt;&gt; answer = Answer.objects.get(id=2)
&gt;&gt;&gt; answer.get_next_in_order()
&lt;Answer: 3&gt;
&gt;&gt;&gt; answer.get_previous_in_order()
&lt;Answer: 1&gt;در پشت صحنه ,اضافه کردن متغییر order_with_respect_to یک فیلد ستون اضافه در دیتابیس اضافه خواهد کرد که جنگو ترتیب اشیا را در آن نگه داری می کند (نام ستون order_ است) . پس در نتیجه نمی توانید ordering و order_with_respect_to را با هم استفاده کنید . همچنین استفاده از order_wth_respect_to به این منظور است که هر گاه از متد های آن (مانند get_answer_to) استفاده کنید ,اشیا به ترتیب به شما نمایش داده می شود ولی ordering همیشه آنها را مرتب خواهد کرد .نکته : بعد از اینکه این متغییر اضافه شد ,حتما برای ایجاد ستون جدیدش در دیتابیس عملیات migrate را انجام دهید . (همان دستور python3 manage.py migrate)11-متغییر بعدی در لیست ما ordering نام دارد . Ordering ترتیب نگه داری اشیا را بر اساس یک فیلد یا چند فیلد ذخیره خواهد کرد . تفاوت آن با متغییر قبلی این است که ordering اشیا را همیشه مرتب نگه می دارد و بدون استفاده از متد خاصی شما اشیای مدل را مرتب شده دریافت می کنید .ordering = [&#039;-order_date&#039;]این یک تاپل یا لیستی از رشته ها است (می تواند یک expression نیز باشد) . هر رشته نام یک فیلد از مدل شما باید باشد . علاوه بر این هر رشته می تواند یک – داشته باشد تا ترتیب مرتب شدن بر اساس آن فیلد نزولی باشد . فیلد های بدون علامت – با ترتیب صعودی نمایش داده می شوند . اگر می خواهید به صورت رندوم اشیا مرتب شده باشند از ? در رشته استفاده کنید .برای مثال ,برای مرتب کردن اشیا یک مدل ,بر اساس فیلد pub_date و ترتیب صعودی از این استفاده کنید :ordering =[&#039;pub_date&#039;]حال برای مرتب سازی بر اساس فیلد pub_date و ترتیب نزولی به صورت زیر اقدام کنید :ordering = [&#039;-pub_date&#039;]حال برای مرتب سازی بر اساس فیلد pub_date (به صورت نزولی) و فیلد author (به صورت صعودی) به صورت زیر اقدام کنید :ordering =[&#039;-pub_date&#039;, &#039;author&#039;]شما می توانید از query expressions نیز استفاده کنید (جلوتر خواهید خواند) . برای مرتب سازی بر اساس فیلد author به ترتیب صعودی و به طوری که مقادیر null در آخر مرتب شوند ,به صورت زیر اقدام کنید .from django.db.models import F
ordering = [F(&#039;author&#039;).asc(nulls_last=True)]نکته : به یاد داشته باشید که فیلد هایی که در ordering استفاده می کنید باید معمولا unique و منحصر به فرد باشند .برای مثال اگر فیلد name منحصر به فرد یا unique=True نباشد ,و شما دو مقدار یکسان را درون دو شی متفاوت ذخیره کرده باشید , جنگو تضمین نمی کند که هر بار با همان ترتیب قبلی به شما آنها را بازگرداند .12-متغییر بعدی permissions است . جنگو برای مدل های شما یک جدول با نام permissions می سازد . مجوزهای افزودن ,حذف ,تغییر و مشاهده برای هر مدل به صورت اتوماتیک اضافه می شود . گرچه بحث مجوز ها را کمی سخت می توان توضیح داد ,اما می توانید برای مثال نگاهی به مدل User و مجوز های آن بیندازید (در ادمین پنل قسمت users و قسمت permissions).در مثال زیر یک مجوز اضافی تحت عنوان can_deliver_pizza را به مدل اضافه می کنیم :permissions =[(&#039;can_deliver_pizzas&#039;, &#039;Can deliver pizzas&#039;)]مقدار هایی که این متغییر قبول می کند یک لیست حاوی تاپل های دو عضوی است . عضو اول مقدار ذخیره سازی شد در دیتابیس و عضو دوم مقداری است که به کاربر نمایش داده خواهد شد .13-متغییر بعدی default_permissions است . مجوز های پیش فرض جنگو برای هر مدل شامل (‘add’ , ‘change’ , ‘veiw’ , ‘delete’) است . شما ممکن است بخواهید این مجوز ها را بازنویسی کنید . برای مثال اگر اپ شما هیچ کدام از مجوز ها را لازم نداشته باشد ,این متغییر را با یک لیست خالی مقدار دهی می کنید . به یاد داشته باشید این فرایند باید قبل از انجام migrate بر روی مدل انجام شود تا از صدور هر گونه مجوز برای مدل جلوگیری شود .14-متغییر بعدی شما proxy است . این متغییر برای مباحث ارث بری استفاده می شود . (در قبل تر توضیح دادیم)15-گزینه بعدی required_db_features است . مقدار خود را به صورت لیست قبول می کند . لیستی از قابلیت های پایگاه داده (database features) که اتصال فعلی شما باید داشته باشد (در مرحله migrate آنها را اجرا کند) . برای مثال اگر مقدار این متغییر را [‘gis_enabled’] تنظیم کنید ,مدل در پایگاه های داده مجهز به GIS میشود (مباحث دیتابیس ها) . همچنین برای نادیده گرفتن برخی از مدل ها در هنگام تست کردن با چند دیتابیس (مبحث چند دیتابیسی در جنگو را بعدا می خوانید) کارایی دارد .15-گزینه بعدی required_db_vendor است . نام یک vendor برای دیتابیس است که باید توسط جنگو پشتیبانی شود. مقدار هایی که می توانید در قالب یک رشته به این متغییر بدهید , sqlite و mysql و postgresql و oracle هستند . اگر این ویژگی و vendor فعلی شما در اتصال به دیتابیس باهم یکسان نباشند ,مدل همگام سازی نمی شود ! (vendor جزو مباحث دیتابیس ها است)16-متغییر بعدی select_on_save است . دو مقدار False و True را می پذیرد . تعیین می کند که آیا جنگو باید از نسخه pre-1.6 متد ()save استفاده کند یا خیر . نسخه قدیمی این متد ,ابتدا دستور SELECT را بر روی ستونی اعمال می کند تا از وجود آن اطمینان حاصل کند و سپس آنها را آپدیت می کند . متد جدید , UPDATE را به صورت مستقیم اعمال می کند و اگر خطایی وجود داشت آن را باز می گرداند . در بعضی از موارد نادر , UPDATE روی ستون های موجود به جنگو نشان داده نمی شود . برای مثال در PostgrSQL دستور ON UPDATE مقدار Null را بازمیگرداند . در چنین مواردی ,متد جدید دستور INSERT را انجام می دهد حتی اگر ستون از قبل وجود داشته باشد (و شما این را نمی خواهید).معمولا این موارد نادر هستند و بهتر است پیش فرض (False) این گزینه را دستکاری نکنید .17-بعدی indexes است که در گذشته آن را توضیح دادیم !18-متغییر بعدی unique_together است . مقدار یک لیست را که حاوی لیست های دیگری است را قبول می کند . هر لیست درون لیست اصلی حاوی نام فیلد هایی است که در کنار یکدیگر باید unique یا منحصر به فرد باشند .unique_together = [[&#039;driver&#039;, &#039;restaurant&#039;]]این عملیات در سطح دیتابیس و ادمین جنگو اعمال می شود . یعنی عبارت UNIQUE مناسب در CREATE TABLE گنجانده می شود . (معمولا از UniqueConstraint یا Constraint استفاده می شود)برای راحتی بیشتر می توانید فقط از یک لیست استفاده کنید اما این زمانی است که فقط با یک سری مجموعه از فیلد ها کار داشته باشید . (یعنی فقط همان فیلد ها با یکدیگر منحصر به فرد شوند و نه دو مجموعه از فیلد ها)unique_together = [&#039;driver&#039;, &#039;restaurant&#039;]یک ManyToManyField را نمی توانید در این متغییر استفاده کنید (زیرا معنای خاصی ندارد!) . اگر نیاز دارید تا منحصر به فرد بودن اشیا ManyToMany را بررسی کنید باید از سیگنال ها (در جلوتر می خوانید) یا از through برای مدل استفاده کنید .اگر شما منحصر به فرد بودن فیلد ها را رعایت نکنید ,جنگو به شما ارور ValidationError را باز میگرداند.19-گزینه بعدی index_together است . (گرچه معمولا از همان indexes استفاده می شود) لیستی از لیست هایی را قبول می کند که هر لیست حاوی فیلد هایی است که باید با یکدیگر index شوند .index_together =[
    [&amp;quotpub_date&amp;quot, &amp;quotdeadline&amp;quot],
]این لیست از فیلد ها با هم index خواهند شد . (عبارت CREATE INDEX مناسب برای آنها اعمال می شود)اگر شما فقط با یک مجموعه از فیلد ها کار دارید ,برای راحتی بیشتر از یک لیست استفاده کنید .index_together = [&amp;quotpub_date&amp;quot, &amp;quotdeadline&amp;quot]20-بعدی constraints است . لیستی از constraintها (بعدا می خوانید) که می خواهید بر روی مدل اعمال شوند . روش استفاده از آنها به صورت زیر است .from django.db import models

class Customer(models.Model):
    age = models.IntegerField()
    class Meta:
        constraints = [
            models.CheckConstraint(check=models.Q(age__gte=18), name=&#039;age_gte_18&#039;),
        ]21-گزینه بعدی verbose_name است . این یک رشته را می پذیرد که نام قابل خواندن برای کاربر برای شی مفرد است .verbose_name = &amp;quotpizza&amp;quotاگر این گزینه داده نشود ,جنگو از یک نام munged از روی نام مدل رشته را ایجاد می کند . مثلا CamelCase به camel case تبدیل می شود .22- گزینه بعدی verbose_name_plural است . این رشته ای است که برای هر شی جنگو در نظر می گیرد . (نام آن)verbose_name_plural = &amp;quotstories&amp;quotاین گزینه در صورت داده نشدن ,روی verbose_name + ‘s’ تنظیم می شود.در این جلسه به طور کامل تمامی تنظیماتی که میتوانید در یک کلاس متا برای مدل خود انجام بدهید را بررسی کردیم . در جلسه بعدی به بررسی کوئری ها (query) در جنگو خواهیم پرداخت .</description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Thu, 19 Oct 2023 00:27:32 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش جنگو (Basic) : جلسه بیست و پنج | اندیس ها (index)</title>
                <link>https://virgool.io/@Django_learn/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AC%D9%86%DA%AF%D9%88-basic-%D8%AC%D9%84%D8%B3%D9%87-%D8%A8%DB%8C%D8%B3%D8%AA-%D9%88-%D9%BE%D9%86%D8%AC-%D8%A7%D9%86%D8%AF%DB%8C%D8%B3-%D9%87%D8%A7-index-anhu697ywskg</link>
                <description>در این جلسه به بررسی اندیس گذاری در دیتابیس های جنگو خواهیم پرداخت . با ما همراه باشید .آموزش جنگو : جلسه بیست و پنج | اندیس ها (index)اندیس ها : اندیس های دیتابیساندیس (index) کردن در واقع راهی است برای بهینه سازی عملکرد یک دیتابیس با به حداقل رساندن تعداد دسترسی‌های مورد نیاز دیسک هنگام پردازش یک query است. هدف از اندیس‌‌‌ گذاری، فراهم کردن شرایطی است که با کمترین جستجو و به سادگی به یک داده‌‌‌ی مشخص دسترسی پیدا کنیم.کلاس Index در جنگو ,کار شما را برای ایجاد اندیس در دیتابیس راحت می کند . برای اضافه کردن Index به مدل خود باید کلاس Meta را برای آن ایجاد کنید و در لیستی با نام indexes تنظیمات خود را قرار دهید . به مثال زیر نگاه کنید .from django.db import models

class Customer(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

    class Meta:
        indexes =[
            models.Index(fields=[&#039;last_name&#039;, &#039;first_name&#039;]),
            models.Index(fields=[&#039;first_name&#039;], name=&#039;first_name_idx&#039;),
         ]کلاس های Models.Index کلاس هایی هستند که برای ایجاد تنظیمات اندیس گذاری دیتابیس به کار می روند. اندیس گذاری نیز مبحثی در مورد دیتابیس ها است , پس در این مقاله توضیح داده نمی شود . کلاس Index چند option یا آرگومان دارد که هر یک کاری را انجام می دهند . ابتدا نگاهی کلی به نحوه استفاده از آن انداختیم. حال بیایید به آرگومان های آن نگاهی بیندازیم . (مقادیری که در داخل کلاس باید استفاده شوند مانند name در بالا و در دومین index)ایندکس ها : Index optionsمقدار پیش فرض استفاده از Index به صورت زیر است :class Index(*expressions, fields=(), name=None, db_tablespace=None, opclasses=(), condition=None, include=None)1-آرگومان expressions در ابتدا توضیح داده خواهد شد . این آرگومان موقعیتی است . یعنی باید استفاده شود و در مواقع مختلف (وابسته به فیلد های مدل) مقدار آن متفاوت است . مقداری که می پذیرد فیلد های شماست و با توجه به مقدار های پاس داده شده به آن , اندیس گذاری دیتابیس را انجام خواهد داد.به مثال زیر دقت کنید .Index(Lower(&#039;title&#039;).desc(), &#039;pub_date&#039;, name=&#039;lower_title_date_idx&#039;)مثال بالا برای شما اندیسی را روی مقدار title (با حروف کوچک) با ترتیب نزولی و روی pub_date با ترتیب صعودی به صورت پیش فرض ایجاد می کند . (شی های شما در دیتابیس بر اساس title و pub_date مرتب خواهند شد)بیایید مثالی دیگر را نیز ببینیم .Index(F(&#039;height&#039;) * F(&#039;weight&#039;), Round(&#039;weight&#039;), name=&#039;calc_idx&#039;)این کد برای شما اندیسی را می سازد که بر اساس نتیجه ضرب دو فیلد height و weight است . همچنین بر اساس مقدار گرد شده weight نیز کار می کند .نکته : استفاده از آرگومان name هنگامی که از expressions استفاده می کنید ,مهم است .2-آرگومان دوم fields است . شما می توانید به دو روش اندیس ها را راه اندازی کنید . روش اول این است که به جای دو آرگومان اول مقدار فیلد های خودتان را قرار دهید . ولی روش دوم که بیشتر توصیه می شود این است که از آرگومان fields استفاده کنید . این آرگومان یک لیست یا تاپل قبول می کند که درون آن نام فیلد هایی است که قرار است بر اساس آنها index شما ایجاد شود .به طور پیش فرض اندیس ها به صورت صعودی تعریف می شوند . اگر می خواهید آنها را نزولی مرتب کنید , باید یک &quot;–&quot; در قبل نام فیلد و در رشته خود قرار دهید . برای مثال :Index(fields=[&#039;headline&#039;, &#039;-pub_date&#039;])این کد اندیس را بر اساس headline (صعودی) و pub_date (نزولی) مرتب می کند .3-آرگومان سوم name است . این آرگومان همانطور که معلوم است مقدار name اندیس را مشخص می کند . مقداری که قبول می کند یک رشته است . اگر این آرگومان را تعیین نکنید ,جنگو به صورت اتوماتیک آن را تولید خواهد کرد . برای سازگاری با دیتابیس های مختلف رشته شما بیش از 30 کاراکتر نباید نداشته باشید . همچنین ابتدای آن با اعداد یا _ نباید شروع بشود .نکته :نام اندیس های شما باید unique باشد . یعنی باید منحصر به فرد باشند و دوبار استفاده نشوند . همچنین می توانید به روش زیر آنها را نیز تولید کنید :(این روش مناسب زمانی است که کلاس Meta قرار است به ارث برسد!)Index(fields=[&#039;title&#039;], name=&#039;%(app_label)s_%(class)s_title_index&#039;)4-نام جدولی از دیتابیس که اندیس در آن ایجاد می شود را در db_tablespace تعیین می کنید . اگر اندیس شما ,فقط برای یک فیلد ایجاد شده باشد ,مقدار db_tablespace برابر با جدولی خواهد شد که فیلد هم اکنون در آن قرار دارد .اگر db_tablespace نیز تعیین نشده باشد (یا ایندکس از چند فیلد استفاده کند) ,مقدار آن از db_tablespace که در کلاس Meta مدل وجود دارد برداشته می شود . اگر این نیز تعیین نشده باشد , db_tablespace شما در همان جدول که اندیس در آن ایجاد شدند ,ایجاد خواهد شد .5-آرگومان پنجم شما opclasses است . این آرگومان مقدار نام کلاس عملگر (operator classes) در دیتابیس PostgreSQL را کنترل می کند . اگر به یک کلاس عملگر سفارشی نیاز دارید , باید برای هر فیلد در index یک کلاس را ارائه دهید .برای مثال GinIndex(name=&#x27;json_index&#x27;, fields=[&#x27;jsonfield&#x27;], opclasses=[&#x27;jsonb_path_ops&#x27;]) یک اندیس از نوع gin(مربوط به مباحث دیتابیس ها) بر روی jsonfield با کلاس عملگر jsonb_path_ops خواهد ساخت .نکته : این آرگومان فقط مخصوص یک دیتابیس است و در دیتابیس های به جز PostgreSQL این آرگومان نادیده گرفته می شود .نکته : مقداردهی آرگومان name برای استفاده از opclasses اجباری است .6-اگر جدول شما بسیار بزرگ است و query یا جستجو های شما در دیتابیس یک بازه از جدول را بیشتر مورد هدف قرار می دهند , شاید بهتر باشد تا آن بازه را با Q مشخص کنید . به این ترتیب اندیس فقط در آن بازه ساخته خواهد شد و به شما در سرعت دیتابیس کمک شایانی خواهد کرد .برای این کار باید از شرط یا condition استفاده کنید . برای مثال :Condition=Q(pages_gt=400)رکورد هایی را که pages آنها بیشتر از 400 است را اندیس می کند .نکته : مقداردهی آرگومان name برای استفاده از condition اجباری است .نکته : استفاده از این آرگومان قوانین مشخصی در دیتابیس های مختلف دارد پس بهتر است حواستان باشد . برای مثال در PostgreSQL نمی توانید بدون ایجاد توابعی , condition را برای DateTimeField تعیین کنید و یا در Oracle شما نمی توانید این گونه conditionها را تعیین کنید . (این ها مباحث دیتابیس های جنگو است که باید بخوانید . در اینجا مهم این است که دیتابیس MySQL و MariaDB از این آرگومان پشتیبانی نمی کنند!)در این جلسه به بررسی مبحث ساده ی اندیس ها در جنگو پرداختیم . در جلسه بعدی به بررسی متاداده و کلاس های متا در هر مدل خواهیم پرداخت . </description>
                <category>?Django learn</category>
                <author>?Django learn</author>
                <pubDate>Tue, 10 Oct 2023 13:13:30 +0330</pubDate>
            </item>
            </channel>
</rss>