این سایت از کوکی استفاده می کند. ادامه مرور در این سایت به منزله این است که با استفاده کوکی ها توسط ما موافقت کرده اید.

 

خوش آمدید به انجمن تخصصی پایتون و جنگو (پایتونی ها)

لطفاً برای دسترسی به تمامی بخش های سایت مراحل ثبت نام و ایجاد حساب کاربری را طی نمایید.

جستجو در تالار: در حال نمایش نتایج برای برچسب های 'انجمن پایتون'.



تنظیمات بیشتر جستجو

  • جستجو بر اساس برچسب

    برچسب ها را با , از یکدیگر جدا نمایید.
  • جستجو بر اساس نویسنده

نوع محتوا


تالار ها

  • پایتون ( Python )
    • تالار پایتون مقدماتی
    • تالار پایتون پیشرفته
    • تالار تفاوت زبان پایتون ورژن ۲ و ۳
  • جنگو ( django )
    • تالار جنگو مقدماتی
    • تالار جنگو پیشرفته
    • تالار کتاب جنگو ( جنگو بوک )
    • تالار آموزش پروژه محور جنگو
  • آموزش تکه کدهای کاربردی پایتون ( Python Code Snippet )
  • دریافت نسخه های پایتون
    • نرم افزار زبان برنامه نویسی پایتون
  • دریافت افزونه های پایتون
    • اجرای اسکریپتهای پایتون در اندروید
  • رفع مشكلات برنامه های پايتون
  • معرفی برنامه های تحت پايتون
  • بخش بایگانی

وبلاگ‌ها

  • مقایسه Python با هشت زبان برنامه نویسی مطرح جهان

دسته ها

  • مقاله های سایت

دسته ها

  • دانلود نرم افزار زبان برنامه نویسی پایتون
  • دریافت افزونه های پایتون
    • اجرای اسکریپتهای پایتون در اندروید
  • کتاب های آموزشی پایتون ( Python )
  • کتاب های آموزش فریم ورک جنگو ( Django )

دسته ها

  • آموزش پایتون
    • آموزش پایتون مقدماتی
    • آموزش پایتون پیشرفته
    • تفاوت های پایتون ۲ و پایتون ۳
  • آموزش جنگو
    • آموزش جنگو مقدماتی
    • آموزش جنگو پیشرفته
    • آموزش پروژه محور جنگو
  • تکه کدهای کاربردی پایتون ( Python Code Snippet )
  • آموزش گیت
    • گیت لب ( GitLab )
    • گیت هاب ( GitHub )

