


آموزش محبوبترین دیزاین پترنها در جنگو
جنگو این یک فریمورک توسعه برنامه وب مبتنی بر پایتون است که در حال حاضر توجه زیادی را از سوی توسعهدهندگان وب به خود جلب کرده است.
الگو یا پترن نوعی ساختار منظم یا طراحی با الگوی تکراری است. یک پترن در واقع یک راهحل کلی برای یک مشکل خاص است. ما در دنیای اطرافمان توسط انواع مختلف الگوها در زمینههای مختلف احاطه شدهایم. همانطور که میدانید هیچ تضمینی وجود ندارد که یک پترن همیشه نتایج مثبتی به شما بدهد. انتخاب صحیح روش الگوسازی بسیار مهم است.
وقتی صحبت از مهندسی نرمافزار به میان میآید، پترنها در سطوح مختلف انتزاعی در کد نرمافزار قابلشناسایی هستند. اصطلاح "دیزاین پترن" اولین بار توسط GoF ( Erich Gamma, Richard Helm, Ralph Johnsonو John Vlissides) معرفی شد. GoF در واقع 23 دیزاین پترن رایج را برای توسعه نرمافزارهای شیءگرا معرفی کرد.
دیزاین پترن یک الگوی ارتباطی بین کلاسهای سیستم مبتنی بر OOP است. ما در این مقاله قرار نیست در مورد این دیزاین پترنها بحث کنیم و تمرکز خود را بر روی دیزاین پترنهای فریمورک جنگو معطوف خواهیم کرد.
جنگو چیست؟
جنگو این یک فریمورک توسعه برنامه وب مبتنی بر پایتون است که در حال حاضر توجه زیادی را از سوی توسعهدهندگان وب به خود جلب کرده است. برخلاف دیزاین پترنهای سنتی، دیزاین پترنهای جنگو (DDP) الگوهایی نیستند که به صورت مشخص در بین کلاسها رخ دهند. DDP ها در واقع بیشتر توسط توسعهدهندگان جنگو استفاده میشوند. قبل از توضیح در مورد دیزاین پترنهای جنگو، به یک دانش اولیه در مورد اجزای مختلف یک برنامه جنگو نیاز داریم. یک برنامه اصلی جنگو چهار کامپوننت اصلی دارد:
• مدلها (models.py): کلاسهای POPO (آبجکتهای قدیمی پایتون) هستند که برای برقراری ارتباط با پایگاه داده و حفظ دادهها به روش OOP استفاده میشوند.
• فرمها (forms.py): برای جمعآوری دادهها از کاربران به کار میروند.
• نماها (views.py): در کنترلر مرکزی برای رسیدگی به درخواست کاربر و برقراری ارتباط با آبجکتهای مدل استفاده میشود.
• تمپلیتها (فولدر تمپلیتها): نمایش پاسخ به کاربران در یک ساختار تعمیم یافته با اطلاعات سفارشی شده را بر عهده دارد.
علاوه بر کامپوننتهای فوق، فایلها و فولدرهای دیگری نیز وجود دارند که به این بحث مربوط نمیشوند. DDP ها عمدتاً در چهار کامپوننت فوق قرار دارند. 15 DDP به صورت شکل زیر طبقهبندی میشوند:
مهمترین DDP یا همان دیزاین پترن جنگوها در این دستهبندی، DDP های مدل و فرم هستند که در ادامه این مقاله هر یک را توضیح خواهیم داد.
دیزاین پترنهای جنگو، مدل و فرم
درادامه با مدلها و فرمها در جنگو آشنا میشویم:
مدلها
DDP های مبتنی بر مدل به پترنهای ساختاری (Structural) و بازیابی (Retrieval) تقسیم میشوند.
پترنهای ساختاری
این پترنها با ساختار و سازماندهی کلاسهای مدل سروکار دارند. به طور کلی، میتوان تمام کلاسها را در یک فایل models.py تعریف نمود.
1. مدلهای نرمالیزه شده: جنگو از مفهوم ORM (Object Relational Mapping) پیروی میکند. مطابق با این مفهوم، ابتدا کلاسهای مدل خود را تعریف میکنیم که جداول پایگاه داده را به طور خودکار تولید میکند.
اگر دوره سیستم مدیریت پایگاه داده را گذرانده باشید، درک مفهوم DDP برای شما ساده است. به زبان سادهتر، ما کلاسها و ویژگیهای را به گونهای ایجاد میکنیم که پایگاه داده نهایی کوچکتر، سازگارتر و بادوامتر شود.
2. مدل Mixin: بسیاری از اوقات با مشکل تکرار ویژگیها در کلاسهای مختلف مواجه میشویم. پایتون یک زبان برنامهنویسی است که با انعطافپذیری بالا و بر اساس ویژگی وراثت عرضه شده است. به جای تکرار مجموعهای از ویژگیها در میان کلاسهای مدل، میتوان در پایتون یک کلاس والد ایجاد کرده و این ویژگیهای مشترک را در کلاس والد نگهداری نمود.
تمام کلاسهای سطح بعدی، کلاس والد را به ارث میبرند. اما مشکل این است که نمیخواهیم جدولی برای این کلاس والد در پایگاه داده خود ایجاد کنیم. راهکار این مشکل، استفاده از مفهوم "وارثت انتزاعی" یا Abstract Inheritance است. در این روش، کلاس والد را به عنوان یک abstract در متای کلاس Parent یا کلاس والد مشخص کرده و سایر مراحل توسط خود جنگو انجام خواهد شد.
3. پروفایل کاربر: یک اپلیکیشن وب ممکن است انواع گوناگون کاربر با سطوح مختلف دسترسی داشته باشد. هر نوع یا تایپ کاربر مجموعهای از صفحات اختصاصی خود را دارد. اما سؤالی که مطرح میشود این است که چگونه میتوان کاربر را در سطح مدل مدیریت نمود؟
استفاده از کلاس اضافی UserProfile به همراه کلاس نظیر به نظیر Mapping to User پاسخ این سؤال خواهد بود. هر زمان که کاربر وارد اپلیکیشن میشود، بر اساس پروفایل آن، میتوان اجازه دسترسی به دادهها را به کاربر داد و یا این مجوز را از وی گرفت.
4. سرویس آبجکت: وقتی حجم بالایی از کد که مستقیماً به هیچ یک از کلاسهای Model مربوط نمیشود در هر یک از کلاسها درج میگردند، مشکل Fat Models به وجود میآید که اندازه مدل ما را به صورت غیرضروری افزایش میدهد. برای حل مشکل، همه بلوکهای کد را استخراج کرده و در یک کلاس Utility مشترک به شکل متد قرار دهید. همه متدها را به صورت "static" علامتگذاری کنید زیرا این متدها به هیچ یک از کلاسهای مدل مربوط نیستند.
پترنهای بازیابی
در بخش قبل، با انواع مختلف ساختارهای بین کلاسی و درون کلاسی که میتوانند در models.py ایجاد شوند، آشنا شدیم. پس از ایجاد مدلها، میبایست به ویژگیها و متدهای مدل خود دسترسی یافته و یا آن را بازیابی کنیم. دو DDP تحت پترنهای بازیابی وجود دارد که به شرح زیر است:
1. فیلد Property: اگر دوره DBMS را گذرانده باشید، میدانید که ویژگیهای مشتق شده را معمولاً در جداول ذخیره نمیکنیم بلکه این ویژگیها را در زمان اجرا با استفاده از ویژگیهای پایه محاسبه میکنیم. به عنوان مثال "تاریخ تولد" کاربر را ذخیره میکنیم و در این صورت "سن" او در هر نقطه از زمان قابلمحاسبه است.
همین مسئله در مورد مدلهای جنگو نیز وجود دارد. میتوان متد get_age() را در کلاس User ایجاد کرد که DOB مربوط به کاربر را استخراج کرده و بعد از محاسبه، سن او را برمیگرداند. این یکی از ویژگیهای پترن بازیابی است که در فیلد Property قابلتعریف است.
2. مدیریت مدل سفارشی CMM: در فریمورک جنگو دو مشکل اساسی در مورد query ها وجود دارد: 1) برای استخراج دادهها، query های تودرتو و پیچیده مورد نیاز است و 2) query های خاص به طور مکرر در سرتاسر کد فراخوانی میشوند که اصل DRY را نیز نقض میکند. برای حل مشکل اول میتوان برای هر حالت، چندین متد ایجاد نمود و این متدها را به ترتیب دلخواه فراخوانی کرد تا دادههای مفید فیلتر شوند. مثال:
User.Objects.all().filter(user_type=’prime’).filter(status=’active’).
در این روش، ما از عملکرد داخلی جنگو برای فیلتر کردن دادهها استفاده میکنیم. در ابتدا تمام آبجکتهای کاربر را استخراج کرده، در مرحله بعد کاربران اصلی را فیلتر کرده و در نهایت کاربران فعال را فیلتر میکنیم. به جای نوشتن یک دستور شرطی، فیلترهای تعریف شده را یکبهیک فراخوانی میکنیم.
فرمها
فرمها برای جمعآوری دادهها از کاربر مورد نظر است. در جنگو همه فرمها را در فایل forms.py ذخیره میکنیم. فرمها ممکن است مبتنی بر تابع یا کلاس باشند. DDP های زیر در دسته فرمها قرار میگیرند.
• تولید فرم پویا: در زمان ایجاد یک فرم ممکن است بخواهیم چند فیلد را به فرم اضافه کرده و یا از فرم مورد نظر دریافت کنیم. برای این منظور میتوان از یک روش مقداردهی اولیه فرم استفاده کرد که لیستی از فیلدهای فرم معتبر را برای اضافه کردن به فرمها دریافت میکند. مثال: گزینه پرداخت در خرید آنلاین.
در این مثال، هنگام تحویل (COD) درخواست پرداخت نقدی یا پرداخت آنلاین برای کاربر ارسال میشود. اگر کاربر COD را انتخاب کند، فیلدهای جزئیات پرداخت آنلاین در صفحه بعدی ارائه نخواهد شد. این یک مثال بسیار ابتدایی است. شما میتوانید مثالهای پیچیدهتری در رابطه با این پترن در کدهای آماده جنگو بیابید.
• فرمهای مبتنی بر کاربر: همه اپلیکیشنهای مبتنی بر وب، چندین نوع کاربر را به شیوهای متفاوت مدیریت میکنند. هر کاربر دارای سطح متفاوتی از مجوزهای دسترسی و امکانات افزودنی است. بر اساس نوع کاربر، میتوانیم فیلدهای فرم را قبل از ارسال به کاربر اضافه یا حذف کنیم. مثال: یک شرکت هواپیمایی گزینه "اضافه کردن ناهار ویژه" را برای مشتریان VIP خود ارائه میدهد، در حالی که مشتریان عادی چنین گزینهای ندارند.
• چندین فرم اکشن در هر نما: چگونه چندین فرم اکشن را در یک نمای واحد مدیریت کنیم؟ وقتی چندین فرم اکشن به یک URL منفرد نگاشت میشوند، چگونه میتوانیم با هر اکشن به روشی متفاوت رفتار کنیم؟ مفهوم نمای مبتنی بر کلاس پاسخ این سؤال است.
برای استفاده از نمای مبتنی بر کلاس، با گسترش TemplateView که متدهای دریافت و ارسال پیشفرض را ارائه میکند، یک کلاس View ایجاد کنید. میتوانید شناسه اکشن فرم را به عنوان پارامتر مورد درخواست ارسال کنید. در سمت view، میتوانید بررسی کنید که کدام اکشن یا همان عملیات توسط کاربر انجام شده است و در ادامه مراحل مربوطه را برای تولید پاسخ انجام دهید.
• نماهای CRUD: در اپلیکیشنهای وب، همیشه عملیات CRUD انجام میگیرد. این کلمه مخفف عبارتهای زیر است:
C (Create Information): ایجاد اطلاعات، R (Read Information): خواندن اطلاعات، U (Update Information): بهروزرسانی اطلاعات و D ( Delete Information): حذف اطلاعات.
به عنوان یک توسعهدهنده وب، باید امکانات CRUD را در اختیار کاربر قرار دهید. به جای ایجاد URL های جداگانه برای هر عملیات، میتوانید از مفهوم نمایش مبتنی بر کلاس نیز استفاده کنید. برای هر مجموعه از اطلاعات مرتبط (ایجاد شده در یک صفحه یا مربوط به یک مدل) با گسترش TemplateView عمومی، یک کلاس View ایجاد کنید. با کمک شناسه اکشن فرم (که از سمت کاربر در آبجکت request میآید) عملیات C/R/U/D را داخل متد post/get از کلاس View روی دادهها انجام دهید.
جمعبندی
دیزاین پترنهای جنگو امکانات زیادی را در اختیار توسعهدهندگان قرار میدهد و استفاده از آنها، کاربری اپلیکیشن نهایی را که به کمک این فریمورک توسعه یافته است، سادهتر میسازد. DDP ها به چهار دسته کلی مدل، فرم، نما و تمپلیت تقسیمبندی میشوند. پرکاربردترین دیزاین پترنهای جنگو در دسته مدل و فرم قرار میگیرند که عملکرد هر یک، زیرشاخههای هر دسته و توضیحات آنها در این مقاله ارائه شد.