15 نتیجه پیدا شد

  1. سلام در این پست میخوام سورس ماشین حساب به زبان پایتون رو بذارم که ساخته خودم هستش. فقط این ماشین حساب یه مشکل داذه اونم اینه که فقط میتونه جمع انجام بده! a = int(input("Num1-->")) b = input(input("Num2-->")) c = a+b print(c) به همین سادگی! فقط دقت کنید که در کد حتما int باشد چون int نشان دهنده ی اعداد در پایتون هستش و اگر نباشد فقط دو عدد را در کنار هم میگذارد و جمع نمی کند. البته این دست شماست که در متغییر c بگویید که a را به علاوه b کند و ضرب کنید تقسیم و یا ... . تا پست بعدی خدا نگهدار... .
  2. سلام من کلاس پایتون میرم و استادمون یه سوال گفته و من نتونستم حل کنم اگه میشه کمکم کنید سوال: يک برنامه بنويسيد که يکان ، دهگان وصدگان که کاربر وارد کرده را جمع و چاپ کند لطفا کمک کنید
  3. با عرض سلام و خسته نباشید به انجمن تخصصی پایتون و جنگو ( پایتونی ها ) خوش آمدید . با قسمت سوم از سری آموزش ساخت وبلاگ ساده با جنگو در خدمت شما دوستان عزیز هستیم . اگر میخواید به آموزش های پروژه محور جنگو دیگری دسترسی داشته باشید از این لینک میتونید به بخش مربوط مراجعه کنید . در قسمت اول و دوم به شما عزیزان یاد دادیم که ابتدا چطوری بستر مورد نیاز برای پروژه جنگویی خودتون رو آماده کنید و بعد پروژه pythonyha رو ایجاد کردیم . توجه کنید که مثلا در این آموزش pythonyha اسم سایت مورد نظر ماست که قراره یک وبلاگ داشته باشه . ساخت یک پروژه برای وبلاگ جنگویی خب دوستان عزیز . در جلسه قبل ما صفحه اول سایت خودمون رو هم دیدیم . ولی در اصل این پروژه خالی هست و هیچی نداره . فقط یک پروژه ایجاد شده تا الان . خب برای ادامه کار باید چیکار کنیم ؟ در طراحی سایت با پایتون و جنگو ، شما وقتی پروژه رو ایجاد می کنید یعنی چارچوب اصلی برای طراحی آماده شده . حالا باید بخش های مختلف سایت رو ایجاد کنید . یعنی چی؟ مثلا شما میخواید بخش ثبت نام برای سایت ایجاد کنید . پس باید یک App برای این کار ایجاد کنید و تمام کارهارو بر روی اون انجام دهید . مثلا شما میخواید یک وبلاگ هم داشته باشه سایتی که دارین طراحی می کنید . پس باید یک App هم برای این کار مثلا با نام blog ایجاد کنید . پس فکر کنم تقریبا متوجه شدید . برای هر بخشی که قرار تو سایت ایجاد بشه مثل فروشگاه ، وبلاگ ، سیستم ثبت نام و ورود کاربر و ... باید برای خودش یک App‌ داشته باشه . البته بهتره که اینطوری باشه تا همه چی مجزا و مرتب باشه و بعدا برای توسعه سایت جنگو به مشکل نخورید . این سری آموزشی که شما در حال مطالعه اون هستید یک وبلاگ خیلی خیلی ساده هست که هیچ بخش خاصی نداره و فقط برای آشنایی شما عزیزان با ایجاد سایت با جنگو هست . یعنی در اصل ما فقط یک App خواهیم داشت با نام blog و یا هرچیز دیگه ای . ایجاد App بلاگ برای پروژه جنگو خب بریم سراغ کار . اگر جلسات رو پشت سر هم دیدید که الان تو دایرکتوری پروژه باید باشید و اگر که از پروژه خارج شدید ، با دستورات زیر دوباره خودتون رو آماده کنید برای ایجاد اپ جنگو : $ cd pythonyha $ source .env/bin/activate $ cd pythonyha وقتی وارد دایرکتوری پروژه شدید ( یعنی جایی که فایل manage.py‌ وجود داره ) ، با کمک دستور زیر اولین App جنگویی خودمون رو میسازیم . این دستور ثابت هست و هر موقع خواستید App بسازید باید در دایرکتوری پروژه قرار بگیرید و این دستور رو بزنید: $ django-admin.py startapp blog خب فکر کنم ظاهر کد خیلی واضح هست و نیازی به توضیح زیادی نداره . بجای اسم blog شما هر اسمی میتونید برای App خودتون بزارید . مهم نیست . فقط طوری اسم انتخاب کنید که کوتاه باشد و مربوط به بخشی که میخواید در سایت ایجاد کنید . خب بعد زدن این دستور با دستور cd pythonyha وارد پروژه اصلی شده و یک نگاهی به فولدر پروژه مون می اندازیم : بله . مشاهده می کنید که یک فولدر ایجاد شده در کنار فولدر پروژه مون به نام blog . پس از این به بعد هر تعداد App درست کنید ، در دایرکتوری اصلی در کنار پوشه pythonyha که پروژه اصلی ماست ، ایجاد خواهند شد . وارد پوشه اپ وبلاگ شوید : در پوشه اپ یکسری فایل و فولدر مشاهده می کنید که کم کم با این ها آشنا خواهید شد . شاید در این سری آموزش خیلی وارد جزئیات نشیم ولی سری آموزش های پروژه محور جنگو رو اگر دنبال کنید کاملا به تمامی موارد مسلط خواهید شد : فایل __init__.py : داخل این فایل به صورت پیش فرض خالیست . ولی یکی از فایل های مهم در پروژه جنگو ماست . شاید تو این آموزش ازش استفاده نکنیم . فایل models.py : در این فایل مدل App‌ خودمون رو تعریف میکنیم تا بتونیم دیتابیس مورد نیازمون رو بسازیم . فایل views.py : در این فایل تمام توابعی که مسئولیت ارتباط دیتابیس و صفحه ای کاربر مشاهده میکنه رو به عهده دارند رو خواهیم نوشت . فایل test.py : برای تست و آزمایش ازش استفاده میکنیم . انشاالله در آموزش های بعدی شاید استفاده کردیم ازش . خیلی وارد جزئیات نمیشم . چون تقریبا در آموزش جنگو مقدماتی این مطالب رو کامل توضیح دادم . مهمترین و اصلی ترین کاری که یادتون باشه بعد از نصب هر App باید انجام بدید ، معرفی اون App به پروژه هست . یعنی فقط ایجاد App کفایت نمیکنه . شما باید به پروژه اطلاع بدید که این اپ به پروژه باید اضافه بشه . پس وارد فایل settings.py شوید و اسم App رو به بخش INSTALLED_APP اضافه کنید : همانطور که در تصویر می بینید یکسری App هم به صورت پیش فرض با نصب جنگو و ایجاد پروژه ، ایجاد می شوند که اتوماتیک به پروژه معرفی شده اند . به دایرکتوری در ستون سمت چپ پای چرم در تصویر نگاه کنید . اگر تمام مراحل رو درست انجام داده باشید ، باید یک دایرکتوری شبیه به عکس بالا داشته باشید . خب بسیار عالی . طبق سرفصلی که در جلسه اول پیش بینی کردیم قرار شد در ۸ قسمت یک وبلاگ جنگویی ایجاد کنیم . شما در این قسمت با نحوه ساخت App آشنا شدید . این آموزش های اولیه رو خوب مطالعه کنید و اگر سوالی دارید در تاپیک های مخصوص هر آموزش بپرسید . چون در سری های بعدی آموزش های مختلف دیگه این مطالب توضیح داده نمیشه و سریع ازش رد می شیم . جلسه سوم از سری آموزش پروژه محور ساخت یک وبلاگ ساده با جنگو به پایان رسید . موفق و پیروز باشید پایتونی ها
  4. این تاپیک پشتیبانی برای آموزش است ساخت یک وبلاگ ساده با جنگو ( قسمت سوم ) 29/05/96 18:13 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  5. با سلام خدمت پایتونی های عزیز با قسمت دیگه ای از آموزش مقدماتی جنگو در خدمت شما هستیم با آموزش نصب پایتون و جنگو بر روی ویندوز به همراه PIP اگر فقط قصد نصب پایتون بر روی ویندوز دارید میتونید از لینک آموزش زیر استفاده کنید : آموزش نصب پایتون ۲ و ۳ بر روی ویندوز برای نصب جنگو بر روی ویندوز همراه ما باشید با این آموزش ساده و روان آموزش نصب فریم ورک جنگو ( Django ) بر روی ویندوز برای نصب جنگو در ویندوز ابتدا میتونید به لینک بالا مراجعه کنید تا نحوه نصب پایتون بر روی ویندوز رو یاد بگیرید . بعد از اینکه به راحتی آخرین ورژن پایتون رو از سایت رسمیش دانلود کردید و نصب کردید از طریق مسیر زیر وارد بخش Environment Variables بشید : Control Panel را باز کنید . System and Security را سلکت کنید . System را انتخاب کنید . Advanced System Settings را انتخاب کنید . Advanced تب را انتخاب کنید . Environment Variables را انتخاب کنید . پایین User variables for در قسمت system variables for بخش PATH رو انتخاب کنید . اگر همچین متغیری وجود نداشت بر روی New کلیک کنید و یک متغیر بسازید و اسم اون رو PATH‌ قرار بدید . در انتهای هر چیزی که در Value Variable نوشته شده است ، موارد زیر را اضافه کنید: C:\Python36;C:\Python36\python.exe;C:\Python\36\Lib\site-packages;C:\Python36\Lib\site-packages\django\bin;C:\Python36\Scripts; اگر چیزی در متغیر بود، نتیجه نهایی به صورت زیر خواهد بود: C:\Windows\System32;C:\Python36;C:\Python36\python.exe;C:\Python\36\Lib\site-packages;C:\Python36\Lib\site-packages\django\bin\;C:\Python36\Scripts; سپس Command Prompt را باز کنید و تایپ کنید python و اینتر بزنید . اگر شبیه زیر چیزی دید پس همه چیز اوکی هست : Python 3.6.0 (default, Feb 13 2017, 13:18:45) >>> سپس خارج بشید از پایتون با دستور زیر : >>> exit() نصب Pip در ویندوز : Pip بسته نصب پایتون است ( Python Package Installer ) که به شما اجازه می دهد چیزهایی مانند Django، Virtualenv، درخواست ها و موارد دیگر را بر روی کامپیوتر محلی خود نصب کنید. لازم است برای توسعه با پایتون pip را نصب کنید . خب ابتدا فایل get-pip.py را از اینجا دانلود کنید و بر روی دسکتاپ خود قرار دهید ( یا به سایت مستندات PIP مراجعه کنید ) . و سپس دستورات زیر را بزنید تا فایل دانلود شده اجرا شوند : >>> C:\ > cd Desktop >>> C:\ > python get-pip.py خب حالا باید دستورات زیر را وارد کنید و تمام : >>> C:\ > pip install virtualenv >>> C:\ > pip freeze >>> C:\ > pip install Django خب تمام شد . شما الان جنگو و virtualenv بر روی سیستم خودتون دارید . ولی به شدت توصیه میکنم اگر میخواید به صورت حرفه ای وارد دنیای پایتون و جنگو بشید حتما از سیستم عامل لینوکس استفاده کنید . برای ادامه کار و شروع آموزش پروژه محور طراحی یک وب سایت با پایتون و جنگو به لینک زیر مراجعه کنید : آموزش پروژه محور طراحی سایت با پایتون و فریم ورک جنگو خسته نباشید دوستان برای هرگونه سوال مطرح کردن به تاپیک این آموزش مراجعه کنید موفق و پیروز باشید پایتونی ها
  6. این تاپیک پشتیبانی برای آموزش است آموزش نصب فریم ورک جنگو ( Django ) بر روی ویندوز 31/04/96 19:44 31/04/96 19:44 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  7. این تاپیک پشتیبانی برای آموزش است آموزش (Template) های پیشرفته در جنگو ( جلسه نهم ) 24/04/96 13:32 24/04/96 13:32 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  8. آموزش (Template) های پیشرفته در جنگو اگرچه بیشتر تعامل های شما با زبان template جنگو در نقش نویسنده ی template می باشد، ولی ممکن است بخواهید موتور template را به طور پیشرفته تری گسترش داده و آن را سفارشی کنید – یا آن را طوری ایجاد کنید که کارهایی را الان نمی تواند انجام دهد را انجام دهد، یا یا به روش دیگری کار شما را آسان تر کند. این فصل به عمق وجودی سیستم قالب جنگو خواهد پرداخت. این فصل آنچه را که نیاز می باشد برای گسترش سیستم لازم می باشد را پوشش می دهد، همچنین در صورتیکه تنها درباره ی نحوه ی کارکرد سیستم template جنگو کنجکاو می باشید می توانید در این فصل جواب سوالات خود را دریافت کنید. همچنین خصوصیت auto-escaping نیز توضیح داده خواهد شد، یک اندازه گیری امنیتی که با استفاده از آن شما دیگر تردیدی در ادامه ی استفاده از جنگو نخواهید داشت. در صورتیکه به دنبال استفاده از سیستم template جنگو به عنوان بخشی از برنامه ی دیگر (بدون باقی فریم ورک) هستید، بخش "پیکربندی سیستم template در حالت مستقل" کمی بعد در همین فصل را مطالعه کنید. مرور زبان Template در ابتدا، اجازه دهید مروری اجمالی به تعدادی از مفاهیم معرفی شده در آموزش template جنگو بپردازیم: template متن سند، یا یک رشته ی عادی پایتون می باشد، که با استفاده از زبان template جنگو ارتقاء پیدا کرده است. یک template می تواند حاوی تگ و متغیر باشد. تگ template یک علامت داخل template می باشد که کاری را انجام می دهد. این تعریف به طور عمدی مبهم می باشد. به عنوان مثال، تگ template می تواند یک محتوی را تولید کند، به صورت یک ساختار کنترلی (مانند عبارت if یا حلقه ی for) عمل کند، مقدار بازیابی شده از یک پایگاه داده و یا حتی قادر به دسترسی به دیگر تگ های template باشد. تگ های template با {% و %} پوشیده شده اند: {% if is_logged_in %} Thanks for logging in! {% else %} Please log in. {% endif %} متغیر یک علامت داخل یک template می باشد که یک مقدار را به عنوان خروجی بر می گرداند. تگ های متغیر با {{ و }} پوشیده شده اند. My first name is {{ first_name }}. My last name is {{ last_name }}. context یک نام -> مقدار مرتبط شده (همانند دیکشنری پایتون) است که به یک template ارسال می شود. template یک context را با جابه جا کردن متغیر با مقادیر context و اجرای تمام تگ ها render می کند. برای جزئیات بیشتر درباره ی اصول اولیه template ها به آموزش template جنگو مراجعه کنید. باقی فصل روش های گسترش دادن موتور template را بحث خواهد کرد. ابتدا، اجازه دهید برای سادگی کار نگاهی کوتاه به مسائلی که از آموزش template جنگو گفته نشده است بیاندازیم. RequestContext و پردازشگرهای Context هنگام ارائه ی یک template، شما به یک context نیاز دارید. معمولا این یک نمونه از django.template.Context می باشد، ولی جنگو همچنین دارای یک کلاس فرزند django.template.RequestContext می باشد، که با کمی تفاوت عمل می کند. RequestContext گروهی از متغیرها را برای template context به طور پیشفرض اضافه می کند – چیزی شبیه به شیء HttpRequest یا اطلاعات درباره ی کاربر فعلی وارد شده به سایت. هنگامی که نمی خواهید مجموعه ای مشابه از متغیرها را در یک سری از template ها تعیین کنید از RequestContext استفاده می شود. برای مثال، دو view زیر را ملاحظه کنید: from django.template import loader, Context def view_1(request): # ... t = loader.get_template('template1.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am view 1.' }) return t.render(c) def view_2(request): # ... t = loader.get_template('template2.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am the second view.' }) return t.render(c) (توجه داشته باشید که به عمد از میانبر render_to_response() در مثال های فوق استفاده نشده است – template ها به صورت دستی بارگذاری شده اند، شیء ها context ساخته شده و template ها ارائه ی شده اند. تمام گام ها برای هدف به وضوح و دقت توضیح داده می شوند.) هر view سه متغیر یکسان – app، user و ip_address – به template خود ارسال می کند. بهتر به نظر نمی رسید اگر کدهای زائد و اضافی حذف می شدند؟ RequestContext و پردازشگرهای context برای حل این مشکل ساخته شده اند. پردازشگرهای context، به شما اجازه می دهند، یک تعداد از متغیرها که در هر context مجموعه ای دریافت می کنند را به طور خودکار تعیین کنید – بدون اینکه مجبور باشید متغیرها را در هر فراخوانی render_to_response تعیین کنید. مشکل این است که هنگام ارائه ی یک template باید بجای Context از RequestContext استفاده کنید. سطح پایین ترین روش برای استفاده از پردازشگرهای context ساختن چند پردازشگر و ارسال آنها به RequestContext می باشد. در زیر نحوه ی نوشتن مثال فوق با پردازشگرهای context وجود دارد: from django.template import loader, RequestContext def custom_proc(request): "A context processor that provides 'app', 'user' and 'ip_address'." return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... t = loader.get_template('template1.html') c = RequestContext(request, {'message': 'I am view 1.'}, processors=[custom_proc]) return t.render(c) def view_2(request): # ... t = loader.get_template('template2.html') c = RequestContext(request, {'message': 'I am the second view.'}, processors=[custom_proc]) return t.render(c) اجازه دهید کد فوق را مورد بررسی قرار دهیم: *** ابتدا، یک تابع به نام custom_proc تعریف شده است. این تابع یک پردازشگر context می باشد که یک شیء HttpRequest دریافت کرده و یک دیکشنری از متغیرها برای استفاده در template context بر می گرداند. این تمام کاری است که این تابع انجام می دهد. *** دو تابع view دیگر برای استفاده از RequestContext به جای Context تغییر کرده اند. دو تفاوت در نحوه ی ساخته شدن context وجود دارد. اول، RequestContext نیاز به یک آرگومان اول برای شیء HttpRequest بودن می باشد که به داخل تابع view در اولین مکان ارسال شده است (request). دوم، RequestContext یک آرگومان اختیاری به نام processors دریافت می کند، که یک لیست یا تاپل از توابع پردازشگر context مورد استفاده می باشد که در کد فوق پردازشگری که در بالا تعریف کردیم را به آن ارسال کرده ایم. *** هر view دیگر لازم نیست شامل app، user یا ip_addressدر ساخت context باشد، زیرا زیرا آن ها با تابع custom_proc تهیه شده اند. *** هر view هنوز دارای انعطاف پذیری برای معرفی هر متغیر template در صورت نیاز می باشد. در این مثال، متغیر template مورد نظر یعنی message به صورت متفاوت در هر view در نظر گرفته شده است. در آموزش template جنگو، میانبر render_to_response() معرفی شد که با استفاده از آن دیگر نیازی به فراخوانی loader.get_template()، سپس ساختن یک Context و فراخوانی متد render() در template نخواهد بود. به منظور نشان دادن کار به صورت سطح پایین با پردازشگرهای context، در مثال های بالا از render_to_response() استفاده نشده است، ولی امکان آن وجود دارد که از پردازشگرهای context با render_to_response() استفاده شود. برای انجام این کار از آرگومان context_instance به صورت زیر استفاده می شود: from django.shortcuts import render_to_response from django.template import RequestContext def custom_proc(request): "A context processor that provides 'app', 'user' and 'ip_address'." return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... return render_to_response('template1.html', {'message': 'I am view 1.'}, context_instance=RequestContext(request, processors=[custom_proc])) def view_2(request): # ... return render_to_response('template2.html', {'message': 'I am the second view.'}, context_instance=RequestContext(request, processors=[custom_proc])) در مثال فوق کد ارائه ی هر template مربوط به view درون یک خط (شکسته شده) خلاصه شده است. این یک پیشرفت می باشد، ولی ارزیابی اختصار کد فوق، باید اعتراف کرد در این روش نیز تقریبا زیاده روی شده است. کدهای اضافه و زائد (متغیرهای template) به قیمت اضافه کردن کدی دیگر (فراخونی processor ها) حذف شده اند. استفاده کردن پردازشگرهای context در صورتیکه مجبور باشید هر بار processor ها را تایپ کنید شما را از تایپ کردن زیاد از حد نجات نمی دهد. به این دلیل، جنگو از پردازشگرهای سراسری پشتیبانی می کند. تنظیم TTEMPLATE_CONTEXT_PROCESSORS (در فایل settings.py) پردازشگرهای context ای که باید همواره برای RequestContext بکار برده شوند را طراحی می کند. این نیاز برای تعیین پردازشگرها را در هر بار که از RequestContext استفاده می کنید را حذف می کند. به طور پیشفرض، TEMPLATE_CONTEXT_PROCESSORS به شکل زیر خواهد بود: TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.media', ) این تنظیم یک تاپل از آیتم های قابل فراخوانی می باشد که از یک رابط یکسان مانند تابع custom_proc که در مثال قبلی مشاهده شد استفاده می کنند – توابعی که یک شیء request به صورت آرگومان آن دریافت کرده و یک دیکشنری از آیتم های ادغام شده درون context بر می گرداند. توجه داشته باشید که متغیرها در TEMPLATE_CONTEXT_PROCESORS به صورت رشته مشخص شده اند، که بدین معنی می باشد که پردازشگرها لازم است جایی در مسیر پایتون باشند (بنابراین می توانید از تنظیم به آن ها مراجعه کنید). هر پردازشگر بدین صورت کار می کند که در صورتیکه یک پردازشگر یک متغیر به context اضافه کند و پردازشگر دوم یک متغیر با همان نام اضافه کند، دومی بر روی اولی بازنویسی می شود. جنگو تعدادی از پردازشگرهای ساده ی context را ارائه می دهد، از جمله آنهایی که به طور پیشفرض فعال می باشند: django.core.context_processors.auth در صورتیکه TEMPLATE_CONTEXT_PROCESSORS حاوی این پردازشگر باشد، هر RequestContext حاوی این متغیرها خواهد بود: user: یک نمونه ی django.auth.models.User می باشد که کاربر فعلی وارد شده (یا در صورتی کاربری وارد نشده باشد یک کاربر بی نام) را نمایش می دهد. messages: یک لیست از پیام ها (به صورت رشته) برای کاربر فعلی وارد شده، در پشت صحنه، این متغیر برای هر درخواست request.user.get_and_delete_messages() را فراخوانی می کند. این متد پیام های کاربر را جمع کرده و آن ها را از پایگاه داده حذف می کند. perms: یک نمونه از django.core.context_processors.PermWrapper که حق دسترسی هایی که کاربر فعلی وارد شده دارا می باشد را نشان می دهد. برای اطلاعات بیشتر در مورد کاربران، حق دسترسی ها و پیام ها به بخش کاربران عضویت و session مراجعه کنید. django.core.context_processors.debug این پردازشگر اطلاعات اشکال زدایی (debugging) به سمت لایه ی template هدایت می کند. در صورتیکه TEMPLATE_CONTEXT_PROCESSORS حاوی این پردازشگر باشد، هر RequestContext حاوی این متغیرها خواهد بود: debug: مقدار تنظیم DEBUG (True or False). شما می توانید از این متغیر برای تست اینکه آیا در حالت debug می باشید یا خیر استفاده کنید. sql_queries: لیست ('sql': ..., 'time': ...) دیکشنری هایی که هر کوئری SQL نشان می دهد که دارای اتفاقاتی که تاکنون در طول درخواست رخ داده اند و چه مدت طول کشیده اند می باشد. لیست به ترتیب کوتئری هایی که صادر شده اند می باشد. بدلیل آنکه اطلاعات اشکال زدایی حساس می باشند، این پردازشگر context تنها در صورتیکه دو وضعیت زیر درست باشند متغیرها را به context اضافه می کند: تنظیم DEBUG، True باشد. درخواست از یک آدرس IP در تنظیم INTERNAL_IPS آمده باشد. خوانندگان زیرک توجه خواهند داشته که متغیر template، debug هرگز مقدار False نخواهد داشت، زیرا در صورتیکه DEBUG مقدارش False باشد، متغیر debug در اولین مکان ساکن نخواهد بود. django.core.context_processors.i18n در صورتیکه این پردازشگر فعال باشد، هر RequestContext حاوی این متغیرها خواهد بود: LANGUAGE: مقدار تنظیم LANGUAGE. LANUAGE_CODE: در صورت وجود request.LANGUAGE_CODE؛ در غیر اینصورت، مقدار تنظیم LANGUAGE_CODE. django.core.context_processors.request در صورتیکه این پردازشگر فعال باشد، هر RequestContext حاوی یک متغیر request خواهد بود که شیء HttpRequest فعلی می باشد، توجه داشته باشید که این پردازشگر به صورت پیشفرض غیر فعال است؛ شما باید آن را فعال کنید. در صورتیکه template های شما نیاز به دسترسی به attribute های HttpRequest فعلی باشند مانند آدرس Ip، ممکن است از این پردازشگر استفاده کنید: {{ request.REMOTE_ADDR }} راهنمایی هایی برای نوشتن پردازشگرهای context خودتان در زیر راهنمایی هایی برای ایجاد پردازشگر خودتان وجود دارد: *** هر پردازشگر context را مسئول کوچکترین زیر مجموعه ی قابلیت های ممکن کنید. استفاده از پردازشگرهای چندگانه ساده می باشد، بنابراین ممکن است عمکرد را به قسمت های منطقی برای استفاده ی دوباره در آینده تقسیم کنید. *** به خاطر داشته باشید که هر پردازشگر context در TEMPLATE_CONTEXT_PROCESSORS در هر template ساخته شده با فایل تنظیمات قابل دسترس خواهد بود، بنابراین سعی کنید نام های متغیری انتخاب کنید که با نام متغیرهایی که به طور مستقل ممکن است استفاده کنید تداخل نداشته باشد. از آنجایی که نام های متغیر به حروف کوچک و بزرگ حساس می باشند، ایده ی بدی نیست که تمام متغیرهایی که یک پردازشگر تولید می کند با تماما با حروف بزرگ باشند. *** تا زمانیکه آن ها در مسیر پایتون می باشند مشکلی نیست که filesystem باشند، بنابراین می توان به آن ها از تنظیم TEMPLATE_CONTEXT_PROCESSORS اشاره کرد. با در نظر گرفتن این، مناسب آن است که آن ها را در یک فایل با نام context_processors.py داخل app یا پروژه ذخیره کرد. HTML Escaping خودکار هنگام تولید HTML از template ها، همواره یک مخاطره وجود دارد که یک متغیر شامل کاراکترهایی باشد که بر روی نتیجه ی HTML تاثیر بگذارد. برای مثال، کد زیر را ملاحظه کنید: Hello, {{ name }}. در ابتدا، به نظر می رسد کد فوق روش بی ضرری برای نمایش نام کاربر می باشد، اما در نظر داشته باشید چه اتفاقی می افتد اگر ورودی کاربر به شکل زیر باشد: <script>alert('hello')</script> با مقدار فوق، template به صورت زیر ارائه خواهد شد: Hello, <script>alert('hello')</script> این بدین معنی است که مرورگر یک جعبه ی خطر جاوا اسکریپت ظاهر خواهد کرد! به طور مشابه، چه می شود اگر name حاوی یک علامت '<' مانند زیر باشد؟ <b>username مقدار فوق نتیجه ای به شکل زیر خواهد داشت: Hello, <b>username مقدار فوق باعث می شود، باقی کد صفحه ی وب به صورت bold نمایش داده خواهند شد! واضح است که، داده ی ارسال شده توسط کاربر به طور کورکورانه قابل اعتماد نمی باشد، زیرا کاربران مخرب از این حفره برای انجام مقاصد پلید استفاده کنند. این قبیل رفتار امنیتی حمله ی Cross Site Scripting (XSS) نامیده می شوند. (برای اطلاعات بیشتر در مورد امنیت، به آموزش امنیت مراجعه کنید.) جهت اجتناب از این مشکل، دو امکان وجود دارد: *** ابتدا، می توان از طریق فیلتر escape برای اجرای هر متغیر مشکوک اطمینان حاصل کرد، به طوری که این فیلتر حروف مضر HTML را به نوع غیر مضر آن تبدیل می کند. این راهکار پیشفرض در جنگو برای سال های اول بوده است، ولی مشکل این است که این حالت توسعه دهندگان و نویسندگان template را مقید به استفاده از escape می کند. در استفاده کردن از فیلتر escape، فراموش کردن استفاده از آن بسیار اتفاق می افتد. *** دوم اینکه، می توان از HTML escaping خودکار جنگو استفاده کرد. باقی این بخش نحوه ی کار auto‑escaping را توضیح خواهد داد. به طور پیشفرض، هر template ای به صورت خودکار حروف گفته شده در خروجی هر تگ متغیر را به حروف غیر مضر تبدیل می کند. به ویژه، این پنج حروف تبدیل می شوند. < به &lt; تبدیل می شود > به &gt; تبدیل می شود ' (تک کتیشن) به &#39; تبدیل می شود " (دابل کتیشن) به &quot; تبدیل می شود & به &amp; تبدیل می شود دوباره تاکید می کنیم که، این رفتار به صورت پیشفرض می باشد. در صورتیکه از سیستم template جنگو استفاده می کنید، شما از این مشکلات محفوظ می باشید. نحوه ی از کار انداختن auto-escaping در صورتیکه بخواهید حالت پیشفرض یعنی auto-escaping را برای هر وب سایت، هر سطح template و یا هر سطح متغیر، تغییر دهید، می توان به چندین روش عمل کرد. چرا می خواهید این حالت را تغییر دهید؟ زیرا، گاهی اوقات متغیرهای template حاوی داده ای می باشند که می خواهید به صورت HTML ارائه شوند، که در این صورت می خواهید محتویات این متغیرها escape نشوند. برای مثال، ممکن است قسمت هایی از کد HTML مورد اعتمادی را در پایگاه داده ذخیره کنید و بخواهید آن را به طور مستقیم درون template استفاده کنید. یا، ممکن است از template جنگو جهت تولید متنی به غیر از HTML استفاده کنید – مانند یک پیام پست الکترونیک برای نمونه. برای متغیرها جهت غیر فعال کردن auto‑scaping برای یک متغیر، از فیلتر safe می توان استفاده کرد: This will be escaped: {{ data }} This will not be escaped: {{ data|safe }} در مثال فوق در صورتیکه data حاوی مقدار '<b>' باشد، خروجی آن به صورت زیر خواهد بود: This will be escaped: <b> This will not be escaped: <b> برای بلاک های template جهت کنترل auto‑escaping برای یک template، template (یا تنها قسمتی از template) را درون تگ autoescape مانند زیر قرار دهید: {% autoescape off %} Hello {{ name }} {% endautoescape %} تگ autoescape یک مقدار on یا off به صورت آرگومان دریافت می کند. گاهی اوقات می خواهید زمانی که auto‑escape در قسمتی از کد غیر فعال شده است آن را مجبور کنید که فعال شود مانند مثال زیر: Auto-escaping is on by default. Hello {{ name }} {% autoescape off %} This will not be auto-escaped: {{ data }}. Nor this: {{ other_data }} {% autoescape on %} Auto-escaping applies again: {{ name }} {% endautoescape %} {% endautoescape %} تگ auto‑escape درون template هایی که از template پدر ارث بری کرده اند نیز تاثیر می گذارد. به عنوان مثال: # base.html {% autoescape off %} <h1>{% block title %}{% endblock %}</h1> {% block content %} {% endblock %} {% endautoescape %} # child.html {% extends "base.html" %} {% block title %}This & that{% endblock %} {% block content %}{{ greeting }}{% endblock %} به این دلیل که auto‑escaping درون template پدر غیر فعال شده است، درون template فرزند نیز غیر فعال خواهد بود، بنابراین در صورتیکه متغیر greeting حاوی رشته ی <b>Hello!</b> باشد، خروجی آن به صورت زیر خواهد بود: <h1>This & that</h1> <b>Hello!</b> نکته ها عموما، نویسندگان template نیازی نیست در مورد auto‑escaping خیلی زیاد نگرانی داشته باشند. توسعه دهندگان سمت پایتون (افرادی که view ها و فیلترهای سفارشی را می نویسند)، نیاز دارند، درباره ی مواردی که در داده نباید escape شوند فکر کنند، و داده را به طور مناسب علامت گذاری کنند. بنابراین کارها در template انجام خواهند شد. در صورتیکه یک template می سازید که ممکن است در وضعیت هایی که مطمئن نیستید آیا auto‑escaping فعال است یا خیر استفاده شده باشد، آنگاه یک فیلتر escape برای هر متغیر که نیاز به escape دارد قرار دهید. هنگامی که auto‑escaping فعال باشد، هیچ مشکلی پیش نخواهد آمد – فیلتر escape تاثیری بر روی متغیرهای auto‑escape شده نخواهد داشت. Escape کردن خودکار رشته ی خام در آرگومان های فیلتر همانطور که پیش تر ذکر شد، آرگومان های فیلتر می توانند رشته باشند: {{ data|default:"This is a string literal." }} تمام رشته های خام بدون هیچ escape خودکاری درون template مندرج شده می باشند – آن ها مثل اینکه از میان فیلتر safe عبور کرده باشند عمل می کنند. دلیل این موضوع این است که نویسنده ی template آنچه را که به صورت رشته ی خام می آید را تحت کنترل دارد، بنابراین آن ها اطمینان حاصل می کنند که متن هنگامی template نوشته می شود به صورت صحیح escape شده باشد. این بدان معناست که خواهید نوشت: {{ data|default:"3 &lt; 2" }} ... به جای {{ data|default:"3 < 2" }} <-- Bad! Don't do this. حالت فوق تاثیری بر روی مقداری که درون خود متغیر می باشد نخواهد داشت. در صورت لزوم محتویات متغیرها همچنان به طور خودکار escpae شده می باشند، زیرا این دیگر خارج از کنترل نویسنده ی template می باشد. بارگذاری داخل Template عموما، شما template ها را در فایل هایی درون filesystem خود ذخیره خواهید کرد، اما می توان از template loader های سفارشی برای بارگذاری template ها از منابع دیگر استفاده کرد. جنگو دارای دو روش برای بارگذاری template می باشد: *** django.template.loader.get_template (template_name): get_template، template کامپایل شده (یک شیء Template) برای template با نام داده شده را بر می گرداند. در صورتیکه template مورد نظر وجود نداشته باشد، خطای TemplateDoesNotExist رخ خواهد داد. *** django.template.loader.select_template (template_name_list): select_template درست مثل get_template می باشد، با این تفاوت که select_template لیستی از نام template ها را دریافت می کند. از لیست موجود، اولین template موجود را بر می گرداند. در صورتیکه هیچکدام از template ها وجود نداشته باشند، یک خطای TemplateDoesNotexist رخ خواهد داد. همانطور که در آموزش template جنگو بحث شد، هر کدام از این توابع به صورت پیشفرض برای بارگذاری template ها از تنظیم TEMPLATE_DIRS استفاده می کنند. بعضی از loader ها به طور پیشفرض غیر فعال می باشند، ولی می توان با ویرایش تنظیم TEMPLATE_LOADER آن ها را فعال کرد. TEMPLATE_LOADERS باید یک تاپل از رشته ها باشد، به طوری که هر رشته یک template loader را نمایش می دهد. این template loader ها به همراه جنگو ارائه شده اند: *** django.template.loaders.filesystem.load_template_source: این loader، template ها را از filesystem به همراه TEMPLATE_DIRS بارگذاری می کند. *** django.template.loaders.app_directories.load_template_source: این loader، template ها را از برنامه های جنگو روی filesystem بارگذاری می کند. برای هر برنامه در INSTALLED_APPS، loader یک دایرکتوری زیرمجموعه ی templates را جستجو می کند. در صورتیکه دایرکتوری وجود داشته باشد، جنگو در آن جا به دنبال template ها می گردد. این بدین معناست که می توان template ها را با برنامه های فردی خودتان ذخیره کنید، برای مثال، اگر INSTALLED_APPS حاوی ('myproject.polls'، 'myproject.music') باشد، در اینصورت get_template('foo.html') به شکل زیر template ها را بدین ترتیب جستجو می کند: *** /path/to/myproject/polls/templates/foo.html *** /path/to/myproject/music/templates/foo.html توجه داشته باشید که loader یک بهینه سازی را اجرا می کند: loader یک لیست از پکیج های INSTALLED_APPS را که دارای دایرکتوری زیرمجموعه ی templates می باشد را cache می کند. این loader به صورت پیشفرض فعال می باشد. *** django.template.loaders.eggs.load_template_source: این loader درست مثل app_directories می باشد، با این تفاوت که template ها را به جای filesystem از egg های پایتون بارگذاری می کند. این loader به طور پیشفرض غیر فعال می باشد؛ در صورتیکه از egg ها برای توزیع برنامه ی خود استفاده می کنید نیاز خواهید داشت که آن را فعال کنید. (egg های پایتون روشی برای فشرده سازی کد پایتون داخل یک فایل تنها می باشد.) جنگو به منظور توجه به تنظیم TEMPLATE_LOADERS از template loader ها استفاده می کند. جنگو از هر loader تا پیدا کردن مطابق آن استفاده می کند. گسترش Template System اکنون که کمی بیشتر درباره ی مسائل داخلی template system فهمیده اید، اجازه دهید نحوه ی گسترش سیستم با کد سفارشی را مورد بررسی قرار دهیم. بیشترین نقش سفارشی سازی template فرمی از تگ های سفارشی و فیلترها می باشد. اگر چه زبان template جنگو دارای بسیاری از تگ ها و فیلترهای داخلی می باشد، شما احتمالا کتابخانه هایی از تگ ها و فیلترهای خودتان را جمع آوری خواهید کرد تا احتیاجات خودتان را رفع کنید. خوشبختانه، تعریف عملکرد برنامه برای خودتان بسیار آسان می باشد. ساختن یک کتابخانه ی Template برای نوشتن تگ ها یا فیلترهای سفارشی، اولین کار ساختن یک کتابخانه ی template است – قسمت کوچکی از زیر ساخت جنگو که می تواند دوباره استفاده شود. ساختن یک کتابخانه ی template دارای یک روند دو مرحله ای می باشد: *** مرحله ی اول، تصمیم گرفتن اینکه کدام برنامه ی جنگو باید به کتابخانه ی template جا دهد. در صورتیکه از طریق دستور manage.py startapp، یک app ساخته اید، می توانید کتابخانه ی خود را در آنجا قرار دهید، یا می توانید منحصرا یک app دیگر برای کتابخانه ی template بسازید. ما ساختن یک app جدا برای کتابخانه ی template را پیشنهاد می کنیم، زیرا فیلترهای شما می توانند در پروژه های آینده نیز برای شما مفید واقع شوند. هر روشی را که انتخاب می کنید، اطمینان حاصل کنید که app مورد نظر را به تنظیم INSTALLED_APPS اضافه کرده اید. به طور کوتاه این موضوع را توضیح خواهیم داد. *** مرحله ی دوم، ساختن یک دایرکتوری به نام templatetags در پکیج مناسب برنامه ی جنگو می باشد. این دایرکتوری باید از نظر مسیر هم سطح با models.py، views.py و غیره باشد. برای مثال: books/ __init__.py models.py templatetags/ views.py دو فایل خالی در دایرکتوری templatetags بسازید: یک فایل __init__.py (برای اینکه به پایتون نشان داده شود که این یک پکیج حاوی کد پایتون می باشد) و یک فایل که حاوی تعریف تگ ها/فیلترهای سفارشی شما خواهد بود. نام فایل دوم آن چیزی خواهد بود که شما برای بارگذاری تگ ها از آن استفاده خواهید کرد. برای مثال، در صورتیکه تگ ها یا فیلترهای سفارشی شما درون فایلی به نام poll_extras.py خواهد بود، شما کدی نظیر کد زیر را درون template خواهید داشت: {% load poll_extras %} تگ {% load %} تنظیم INSTALLED_APPS را مورد بررسی قرار داده و تنها اجازه به بارگذاری کردن کتابخانه های template داخل برنامه های نصب شده جنگو می دهد. این یک ویژگی امنیتی می باشد؛ این ویژگی اجازه می دهد تا مکانی برای بسیاری از کتابخانه های template در یک رایانه ی تنها را بدون فعال کردن دسترسی به تمام آنها برای هر نصب جنگو ایجاد کنید. در صورتیکه یک کتابخانه ی template نوشته اید که به هیچ models/views ای مرتبط نیست، این حالت برای داشتن پکیج برنامه ی جنگو که حاوی تنها یک پکیج templatetags می باشد معتبر و کاملا عادی است. هیچ محدودیتی نسبت به تعداد ماژول هایی که شما در پکیج templatetags قرار می دهید وجود ندارد. تنها به خاطر داشته باشید که یک عبارت {% load %} تگ ها یا فیلترهایی برای نام ماژول پایتون داده شده بارگذاری خواهد کرد، نه نام برنامه. هنگامی که آن ماژول پایتون را ساخته باشید، تنها ملزم به نوشتن مقدار کمی از کد پایتون بسته به اینکه آیا چه فیلتر یا تگی می نویسید خواهید بود. برای معتبر بودن کتابخانه ی تگ، ماژول باید حاوی یک متغیر در سطح ماژول به نام register باشد که یک نمونه از template.Library می باشد. این یک ساختار داده می باشد که تمام تگ ها و فیلترها درون آن عضو شده اند. بنابراین، در بالا ماژول، کد زیر را اضافه کنید: from django import template register = template.Library() برای یک انتخاب خوب از مثال ها، کد منبع فیلترها و تگ های پیشفرض جنگو را بخوانید. آن ها به ترتیب در django/template/defaultfilters.py و django/template/defaulttags.py می باشند. همچنین برخی برنامه های موجود در django.contrib حاوی کتابخانه های template می باشد. هنگامی که متغیر register را ساختید، شما برای ساختن فیلترها و تگ ها template از آن استفاده خواهید کرد. نوشتن فیلترهای سفارشی Template فیلترهای سفارشی تنها توابع پایتون می باشند که یک یا دو آرگومان دریافت می کنند: ***مقدار متغیر (ورودی) ***مقدار آرگومان، که می تواند یک مقدار پیشفرض یا داشته و یا رو هم رفته ترک شده باشد برای مثال، در فیلتر {{ var|foo:"bar" }}، فیلتر foo محتویات متغیر var و آرگومان "bar" را ارسال خواهد کرد. توابع فیلتر باید همواره چیزی را بر گردانند. آن نباید خطایی ایجاد کنند، و باید به آرامی خطاها را رد کنند. در صورتیکه یک خطا وجود داشته باشد، یا باید ورودی اصلی را برگردانند یا یک رشته ی خالی را، هر کدام که حساسیت بیشتری خواهد داشت. در اینجا یک مثال از تعریف فیلتر را ملاحظه می کنید: def cut(value, arg): "Removes all values of arg from the given string" return value.replace(arg, '') در زیر نحوه ی استفاده حذف کردن فاصله های مقدار یک متغیر توسط فیلتر بالا نشان داده شده است: {{ somevariable|cut:" " }} اغلب فیلترها آرگومانی دریافت نمی کنند. در این مورد، تنها آرگومان تابع را حذف کنید: def lower(value): # Only one argument. "Converts a string into all lowercase" return value.lower() هنگامی که تعریف فیلتر خود را نوشتید، برای اینکه آن را برای زبان template جنگو در دسترس قرار دهید لازم است که آن رابه نمونه ی Library خود معرفی کنید: register.filter('cut', cut) register.filter('lower', lower) متد Library.filter() دو آرگومان دریافت می کند: *** نام فیلتر (یک رشته) *** خود تابع فیلتر در صورتیکه از پایتون 2.4 یا بالاتر استفاده می کنید، می توانید بجای روش فوق از regiser.filter() به صورت یک decorator استفاده کنید: @register.filter(name='cut') def cut(value, arg): return value.replace(arg, '') @register.filter def lower(value): return value.lower() در صورتیکه آرگومان name را قرار ندهید، همانطور که در مثال دوم مشاهده کردید، جنگو از نام تابع به صورت نام فیلتر استفاده خواهد کرد. مثال فوق نیز مثال کامل کتابخانه ی template یعنی تهیه ی فیلتر cut می باشد: from django import template register = template.Library() @register.filter(name='cut') def cut(value, arg): return value.replace(arg, '') توشتن تگ های template سفارشی تگ ها پیچیده تر از فیلترهای می باشند، زیرا تقریبا هرکاری را می توانند انجام دهند. آموزش template جنگو نحوه ی کار سیستم template جنگو را در یک روند دو مرحله ای توضیح می دهد: کامپایل و ارائه (compling and rendering). برای تعریفی یک تگ template سفارشی، لازم است نحوه ی مدیریت هر دو مرحله ی فوق در زمانی که جنگو تگ شما را به دست می آورد به آن گفته شود. هنگامی که جنگو یک template را کامپایل می کند، متن خام template را به note هایی تقسیم می کند. هر node یک نمونه از django.template.Node بوده و دارای یک متد render() می باشد. در نتیجه، یک template کامپایل شده یک لیست از شیء های Node می باشد. برای مثال، template زیر را ملاحظه کنید: Hello, {{ person.name }}. {% ifequal name.birthday today %} Happy birthday! {% else %} Be sure to come back on your birthday for a splendid surprise message. {% endifequal %} در حالت کامپایل شدهه ی template، template فوق به صورت لیستی از node های زیر نشان داده شده است: node متن: "Hello, " node متغیر: person.name node متن: ".\n\n" ifEqual node: name.birthday and today هنگامی که در یک template کامپایل شده render() فراخوانی می شود، template متد render() در هر Node موجود در لیست node را با context داده شده فراخوانی می کند. نتیجه ی کار برای شکل دادن به خروجی template همه ی node های وصل شده به یکدیگر است. در نتیجه، برای تعریف یک تگ template سفارشی، شما نحوه ی تبدیل شدن تگ خام template به یک Node (کامپایل تابع) و آنچه را که متد render() ند انجام می دهد را تعیین می کنید. بخش های بعدی، تمامی مراحل نوشتن تگ سفارشی پوشش داده خواهد شد. نوشتن کامپایل تابع برای هر تگ template ای که parser با آن مواجه است، یک تابع پایتون با محتویات تگ و خود شیء parser فراخوانی می شود. این تابع موظف است یک نمونه Node بر اساس محتویات تگ بر گرداند. برای مثال، اجازه دهید یک تگ template بنویسیم، {% current_time %}، که زمان/تاریخ فعلی را نمایش دهد، که قالب بندی آن نیز بر حسب پارامتر داده شده در تگ در strftime (http://www.djangoproject.com/r/python/strftime/ را مشاهده کنید) باشد. در این مورد تصور می کنیم تگ باید به شکل زیر استفاده شود: نکته بله، این تگ template یک تگ اضافه و زائد می باشد – تگ پیشفرض {% now %} با روشی ساده تر همین کار را انجام می دهد. تگ template فوق تنها با هدف آشنایی با ساختن تگ های سفارشی ارائه شده است. parser برای این تابع پارامتر را دریافت کرده و یک شیء Node بسازد: from django import template register = template.Library() def do_current_time(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, format_string = token.split_contents() except ValueError: msg = '%r tag requires a single argument' % token.split_contents()[0] raise template.TemplateSyntaxError(msg) return CurrentTimeNode(format_string[1:-1]) اجازه دهید کد فوق را مورد بررسی قرار دهیم: *** هر کامپایل تابع تگ template دو آرگومان دریافت می کند، parser و token. parser شیء template parser می باشد که در این مثال از آن استفاده نشده است. token علامتی است که در حال حاضر توسط parser تجزیه شده است. *** token.contents یک رشته از محتویات خام تگ می باشد. در مثال فوق 'current_time "%Y‑%m‑%d %I:%M %p"' می باشد. *** متد token.split_contents() بر حسب فاصله، تا زمانی که متن توسط کتیشن پوشیده شده است آن را جدا می کند. از token.contents.split() استفاده نکنید () در دست ترجمه ... *** تابع فوق موظف است خطای django.template.TemplateSyntaxError با یک پیام مفید برای هر خطا ایجاد کند. *** نام تگ را به طور مستقیم در پیام های خطا استفاده نکنید، زیرا این کار باعث می شود نام تگ به تابع شما وصل شود. token.split_contents()[0] همواره نام تگ شما خواهد بود – حتی هنگامی که تگ دارای هیچ آرگومانی نیست. *** تابع فوق یک CurrentTimeNode (که کمی بعد آن را ایجاد خواهیم کرد) حاوی هرچیزی که node برای شناختن این تگ نیاز دارد بر می گرداند. در این مورد، تنها آرگومان "%Y‑%m‑%d %I:%M %p" ارسال می شود. کتیشن عقبی و جلویی تگ template توسط format_string[1:-1] حذف می شود. *** توابع کامپایل تگ template باید یک کلاس فرزند Node بر گردانند؛ در غیر اینصورت مقدار برگشتی یک خطا است. نوشتن Template Node گام بعدی در نوشتن تگ های سفارشی، تعریف یک کلاس فرزند Node که حاوی یک متد render() است می باشد. در ادامه ی مثال قبلی، نیاز به تعریف CurrentTimeNode می باشد: import datetime class CurrentTimeNode(template.Node): def __init__(self, format_string): self.format_string = str(format_string) def render(self, context): now = datetime.datetime.now() return now.strftime(self.format_string) این دو تابع (__init__() و render()) به طور مستقیم به دو مرحله ی در روند template (compilation and rendering) مرتبط هستند. در نتیجه، تابع __init__() تنها ملزم به ذخیره ی قالب رشته ی برای استفاده ی بعدی می باشد، و تابع render() کار واقعی را انجام می دهد. مانند فیلترهای template، این توابع باید به جای ایجاد خطا به طور بی صدا خطاهای ایجاد شده را رد کنند. تنها زمانی که تگ های template اجازه دارند خطاها را ایجاد کنند زمان کامپایل می باشد. معرفی تگ در پایان، نیاز به معرفی تگ با نمونه ی ماژول Library می باشد. معرفی تگ های سفارشی بسیار شبیه به معرفی فیلترهای سفارشی (همانطور که توضیح داده شد) می باشد. تنها یک نمونه template.Library را معرفی کرده و متد tag() آن را فراخوانی کنید. برای مثال: register.tag('current_time', do_current_time) متد tag() دو آرگومان دریافت می کند: *** نام تگ template (رشته). *** تابع کامپایل. همانند معرفی فیلتر، امکان این وجود دارد که از register.tag به صورت یک decorator در پایتون 2.4 و بالاتر استفاده کرد: @register.tag(name="current_time") def do_current_time(parser, token): # ... @register.tag def shout(parser, token): # ... در صورت حذف آرگومان name، همانند مثال دوم، جنگو از نام تابع برای نام تگ استفاده خواهد کرد. تنظیم یک متغیر در Context مثال بخش قبلی به سادگی یک مقدار را بر می گرداند. خیلی اوقات قرار دادن متغیرها به جای برگشت دادن مقادیر مفید خواهد بود. در این روش، نویسندگان template می توانند تنها متغیرهایی که تگ های template شما قرار داده اند را استفاده کنند. برای قرار دادن یک متغیر در context، از اختصاص دادن دیکشنری برای شیء context در متد render() استفاده می شود. در زیر نسخه ی تغییر کرده ی CurrentTimeNode مشاهده می کنید: class CurrentTimeNode2(template.Node): def __init__(self, format_string): self.format_string = str(format_string) def render(self, context): now = datetime.datetime.now() context['current_time'] = now.strftime(self.format_string) return '' (ساختن یک تابع do_current_time2، به اضافه ی معرفی آن تابع به تگ template، current_time2 انجام نشده است، تا خواننده آن ها را به عنوان تمرین انجام داد.) توجه داشته باشید که render() یک رشته ی خالی را بر می گرداند. render() همواره باید یک رشته بر گرداند، بنابراین در صورتیکه تمام تگ های template یک متغیر را قرار دهند، render() باید یک رشته ی خالی بر گرداند. در اینجا نحوه ی استفاده ی نسخه ی جدید از تگ نشان داده شده است: {% current_time2 "%Y-%M-%d %I:%M %p" %} ولی یک مشکل با CurrentTimeNode2 وجود دارد: نام متغیر current_time به صورت مستقیم استفاده شده است. این بدان معناست که شما نیاز است اطمینان حاصل کنید که template شما از {{ current_time }} هیچ جای دیگری استفاده نکرده است، زیرا {% current_time2 %} مقدار آن متغیر را باز نویسی خواهد کرد. راهکار درست این است که تگ template نامی برای متغیر قرار داده شده تعیین نماید: {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %} برای انجام چنین کاری، نیاز به تغییر هر دو تابع کامپایل و کلاس Node به صورت زیر می باشد: import re class CurrentTimeNode3(template.Node): def __init__(self, format_string, var_name): self.format_string = str(format_string) self.var_name = var_name def render(self, context): now = datetime.datetime.now() context[self.var_name] = now.strftime(self.format_string) return '' def do_current_time(parser, token): # This version uses a regular expression to parse tag contents. try: # Splitting by None == splitting by spaces. tag_name, arg = token.contents.split(None, 1) except ValueError: msg = '%r tag requires arguments' % token.contents[0] raise template.TemplateSyntaxError(msg) m = re.search(r'(.*?) as (\w )', arg) if m: fmt, var_name = m.groups() else: msg = '%r tag had invalid arguments' % tag_name raise template.TemplateSyntaxError(msg) if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")): msg = "%r tag's argument should be in quotes" % tag_name raise template.TemplateSyntaxError(msg) return CurrentTimeNode3(fmt[1:-1], var_name) حالا do_current_time() قالب رشته و نام متغیر را به CurrentTimeNode3 ارسال می کند. Parse کردن تا تگ Template دیگر تگ های template می توانند به صورت بلاک های حاوی تگ های دیگر (مانند {% if %} و {% for %}) کار کنند. برای ساختن یک تگ template مانند این، در تابع کامپایل خود از parser.parse() استفاده کنید. در زیر نحوه ی کار تگ {% comment %} انجام شده است: def do_comment(parser, token): nodelist = parser.parse(('endcomment',)) parser.delete_first_token() return CommentNode() class CommentNode(template.Node): def render(self, context): return '' parser.parse() یک تاپل از نام تگ های template برای استفاده داخل تگ دریافت می کند و یک نمونه از django.template.NodeList بر می گرداند، که لیست تمام شیء های Node ای می باشد که parser با آن ها قبل از برخورد با نام تگ موجود در تاپل برخورد کرده است. بنابراین در مثال قبلی، nodelist یک لیست از تمام node های بین {% comment %} و {% endcomment %} بدون در نظر گرفتن خود آن ها می باشد. بعد از آن که parser.parse() فراخوانی شده است، parser در دست ترجمه ... سپس CommentNode.render() به سادگی یک رشته ی خالی را بر می گرداند. هر چیزی بین {% comment %} و {% endcomment %} رد شده است. Parse کردن تا تگ Template دیگر و ذخیره ی محتویات در مثال قبلی، do_comment هر چیزی را بین {% comment %} و {% endcomment %} رد کرد. همچنین این امکان وجود دارد که با کد بین تگ های template کاری انجام داد. برای مثال، در اینجا یک تگ template سفارشی، {% upper %}، که هر چیزی بین خودش و {% endupper %} را به حروف بزرگ تبدیل می کند: {% upper %} This will appear in uppercase, {{ user_name }}. {% endupper %} همانند مثال قبلی، از parser.parse() استفاده کرده ایم. این بار، نتیجه ی nodelist را به Node ارسال کرده ایم: def do_upper(parser, token): nodelist = parser.parse(('endupper',)) parser.delete_first_token() return UpperNode(nodelist) class UpperNode(template.Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): output = self.nodelist.render(context) return output.upper() تنها مفهوم جدید در کد فوق self.nodelist.render(context) در UpperNode.render() می باشد که به سادگی برای هر Node در لیست node متد render() فراخوانی شده است. برای مثال های بیشتر از ارائه ی پیچیده، کد منبع {% if %}، {% for %}، {% ifequal %} و {% ifchanged %} را مشاهده کنید. این تگ ها در django/template/defaulttags.py موجود می باشند. میانبر برای تگ های ساده بسیاری از تگ های template یک آرگومان تنها دریافت می کنند – یک رشته یا یک مرجع متغیر template – و یک رشته را بعد از انجام برخی پردازش های منحصرا بر روی آرگومان ورودی و برخی اطلاعات خارجی بر می گردانند. برای مثال، تگ current_time ای که قبلا نوشته شد. یک قالب رشته به آن داده شد، و current_time زمان را به صورت یک رشته بر گرداند. جهت ساده کردن ساختن این قبلی تگ ها، جنگو یک تابع کمک کننده ارائه می کند، simple_tag. این تابع یک متد از django.template.Library می باشد که یک تابع که آن هم یک آرگومان قبول می کند دریافت می کند، کار این تابع پیچیدن در تابع render و کارهای ضروری می باشد که قبلا ذکر شده است مانند معرفی کردن را با template system انجام می دهد. تابع current_time را با حالت نوشته شده ی فوق در زیر مشاهده می کنید: def current_time(format_string): try: return datetime.datetime.now().strftime(str(format_string)) except UnicodeEncodeError: return '' register.simple_tag(current_time) در پایتون 2.4 و بالاتر، می توان از decorator استفاده کرد: @register.simple_tag def current_time(token): # ... توجه به چند نکته ضروری می باشد: *** تنها یک آرگومان (تک) داخل تابع ما ارسال شده است. *** بررسی برای تعداد آرگومان های مورد به وسیله تعداد فراخوانی تابع انجام شده است، بنابراین نیازی به انجام آن نیست. *** کتیشن اطراف آرگومان (در صورت وجود) حذف شده است، بنابراین ما یک رشته ی یونیکد عادی دریافت می کنیم. تگ های Inclusion تگ template رایج دیگر نوعی است که برخی داده ها را از طریق ارائه ی template دیگر نمایش می دهد. برای مثال، رابط مدیر جنگو از تگ های template سفارشی برای نمایش دکمه ها زیر فرم صفحات "add/change" استفاده می کند. آن دکمه های همواره دارای ظاهر یکسان می باشند، ولی نشانه های لینک بسته به ویرایش شیء تغییر می کند. آن ها یک مورد عالی برای استفاده از یک template کوچک می باشد که با جزئیات فرم شیء فعلی پر شده اند. این قبیل از تگ ها، تگ های inclusion نامیده می شوند. نوشتن تگ های inclusion بهتر موضوعات دیگر با مثال قابل نشان دادن است. اجازه دهید یک تگ بنویسیم که یک لیست از کتاب ها برای یک شیء Author داده شده تولید می کند. ما از تگ به صورت زیر استفاده می کنیم: {% books_for_author author %} نتیجه چیزی شبیه به کد زیر خواهد بود: <ul> <li>The Cat In The Hat</li> <li>Hop On Pop</li> <li>Green Eggs And Ham</li> </ul> ابتدا، تابعی تعریف می شود که آرگومانی دریافت کرده و یک دیکشنری از داده ها برای نتیجه تولید کند. تقدت داشته باشید که نیاز به برگرداندن تنها یک دیکشنری می باشد، نه چیز پیچیده تر دیگری. این به صورت context برای قطعه ی template استفاده شده است: def books_for_author(author): books = Book.objects.filter(authors__id=author.id) return {'books': books} در قدم بعدی، template ای با استفاده جهت ارائه ی خروجی تگ ساخته می شود. مثال زیر، template ای بسیار ساده می باشد: <ul> {% for book in books %} </li> {% endfor %} </ul> در پایان، تگ inclusion با استفاده از فراخوانی متد inclusion_tag() در یک شیء Library ساخته و معرفی می شود. مثال زیر، در صورتیکه template قبلی در یک فایل با نام book_snipper.html باشد، به صورت زیر معرفی می شود: register.inclusion_tag('book_snippet.html')(books_for_author) همچنین در پایتون 2.4 به بالا می توان به شکل زیر نیز کار کرد: @register.inclusion_tag('book_snippet.html') def books_for_author(author): # ... گاهی اوقات، تگ های inclusion شما نیاز به دسترسی به مقادیری از context مربوط به template پدر دارد. برای حل این موضوع، جنگو یک انتخاب takes_context را برای تگ ها inclusion ارائه کرده است. در صورتیکه در ساختن یک تگ inclusion از takes_context استفاده کنید، تگ آرگومان های الزامی نخواهد داشت، و تابع زیرین پایتون یک آرگومان خواهد داشت: template context تا زمانیکه تگ فراخوانی شده بود. برای مثال، فرض کنید شما یک تگ inclusion می نویسید که همواره در یک context که حاوی متغیرهای home_link و home_title می باشد استفاده خواهد شد که به صفحه ی اصلی اشاره می کند. تابع پایتون آن به شکل زیر خواهد بود: @register.inclusion_tag('link.html', takes_context=True) def jump_link(context): return { 'link': context['home_link'], 'title': context['home_title'], } (توجه داشته باشید که اولین پارامتر باید context نامیده شود.) template مورد نظر یعنی link.html باید حاوی کد زیر باشد: Jump directly to <a href="{{ link }}">{{ title }}</a>. سپس، هر زمان که بخواهید از آن تگ سفارشی استفاده کنید، کتابخانه ی آن را بارگذاری کرده و آن را بدون هیچ آرگومانی فراخوانی کنید: {% jump_link %} نوشتن Template Loader های سفارشی template loader های داخلی جنگو (که در بخش "داخل بارگذاری Template" توضیح داده شد) معمولا تمام احتیاجات بارگذاری template های شما را پوشش می دهند، ولی در صورتی که نیاز به منطق ویژه ی بارگذاری باشد، نوشتن Template loader برای خودتان واقعا ساده می باشد. برای مثال، می توان template ها را از یک پایگاه داده، یا به طور مستقیم از یک Subversion repository و یا (همانطور که کمی بعد توضیح داده خواهد شد) از یک ZIP archive بارگذاری کرد. انتظار می رود یک template loader (هر آیتم در تنظیم TEMPLATE_LOADERS می باشد) یک شیء قابل فراخوانی با رابط زیر باشد: load_template_source(template_name, template_dirs=None) آرگومان template_name نام template برای بارگذاری می باشد (همانطور که به loader.get_template() یا loader.select_template() ارسال شده است)، و template_dirs یک لیست اختیاری از دیکشنری ها برای جستجو به جای TEMPLATE_DIRS است. در صورتیکه یک loader قادر به بارگذاری یک template به طور موفقیت آمیز باشد، باید یک تاپل بر گرداند: (template_source، template_path). در اینجا template_source رشته ی template ای است که از طریق موتور template کامپایل خواهد شد، و template_path مسیر template ای است که از آن بارگذاری شده است. این مسیر ممکن است برای اهداف اشکال زدایی برای نشان داده شود، بنابراین باید جایی که template از آن بارگذاری شده است را به سرعت شناسایی کند. در صورتیکه loader برای بارگذاری یک template ناتوان باشد، خطای django.template.TemplateDoesNotExist ایجاد خواهد شد. همچنین هر تابع loader باید یک attribute تابع is_usable داشته باشد. این attribute یک Boolean می باشد که به موتور template این موضوع را که آیا این loader در نصب پایتون فعلی در دسترس است یا خیر را اطلاع می دهد. برای مثال egg های loader (که قابلیت بارگذاری template ها از egg های پایتون را دارند) در صورتیکه ماژول pkg_resources نصب نشده باشد is_usable را False قرار می دهند، زیرا pkg_resource جهت خواندن داده از egg ها ضروری می باشد. یک مثال برای روشن کردن موضوع می تواند موثر باشد. در اینجا تابع template loader که می تواند template ها را از یک فایل ZIP بارگذاری کند وجود دارد. مثال زیر به جای TEMPLATE_DIRS به صورت یک مسیر جستجو، یک تنظیم سفارشی با نام TEMPLATE_ZIP_FILES را استفاده می کند، و انتظار دارد که هر آیتم در آن مسیر یک فایل ZIP حاوی template هایی باشد: from django.conf import settings from django.template import TemplateDoesNotExist import zipfile def load_template_source(template_name, template_dirs=None): "Template loader that loads templates from a ZIP file." template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", []) # Try each ZIP file in TEMPLATE_ZIP_FILES. for fname in template_zipfiles: try: z = zipfile.ZipFile(fname) source = z.read(template_name) except (IOError, KeyError): continue z.close() # We found a template, so return the source. template_path = "%s:%s" % (fname, template_name) return (source, template_path) # If we reach here, the template couldn't be loaded raise TemplateDoesNotExist(template_name) # This loader is always usable (since zipfile is included with Python) load_template_source.is_usable = True تنها قدم کج در صورتیکه بخواهیم از این loader برای اضافه کردن آن به تنظیم TEMPLATE_LOADERS استفاده کنیم. در صورتیکه کد فوق را در یک پکیج به نام mysite.zip_loader قرار دهیم، سپس mysite.zip_loader.load_template_source را به TEMPLATE_LOADERS اضافه کنیم. پیکربندی Template System در حالت مستقل نکته این بخش برای افرادی که در تلاش برای استفاده از template system به صورت یک جزء خروجی در برنامه ی دیگر هستند جالب است. در صورتیکه از template system به صورت بخشی از یک برنامه ی جنگو استفاده می کنید، اطلاعات ارائه شده در اینجا برای شما بکار نخواهد رفت. به طور عادی، جنگو تمام اطلاعات پیکربندی ای مورد نیاز را از فایل پیکربندی پیشفرض خود بارگذاری می کند، ترکیب شده با تنظیمات داده شده در متغیر محیطی DJANGO_SETTINGS_MODULE. (این موضوع در آموزش template جنگو توضیح داده شده است.) ولی در صورتیکه template system را مستقل از باقی جنگو می خواهید استفاده کنید، روش متغیر محیطی خیلی مناسب نمی باشد، زیرا شاید بخواهید template system را با باقی برنامه ی خود به جای سر و کار داشتن با تنظیم فایل ها و اشاره به آن ها از طریق متغیر محیطی پیکربندی کنید. برای حل این مشکل، نیاز است امکان پیکربندی دستی را استفاده کنید. به طور خلاصه، نیاز است قسمت های مناسب template systm را import کرده و سپس، قبل از فراخوانی هر تابع template ای، django.conf.settings.configure() را با هر تنظیمی که می خواهید تعیین کنید فراخوانی کنید. خسته نباشید دوستان عزیز . جلسه نهم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . ترجمه از سایت www.djangobook.com
  9. با سلام برای پیدا کردن تمام اعداد به صورت نماد علمی با استفاده از ماژول re در پایتون این را پیدا کرده ام. کسی میدونه که نشانه های داخل پرانتز چه معنی ای میدهند؟ re.findall(\d + \.\d + [Ee]?[+-]?\d+ )
  10. View ها و URLconf های پیشرفته در جنگو ( جلسه هشتم ) در آموزش view و urlconf جنگو ، اصول اولیه توابع view و URLconf ها توضیح داده شد. این فصل به جزئیان بیشتری درباره ی عمکرد پیشرفته ی این دو مبحث درون فریم ورک جنگو (Django) خواهد پرداخت. فوت و فن URLconf نکته ی ویژه ای درباره ی URLconf ها وجود ندارد – همانند جنگو، همه چیز فقط کد پایتون می باشد. می توان از این موضوع در روش های مختلفی بهره جست، همانطور که در بخش های این فصل توضیح داده شده است. ساده کردن import توابع URLconf زیر را مشاهده کنید، که در آموزش view و urlconf جنگو ساخته شده است: from django.conf.urls.defaults import * from mysite.views import hello, current_datetime, hours_ahead urlpatterns = patterns('', (r'^hello/$', hello), (r'^time/$', current_datetime), (r'^time/plus/(\d{1,2})/$', hours_ahead), ) همانطور که در آموزش view و urlconf جنگو توضیح داده شده است، هر آیتم در URLconf شامل تابع view همراه خود می باشد، که به طور مستقیم به صورت شیء تابع، ارسال شده است. این بدین معناست که import کردن توابع view در بالای ماژول امری ضروری می باشد. ولی همانطور که یک برنامه ی جنگو به سمت پیچیدگی رشد می کند، URLconf آن نیز رشد می کند، از این رو استفاده از روش import قبلی می تواند کمی خسته کننده باشد. (برای هر تابع view جدید، باید import کردن آن را نیز فراموش نکرد، و عبارت import در صورتیکه از این روش استفاده کنید بیش از حد بلند می باشد.) این امکان وجود دارد که روش قبلی را با import کردن خود ماژول views دُر بزنید. مثال URLconf زیر برابر با مثال قبلی می باشد: from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^hello/$', views.hello), (r'^time/$', views.current_datetime), (r'^time/plus/(d{1,2})/$', views.hours_ahead), ) جنگو روش دیگری را نیز جهت تعیین تابع view برای یک الگوی خاص در URLconf ارائه می کند: می توان یک رشته حاوی نام ماژول و نام تابع به جای خود شیء تابع ارسال کرد: from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^hello/$', 'mysite.views.hello'), (r'^time/$', 'mysite.views.current_datetime'), (r'^time/plus/(d{1,2})/$', 'mysite.views.hours_ahead'), ) (به علامت تک کتیشن دور نام view ها توجه کنید. در کد فوق از 'mysite.views.current_datetime' با علامت کتیشن به جای mysite.views.current_datetime استفاده شده است.) با استفاده از این تکنیک، import کردن توابع view دیگر ضروری نمی باشد؛ جنگو به طور خودکار با استفاده از رشته ی مشخص شده با نام و مسیر تابع view، تابع view مناسب را import می کند. یک میانبر دیگر که می توان هنگام استفاده از تکنیک رشته از آن استفاده کرد، از قلم انداختن پیشوند view می باشد. در مثال URLconf، هر رشته ی view با 'mysite.views' شروع شده است، که یک حالت اضافی می باشد. می توان پیشوند مشترک این رشته را حذف کرده و آن را به عنوان اولین آرگومان patterns() مانند زیر استفاده کرد: from django.conf.urls.defaults import * urlpatterns = patterns('mysite.views', (r'^hello/$', 'hello'), (r'^time/$', 'current_datetime'), (r'^time/plus/(d{1,2})/$', 'hours_ahead'), ) توجه داشته باشید که نقطه ی عقبی را در پیشوند و رشته های view قرار ندهید. جنگو این نقطه ها را به صورت خودکار قرار خواهد داد. با در نظر گرفتن دو روش فوق، کدام روش بهتر است؟ این بستگی به نحوه ی کد زدن شما و نیازهای کد دارد. مزایای روش رشته: *** این روش بسیار جمع و جور می باشد، زیرا در این روش دیگر نیازی به import کردن توابع view نمی باشد. *** این روش خواناتر بوده و قابلیت مدیریت URLconf ها در صورتیکه توابع view درون چندین ماژول مختلف پخش شده باشد آسان تر خواهد بود. مزایای روش شیء تابع: *** در این روش دسته بندی توابع view آسان می باشد. بخش "دسته بندی توابع view" که در این فصل می باشد را مطالعه کنید. *** این روش پایتونی تر می باشد – چرا که این روش بیشتر در راستای سنت های پایتون می باشد، مانند ارسال توابع به صورت شیء. هر دو روش معتبر می باشند، و می توان حتی این دو روش را درون یک URLconf یکسان با یکدیگر ترکیب کرد. انتخاب با خود شماست. استفاده از پیشوند چندگانه ی view در عمل، در صورتیکه از تکنیک رشته استفاده می کنید، ممکن است هنگامی که URLconf دارای پیشوند مشترک نیست ترکیب view ها را پایان دهید. البته در این حالت نیز می توان هنوز از میانبر پیشوند view برای حذف موارد تکراری سود برد. تنها کافیست از شیء های چندگانه ی patterns() به صورت زیر استفاده کنیم: کد قدیمی: from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^hello/$', 'mysite.views.hello'), (r'^time/$', 'mysite.views.current_datetime'), (r'^time/plus/(\d{1,2})/$', 'mysite.views.hours_ahead'), (r'^tag/(\w )/$', 'weblog.views.tag'), ) کد جدید: from django.conf.urls.defaults import * urlpatterns = patterns('mysite.views', (r'^hello/$', 'hello'), (r'^time/$', 'current_datetime'), (r'^time/plus/(\d{1,2})/$', 'hours_ahead'), ) urlpatterns = patterns('weblog.views', (r'^tag/(\w )/$', 'tag'), ) تمام چهارچوب کار این است که یک متغیر در سطح ماژول با نام urlpatterns وجود داشته باشد. این متغیر می تواند به صورت پویا ساخته شده باشد، همانطور که در مثال فوق انجام داده شده است. به طور خاص باید اشاره کرد که شیء های برگردانده شده با patterns() می توانند به یکدیگر اضافه شده باشند، که این موضوعی است که امکان دارد انتظار آن را نداشته باشید. پوشش خاص URL ها در حالت Debug بعد از صحبت درباره ی ساختن urlpatterns به طور پویا، ممکن است بخواهید از این تکنیک برای تغییر رفتار URLconf ها در حالت debug جنگو استفاده کنید. برای انجام این کار، تنها کافیست مقدار تنظیم DEBUG را در زمان اجرا بررسی کنید، مانند زیر: from django.conf import settings from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^$', views.homepage), (r'^(\d{4})/([a-z]{3})/$', views.archive_month), ) if settings.DEBUG: urlpatterns = patterns('', (r'^debuginfo/$', views.debug), ) در این مثال، آدرس /debuginfo/ تنها در صورتیکه مقدار تنظیم DEBUG، True باشد در دسترس خواهد بود. استفاده از Named Groups در تمام مثال های URLconf ای که تاکنون در این کتاب آورده شده است، از regular expression های ساده ی بدون نام استفاده شده است – یعنی اینکه، تنها آن قسمت از URL مورد نظر درون پرانتز قرار داده شده است، و جنگو آن متن داخل پرانتز را به تابع view به صورت یک آرگومان موضعی (positional argument) ارسال می کرد. برای کاربرد پیشرفته تر، این امکان وجود دارد که از گروه های regular expression نام گذاری شده، برای ارسال قسمت هایی از URL، به صورت آرگومان های کیورد به view استفاده کرد. آرگومان های کیورد در مقابل آرگومان های موضعی یک تابع پایتون می تواند با استفاده از آرگومان های کیورد یا آرگومان های موضعی فراخوانی شود – و در برخی موارد، با استفاده از هردوی آن ها در یک زمان. در یک فراخوانی با استفاده از آرگومان کیورد، شما نام هایی را، برای آرگومان های به همراه مقدارهایی که ارسال خواهد شد، تعیین می کنید. در یک فراخوانی با استفاده آرگومان موضعی، شما به سادگی آرگومان هایی را بدون تعیین صریح مقدار برای آن ارسال می کنید؛ مقدار آن آرگومان به ترتیب قرار گیری آرگومان ها بستگی دارد. برای مثال تابع ساده ی زیر را ملاحظه کنید: def sell(item, price, quantity): print "Selling %s unit(s) of %s at %s" % (quantity, item, price) جهت فراخوانی تابع فوق با آرگومان های موضعی، شما آرگومان ها را به ترتیبی که در تعریف تابع چیده شده اند قرار می دهید: sell('Socks', '$2.50', 6) جهت فراخوانی آن با آرگومان های کیورد، شما نام های آرگومان ها را به همراه مقادیر برای آنها تعیین می کنید. عبارات زیر با هم برابر می باشند: sell(item='Socks', price='$2.50', quantity=6) sell(item='Socks', quantity=6, price='$2.50') sell(price='$2.50', item='Socks', quantity=6) sell(price='$2.50', quantity=6, item='Socks') sell(quantity=6, item='Socks', price='$2.50') sell(quantity=6, price='$2.50', item='Socks') در پایان، تا زمانی که تمام آرگومان های موضعی قبل از آرگومان های کیورد قرار بگیرند می توان آرگومان های کیورد و موضعی را ترکیب کرد،: sell('Socks', '$2.50', quantity=6) sell('Socks', price='$2.50', quantity=6) sell('Socks', quantity=6, price='$2.50') در regular expression های پایتون، نحوه ی کد نویسی برای گروه های نام گذاری شده ی regular expression به این شکل می باشد: (?P<name>pattern)، که name نام گروه و pattern الگوی تطبیق داده شده می باشد. مثال زیر URLconf ای می باشد که از گروه های بدون نام استفاده کرده است: from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^articles/(\d{4})/$', views.year_archive), (r'^articles/(\d{4})/(\d{2})/$', views.month_archive), ) مثال زیر URLconf همسان با مثال قبلی می باشد که با استفاده از گروه های نام گذاری شده باز نویسی شده است: from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^articles/(?P<year>\d{4})/$', views.year_archive), (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive), ) کد فوق دقیقا همان کاری را که در مثال قبلی آمد انجام می دهد، تنها با یک تفاوت: مقادیر داخل پرانتز به جای آرگومان های موضعی به صورت آرگومان های کیورد به توابع view ارسال می شوند. به عنوان مثال، برای گروه های بدون نام، یک درخواست به /articles/2006/03/ در یک فراخوانی تابع برابر با کد زیر خواهد بود: month_archive(request, '2006', '03') برای گروه های نام گذاری شده، فراخوانی تابع به صورت زیر خواهد بود: month_archive(request, year='2006', month='03') در عمل، استفاده از گروه های نام گذاری شده، URLconf ها را کمی واضح تر می کند و آن ها را کمتر در معرض خطاهای ترتیب آرگومان قرار می دهد – و می توان با استفاده از گروه های نام گذاری شده ترتیب آرگومان ها را در تعریف توابع view تغییر داد. در مثال قبلی، در صورت استفاده از گروه های بدون نام اگر می خواستیم آدرس ها را طوری تغییر دهیم که ماه قبل از سال قرار بگیرد، باید این تغییرات را درون تابع month_archive نیز اعمال می کردیم. ولی در صورت استفاده از گروه های نام گذاری شده، تغییر دادن ترتیب پارامتر های داخل پرانتز در regular expression درون URL هیچ تاثیری در view نخواهد داشت. البته،در کنار مزایای گفته شده برای گروه های نام گذاری شده، این روش با هزینه هایی در اختصار همراه است، برخی توسعه دهندگان، گروه های نام گذاری شده را، کد اضافه و بد منظر تشخیص می دهند. ولی همچنان مزیت دیگر گروه های نامگذاری شده خوانایی آن ها می باشد، به ویژه برای کسانی که با regular expression آشنایی نزدیکی در برنامه های جنگو دارند. با یک نگاه در یک URLconf ای که از گروه های نامگذاری شده استفاده کرده است به سادگی می توان آنچه را که اتفاق افتاده است را تشخیص داد. فهمیدن الگوریتم Matchin/Grouping نکته ی مهمی که در استفاده از گروه های نامگذاری شده قابل اهمیت می باشد این است که، یک الگوی URLconf به تنهایی، نمی تواند حاوی گروه های بدون نام و گروه های نام گذاری شده به طور همزمان باشد. در صورتیکه همچین حالتی پیش آید، جنگو هیچ خطایی ایجاد نخواهد کرد، ولی ممکن است URL های شما را، آن طور که انتظار آن را دارید تشخیص ندهد. به طور خاص، در اینجا الگوریتم تجزیه کننده ی URLconf، با توجه به گروه های نام گذاری شده در مقابل گروه های بدون نام در یک regular expression ذکر شده است. *** در صورتیکه هر آرگومان نام گذاری شده ای وجود داشته باشد، از آن ها استفاده خواهد شد و آرگومان های بدون نام نادیده گرفته می شوند. *** در غیر اینصورت، تمام آرگومان های بدون نام به صورت آرگومان های موضعی ارسال می شوند. *** در هر دو مورد، option هایی اضافه به صورت آرگومان های کیورد ارسال می شود. برای اطلاعات بیشتر بخش بعدی را مطالعه کنید. ارسال Option های اضافی به توابع view گاهی اوقات متوجه این موضوع می شوید که توابع view نوشته شده، با وجود اندکی تفاوت، کاملا مشابه با یکدیگر می باشند. برای مثال، فرض می کنیم دو تابع view وجود دارد که بجز template هایی که مورد استفاده قرار می دهند حاوی محتویات یکسان می باشند: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^foo/$', views.foo_view), (r'^bar/$', views.bar_view), ) # views.py from django.shortcuts import render_to_response from mysite.models import MyModel def foo_view(request): m_list = MyModel.objects.filter(is_new=True) return render_to_response('template1.html', {'m_list': m_list}) def bar_view(request): m_list = MyModel.objects.filter(is_new=True) return render_to_response('template2.html', {'m_list': m_list}) در کد فوق تقریبا همه چیز تکرار شده است و این نحوه ی کد زنی به هیچ وجه روش زیبایی به نظر نمی رسد. در ابتدا، ممکن است تصور کنید که با استفاده از یک view برای هر دو URL می توان کدهای اضافه را حذف کرد، و URL ها را در URLconf داخل پرانتز قرار داد، و در آخر نیز آن ها را درون view گفته شده مانند زیر بررسی کنیم: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^(foo)/$', views.foobar_view), (r'^(bar)/$', views.foobar_view), ) # views.py from django.shortcuts import render_to_response from mysite.models import MyModel def foobar_view(request, url): m_list = MyModel.objects.filter(is_new=True) if url == 'foo': template_name = 'template1.html' elif url == 'bar': template_name = 'template2.html' return render_to_response(template_name, {'m_list': m_list}) مشکل راهکار فوق این است که، اگرچه URL های شما با کد شما جفت شده است، ولی در صورتیکه تصمیم بگیرید /foo/ را به /fooey/ تغییر نام دهید، شما نباید فراموش کنید که کد view را نیز تغییر دهید. راهکار زیبا برای این مورد استفاده از یک پارامتر اختیاری برای URLconf می باشد. هر الگویی در یک URLconf ممکن است شامل یک آیتم سوم باشد: یک دیکشنری از آرگومان های کیورد برای ارسال به تابع view. با در نظر گرفتن این، می توان مثال قبلی را به این شکل باز نویسی کرد: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}), (r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}), ) # views.py from django.shortcuts import render_to_response from mysite.models import MyModel def foobar_view(request, template_name): m_list = MyModel.objects.filter(is_new=True) return render_to_response(template_name, {'m_list': m_list}) همانطور که مشاهده می کنید، URLconf در مثال فوق، template_name را درون URLconf تعیین کرده است. تابع view مانند پارامترهای دیگر با template_name رفتار می کند. تکنیک option های اضافه ی URLconf روش مناسبی برای ارسال اطلاعات اضافی برای توابع view با حداقل کد می باشد. به همین دلیل، در بسیاری از برنامه های جنگو از آن استفاده می شود، مهمترین خصوصیت این روش حالت generic داشتن view ها می باشد، که در view های generic درباره ی آن صحبت خواهد شد. بخش های بعدی حاوی تعدادی از ایده ها، نسبت به نحوه ی استفاده از تکنیک option های اضافه ی URLconf در پروژه ها می باشد. Faking Captured URLconf Values فرض را بر این بگیرید که مجموعه ای از view ها دارید که از یک الگو طبعیت می کنند، البته به همراه URL ای دیگر که با الگو سازگار نبوده ولی منطق view آن یکی است. در این مورد، می توان مقدار داخل پرانتز URL را، با استفاده از option های اضافه ی URLconf، برای کنترل URL اضافه با view همسان جا زد. برای مثال، ممکن است برنامه ای داشته باشید که برخی داده ها برای یک روز خاص را نمایش دهند، با URL هایی مانند زیر: /mydata/jan/01/ /mydata/jan/02/ /mydata/jan/03/ # ... /mydata/dec/30/ /mydata/dec/31/ به کار بردن URL های فوق به اندازه ی کافی ساده می باشد – می توان آن ها را درون یک URLconf مانند زیر کنترل کرد (با استفاده از گروه نام گذاری شده): urlpatterns = patterns('', (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view), ) و یک تابع view مانند زیر: def my_view(request, month, day): # .... روش فوق بسیار ساده می باشد – چیزی وجود ندارد که قبلا گفته نشده باشد. تکنیک و فن خاص زمانی پیش می آید، که بخواهید URL دیگری اضافه کنید که از my_view استفاده می کند، ولی حاوی month و day نباشد. برای مثال، ممکن است بخواهید URL دیگری مانند /mydata/birthday/ اضافه کنید، که برابر با /mydata/jan/06/ باشد. می توان از امکان option های اضافه URLconf مانند زیر استفاده کرد: urlpatterns = patterns('', (r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}), (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view), ) نکته ی جالب در کد فوق این است که شما هیچوقت مجبور به تغییر تابع view نمی باشد. تابع view تنها پارامترهای month و day سر و کار دارد – مشکلی نیست اگر این پارامترها به صورت URL داخل پرانتز و یا پارامترهای اضافه ارسال شوند. ساختن یک View Generic حذف کد های تکراری یک تمرین مناسب برای برنامه نویسی می باشد. برای مثال، با این دو تابع پایتون: def say_hello(person_name): print 'Hello, %s' % person_name def say_goodbye(person_name): print 'Goodbye, %s' % person_name می توان hello و goodbye را حذف کرده و آنرا به عنوان پارامتر یک تابع قرار داد: def greet(person_name, greeting): print '%s, %s' % (greeting, person_name) می توان این فلسفه را در view های جنگو نیز با استفاده از پارامترهای اضافه ی URLconf به کار برد. با در نظر گرفتن این موضوع، می توان تصورات سطح بالایی را از view ها ایجاد نمود. بجای آنکه فرض کنیم "این view لیستی از شیء های Event را نمایش می دهد،" می توان در نظر گرفت که "آن view لیستی از شیء های BlogEntry را نمایش می دهد". البته به این موضوع دقت کنید که هر دوی آن ها مورد خاصی هستند "یک view لیستی از شیء ها را نمایش می دهد، که نوع شیء قابل تغییر می باشد." به عنوان مثال نگاهی به کد زیر بیاندازید: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^events/$', views.event_list), (r'^blog/entries/$', views.entry_list), ) # views.py from django.shortcuts import render_to_response from mysite.models import Event, BlogEntry def event_list(request): obj_list = Event.objects.all() return render_to_response('mysite/event_list.html', {'event_list': obj_list}) def entry_list(request): obj_list = BlogEntry.objects.all() return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list}) هر دو view فوق اساسا یک کار انجام می دهند: آن ها لیستی از شیء ها را نمایش می دهند. بنابراین اجازه دهید نوع شیءی را که نمایش می دهند را حذف کنیم: # urls.py from django.conf.urls.defaults import * from mysite import models, views urlpatterns = patterns('', (r'^events/$', views.object_list, {'model': models.Event}), (r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}), ) # views.py from django.shortcuts import render_to_response def object_list(request, model): obj_list = model.objects.all() template_name = 'mysite/%s_list.html' % model.__name__.lower() return render_to_response(template_name, {'object_list': obj_list}) با تغییرات کوچک فوق، ما به طور ناگهانی دارای یک view قابل استفاده ی مجدد و عمومی شده ایم! از حالا به بعد، در هر زمان که نیاز به یک view داشته باشیم که مجموعه ای از شیء ها را لیست کند، می توان به سادگی از object_list به جای نوشتن کد view استفاده کرد. در زیر توضیحاتی درباره ی کارهای انجام داده ی فوق وجود دارد: کلاس های مدل به طور مستقیم به صورت پارامتر model ارسال شده اند. پارامترهای ارسالی اضافه در URLconf می توانند نه تنها رشته ها، بلکه هر نوعی از شیء های پایتون را ارسال کنند. *** خط model.objects.all() یک مثال ساده از duck typing می باشد: "در صورتیکه چیزی مانند اردک راه برود و مانند اردک صحبت کند، می توان با آن مانند اردک رفتار کرد." دقت کنید که کد مورد نظر نمی داند، که نوع شیء model چه می باشد، تنها نکته ی لازم این است که model دارای یک attribute به نام objects باشد که آن نیز به نوبه ی خود دارای یک متد all() باشد. *** در تعیین نام template از model.__name__.lower() استفاده شده است. هر کلاس پایتون دارای یک attribute با نام __name__ می باشد که نام کلاس را بر می گرداند. این ویژگی برای زمان هایی مانند مثال فوق مفید می باشد، هنگامی که نام کلاس را تا زمان اجرای برنامه نمی دانیم. برای مثال مقدار __name__ برای کلاس BlogEntry رشته ی 'BlogEntry' می باشد. *** تفاوت جزئی بین این مثال و مثال قبلی این است که، متغیر generic ای با نام object_list به template ارسال شده است. می توان به سادگی نام این متغیر را به blogentry_list و یا even_list تغییر داد، ولی ما به عنوان تمرین برای خواننده آن را بدین صورت باقی گذاشته ایم. به دلیل آن که وب سایت های پایگاه داده محور، دارای چندین الگوی مشترک می باشند، جنگو مجموعه ای از "generic view" ها را که دقیقا از تکنیک فوق برای صرفه جویی در زمان استفاده می کنند، ارائه می کند. view های داخلی generic در view های generic توضیح داده شده اند. دادن Option های پیکربندی View در صورتیکه یک برنامه ی جنگو را توزیع (distribute) می کنید، این احتمال وجود دارد که کاربران شما برخی درجات پیکربندی را بخواهند. در این مورد، ایده ی خوبی است که برای هر انتخاب پیکربندی که فکر می کنید افراد ممکن است بخواهند آن را تغییر دهند، hook هایی را در view ها اضافه کنید. یک قسمت مشترک از یک برنامه برای ایجاد قابلیت پیکربندی، نام template می باشد: def my_view(request, template_name): var = do_something() return render_to_response(template_name, {'var': var}) فهمیدن اولویت مقدار داخل پرانتز در مقابل Option های اضافه هنگام وجود مغایرت، پارامترهای اضافه ی URLconf بر پارامترهای داخل پرانتز اولویت دارند. به عبارت دیگر، در صورتیکه URLconf شما، یک متغیر از نوع گروه نام گذاری شده درون پرانتز داشته باشد، و همچنین یک پارامتر اضافه ی URLconf را با متغیر همنام با آن، در این صورت مقدار پارامتر اضافه ی URLconf استفاده خواهد شد. برای مثال URLconf زیر را مشاهده کنید: from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^mydata/(?P<id>\d )/$', views.my_view, {'id': 3}), ) در کد فوق، هر دوی regular expression و دیکشنری اضافه، حاوی یک id می باشند که دیکشنری اضافه دارای اولویت می باشد. این بدین معناست که هر درخواستی (مانند /mydata/2/ یا /mydata/432432) بدین صورت رفتار خواهد کرد که مقدار id بدون در نظر گرفتن مقدار داخل پرانتز URLconf عدد 3 می باشد. خوانندگان زیرک در این مورد توجه خواهند داشت، که قرار دادن id داخل پرانتز در regular expression اتلاف وقت می باشد، زیرا مقدار آن همواره توسط مقدار دیکشنری باز نویسی می شود. درست آن است که؛ در دست ترجمه ... استفاده از آرگومان های پیشفرض View فوت و فن مناسب دیگر، تعیین پارامتر های پیشفرض برای آرگومان های view می باشد. با استفاده از این تکنیک در صورتیکه مقدار یک پارامتر اصلا تعیین نشود از مقدار پیشفرض تعیین شده استفاده می کند. مثال: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^blog/$', views.page), (r'^blog/page(?P<num>\d )/$', views.page), ) # views.py def page(request, num='1'): # Output the appropriate page of blog entries, according to num. # ... در کد فوق، هر دو الگوی URL به یک view اشاره می کنند – views.page – ولی اولین الگو درون URL هیچ مقداری داخل پرانتز قرار نداده است. در صورتیکه اولین الگو تطبیق پیدا کند، تابع page() از مقدار آرگومان پیشفرض num یعنی '1' استفاده خواهد کرد. همچنین در صورت تطبیق الگوی دوم، تابع page() از هر مقداری که توسط regular expression درون پرانتز حاصل می شود استفاده خواهد کرد. (توحه داشته باشید که بایستی مقدار پیشفرض آرگومان را رشته ی '1' قرار دهیم، نه یک integer. زیرا هر مقداری که داخل پرانتز URLconf برای num قرار گرفته است همواره یک رشته خواهد بود.) استفاده از این تکنیک، در رابطه با option های پیکربندی نیز متداول می باشد، همانطور که پیش تر توضیح داده شد. مثال زیر مثال بهبود پیدا کرده ی بخش "دادن option های پیکربندی view" با تهیه ی یک مقدار پیشفرض برای template_name می باشد: def my_view(request, template_name='mysite/my_view.html'): var = do_something() return render_to_response(template_name, {'var': var}) موارد خاص View ها گاهی اوقات شما الگویی در URLconf خواهید داشت که مجموعه ی بزرگی از URL ها را کنترل می کند، ولی شما تنها به یک مورد خاص از آن ها نیاز خواهید داشت. در این موارد، از روش خطی یک URLconf ای که پردازش شده است می توانید استفاده کنید و مورد خاص را اول قرار دهید. برای مثال، می توانید صفحات "add an object" در سایت مدیر جنگو را به صورت نمایش داده شده با یک URLpattern مانند زیر تصور کنید: urlpatterns = patterns('', # ... ('^([^/] )/([^/] )/add/$', views.add_stage), # ... ) کد فوق با URL هایی از قبیل /myblog/entries/add/ و /auth/groups/add/ تطبیق پیدا می کند. اگرچه صفحه ی "add" برای یک شیء user (/auth/user/add/) یک مورد خاص می باشد – تمام فیلدهای فرم را نمایش نمی دهد، بلکه دو فیلد رمز عبور و الی آخر را نمایش می دهد. ما این مشکل را به شکل زیر حل کرده ایم: def add_stage(request, app_label, model_name): if app_label == 'auth' and model_name == 'user': # do special-case code else: # do normal code ولی روش فوق برای یک دلیل که چندین بار در این فصل آن را لمس کرده ایم روش زیبایی نیست: روش فوق منطق URL را درون view قرار می دهد. راهکار بهتر این است که، از این واقعیت سود ببریم که URLconf ها از بالا به پایین پردازش می شوند: urlpatterns = patterns('', # ... ('^auth/user/add/$', views.user_add_stage), ('^([^/] )/([^/] )/add/$', views.add_stage), # ... ) با استفاده از روش فوق، درخواست برای /auth/user/add/ از طریق user_add_stage کنترل خواهد شد. اگرچه که URL با الگوی دوم مطابقت دارد، ولی ابتدا با الگوی بالاتر تطبیق پیدا می کند. پوشش دادن متن در URL ها هر آرگومان پوشش داده شده ای به صورت یک رشته ی یونیکد پایتون به view فرستاده می شود. به عنوان مثال در خط URLconf زیر: (r'^articles/(?P<year>\d{4})/$', views.year_archive), آرگومان year برای views.year_archive() یک رشته خواهد بود، نه یک integer، حتی اگر \d{4} تنها با رشته های از نوع integer مطابقت داشته باشد. بخاطر داشتن این موضوع هنگامی که کد view را می نویسید مهم می باشد. بسیاری از توابع داخلی پایتون نسبت به قبول کردن تنها شیء های از نوع مشخص حساس می باشند. یکی از خطاهای متداول ساختن یک شیء datetime.date با مقادیر رشته به جای مقادیر integer می باشد: >>> import datetime >>> datetime.date('1993', '7', '9') Traceback (most recent call last): ... TypeError: an integer is required >>> datetime.date(1993, 7, 9) datetime.date(1993, 7, 9) تبدیل شده ی به یک URLconf و view، خطایی مانند زیر خواهد بود: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', (r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive), ) # views.py import datetime def day_archive(request, year, month, day): # The following statement raises a TypeError! date = datetime.date(year, month, day) به جای day_archive() می توان به این شکل آنرا اصلاح نمود: def day_archive(request, year, month, day): date = datetime.date(int(year), int(month), int(day)) توجه داشته باشید که تابع int() خودش هنگامی که یک رشته ی غیر قابل تبدیل به integer به آن ارسال شود یک خطای ValueError ایجاد می کند، ولی از آن جهت که regular expression در URLconf در این مورد تنها رشته های قابل تبدیل به integer (مانند "245") را قبول می کند دیگر نگرانی برای این موضوع وجود نخواهد داشت. تعیین آنچه را که URLconf به دنبال آن جستجو می کند هنگامی که یک درخواست ارسال می شود، جنگو در تلاش بر می آید که الگوهای URLconf را برای URL درخواست شده به صورت رشته ی پایتون تطبیق دهد. این شامل پارامترهای GET یا POST و یا نام دامین نمی شود. همچنین شامل علامت (/) آغازین نیز نمی شود، زیرا هر URL دارای یک علامت (/) آغازین می باشد. برای عنوان مثال، در یک درخواست به http://www.example.com/myapp/، جنگو تلاش می کند تا آن را با myapp/ تطبیق دهد. در یک درخواست به exampl.com جنگو تلاش می کند تا آن را با myapp/ تطبیق دهد. method درخواست (مانند POST، GET) هنگام عبور کردن URLconf به حساب نمی آید. به عبارت دیگر، تمام method های درخواست درون تابع همسان برای URL همسان بررسی می شوند. اجرا کردن method های درخواست به عهده ی تابع view می باشد. تصور سطح بالا از توابع view صحبت کردن درباره ی روش branching based در method درخواست، اجازه دهید به نحوه ی ساخت یک روش زیبا از انجام این روش نگاهی بیاندازیم. لایه ی URLconf و view زیر را ملاحظه کنید: # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', # ... (r'^somepage/$', views.some_page), # ... ) # views.py from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response def some_page(request): if request.method == 'POST': do_something_for_post() return HttpResponseRedirect('/someurl/') elif request.method == 'GET': do_something_for_get() return render_to_response('page.html') else: raise Http404() در مثال فوق، some_page()، درخواست های POST و GET را کاملا متفاوت کنترل می کند. تنها چیزی که به صورت مشترک دارا می باشند یک URL به اشتراک گذاشته می باشد: /somepage/. به همین دلیل، اینکه با هر دوی POST و GET در یک تابع view سر و کار داشت، روش زیبایی نمی باشد. روش مناسب آن است که دو تابع view مجزا داشته باشیم – یکی درخواست های GET را کنترل کرده و دیگری درخواست های POST را – و مطمئن بود که هر یک تنها در زمان مناسب فراخوانی می شوند. می توان با توشتن یک تابع view که نماینده ی دیگر view ها می باشد، قبل و بعد اجرا برخی جملات منطقی این کار را انجام داد. در مثال زیر نحوه ی انجام این روش در some_page() وجود دارد: # views.py from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response def method_splitter(request, GET=None, POST=None): if request.method == 'GET' and GET is not None: return GET(request) elif request.method == 'POST' and POST is not None: return POST(request) raise Http404 def some_page_get(request): assert request.method == 'GET' do_something_for_get() return render_to_response('page.html') def some_page_post(request): assert request.method == 'POST' do_something_for_post() return HttpResponseRedirect('/someurl/') # urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns('', # ... (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}), # ... ) اجازه دهید کد فوق را مورد بررسی قرار دهیم: *** یک view جدید با نام method_splitter() نوشته شده است، که نماینده ی view های دیگر می باشد. این تابع دو آرگومان کیورد یعنی GET و POST که باید توابع view باشند را جستجو می کند. در صورتیکه request.method مقدارش 'GET' باشد، view مورد نظر یعنی GET را فراخوانی و در صورتیکه مقدار آن 'POST' باشد POST را فراخوانی می کند. همچنین در صورتیکه request.method چیزی غیر از موارد ذکر شده باشد (مانند HEAD و غیره ...) و یا در صورتیکه GET یا POST در تابع عرضه نشده باشند، سپس یک Http404 ایجاد خواهد شد. *** درون URLconf به /somepage/ در method_splitter() اشاره شده و آرگومان های اضافه به آن ارسال شده اند – توابع view به ترتیب برای استفاده از GET و POST. *** در پایان، تابع some_page() به دو تابع view تقسیم شده است – some_page_get() و some_page_post(). این روش بسیار زیباتر از نشان دادن تمام منطق درون یک view می باشد. توجه داشته باشید این توابع view از نظر فنی دیگر لازم نیست request.method را بررسی کنند، زیرا method_splitter() این کار را انجام می دهد. (در آن زمان که some_page_post() فراخوانی شده است، می توان مطئمن بود که مقدار request.method، 'post' می باشد.) حالا، ما دارای یک تابع generic خوب برای خودمان می باشیم که منطق نمایندگی دادن به یک view را با استفاده از request.method به اصطلاح encapsulate کرده است. هیچ چیزی درباره ی method_splitter() گره خورده به برنامه ی خاص نیست، بنابراین می توان در پروژه های دیگر نیز از آن استفاده کرد. یک روش برای بهبود method_splitter() وجود دارد. همانطور که نوشته شده است، این تابع فرض می کند که view های GET و POST آرگومان های غیر از request دریافت نمی کنند. چه می شود اگر بخواهیم از method_splitter یا view هایی که، پارامترهایی متنی را از URL ها می گیرند و یا آرگومان های اختیاری برای خود می گیرند استفاده کنیم؟ برای انجام چنین کاری، می توان از یک ویژگی زیبای پایتون استفاده کرد: آرگومان های متغیر با علامت ستاره. در ابتدا مثالی را نشان خواهیم داد و سپس آن را توضیح می دهیم: def method_splitter(request, *args, **kwargs): get_view = kwargs.pop('GET', None) post_view = kwargs.pop('POST', None) if request.method == 'GET' and get_view is not None: return get_view(request, *args, **kwargs) elif request.method == 'POST' and post_view is not None: return post_view(request, *args, **kwargs) raise Http404 در کد فوق، method_splitter() را تغییر داده ایم، به طوری که آرگومان های کیورد GET و POST در آن حذف شده اند و جای آن ها از *args و **kwargs (به علامت ستاره توجه کنید) استفاده شده است. این یک خصوصیت پویای پایتون می باشد که به یک تابع اجازه می دهد یک تعداد دلخواه از آرگومان ها را که نام آن ها تا زمان اجرا مشخص نخواهد بود قبول کند. در صورتیکه یک علامت ستاره تکی در تعریف یک تابع قبل یک پارامتر قرار دهید، هر آرگومان موضعی به آن تابع در یک تاپل جمع خواهد شد. در صورتیکه دو علامت ستاره قبل از یک پارامتر در تعریف تابع قرار دهید، هر آرگومان کیورد به آن تابع داخل یک دیکشنری جمع می شوند. def foo(*args, **kwargs): print "Positional arguments are:" print args print "Keyword arguments are:" print kwargs در زیر نحوه ی عملکرد آن مشخص شده است: >>> foo(1, 2, 3) Positional arguments are: (1, 2, 3) Keyword arguments are: {} >>> foo(1, 2, name='Adrian', framework='Django') Positional arguments are: (1, 2) Keyword arguments are: {'framework': 'Django', 'name': 'Adrian'} به method_splitter() باز می گردیم، می توانید مشاهده کنید که برای قبول کردن هر آرگومان به تابع و ارسال آنها به همراه view مناسب از *args و **kwargs استفاده کرده ایم. ولی بعد از انجام این کار، دو فراخوانی به kwargs.pop() برای بدست آوردن آرگومان های GET و POST در صورت دسترس ایجاد کرده ایم. (از pop() با یک مقدار پیشفرض None جهت دوری از خطای KeyError در صورتیکه یکی از آن ها تعریف نشده باشد استفاده شده است.) Wrapping View Functions آخرین فن view سود جستن از یک تکنیک پیشرفته ی پایتون می باشد. تصور کنید در سرتاسر view های مختلف متوجه دسته ای از کدهای تکراری شده اید مانند مثال زیر: def my_view1(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/') # ... return render_to_response('template1.html') def my_view2(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/') # ... return render_to_response('template2.html') def my_view3(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/') # ... return render_to_response('template3.html') در کد فوق، هر view با بررسی اینکه request.user تایید شده است یا خیر شروع می شود – کاربر فعلی با موفقیت وارد سایت شده است – و به مسیر /accounts/login/ تغییر مسیر داده می شود. (توجه داشته باشید که هنوز request.user توضیح داده نشده است – در بخش کاربران عضویت و session درباره ی این موضوع بحث شده است – ولی، همانطور که ممکن است تصور کنید، request.user کاربر فعلی را نمایش می دهد) بسیار زیبا می باشد اگر قسمت های تکراری کد موجود در هر کدام از view ها حذف شوند و تنها در صورت نیاز آن ها را علامت گذاری کرد. می توان این کار را با ساختن یک view wrapper انجام داد. چند لحظه ای کد زیر را مطالعه کنید: def requires_login(view): def new_view(request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/') return view(request, *args, **kwargs) return new_view تابع فوق، requires_login، یک تابع view (view) را دریافت می کند و یک تابع view جدید (new_view) بر می گرداند. تابع جدید، new_view داخل requires_login تعریف شده و منطق بررسی request.user.is_authenticated() و محول کردن view اصلی (view) را کنترل می کند. حالا می توان بررسی if not request.user.is_authenticated() از view ها حذف کرد و با requires_login در URLconf قرار داد: from django.conf.urls.defaults import * from mysite.views import requires_login, my_view1, my_view2, my_view3 urlpatterns = patterns('', (r'^view1/$', requires_login(my_view1)), (r'^view2/$', requires_login(my_view2)), (r'^view3/$', requires_login(my_view3)), ) کد فوق تاثیر یکسانی را به صورت قبل داراست، ولی با زوائد کد کمتر. حالا ما یک تابع generic زیبا ساخته ایم – requires_login() که می توان برای هر تابع view به منظور نیاز به ورود به سایت استفاده کرد. در بر داشتن URLconf های دیگر در صورتیکه قصد دارید کد شما در سایت های گوناگون ایجاد شده توسط جنگو استفاده شود، شما باید URLconf های خود را به روشی تنظیم کنید که "including" برای آن ها مقدور باشد. در هر نقطه، URLconf شما می تواند ماژول های دیگر URLconf را شامل شود. برای مثال، URLconf زیر حاوی URLconf های دیگری نیز می باشد: from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^weblog/', include('mysite.blog.urls')), (r'^photos/', include('mysite.photos.urls')), (r'^about/$', 'mysite.views.about'), ) (روش فوق را قبل تر در سایت مدیر مشاهده کرده اید، هنگامی که سایت مدیر جنگو معرفی شده است. سایت مدیر دارای URLconf خودش می باشد که شما تنها داخل URLconf دیگر آن ها را وارد کردید.) یک مفهوم مهم در اینجا وجود دارد: regular expression ها که در این مثال به یک include() اشاره می کنند، دارای علامت ($) نمی باشند. هر زمان که جنگو با include() برخورد می کند، هر بخش از URL مطابقت داده شده با آن نقطه را برش می دهد و رشته ی باقیمانده برای URLconf ای که Include شده را جهت پردازش بیشتر می فرستد. به مثال زیر توجه کنید: from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^(\d\d\d\d)/$', 'mysite.blog.views.year_detail'), (r'^(\d\d\d\d)/(\d\d)/$', 'mysite.blog.views.month_detail'), ) با این دو URLconf، در اینجا نحوه ی کنترل تعدادی از درخواست ها وجود دارند: *** /weblog/2007/: در اولین URLconf، الگوی r'weblog/' تطبیق داده می شود. زیرا آن یک include() است، جنگو تمام متن مطابق را در می آورد، که در این مورد 'weblog/' می باشد. پاقیمانده ی بخش URL 2007/ می باشد، که اولین خط در URlconf مورد نظر یعنی mysite.blog.urls تطبیق داده می شود. *** /weblog//2007/ (با دو علامت /) در اولین URLconf، الگوی r'weblog/' تطبیق داده می شود. زیرا یک include() می باشد، جنگو تمام متن مطابق را در می آورد، که در این مورد 'weblog/' می باشد. باقیمانده ی بخش URL /2007/ می باشد (با یک علامت / اولیه)، که هیچ خطی از URLconf مورد نظر یعنی mysite.blog.urls با آن تطبیق داده نمی شود. *** /about/: این URL در URLconf اول و view مورد نظر یعنی mystie.views.about تطبیق داده می شود، نشان می دهد که می توان الگوهای include() را با الگوهای non-include() ترکیب کرد. نحوه ی کار با include() پارامترهای پوشش داده شده یک URLconf شامل شده، هر پارامتر پوشش داده شده ای از URLconf های پدر را دریافت می کند، برای مثال: # root urls.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^(?P<username>\w )/blog/', include('foo.urls.blog')), ) # foo/urls/blog.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^$', 'foo.views.blog_index'), (r'^archive/$', 'foo.views.blog_archive'), ) در مثال فوق، متغیر پوشش داده شده ی username به URLconf شامل شده و هر تابع view موجود در آن URLconf ارسال شده است. توجه داشته باشید که پارامترهای پوشش داده شده همواره به هر خط در URLconf شامل شده صرف نظر از اینکه آیا view آن خط واقعا آن پارامتر های به صورت معتبر قبول خواهد کرد یا خیر ارسال شده خواهند بود. به همین دلیل، این تکنیک تنها در صورتیکه شما مطمئن باشید که هر view در URLconf شامل شده پارامترهای ارسال شده را قبول می کند مفید خواهد بود. نحوه option های اضافه URLconf در کار با include() به طور مشابه، می توان انتخاب های اضافه ی URLconf به include() ارسال کرد، همانطور که می توان option های اضافه ی URLconf را به یک view معمولی ارسال کرد – به صورت یک دیکشنری. هنگامی که شما این کار را انجام دهید، هر خط در URLconf شامل شده، option اضافه ی ارسال شده خواهد بود. برای مثال، دو دسته ی URLconf زیر دارای عملکرد یکسان می باشند. دسته ی اول: # urls.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^blog/', include('inner'), {'blogid': 3}), ) # inner.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^archive/$', 'mysite.views.archive'), (r'^about/$', 'mysite.views.about'), (r'^rss/$', 'mysite.views.rss'), ) دسته دوم : # urls.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^blog/', include('inner')), ) # inner.py from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^archive/$', 'mysite.views.archive', {'blogid': 3}), (r'^about/$', 'mysite.views.about', {'blogid': 3}), (r'^rss/$', 'mysite.views.rss', {'blogid': 3}), ) خسته نباشید دوستان عزیز . جلسه هشتم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . برگرفته از سایت oruji.org ترجمه از سایت www.djangobook.com
  11. این تاپیک پشتیبانی برای آموزش است آموزش ایجاد پنل مدیریت در جنگو ( جلسه ششم ) - بخش دوم 12/04/96 11:13 12/04/96 11:13 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  12. آموزش سایت مدیرت پیشفرض در جنگو ( بخش دوم ) صفحه ی فوق تمام کاربران موجود در پایگاه داده را نمایش می دهد، می توانید این را به صورت پرس وجو پایگاه داده یک نسخه ی زیبا شده از عبارت SELECT * FROM auth_user تصور کنید. در صورتیکه همراه با مثال های کتاب حرکت می کنید، تنها یک کاربر را مشاهده خواهید کرد، ولی هنگامی که کاربران بیشتری وجود داشته باشد، امکانات مفیدی از قبیل فیلتر کردن، چیدن و جستجوی بین آیتم ها را مشاهده خواهید کرد. امکان فیلتر کردن در سمت راست صفحه موجود می باشد، چیدمان از طریق کلیک کردن روی یک header ستون عملی می شود، و جعبه ی جستجو (search box) در قسمت بالا صفحه می باشد اجازه می دهد بر اساس نام کاربری به جستجو بپردازید. بر روی نام کاربری که ساخته اید کلیک کنید، فرم ویرایش برای کاربر را مشاهده خواهید کرد. صفحه ی فوق این اجازه را می دهد که خصوصیات کاربر را تغییر دهید، مانند نام و نام خانوادگی و حق دسترسی های گوناگون. (توجه داشته باشید که برای تغییر رمز عبور کاربر به جای ویرایش کد hash، می بایست بر روی "change password form" در زیر فیلد رمز عبور کلیک کرد.) نکته ی دیگر قابل توجه اینکه فیلدهای مختلف به شکل های مختلف پیاده سازی شده اند؛ به عنوان مثال، فیلد زمان و تاریخ دارای کنترل تقویم، فیلدهای Boolean دارای checkbox ها و فیلدهای حروف حاوی فیلد ساده ی مربوط به متن می باشند. می توان برای حذف کردن یک رکورد بر روی دکمه ی delete در قسمت پایین سمت چپ از فرم ویرایش کلیک کرد. بسته به شیء هایی که قصد حذف کردن آن را دارید، صفحه ی مربوط به تایید برای حذف رکورد نمایان خواهد شد، به عنوان مثال، در صورتیکه یک ناشر را حذف کنید، هر کتاب مربوط به ناشر نیز حذف خواهد شد! همچنین می توان با کلیک کردن بر روی "Add" درون ستون مناسب از صفحه ی خانگی مدیر یک رکورد را اضافه کرد. بعد از کلیک صفحه ای با فیلدهای خصوصیات خالی نمایان می شود که آماده برای پر شدن است. همچنین متوجه این موضوع خواهید شد که رابط مدیر همچنین معتبر بودن ورودی ها را برای شما کنترل می کند. یکی از فیلدهایی را که باید حتما پر شود را خالی بگذارید و یا یک مقدار نا معتبر درون یکی از فیلدها قرار دهید، سپس شما خطاهایی را هنگام ذخیره مانند شکل 5-6 مشاهده خواهید نمود. هنگامی که یک شیء موجود را ویرایش می کنید، متوجه یک لینک History در قسمت راست بالای صفحه خواهید شد. هر تغییری درون رابط مدیر اتفاق بیافتد در اینجا ثبت خواهد شد، و می توان با کیک کردن بر روی لینک History آن تغییر را بررسی کرد. اضافه کردن مدل ها به سایت مدیر یک بخش بسیار مهمی هنوز انجام نشده است. اجازه دهید مدل های خود را به سایت مدیر اضافه کنیم، بنابراین می توان شیء های درون جداول پایگاه داده ی مورد نظر را با این رابط عالی حذف، اضافه و تغییر داد. در این فصل نیز مثال books را دنبال خواهیم کرد که در آن سه مدل به نام های Publisher، َAuthor و book تعریف شده است. درون دایرکتوری books (mysite/books)، یک فایل با نام admin.py ایجاد کرده و کد زیر درون آن وارد کنید: from django.contrib import admin from mysite.books.models import Publisher, Author, Book admin.site.register(Publisher) admin.site.register(Author) admin.site.register(Book) کد فوق برای هر یک از مدل ها یک رابط به مدیر جنگو اضافه می کند. هنگامی که این کار انجام داده شد، به صفحه ی خانگی مدیر درون مرورگر خود رفته (http://127.0.0.1/admin/)، شما باید قسمت "Books" را با لینک های Authors، Books و Publishers مشاهده کنید. (برای موثر واقع شدن تغییرات می بایست سرور را متوقف کرده و دوباره اجرا runserver کنید) شما هم اکنون برای هر از این سه مدل یک رابط مدیر در حال کار خواهید داشت. بسیار ساده است! می توانید زمانی را برای اضافه کردن و تغییر رکوردها اختصاص دهید، تا داده هایی را درون پایگاه داده اضافه کنید. در صورتیکه مثال های آموزش مدل جنگو (ساختن شیء های Publisher) را دنبال کرده و آن ها را حذف نکرده باشید، آن رکوردهای ساخته شده در آموزش مدل جنگو را در صفحه ی لیست تغییرات publisher مشاهده خواهید کرد. یکی از خصوصیات با ارزشی که می توان در اینجا ذکر کرد، کنترل سایت مدیر برای کلیدهای خارجی و رابطه های چند به چند می باشد، هر دوی این حالت ها در مدل Book وجود دارند، برای یادآوری کد زیر مدل Book می باشد: class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title درون صفحه ی "Add book" مدیر (http://127.0.0.1:8000/admin/books/book/add/)، Publisher (یک ForeignKey) با یک جعبه ی انتخاب (select box) نمایش داده شده است، و فیلد Authors (یک ManyToManyField) با یک جعبه ی چند انتخابی (multiple-select box) نمایش داده شده است. هر دو فیلد فوق دارای یک علامت سبز رنگ جمع در جلوی خود می باشند که برای اضافه کردن رکوردهای مرتبط به آن ها می باشد. برای مثال، در صورتیکه شما بر روی آن علامت سبز رنگ جلوی فیلد "Publisher" کلیک کنید، یک پنجره ی pop-up برای اضافه کردن یک ناشر باز می باشد. بعد از آنکه یک ناشر در پنجره ی باز شده ساخته شود، صفحه ی "Add book" با جدید ترین ناشر ساخته شده به روزرسانی می شود. نحوه ی کار سایت مدیر در پشت صحنه، سایت مدیر چه طور کار می کند؟ نحوه ی کار آن بسیار ساده می باشد. هنگامی که جنگو URLconf را هنگام اجرای سرور در urls.py بارگذاری می کند، عبارت admin.autodiscover() که در درون این برای فعال کردن سایت مدیر قرار داده شده است اجرا می شود. این تابع درون تنظیم INSTALLED_APPS به دنبال فایلی به نام admin.py که درون هر یک از ایتم ها جستجو می کند. در صورتیکه admin.py درون یکی از app های داده شده وجود داشته باشد، کد درون آن فایل اجرا می شود. در فایل admin.py درون app مورد نظر یعنی books، فراخوانی هر admin.site.register() به سادگی مدل داده شده را درون سایت مدیر عضو می کند. سایت مدیر تنها یک رابط ویرایش/تغییر برای مدل های عضو شده را نمایش می دهد. همانطور که مشاهده شد، سایت مدیر به صورت خودکار Users و Groups را نمایش می دهد که درون app خود یعنی django.contrib.auth می باشند که این app شامل فایل admin.py خود و Users و Groups درون آن می باشد. app های دیگر django.contrib، مانند django.contrib.redirects، نیز خودشان را درون سایت مدیر اضافه می کنند. از این گذشته، سایت مدیر جنگو تنها یک application جنگو با مدل های خود، view ها و URLpattern ها می باشد. با استفاده از URLconf آن را به برنامه ی خود اضافه می کنید، تنها درون view ها خود از آن استفاده می کنید. می توان template ها، view ها و URLpattern های آن را با رفتن به مسیر django.contrib/admin مشاهده و بازرسی کنید ولی تحریک به تغییر چیزی به طور مستقیم در آنجا نشوید چرا که امکانات استفاده ی غیر مستقیم زیادی برای شما وجود دارد. ایجاد فیلدهای اختیاری بعد از آنکه کمی با سایت مدیر آشنا شده و از آن استفاده کردید، ممکن است متوجه محدودیت هایی شده باشید، مانند اینکه هر فیلد باید پر شده باشد، در حالیکه در بسیاری از موارد شما می خواهید پر کردن بعضی از فیلدها اختیاری باشد، به عنوان مثال، می خواهیم فیلد email مدل Author اختیاری باشد یعنی بتوان فیلد را خالی گذاشت. در دنیای واقعی، شما ممکن است هیچ آدرس الکترونیکی نداشته باشید. برای تعیین کردن فیلد email اختیاری، مدل Book را ویرایش می کنیم (این مدل درون فایل mysite/books/models.py می باشد.) برای این کار به سادگی blank=True را به مانند مثال زیر به فیلد email اضافه می کنیم: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True) حرکت فوق به جنگو می گوید که برای آدرس پست الکترونیک نویسندگان می توان مقدار خالی را نیز در نظر گرفت. به صورت پیش فرض، تمام فیلدها blank=False می باشند، بدین معنی که مقدار خالی را نمی توان برای آن ها در نظر گرفت. تا حالا، بجز متد __unicode__()، مدل های ما به صورت جداول پایگاه داده ما بکار می رفتند یعنی مدل ها عبارات پایتونی معادل عبارات CREATE TABE در SQL بودند. با اضافه کردن blank=True، ما شروع به گسترش دادن مدل فراتر از تعریف ساده ی مشابه با جدول پایگاه داده کرده ایم. حال، کلاس مدل در حال تبدیل شدن به یک مجموعه ی غنی از دانش درباره ی آنچه که شیء های Author هستند و می توانند انجام دهند، می باشد. نه تنها فیلد email یک ستون VARCHAR در پایگاه داده می باشد، بلکه همچنین می دانیم که یک فیلد اختیاری در سایت مدیر جنگو نیز می باشد. این حالت بدین معنی می باشد که فیلد مورد نظر اختیاری می باشد. حالا می توان نویسندگان را بدون نیاز به تهیه ی آدرس الکترونیک اضافه کرد؛ در صورتیکه فرم مورد نظر را با فیلد خالی آدرس الکترونیک Submit کنید دیگر هیچ پیام "This field is required" با رنگ قرمز مشاهده نخواهید کرد. هنگامی که blank=True اضافه شد، فرم ویرایش "Add author" را دوباره بارگذاری کنید (http://127.0.0.1:8000/admin/books/author/add/)، متوجه خواهید شد که نام فیلد "Email" دیگر به صورت تو پر و پر رنگ نمی باشد. ایجاد فیلد های از نوع تاریخ و عددی به صورت اختیاری نکته ی مهم در رابطه با blank=True این است که انجام این حرکت بر روی فیلدهای از نوع عددی و تاریخ نیازمند مقداری توضیح و پیش زمینه می باشد. SQL برای تعیین مقدار خالی دارای روش خاص خودش می باشد – یک مقدار ویژه به نام NULL. NULL می تواند به معنای ناشناخته، نامعتبر یا ... معنی شود. در SQL یک مقدار NULL با یک مقدار رشته ی خالی متفاوت می باشد، درست مثال شیء None در پایتون که با یک رشته ی خالی پایتون ("") متفاوت می باشد. این بدان معنی است که ممکن است یک فیلد حرفی (مانند یک ستون VARCHAR) به طور همزمان هم حاوی مقدار NULL و هم حاوی یک رشته ی خالی باشد. این موضوع می تواند موجب ابهام و گیجی نا خواسته شود. "چرا این رکورد دارای مقدار NULL ولی آن رکورد دیگر دارای یک رشته ی خالی است؟ ایا تفاوتی وجود دارد، یا فقط داده به صورت نا سازگار وارد شده است؟" و: "چطور می توان تمام رکوردهایی که دارای مقدار خالی می باشند را بدست آورد – ایا می توان هر دو رکوردهای NULL و رشته ی خالی را جستجو کرد، یا یک از آن ها را با مقدار رشته ی خالی انتخاب کرد؟" برای کمک به خلاص شدن از این قبیل ابهامات، جنگو به صورت خودکار عبارت های CREATE TABLE (که در آموزش مدل جنگو توضیح داده شده اند) اضافه و برای هر ستون مقدار NOT NULL را در نظر می گیرد. برای مثال، کد زیر عبارت تولید شده برای مدل Author در آموزش مدل جنگو می باشد: CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL ) ; در اغلب موارد، این حالت پیشفرض برای برنامه مطلوب بوده و شما را از درد سر های تناقض و نا هماهنگی های داده حفظ می کند. و برای بقیه ی قسمت های جنگو به خوبی کار می کند، مانند سایت مدیر جنگو، که در زمان خالی گذاشتن یک فیلد یک رشته ی خالی (نه مقدار NULL) وارد می کند. ولی برای نوع ستون هایی که رشته ی خالی را به عنوان مقدار معتبر قبول نمی کنند – ازقبیل تاریخ ها، زمان ها، و اعداد، یک استثناء وجود دارد. در صورتیکه سعی کنید یک رشته ی خالی برای ستون های ذکر شده وارد کنید، بسته به نوع پایگاه داده ی شما احتمالا خطای پایگاه داده رخ خواهد. در این حالت، NULL تنها راه برای تعیین مقدار خالی می باشد. در مدل های جنگو، می توان مقدار NULL را با اضافه کردن null=True به یک فیلد تعیین کرد. در صورتیکه بخواهید اجازه دهید مقادیر خالی در فیلد تاریخ (مانند DateField، TimeField، DateTimeField) یا فیلد عددی (مانند IntegerField، DecimalField، FloatField) قرار گیرند نیاز است از null=True و blank=True با هم استفاده کنید. اجازه دهید مدل Book را برای اینکه بتوان فیلد publication_date را خالی گذاشت تغییر دهیم: class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True) اضافه کردن null=True پیچیده تر از اضافه کردن blank=True می باشد، زیرا null=True از نظر معنایی پایگاه داده را تغییر می دهد – در این حالت عبارت CREATE TABLE تغییر می کند بدین شکل که NOT NULL از فیلد publication_date حذف می شود. برای کامل کردن این تغییر، نیاز است پایگاه داده به روز رسانی شود. به دلایلی، جنگو تلاشی برای تغییرات خودکار در شمای پایگاه داده نمی کند، بنابراین مسئولیت اجرای یک عبارت ALTER TABLE هر زمان که که تغییری در مدل ایجاد شد، به عهده خودتان می باشد. می توان از manage.py dbshell برای این منظور استفاده کرد. در کد زیر نحوه ی حذف NOT NULL در این مورد خاص را مشاهده می کنید: ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL; (توجه داشته باشید که کد فوق برای پایگاه داده ی PostgreSQL می باشد.) توضیح و بحث عمیق تر در مورد این تغییرات در آموزش مدل پیشرفته بحث شده است. به سایت مدیر بر می گردیم، اکنون فرم ویرایش "Add book" اجازه می دهد تا مقدار خالی را برای publication date قرار دهیم. سفارشی کردن نام فیلدها در فرم های ویرایش سایت مدیر، نام هر فیلد از روی نام مدل فیلد تولید می شود. الگوریتم آن بسیار ساده می باشد: جنگو تنها خط های تیره (_) را با فاصله ها جایگزین کرده و حروف اول را تبدیل به حروف بزرگ می کند، بنابراین، برای مثال، فیلد publication_date مدل Book دارای نام "Publication date" خواهد بود. اگر چه نام فیلدها درون مدل به طور زیبایی درون سایت مدیر نیز تغییر می کنند، ولی در برخی موارد ممکن است بخواهید آن نام ها را خودتان تعیین کنید. با استفاده از verbose_name می توان این کار را انجام داد. برای مثال، در زیر نحوه ی تغییر نام فیلد Author.email به "e-mail" نشان داده شده است: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name='e-mail') تغییر فوق را ایجاد کرده و صفحه را دوباره بارگذاری کنید، شما باید فیلد مورد نظر با نام جدید مشاهده کنید. توجه داشته باشید، در صورتیکه حرف اول نامی که انتخاب می کند حرف بزرگ نباشد جنگو به طور خودکار آن را تبدیل به حروف بزرگ می کند. در پایان، توجه داشته باشید که می توان verbos_name را به صوت یک آرگومان موضعی نیز ارسال کرد، کد زیر برابر با مثال قبلی می باشد: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField('e-mail', blank=True) حرکت فوق برای فیلدهای ManyToManyField و ForeignKey کار نخواهد کرد، زیرا در این فیلدها باید اولین آرگومان یک کلاس مدل باشد. در این موارد، می بایست از verbose_name به صورت واضح استفاده کرد. کلاس های ModelAdmin مرسوم نغییراتی که تاکنون اعمال شد – blank=True، null=True و verbose_name – تغییراتی در سطح مدل بوده اند، نه تغییرات در سطح مدیر. خیلی پیش می آید که تغییرات مورد نیاز سایت مدیر در یک بخش از مدل انجام پذیرد و استفاده شوند، هیچ چیز تعیین شده ای برای مدیر وجود ندارد. گذشته از این، سایت مدیر جنگو امکانات و اختیارات قدترمندی برای سفارشی کردن نحوه ی کار سایت مدیر برای مدل خاص ارائه می کند. سفارشی کردن لیست های تغییر اجازه دهید از طریق تعیین کردن فیلدها که درون لیست تغییر برای مدل Author قرار دارند وارد مسائل سفارشی سازی مدیر شویم. به طور پیشفرض، لیست تغییر نتیجه ی __unicode__() برای هر شیء را نمایش می دهد. در آموزش مدل جنگو، متد __uncode__() برای شیء های Author جهت نمایش نام و فامیلی با هم تعریف شد. class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name='e-mail') def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) در نتیجه لیست تغییر برای شیء های Author نام و نام خانوادگی را با هم نمایش می دهد، همانطور که در شکل 8-6 مشاهده می کنید. می توان این رفتار پیشفرض را با اضافه کردن تعدادی از فیلدها برای تغییر نمایش لیست بهبود بخشید. برای مثال، برای دیدن آدرس الکترونیک هر نویسنده در این لیست، و همچنین اضافه کردن امکان چیدمان بر اساس نام و نام خانوادگی می توان یک کلاس ModelAdmin برای مدل Author تعریف کرد. این کلاس کلید سفارشی سازی مدیر، و یکی از اولیه ترین چیزهایی است که اجازه می دهد لیست فیلدها را جهت نمایش در صفحه ی لیست تغییر تعیین کنید. فایل admin.py را جهت این تغییرات ویرایش کنید: from django.contrib import admin from mysite.books.models import Publisher, Author, Book class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book) در زیر مثال فوق، توضیح داده شده است: ** کلاس AuthorAdmin ساخته شده است. کلاس AuthorAdmin که از کلاس پدر خود یعنی django.admin.ModelAdmin ارث بری کرده است پیکربندی های تعیین شده برای مدل مدیر را نگه می دارد. تنها یک پیکربندی – list_display، انجام شده است که نام فیلدهایی را که قرار است درون لیست تغییر نمایش داده شوند، درون یک تاپل قرار گرفته اند. البته نام فیلدها باید در مدل وجود داشته باشند. ** فراخوانی admin.site.register با اضافه کردن AuthorAdmin بعد از Author تغییر کرده است که می توان آنرا بدین صورت خواند: "مدل Author با امکانات AuthorAdmin عضو شده است." تابع admin.site.register() کلاس فرزند ModelAdmin را به صورت اختیاری به عنوان دومین آرگومان دریافت می کند. در صورتیکه دومین آرگومان را تعیین نکنید (همانطور در مورد Publisher و Book صدق می کند)، جنگو از اختیار پیشفرض مدیر برای آن مدل استفاده می کند. با تغییرات فوق، لیست تغییر نویسنده را دوباره بارگذاری کنید، شما حالا در این صفحه سه ستون را مشاهده خواهید کرد – نام، نام خانوادگی و آدرس الکترونیک. علاوه بر آن، هر کدام از این ستون ها قابلیت چیدمان بر اساس سرتیتر ستون را با کلیک کردن رو آن خواهند داشت. در قدم بعدی اجازه دهید یک نوار جستجوی ساده اضافه کنیم. مانند زیر search_fields را به AuthorAdmin اضافه کنید: class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') search_fields = ('first_name', 'last_name') صفحه را درون مرورگر دوباره بارگذاری کنید، شما باید یک نوار جستجو را در قسمت بالای صفحه مشاهده کنید (شکل 10-6). در کد فوق تنها به صفحه ی تغییر گفته شده است که یک نوار جستجو برای جستجو بر حسب فیلدهای نام و نام خانوادگی ایجاد کند. همانطور که ممکن است کاربر انتظار داشته باشد، جستجو به حروف کوچک و بزرگ حساس نمی باشد و درون هر دو فیلد صورت می گیرد، بنابراین برای رشته ی "bar" هم نویسنده ای که نام وی Barney بوده را خواهد یافت و هم نویسنده ای که نام خانوادگی وی Hobarson می باشد را خواهد یافت. در قدم بعدی، اجازه دهید تعدادی فیلتر برای صفحه ی لیست تغییر مدل Book اضافه کنیم: from django.contrib import admin from mysite.books.models import Publisher, Author, Book class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') search_fields = ('first_name', 'last_name') class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) در کد فوق با آیتم های زیادی سر و کار داریم، در کد فوق یک کلاس BookAdmin از نوع ModelAdmin به طور جداگانه ساخته شده است. ابتدا، یک list_display تنها برای ایجاد لیست زیباتر تعریف شده است. سپس، از list_filter استفاده شده است، که فیلدهایی برای استفاده را درون تاپل قرار می دهد و در سمت راست صفحه ی تغییر لیست نمایش داده می شود. برای فیلدهای تاریخ، جنگو میانبرهایی را برای فیلتر لیست می کند "Today"، "Past 7 days"، "This month"و "This year". list_filter فقط محدود به DateField نمی باشد بلکه با داده های نوع دیگر نیز کار می کند. (سعی کنید با فیلدهای BooleanField و foreignKey هم کار کنید) فیلترها تا زمانیکه حداقل دو مقدار برای انتخاب فرم وجود داشته باشد نمایش داده می شوند. روش دیگربرای ارائه ی فیلترهای تاریخ استفاده از date_hierarcy مانند کد زیر می باشد: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' با اضافه کرد date_hierarchy صفحه ی لیست تغییر یک نوار تاریخ در بالای لیست ایجاد می شود، که در شکل 12-6 مشاهده می کنید، که با یک لیست از سال های در دسترس شروع می کند سپس می توان برای ماه حتی نمایان شدن روز بر روی آن کلیک کرد. توجه داشته باشید که date_hierarchy یک رشته دریافت می کند، نه یک تاپل، زیرا تنها یک فیلد تاریخ می تواند در آن استفاده شود. در پایان، اجازه دهید ترتیب چیدمان پیشفرض لیست را به طوری تغییر دهیم که کتاب ها در صفحه ی لیست تغییر همیشه بر اساس تاریخ به صورت نزولی قرار بگیرند. به طور پیشفرض، لیست تغییر شیء ها را بر اساس کلاس Meta مربوط به مدل مودر نظر چیده می شوند (که در آموزش مدل جنگو به آن اشاره شده است.) – ولی شما این مقدار مرتب کردن را تعیین نکرده اید، پس ترتیب تعریف نشده است. class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) این اختیار چیدمان مدیر دقیقا به صورت چیدمان در کلاس Meta کار می کند، با این تفاوت که آن تنها از اولین فیلد نام در لیست استفاده می کند. تنها یک لیست یا تاپل از نام فیلدها به آن ارسال کنید و یک علامت (-) در ابتدای فیلد برای چیدمان نزولی آن قرار دهید. صفحه ی لیست تغییر را برای مشاهده ی حرکت فوق دوباره بارگذاری کنید. توجه داشته باشید که تیتر "Publication date" حالا شامل یک فلش کوچک می باشد که به نحوه ی چیدمان اشاره می کند. اختیارات اصلی لیست تغییر در اینجا توضیح داده شد. با استفاده از این اختیارات می توانید با قدرت تر کار کنید، با استفاده از تنها چند خط می توانید از یک رابط آماده و پر قدرت استفاده کنید. سفارشی کردن فرم های ویرایش همانطور که لیست تغییر می تواند سفارشی شده باشد، فرم های ویرایش نیز می توانند با روش های مختلف سفارشی شوند. ابتدا اجازه دهید روش چیده شدن فیلدها را سفارشی کنیم. به طور پیشفرض، ترتیب فیلدها در یک فرم ویرایش مطابق با ترتیبی است که آنها در مدل تعریف شده اند. می توان با استفاده از اختیار fields در کلاس فرزند ModelAdmin آن را تغییر داد: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) fields = ('title', 'authors', 'publisher', 'publication_date') بعد از تغییر فوق، فرم ویرایش برای کتاب ها از ترتیب داده شده برای فیلدها استفاده خواهد کرد. این که نویسنده بعد از عنوان کتاب قرار بگیرد کمی طبیعی تر می باشد. البته، ترتیب فیلد باید بسته به به اطلاعات ورودی جریان کار باشد. هر فرم متفاوت است. نکته ی دیگر مفید در اختیار fields این است که اجازه می دهد بعضی فیلد ها را از قرار داشتن در فرم ویرایش کلا محروم کرد. تنها کافیست، فیلدهایی که نمی خواهید در فرم باشند را درون تاپل قرار ندهید. ممکن است از این امکان هنگامی که کاربران مدیر تنها برای ویرایش بعضی از بخش ها مورد اعتماد هستند استفاده کنید. برای مثال، در پایگاده داده ی کتاب ما، فیلد publication_date از حالت قابل ویرایش بودن خارج و مخفی شده است: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) fields = ('title', 'authors', 'publisher') نتیجتا، فرم ویرایش برای کتاب ها هیچ روشی برای تعیین publication date ارائه نمی کند. این حرکت می تواند در صورتیکه یک ویرایشگر ترجیح می دهد نویسندگانش تاریخ را عقب نزنند مفید باشد. هنگامی که یک کاربر از این فرم برای اضافه کردن یک کتاب جدید استفاده می کند، جنگو به آسانی مقدار publication_date را None در نظر می گیرد – بنابراین اطمینان حاصل کنید که فیلد مورد نظر دارای null=True می باشد. استفاده ی عمومی دیگر فرم ویرایش سفارشی برای کار با فیلدهای چند به چند می باشد. همانطور که در فرم ویرایش برای کتاب ها مشاهده کردید، سایت مدیر هر ManyToManyField را به صورت یک جعبه ی چند انتخابه نمایش می دهد، که منطقی ترین راه برای ورودی HTML برای استفاده می باشد – ولی استفاده از جعبه های چند انتخابه می تواند کمی سخت باشد. در صورتیکه که می خواهید چند ایتم را انتخاب کنید، باید برای انجام چنین کاری کلید control و یا در Mac کلید command را نگه پایین نگه داشته و آن ها را انتخاب کنید. سایت مدیر برای این مشکل یک توضیحی را در زیر این فیلد قرار داده است، ولی هنوز هم دارای مشکلاتی می باشد مخصوصا زمانی که فیلد شما حاوی صدها انتخاب باشد. راه حل سایت مدیر filter_horizontal می باشد. آنرا به BookAdmin اضافه کرده و نتیجه را مشاهده کنید: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',) فرم ویرایش برای کتاب ها را دوباره بارگذاری کنید، بخش "Authors" حالا از یک رابط جاوا اسکریپت کاراتر استفاده می کند که می توان به راحتی درون انتخاب ها جستجو کردن و هر آنچه را که درون "Available authors" انتخاب می کنید را به درون "Chosen authors" و بر عکس ارسال می کند. اکیدا توصیه می شود که برای فیلدهای ManyToManyField که بیشتر از 10 ایتم دارند از filter_horizontal استفاده کنید. روش فوق بسیار آسانتر از استفاده از جعبه ی چند انتخابه می باشد. توجه داشته باشید که همچنین می توان برای چندین فیلد نیز از filter_horizontal استفاده کرد – تنها کافیست نام هر کدام را درون تاپل قرار دهید. کلاس های ModelAdmin همچنین از filter_vertical که تنها در فیلدهای ManyToManyField و نه ForeignKey کار می کند نیز پشتیبانی می کنند، به طور پیشفرض، سایت مدیر برای فیلدهای ForeignKey به سادگی از <select> استفاده می کند، ولی همانطور که برای ManyToManyField نیز از <select> استفاده می شود، ممکن است شما مایل نباشید برای نمایش تمام شیء ها به صورت منوی باز شونده در پایین بار اضافی ای را متحمل شوید. برای مثال در صورتیکه پایگاه داده ی کتاب حاوی صدها ناشر شده باشد، فرم "Add book" مدتی برای بارگذاری زمان خواهد برد، زیرا باید تمام ناشران را برای نمایش در <select> بارگذاری کند. برای حل این مشکل می بایست از raw_id_fields استفاده کرد. نام فیلد ForeignKey را درون این تاپل قرار دهید، فیلدهای مذکور به جای <select> به صورت یک متن ساده نمایش داده می شوند (<input type="text") class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',) raw_id_fields = ('publisher',) درون جعبه ی ورودی چه چیزی وارد می کنید؟ ID مربوط به پایگاه داده ی ناشر. نمی توان به طور عادی ID هر شخص را به خاطر سپرد، به همین دلیل یک شکل ذره بین نیز در شکل فوق مشاهده می کنید که می توان با کلیک بر روی آن توسط یک پنجره ی pop-up ناشران را که می خواهید اضافه کنید. کاربران، گروه ها و حق دسترسی ها بدلیل اینکه با یک superuser وارد می شویم، می توان برای ساختن، ویرایش و حذف کردن هر شیء اقدام کرد. به طور طبیعی، محیط های مختلف نیازمند حق دسترسی های متفاوتی می باشد – بدین صورت نباید باشد که هر شخصی بتواند superuser باشد. سایت مدیر جنگو یک سیستم حق دسترسی را بکاربرده است که می توان برای دادن دسترسی به کاربران خاص برای بخش هایی ار رابط که مورد نیاز است استفاده کرد. همانطور که در اوایل فصل مشاهده کردیم می توان کاربران و حق دسترسی ها را از طریق رابط مدیر همانند دیگر شیء ها ویرایش کرد. شیء های کاربر دارای فیلد های استاندارد نام کاربری، رمز عبور، آدرس پست الکترونیک و نام واقعی به همراه یک مجموعه از فیلد ها که اختیارات کاربر را در درون رابط مدیر تعیین می کند، می باشد. در ابتدا، سه گزینه ی boolean وجود دارد: ** گزینه ی "active" کنترل می کند که ایا کاربر فعال است یا خیر. در صورتیکه این گزینه تیک نخورده باشد و کاربر سعی کند وارد شود، به وی اجازه ی ورود داده نمی شود حتی اگر رمز عبور وی معتبر باشد. ** گزینه ی "staff" کنترل می کند که ایا کاربر اجازه ی ورود به رابط مدیر را دارد یا خیر (مانند اینکه ایا کاربر مورد نظر به عنوان یکی از کارمندان شرکت در نظر گرفته شده است یا خیر). ** گزینه ی "superuser" به کاربر دسترسی کامل را برای اضافه، ساختن و حذف کردن هر ایتم در رابط مدیر را می دهد. در صورتیکه یک کاربر این گزینه را داشته باشد، در آن صورت تمام قوانین دسترسی برای آن کاربر نقض خواهد شد. کاربران مدیر "Normal"، کاربر superuser نیستند بلکه دارای دسترسی مدیر از طریق دسترسی های اختصاص داده شده به آنها می باشند که می تواند شامل محدودیت های دسترسی باشد. هر شیء قابل ویرایش از طریق رابط مدیر (مانند کتاب ها، نویسندگان، ناشران) دارای سه حق دسترسی می باشد: حق دسترسی ساخت، حق دسترسی ویرایش و حق دسترسی حذف. اختصاص دادن حق دسترسی ها به یک کاربر، محدوده اختیارات آن کاربر را برای ایجاد، حذف و یا اعمال تغییرات تعیین می کند. زمانیکه یک کاربر می سازید، کاربر ساخته شده هیچ حق دسترسی ای ندارد، و برای دادن حق دسترسی های خاص حاضر می باشد. برای مثال، شما می توانید حق دسترسی ای برای تغیییر ناشران به آن بدهید، ولی حق حذف کردن ناشران را به کاربر ندهید. توجه داشته باشید که این حق دسترسی ها برای هر مدل تعریف شده می باشند و نه برای هر شیء، بنابراین شما می توانید اینطور بگویید که "john می تواند روی هر کتاب تغییر ایجاد کند"، ولی نمی توان گفت "john می تواند بر روی هر کتابی که با Apress منتشر شده است تغییرات ایجاد کند." قابلیت دوم، حق دسترسی های هر شیء می باشد که کمی پیچیده تر بوده و خارج از حوصله ی این کتاب می باشد ولی در مستندات جنگو درباره ی آن صحبت شده است. نکته : دسترسی به ویرایش کاربران و حق دسترسی ها نیز از طریق سیستم حق دسترسی کنترل شده است. در صورتیکه به شخصی حق دسترسی برای ویرایش کاربران را بدهید، آن شخص قادر به ویرایش حق دسترسی های خود می باشد که این ممکن است آن چیزی نباشد که شما می خواهید! دادن حق دسترسی کاربر برای ویرایش کاربران اساسا تغییر کاربر به superuser می باشد. همچنین می توان کاربران را به گروه ها اختصاص داد. گروه مجموع ای از حق دسترسی هایی می باشد که برای تمام اعضای آن گروه بکار برده می شود. گروه ها برای دادن حق دسترسی های یکسان به زیرمجموعه ای از کاربران مفید می باشد. چه زمانی و به چه دلیل از رابط مدیر استفاده می شود و چه زمانی استفاده نمی شود بعد از آشنایی و کار در این فصل، شما باید یک ایده ی خوب در مورد نحوه ی استفاده از سایت مدیر جنگو داشته باشید. ولی می خواهیم در اینجا چند موضوع را باز کنید و آن هم این است که چرا و کی باید از سایت مدیر جنگو استفاده کرد و چه زمانی نباید از آن استفاده کرد. سایت مدیر جنگو زمانی بسیار پر ارزش می شود که کاربران غیر فنی به وارد کردن داده نیاز داشته باشند، در روزنامه جایی که جنگو اولین توسعه را در آنجا پیدا کرد یک گزارش ویژه در مورد کیفیت آب شهری که چیزی شبیه به مراحل زیر بود، ارائه شده بود: ** گزارشگر مسئولیت داشت برای پروژه با یکی از توسعه دهندگان ملاقات کند و داده ی در دسترس را توضیح دهد. ** توسعه دهنده مدل های جنگو را برای متناسب بودن با آن داده ها طراحی کرده سپس سایت مدیر را برای گزارشگر باز می کرد. ** گزارشگر سایت مدیر را بررسی کرده و کمبود ها و فیلدهای غیر اصلی را به آن اشاره می کرد، توسعه دهنده مکررا مدل ها را تغییر می داد. ** هنگامی که مدل ها مورد توافق قرار گرفت، گزارشگر شروع به وارد کردن داده با استفاده از سایت مدیر می کرد. هم زمان، برنامه نویس می تواند روی توسعه ی عمومی قسمت های views و template ها کار کند (قسمت جالب کار). ورای وظایف واضح برای وارد کردن داده، سایت مدیر در موارد دیگر که در زیر آمده است نیز مفید می باشد: ** بررسی کردن داده ی مدل ها: هنگامی که تعدادی مدل تعریف می کنید، فراخوانی آن ها در رابط مدیر و وارد کردن برخی داده های ساختگی می تواند کاملا مفید باشد. در برخی موارد، می تواند باعث آشکار شدن اشتباهات data-modeling و یا مشکلات دیگر با مدل های شما شود. ** مدیریت داده ی بدست آمده: برای برنامه هایی که بر روی آمدن داده از منابع بیرونی تکیه دارند، سایت مدیر روشی ساده برای بازرسی یا ویرایش این داده ها به شما می دهد. ممکن است آن را کم قدرت تصور کنید، ولی بسیار مناسب تر از امکان خط فرمان پایگاه داده ی شما می باشد. ** مدیریت داده ی app ها به صورت سریع: می توان برای ساخت مدیریت داده ی بسیار سبک app از سایت مدیر استفاده کرد. در صورتیکه که تنها می خواهید برای نیازهای خود چیزی بسازید، نه برای مصارف عمومی، سایت مدیر می تواند راه مناسبی باشد. یک نکته ی پایانی اینکه می خواهیم واضح باشد: سایت مدیر برای همه وقت و همه چیز نیست. سایت مدیر برای یک رابط عمومی بودن در نظر گرفته نشده است، همچنین برای چیدمان و جستجوی پیچیده ی داده ها نیز در نظر گرفته نشده است. همانطور پیش تر در این فصل گفته شد، سایت مدیر برای مدیران مورد اعتماد سایت می باشد. خسته نباشید دوستان عزیز . جلسه ششم بخش دوم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . ترجمه از سایت www.djangobook.com
  13. به نام خدا در خدمت شما عزیزان هستیم با جلسه نهم از آموزش صفر تا صد پایتون مقدماتی . امیدوارم تا اینجای آموزش ها لذت برده باشید . در این قسمت می پردازیم به معرفی متغییر ها در پایتون و ثابت ها . معرفی متغیر ها ( Variables ) و ثابت ها در زبان پایتون متغیر ها ( Variables ) : در جلسه های قبل درباره انواع داده صحبت کردیم . حال وقتی تعدادی انواع داده در زبان پایتون وجود داره باید متغیر هایی داشته باشیم که مقادیری از آن انواع داده را ذخیره کند . مثلا وقتی نوع داده عددی داریم ( مثلا ۳۴ ) حال باید یک ظرفی در نظر بگیریم که این مقدار را در خودش ذخیره کنه و هرجا خواستیم بتونیم ازش استفاده کنیم . پایتون در واقع فاقد چنین متغیر هایی است بلکه دارای مرجع شی است . تفاوت زیادی بین متغییر و مرجع شی وجود نداره . وقتی داریم از اشیای غیر قابل تغییر مثل int و str استفاده می کنیم تفاوت فاحشی بین متغیر و مرجع شی وجود نداره . اما برای تعریف اشیای قابل تغییر بین آن ها تفاوت است ولی در عمل چندان مهم نیست برای ما . ما در آموزش های خودمون از واژه های متغیر به جای مرجع شی استفاده می کنیم . اسامی ای که برای مرجع اشیا(متغیرها)به کار می رود شناسه نیز نامیده می شود. ما باید برای متغیر های خودمون که قرار هست مقدار داده ای در خود ذخیره کنه یک اسمی در نظر بگیریم . این تعریف اسم برای متغییر چندتا قانون کلی داره که بهتره یک بار برای همیشه یاد بگیریم و خودمون رو راحت کنیم : 1 - نباید با واژه های کلیدی در پایتون یکسان باشند ( در جلسه قبل کلمات کلیدی را در یک جدول معرفی کردیم ) . 2 - باید با حرف یا خط زیر (ـــ) شروع شود و پس از آن چند جرف و رقم بیاد و فاقد فضای خالی باشد . 3 - محدودیتی درطول نام شناسه وجود ندارد . یعنی برای شناسه نامی با هر طول می توانید انتخاب کنید. 4 - شناسه ها نسبت به حروف کوچک وبزرگ حساس هستند. در زیر چند نمونه از نامگذاری متغییر به صورت معتبر و غیر معتبر براتون مثال میزنیم : اسامی متغییر های نا معتبر : 1myname my.name .myname my name اسامی متغییر های معتبر : count test2 t_1 _myname اعلان متغییر ها ( Variable Declaration ) : یکی از مزیت های زبان برنامه نویسی پایتون نسبت به زبان های دیگه این است که لازم نیست متغییر رو قبل از استفاده اعلان کنید . شاید تعجب کنید !! ولی واقعا همینطور است . فقط کافیه به متغیر مقدار بدید و ازش استفاده کنید . نکته قابل توجه دیگه این است که لازم نیست تعریف کنید که متغیر من از چه نوعی است !! فقط کافیست به متغیر خود مقدار دهید تا پایتون بقیه کارها رو انجام بده . برای نسبت دادن یک مقدار به متغیر فقط کافیست از علامت = استفاده کنید . بدون هیچ پسوند یا پیشوندی !!! مثال : تمامی متغیر های زیر حاوی مقداری از انواع نوع داده هستند . بدون اینکه از قبل اعلان شوند و یا نوع داده براشون تعریف بشه : message = " Hello My Brother " number = 12345344 flag = True myAge = 12.365 توجه کنید که برای نوع داده استرینگ فقط به دابل کوتیشن احتیاج داریم . متغیر اول یه مقدار رشته ای را در خود ذخیره کرده است متغیر دوم یه مقدار int یا عددی را در خود ذخیره کرده است . متغیر سوم یک مقدار بولین و با مقدار True ( درست ) را در خود ذخیره کرده است . متغیر چهارم یک مقدار اعشاری را در خود ذخیره کرده است . به همین راحتی و خوش مزگی !! تعیین نوع داده در زبان پایتون به صورت پویا انجام می شود . خواهشا جمله بالا رو چندین بار بخونید تا قشنگ متوجه بشید . این جمله یعنی چی ؟؟ یعنی اینکه شما در سراسر برنامه می توانید مدام بدون هیچ گونه مشکلی مقدار یک متغیر رو عوض کنید ! به کد زیر دقت کنید : MyValue = " Book " ... ... ... MyValue = 1200 زمانی که متغیر فوق مقدار بوک براش در نظر گرفته شد با همون مقدار در برنامه حضور خواهد داشت و پیش میره . ما میتونیم بدون هیچ گونه هماهنگی قبلی ! مقدارش رو یهو عوض کنیم و از این به بعد این متغیر مقدارش برابر ۱۲۰۰ هست و در محاسبات با این مقدار حضور خواهد داشت . این نکته رو در ذهنتون داشته باشید که با تمام این تفاسیر هر متغیر در هر لحظه فقط یک مقدار دارد . عملگر = در پایتون مانند زبان های دیگر عمل نمی کند . این عملگر فقط یک متغیر را به یک شی موجود مقید می کند . جمله بالا یعنی چی ؟؟ لطفا این بخش رو با دقت مطالعه کنید . یک مثال میزنم تا کاملا متوجه بشید . X = 1200 ... ... X = 3000 در ابتدا مقدار X برابر عدد ۱۲۰۰ است ولی یک یا چند خط جلوتر دیگه X به مقدار ۱۲۰۰ اشاره نمی کنه . بلکه داره به مقدار ۳۰۰۰ اشاره میکنه . خب تا اینجا که مشکلی ندارید !! حالا به کد زیر دقت کنید : X = 1200 Y = X X = 3000 به نظر شما الان مقدار x و y چقدر است ؟ بله درست حدس زدید . مقدار X‌ در حال حاضر برابر ۳۰۰۰ است و مقدار Y برابر ۱۲۰۰۰ است . چرا ؟ چون Y داره به X ای اشاره میکنه که مقدارش برابر ۱۲۰۰ هست . شاید یکم براتون گنگ باشه ولی یکم کار کنید قشنگ متوجه می شید . نکته : اگر میخواید یک متغیری ایجاد کنید که فعلا مقداری نداره از کلمه None استفاده کنید : MyVariable = None ثابت ها ( Constants ) و نحوه اعلان آنها درد برنامه : بر خلاف متغیر ها که مقدارشون مدام میتونه در برنامه تغییر کنه ُ ثوابت شناسه هایی هستند که در برنامه وجود دارند ولی غیر قابل تغییر هستند . ثابت ها در زبان پایتون با حروف بزرگ مشخص می شوند . مثال : LABLE = " computer " NUM = 1000 این ثابت ها در تمام برنامه دیگه نمیتونن مقدار جدیدی بگیرند . استفاده از ثابت ها در برنامه باعث افزایش خوانایی برنامه می شه . در جلسات بعدی به طور کامل متوجه خواهید شد که این ثوابت چه کاربردی دارند . خب به پایان جلسه نهم از آموزش برنامه نویسی پایتون مقدماتی به زبان فارسی رسیدیم . امیدوارم که لذت برده باشید . موفق و پیروز باشید . امیدوارم که از جلسه نهم آموزش استفاده کامل رو برده باشید . تمامی حقوق برای انجمن پایتونی ها محفوظ می باشد . پایتونی ها
  14. این تاپیک پشتیبانی برای آموزش است معرفی متغیر ها ( Variables ) و ثابت ها در زبان پایتون ( جلسه نهم ) 06/03/96 01:09 06/03/96 01:09 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.