رفتن به مطلب

آموزش جنگو Django Framework


Mohammad Aref

ارسال های توصیه شده

دادن Option های پیکربندی View

در صورتیکه یک برنامه ی جنگو را توزیع (distribute) می کنید، این احتمال وجود دارد که کاربران شما برخی درجات پیکربندی را بخواهند. در این مورد، ایده ی خوبی است که برای هر انتخاب پیکربندی که فکر می کنید افراد ممکن است بخواهند آن را تغییر دهند، hook هایی را در view ها اضافه کنید.

یک قسمت مشترک از یک برنامه برای ایجاد قابلیت پیکربندی، نام template می باشد:

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

فهمیدن اولویت مقدار داخل پرانتز در مقابل Option های اضافه

هنگام وجود مغایرت، پارامترهای اضافه ی URLconf بر پارامترهای داخل پرانتز اولویت دارند. به عبارت دیگر، در صورتیکه URLconf شما، یک متغیر از نوع گروه نام گذاری شده درون پرانتز داشته باشد، و همچنین یک پارامتر اضافه ی URLconf را با متغیر همنام با آن، در این صورت مقدار پارامتر اضافه ی URLconf استفاده خواهد شد.

برای مثال URLconf زیر را مشاهده کنید:

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

در کد فوق، هر دوی regular expression و دیکشنری اضافه، حاوی یک id می باشند که دیکشنری اضافه دارای اولویت می باشد. این بدین معناست که هر درخواستی (مانند /mydata/2/ یا /mydata/432432) بدین صورت رفتار خواهد کرد که مقدار id بدون در نظر گرفتن مقدار داخل پرانتز URLconf عدد 3 می باشد.

خوانندگان زیرک در این مورد توجه خواهند داشت، که قرار دادن id داخل پرانتز در regular expression اتلاف وقت می باشد، زیرا مقدار آن همواره توسط مقدار دیکشنری باز نویسی می شود. درست آن است که؛ در دست ترجمه ...

استفاده از آرگومان های پیشفرض View

فوت و فن مناسب دیگر، تعیین پارامتر های پیشفرض برای آرگومان های view می باشد. با استفاده از این تکنیک در صورتیکه مقدار یک پارامتر اصلا تعیین نشود از مقدار پیشفرض تعیین شده استفاده می کند.

مثال:

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

در کد فوق، هر دو الگوی URL به یک view اشاره می کنند – views.page – ولی اولین الگو درون URL هیچ مقداری داخل پرانتز قرار نداده است. در صورتیکه اولین الگو تطبیق پیدا کند، تابع page() از مقدار آرگومان پیشفرض num یعنی '1' استفاده خواهد کرد. همچنین در صورت تطبیق الگوی دوم، تابع page() از هر مقداری که توسط regular expression درون پرانتز حاصل می شود استفاده خواهد کرد.

(توحه داشته باشید که بایستی مقدار پیشفرض آرگومان را رشته ی '1' قرار دهیم، نه یک integer. زیرا هر مقداری که داخل پرانتز URLconf برای num قرار گرفته است همواره یک رشته خواهد بود.)

استفاده از این تکنیک، در رابطه با option های پیکربندی نیز متداول می باشد، همانطور که پیش تر توضیح داده شد. مثال زیر مثال بهبود پیدا کرده ی بخش "دادن option های پیکربندی view" با تهیه ی یک مقدار پیشفرض برای template_name می باشد:

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

موارد خاص View ها

گاهی اوقات شما الگویی در URLconf خواهید داشت که مجموعه ی بزرگی از URL ها را کنترل می کند، ولی شما تنها به یک مورد خاص از آن ها نیاز خواهید داشت. در این موارد، از روش خطی یک URLconf ای که پردازش شده است می توانید استفاده کنید و مورد خاص را اول قرار دهید.

برای مثال، می توانید صفحات "add an object" در سایت مدیر جنگو را به صورت نمایش داده شده با یک URLpattern مانند زیر تصور کنید:

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

کد فوق با URL هایی از قبیل /myblog/entries/add/ و /auth/groups/add/ تطبیق پیدا می کند. اگرچه صفحه ی "add" برای یک شیء user (/auth/user/add/) یک مورد خاص می باشد – تمام فیلدهای فرم را نمایش نمی دهد، بلکه دو فیلد رمز عبور و الی آخر را نمایش می دهد. ما این مشکل را به شکل زیر حل کرده ایم:

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

ولی روش فوق برای یک دلیل که چندین بار در این فصل آن را لمس کرده ایم روش زیبایی نیست: روش فوق منطق URL را درون view قرار می دهد. راهکار بهتر این است که، از این واقعیت سود ببریم که URLconf ها از بالا به پایین پردازش می شوند:

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

با استفاده از روش فوق، درخواست برای /auth/user/add/ از طریق user_add_stage کنترل خواهد شد. اگرچه که URL با الگوی دوم مطابقت دارد، ولی ابتدا با الگوی بالاتر تطبیق پیدا می کند.

پوشش دادن متن در URL ها

هر آرگومان پوشش داده شده ای به صورت یک رشته ی یونیکد پایتون به view فرستاده می شود. به عنوان مثال در خط URLconf زیر:

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

آرگومان year برای views.year_archive() یک رشته خواهد بود، نه یک integer، حتی اگر \d{4} تنها با رشته های از نوع integer مطابقت داشته باشد.

بخاطر داشتن این موضوع هنگامی که کد view را می نویسید مهم می باشد. بسیاری از توابع داخلی پایتون نسبت به قبول کردن تنها شیء های از نوع مشخص حساس می باشند. یکی از خطاهای متداول ساختن یک شیء datetime.date با مقادیر رشته به جای مقادیر integer می باشد:

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

تبدیل شده ی به یک URLconf و view، خطایی مانند زیر خواهد بود:

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

به جای day_archive() می توان به این شکل آنرا اصلاح نمود:

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

توجه داشته باشید که تابع int() خودش هنگامی که یک رشته ی غیر قابل تبدیل به integer به آن ارسال شود یک خطای ValueError ایجاد می کند، ولی از آن جهت که regular expression در URLconf در این مورد تنها رشته های قابل تبدیل به integer (مانند "245") را قبول می کند دیگر نگرانی برای این موضوع وجود نخواهد داشت.

تعیین آنچه را که URLconf به دنبال آن جستجو می کند

هنگامی که یک درخواست ارسال می شود، جنگو در تلاش بر می آید که الگوهای URLconf را برای URL درخواست شده به صورت رشته ی پایتون تطبیق دهد. این شامل پارامترهای GET یا POST و یا نام دامین نمی شود. همچنین شامل علامت (/) آغازین نیز نمی شود، زیرا هر URL دارای یک علامت (/) آغازین می باشد.

برای عنوان مثال، در یک درخواست به

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

method درخواست (مانند POST، GET) هنگام عبور کردن URLconf به حساب نمی آید. به عبارت دیگر، تمام method های درخواست درون تابع همسان برای URL همسان بررسی می شوند. اجرا کردن method های درخواست به عهده ی تابع view می باشد.

تصور سطح بالا از توابع view

صحبت کردن درباره ی روش branching based در method درخواست، اجازه دهید به نحوه ی ساخت یک روش زیبا از انجام این روش نگاهی بیاندازیم. لایه ی URLconf و view زیر را ملاحظه کنید:

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

در مثال فوق، some_page()، درخواست های POST و GET را کاملا متفاوت کنترل می کند. تنها چیزی که به صورت مشترک دارا می باشند یک URL به اشتراک گذاشته می باشد: /somepage/. به همین دلیل، اینکه با هر دوی POST و GET در یک تابع view سر و کار داشت، روش زیبایی نمی باشد. روش مناسب آن است که دو تابع view مجزا داشته باشیم – یکی درخواست های GET را کنترل کرده و دیگری درخواست های POST را – و مطمئن بود که هر یک تنها در زمان مناسب فراخوانی می شوند.

می توان با توشتن یک تابع view که نماینده ی دیگر view ها می باشد، قبل و بعد اجرا برخی جملات منطقی این کار را انجام داد. در مثال زیر نحوه ی انجام این روش در some_page() وجود دارد:

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

اجازه دهید کد فوق را مورد بررسی قرار دهیم:

  • یک 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 ها می گیرند و یا آرگومان های اختیاری برای خود می گیرند استفاده کنیم؟

برای انجام چنین کاری، می توان از یک ویژگی زیبای پایتون استفاده کرد: آرگومان های متغیر با علامت ستاره. در ابتدا مثالی را نشان خواهیم داد و سپس آن را توضیح می دهیم:

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

در کد فوق، method_splitter() را تغییر داده ایم، به طوری که آرگومان های کیورد GET و POST در آن حذف شده اند و جای آن ها از *args و **kwargs (به علامت ستاره توجه کنید) استفاده شده است. این یک خصوصیت پویای پایتون می باشد که به یک تابع اجازه می دهد یک تعداد دلخواه از آرگومان ها را که نام آن ها تا زمان اجرا مشخص نخواهد بود قبول کند. در صورتیکه یک علامت ستاره تکی در تعریف یک تابع قبل یک پارامتر قرار دهید، هر آرگومان موضعی به آن تابع در یک تاپل جمع خواهد شد. در صورتیکه دو علامت ستاره قبل از یک پارامتر در تعریف تابع قرار دهید، هر آرگومان کیورد به آن تابع داخل یک دیکشنری جمع می شوند.

برای مثال:

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

در زیر نحوه ی عملکرد آن مشخص شده است:

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

به method_splitter() باز می گردیم، می توانید مشاهده کنید که برای قبول کردن هر آرگومان به تابع و ارسال آنها به همراه view مناسب از *args و **kwargs استفاده کرده ایم. ولی بعد از انجام این کار، دو فراخوانی به kwargs.pop() برای بدست آوردن آرگومان های GET و POST در صورت دسترس ایجاد کرده ایم. (از pop() با یک مقدار پیشفرض None جهت دوری از خطای KeyError در صورتیکه یکی از آن ها تعریف نشده باشد استفاده شده است.)

Wrapping View Functions

آخرین فن view سود جستن از یک تکنیک پیشرفته ی پایتون می باشد. تصور کنید در سرتاسر view های مختلف متوجه دسته ای از کدهای تکراری شده اید مانند مثال زیر:

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

در کد فوق، هر view با بررسی اینکه request.user تایید شده است یا خیر شروع می شود – کاربر فعلی با موفقیت وارد سایت شده است – و به مسیر /accounts/login/ تغییر مسیر داده می شود. (توجه داشته باشید که هنوز request.user توضیح داده نشده است – در بخش کاربران عضویت و session درباره ی این موضوع بحث شده است – ولی، همانطور که ممکن است تصور کنید، request.user کاربر فعلی را نمایش می دهد)

بسیار زیبا می باشد اگر قسمت های تکراری کد موجود در هر کدام از view ها حذف شوند و تنها در صورت نیاز آن ها را علامت گذاری کرد. می توان این کار را با ساختن یک view wrapper انجام داد. چند لحظه ای کد زیر را مطالعه کنید:

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

تابع فوق، 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 قرار داد:

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

کد فوق تاثیر یکسانی را به صورت قبل داراست، ولی با زوائد کد کمتر. حالا ما یک تابع generic زیبا ساخته ایم – requires_login() که می توان برای هر تابع view به منظور نیاز به ورود به سایت استفاده کرد.

در بر داشتن URLconf های دیگر

در صورتیکه قصد دارید کد شما در سایت های گوناگون ایجاد شده توسط جنگو استفاده شود، شما باید URLconf های خود را به روشی تنظیم کنید که "including" برای آن ها مقدور باشد.

در هر نقطه، URLconf شما می تواند ماژول های دیگر URLconf را شامل شود. برای مثال، URLconf زیر حاوی URLconf های دیگری نیز می باشد:

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

یک مفهوم مهم در اینجا وجود دارد: regular expression ها که در این مثال به یک include() اشاره می کنند، دارای علامت ($) نمی باشند. هر زمان که جنگو با include() برخورد می کند، هر بخش از URL مطابقت داده شده با آن نقطه را برش می دهد و رشته ی باقیمانده برای URLconf ای که Include شده را جهت پردازش بیشتر می فرستد.

به مثال زیر توجه کنید:

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

با این دو 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 های پدر را دریافت می کند، برای مثال:

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

در مثال فوق، متغیر پوشش داده شده ی username به URLconf شامل شده و هر تابع view موجود در آن URLconf ارسال شده است.

توجه داشته باشید که پارامترهای پوشش داده شده همواره به هر خط در URLconf شامل شده صرف نظر از اینکه آیا view آن خط واقعا آن پارامتر های به صورت معتبر قبول خواهد کرد یا خیر ارسال شده خواهند بود. به همین دلیل، این تکنیک تنها در صورتیکه شما مطمئن باشید که هر view در URLconf شامل شده پارامترهای ارسال شده را قبول می کند مفید خواهد بود.

نحوه option های اضافه URLconf در کار با include()

به طور مشابه، می توان انتخاب های اضافه ی URLconf به include() ارسال کرد، همانطور که می توان option های اضافه ی URLconf را به یک view معمولی ارسال کرد – به صورت یک دیکشنری. هنگامی که شما این کار را انجام دهید، هر خط در URLconf شامل شده، option اضافه ی ارسال شده خواهد بود.

برای مثال، دو دسته ی URLconf زیر دارای عملکرد یکسان می باشند.

دسته اول:

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

دسته دوم:

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

 

لینک به دیدگاه

Template های پیشرفته در جنگو

اگرچه بیشتر تعامل های شما با زبان template جنگو (Django) در نقش نویسنده ی template می باشد، ولی ممکن است بخواهید موتور template را به طور پیشرفته تری گسترش داده و آن را سفارشی کنید – یا آن را طوری ایجاد کنید که کارهایی را الان نمی تواند انجام دهد را انجام دهد، یا یا به روش دیگری کار شما را آسان تر کند.

این آموزش، به عمق وجودی سیستم قالب جنگو خواهد پرداخت. این آموزش آنچه را که نیاز می باشد برای گسترش سیستم لازم می باشد را پوشش می دهد، همچنین در صورتیکه تنها درباره ی نحوه ی کارکرد سیستم template جنگو کنجکاو می باشید می توانید در این فصل جواب سوالات خود را دریافت کنید. همچنین خصوصیت auto-escaping نیز توضیح داده خواهد شد، یک اندازه گیری امنیتی که با استفاده از آن شما دیگر تردیدی در ادامه ی استفاده از جنگو نخواهید داشت.

در صورتیکه به دنبال استفاده از سیستم template جنگو به عنوان بخشی از برنامه ی دیگر (بدون باقی فریم ورک یا چارچوب) هستید، بخش "پیکربندی سیستم template در حالت مستقل" کمی بعد در همین فصل را مطالعه کنید.

مرور زبان Template

در ابتدا، اجازه دهید مروری اجمالی به تعدادی از مفاهیم معرفی شده در آموزش template جنگو بپردازیم:

template متن سند، یا یک رشته ی عادی پایتون می باشد، که با استفاده از زبان template جنگو ارتقاء پیدا کرده است. یک template می تواند حاوی تگ و متغیر باشد.
تگ template یک علامت داخل template می باشد که کاری را انجام می دهد. این تعریف به طور عمدی مبهم می باشد. به عنوان مثال، تگ template می تواند یک محتوی را تولید کند، به صورت یک ساختار کنترلی (مانند عبارت if یا حلقه ی for) عمل کند، مقدار بازیابی شده از یک پایگاه داده و یا حتی قادر به دسترسی به دیگر تگ های template باشد.
تگ های template با {% و %} پوشیده شده اند:

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

متغیر یک علامت داخل یک template می باشد که یک مقدار را به عنوان خروجی بر می گرداند.
تگ های متغیر با {{ و }} پوشیده شده اند.

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

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 زیر را ملاحظه کنید:

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

(توجه داشته باشید که به عمد از میانبر 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 وجود دارد:

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

اجازه دهید کد فوق را مورد بررسی قرار دهیم:

  • ابتدا، یک تابع به نام 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 به صورت زیر استفاده می شود:

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

در مثال فوق کد ارائه ی هر template مربوط به view درون یک خط (شکسته شده) خلاصه شده است.

این یک پیشرفت می باشد، ولی ارزیابی اختصار کد فوق، باید اعتراف کرد در این روش نیز تقریبا زیاده روی شده است. کدهای اضافه و زائد (متغیرهای template) به قیمت اضافه کردن کدی دیگر (فراخونی processor ها) حذف شده اند. استفاده کردن پردازشگرهای context در صورتیکه مجبور باشید هر بار processor ها را تایپ کنید شما را از تایپ کردن زیاد از حد نجات نمی دهد.

به این دلیل، جنگو از پردازشگرهای سراسری پشتیبانی می کند. تنظیم TTEMPLATE_CONTEXT_PROCESSORS (در فایل settings.py) پردازشگرهای context ای که باید همواره برای RequestContext بکار برده شوند را طراحی می کند. این نیاز برای تعیین پردازشگرها را در هر بار که از RequestContext استفاده می کنید را حذف می کند.

به طور پیشفرض، TEMPLATE_CONTEXT_PROCESSORS به شکل زیر خواهد بود:

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

این تنظیم یک تاپل از آیتم های قابل فراخوانی می باشد که از یک رابط یکسان مانند تابع 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، ممکن است از این پردازشگر استفاده کنید:

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

راهنمایی هایی برای نوشتن پردازشگرهای context خودتان

در زیر راهنمایی هایی برای ایجاد پردازشگر خودتان وجود دارد:

  • هر پردازشگر context را مسئول کوچکترین زیر مجموعه ی قابلیت های ممکن کنید. استفاده از پردازشگرهای چندگانه ساده می باشد، بنابراین ممکن است عمکرد را به قسمت های منطقی برای استفاده ی دوباره در آینده تقسیم کنید.
  • به خاطر داشته باشید که هر پردازشگر context در TEMPLATE_CONTEXT_PROCESSORS در هر template ساخته شده با فایل تنظیمات قابل دسترس خواهد بود، بنابراین سعی کنید نام های متغیری انتخاب کنید که با نام متغیرهایی که به طور مستقل ممکن است استفاده کنید تداخل نداشته باشد. از آنجایی که نام های متغیر به حروف کوچک و بزرگ حساس می باشند، ایده ی بدی نیست که تمام متغیرهایی که یک پردازشگر تولید می کند با تماما با حروف بزرگ باشند.
  • تا زمانیکه آن ها در مسیر پایتون می باشند مشکلی نیست که filesystem باشند، بنابراین می توان به آن ها از تنظیم TEMPLATE_CONTEXT_PROCESSORS اشاره کرد. با در نظر گرفتن این، مناسب آن است که آن ها را در یک فایل با نام context_processors.py داخل app یا پروژه ذخیره کرد.

HTML Escaping خودکار

هنگام تولید HTML از template ها، همواره یک مخاطره وجود دارد که یک متغیر شامل کاراکترهایی باشد که بر روی نتیجه ی HTML تاثیر بگذارد. برای مثال، کد زیر را ملاحظه کنید:

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

در ابتدا، به نظر می رسد کد فوق روش بی ضرری برای نمایش نام کاربر می باشد، اما در نظر داشته باشید چه اتفاقی می افتد اگر ورودی کاربر به شکل زیر باشد:

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

با مقدار فوق، template به صورت زیر ارائه خواهد شد:

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

... این بدین معنی است که مرورگر یک جعبه ی خطر جاوا اسکریپت ظاهر خواهد کرد!

به طور مشابه، چه می شود اگر name حاوی یک علامت '<' مانند زیر باشد؟

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

مقدار فوق نتیجه ای به شکل زیر خواهد داشت:

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

مقدار فوق باعث می شود، باقی کد صفحه ی وب به صورت bold نمایش داده خواهند شد!

واضح است که، داده ی ارسال شده توسط کاربر به طور کورکورانه قابل اعتماد نمی باشد، زیرا کاربران مخرب از این حفره برای انجام مقاصد پلید استفاده کنند. این قبیل رفتار امنیتی حمله ی Cross Site Scripting (XSS) نامیده می شوند. (برای اطلاعات بیشتر در مورد امنیت، به آموزش امنیت مراجعه کنید.)

جهت اجتناب از این مشکل، دو امکان وجود دارد:

  • ابتدا، می توان از طریق فیلتر escape برای اجرای هر متغیر مشکوک اطمینان حاصل کرد، به طوری که این فیلتر حروف مضر HTML را به نوع غیر مضر آن تبدیل می کند. این راهکار پیشفرض در جنگو برای سال های اول بوده است، ولی مشکل این است که این حالت توسعه دهندگان و نویسندگان template را مقید به استفاده از escape می کند. در استفاده کردن از فیلتر escape، فراموش کردن استفاده از آن بسیار اتفاق می افتد.
  • دوم اینکه، می توان از HTML escaping خودکار جنگو استفاده کرد. باقی این بخش نحوه ی کار auto‑escaping را توضیح خواهد داد.

به طور پیشفرض، هر template ای به صورت خودکار حروف گفته شده در خروجی هر تگ متغیر را به حروف غیر مضر تبدیل می کند. به ویژه، این پنج حروف تبدیل می شوند.

  • < به < تبدیل می شود
  • > به > تبدیل می شود
  • ' (تک کتیشن) به ' تبدیل می شود
  • " (دابل کتیشن) به " تبدیل می شود
  • & به & تبدیل می شود

دوباره تاکید می کنیم که، این رفتار به صورت پیشفرض می باشد. در صورتیکه از سیستم template جنگو استفاده می کنید، شما از این مشکلات محفوظ می باشید.

نحوه ی از کار انداختن auto-escaping

در صورتیکه بخواهید حالت پیشفرض یعنی auto-escaping را برای هر وب سایت، هر سطح template و یا هر سطح متغیر، تغییر دهید، می توان به چندین روش عمل کرد.

چرا می خواهید این حالت را تغییر دهید؟ زیرا، گاهی اوقات متغیرهای template حاوی داده ای می باشند که می خواهید به صورت HTML ارائه شوند، که در این صورت می خواهید محتویات این متغیرها escape نشوند. برای مثال، ممکن است قسمت هایی از کد HTML مورد اعتمادی را در پایگاه داده ذخیره کنید و بخواهید آن را به طور مستقیم درون template استفاده کنید. یا، ممکن است از template جنگو جهت تولید متنی به غیر از HTML استفاده کنید – مانند یک پیام پست الکترونیک برای نمونه.

برای متغیرها

جهت غیر فعال کردن auto‑scaping برای یک متغیر، از فیلتر safe می توان استفاده کرد:

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

در مثال فوق در صورتیکه data حاوی مقدار '<b>' باشد، خروجی آن به صورت زیر خواهد بود:

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

برای بلاک های template

جهت کنترل auto‑escaping برای یک template، template (یا تنها قسمتی از template) را درون تگ autoescape مانند زیر قرار دهید:

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

تگ autoescape یک مقدار on یا off به صورت آرگومان دریافت می کند. گاهی اوقات می خواهید زمانی که auto‑escape در قسمتی از کد غیر فعال شده است آن را مجبور کنید که فعال شود مانند مثال زیر:

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

تگ auto‑escape درون template هایی که از template پدر ارث بری کرده اند نیز تاثیر می گذارد. به عنوان مثال:

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

به این دلیل که auto‑escaping درون template پدر غیر فعال شده است، درون template فرزند نیز غیر فعال خواهد بود، بنابراین در صورتیکه متغیر greeting حاوی رشته ی <b>Hello!</b> باشد، خروجی آن به صورت زیر خواهد بود:

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

نکته ها

عموما، نویسندگان template نیازی نیست در مورد auto‑escaping خیلی زیاد نگرانی داشته باشند. توسعه دهندگان سمت پایتون (افرادی که view ها و فیلترهای سفارشی را می نویسند)، نیاز دارند، درباره ی مواردی که در داده نباید escape شوند فکر کنند، و داده را به طور مناسب علامت گذاری کنند. بنابراین کارها در template انجام خواهند شد.

در صورتیکه یک template می سازید که ممکن است در وضعیت هایی که مطمئن نیستید آیا auto‑escaping فعال است یا خیر استفاده شده باشد، آنگاه یک فیلتر escape برای هر متغیر که نیاز به escape دارد قرار دهید. هنگامی که auto‑escaping فعال باشد، هیچ مشکلی پیش نخواهد آمد – فیلتر escape تاثیری بر روی متغیرهای auto‑escape شده نخواهد داشت.

Escape کردن خودکار رشته ی خام در آرگومان های فیلتر

همانطور که پیش تر ذکر شد، آرگومان های فیلتر می توانند رشته باشند:

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

تمام رشته های خام بدون هیچ escape خودکاری درون template مندرج شده می باشند – آن ها مثل اینکه از میان فیلتر safe عبور کرده باشند عمل می کنند. دلیل این موضوع این است که نویسنده ی template آنچه را که به صورت رشته ی خام می آید را تحت کنترل دارد، بنابراین آن ها اطمینان حاصل می کنند که متن هنگامی template نوشته می شود به صورت صحیح escape شده باشد.

این بدان معناست که خواهید نوشت:

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

... به جای

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

حالت فوق تاثیری بر روی مقداری که درون خود متغیر می باشد نخواهد داشت. در صورت لزوم محتویات متغیرها همچنان به طور خودکار 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 و غیره باشد. برای مثال:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

دو فایل خالی در دایرکتوری templatetags بسازید: یک فایل __init__.py (برای اینکه به پایتون نشان داده شود که این یک پکیج حاوی کد پایتون می باشد) و یک فایل که حاوی تعریف تگ ها/فیلترهای سفارشی شما خواهد بود. نام فایل دوم آن چیزی خواهد بود که شما برای بارگذاری تگ ها از آن استفاده خواهید کرد. برای مثال، در صورتیکه تگ ها یا فیلترهای سفارشی شما درون فایلی به نام poll_extras.py خواهد بود، شما کدی نظیر کد زیر را درون template خواهید داشت:

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

تگ {% load %} تنظیم INSTALLED_APPS را مورد بررسی قرار داده و تنها اجازه به بارگذاری کردن کتابخانه های template داخل برنامه های نصب شده جنگو می دهد. این یک ویژگی امنیتی می باشد؛ این ویژگی اجازه می دهد تا مکانی برای بسیاری از کتابخانه های template در یک رایانه ی تنها را بدون فعال کردن دسترسی به تمام آنها برای هر نصب جنگو ایجاد کنید.

در صورتیکه یک کتابخانه ی template نوشته اید که به هیچ models/views ای مرتبط نیست، این حالت برای داشتن پکیج برنامه ی جنگو که حاوی تنها یک پکیج templatetags می باشد معتبر و کاملا عادی است. هیچ محدودیتی نسبت به تعداد ماژول هایی که شما در پکیج templatetags قرار می دهید وجود ندارد. تنها به خاطر داشته باشید که یک عبارت {% load %} تگ ها یا فیلترهایی برای نام ماژول پایتون داده شده بارگذاری خواهد کرد، نه نام برنامه.

هنگامی که آن ماژول پایتون را ساخته باشید، تنها ملزم به نوشتن مقدار کمی از کد پایتون بسته به اینکه آیا چه فیلتر یا تگی می نویسید خواهید بود.

برای معتبر بودن کتابخانه ی تگ، ماژول باید حاوی یک متغیر در سطح ماژول به نام register باشد که یک نمونه از template.Library می باشد. این یک ساختار داده می باشد که تمام تگ ها و فیلترها درون آن عضو شده اند. بنابراین، در بالا ماژول، کد زیر را اضافه کنید:

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

نکته

برای یک انتخاب خوب از مثال ها، کد منبع فیلترها و تگ های پیشفرض جنگو را بخوانید. آن ها به ترتیب در django/template/defaultfilters.py و django/template/defaulttags.py می باشند. همچنین برخی برنامه های موجود در django.contrib حاوی کتابخانه های template می باشد.

هنگامی که متغیر register را ساختید، شما برای ساختن فیلترها و تگ ها template از آن استفاده خواهید کرد.

نوشتن فیلترهای سفارشی Template

فیلترهای سفارشی تنها توابع پایتون می باشند که یک یا دو آرگومان دریافت می کنند:

مقدار متغیر (ورودی)
مقدار آرگومان، که می تواند یک مقدار پیشفرض یا داشته و یا رو هم رفته ترک شده باشد
برای مثال، در فیلتر {{ var|foo:"bar" }}، فیلتر foo محتویات متغیر var و آرگومان "bar" را ارسال خواهد کرد.

توابع فیلتر باید همواره چیزی را بر گردانند. آن نباید خطایی ایجاد کنند، و باید به آرامی خطاها را رد کنند. در صورتیکه یک خطا وجود داشته باشد، یا باید ورودی اصلی را برگردانند یا یک رشته ی خالی را، هر کدام که حساسیت بیشتری خواهد داشت.

در اینجا یک مثال از تعریف فیلتر را ملاحظه می کنید:

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

در زیر نحوه استفاده حذف کردن فاصله های مقدار یک متغیر توسط فیلتر بالا نشان داده شده است:

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

اغلب فیلترها آرگومانی دریافت نمی کنند. در این مورد، تنها آرگومان تابع را حذف کنید:

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

هنگامی که تعریف فیلتر خود را نوشتید، برای اینکه آن را برای زبان template جنگو در دسترس قرار دهید لازم است که آن رابه نمونه ی Library خود معرفی کنید:

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

متد Library.filter() دو آرگومان دریافت می کند:

  • نام فیلتر (یک رشته)
  • خود تابع فیلتر

در صورتیکه از پایتون 2.4 یا بالاتر استفاده می کنید، می توانید بجای روش فوق از regiser.filter() به صورت یک decorator استفاده کنید:

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

در صورتیکه آرگومان name را قرار ندهید، همانطور که در مثال دوم مشاهده کردید، جنگو از نام تابع به صورت نام فیلتر استفاده خواهد کرد.

مثال فوق نیز مثال کامل کتابخانه ی template یعنی تهیه ی فیلتر cut می باشد:

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

توشتن تگ های template سفارشی

تگ ها پیچیده تر از فیلترهای می باشند، زیرا تقریبا هرکاری را می توانند انجام دهند.

آموزش template جنگو نحوه ی کار سیستم template جنگو را در یک روند دو مرحله ای توضیح می دهد: کامپایل و ارائه (compling and rendering). برای تعریفی یک تگ template سفارشی، لازم است نحوه ی مدیریت هر دو مرحله ی فوق در زمانی که جنگو تگ شما را به دست می آورد به آن گفته شود.

هنگامی که جنگو یک template را کامپایل می کند، متن خام template را به note هایی تقسیم می کند. هر node یک نمونه از django.template.Node بوده و دارای یک متد render() می باشد. در نتیجه، یک template کامپایل شده یک لیست از شیء های Node می باشد. برای مثال، template زیر را ملاحظه کنید:

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

در حالت کامپایل شدهه ی 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 بسازد:

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

اجازه دهید کد فوق را مورد بررسی قرار دهیم:

  • هر کامپایل تابع تگ 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 می باشد:

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

این دو تابع (__init__() و render()) به طور مستقیم به دو مرحله ی در روند template (compilation and rendering) مرتبط هستند. در نتیجه، تابع __init__() تنها ملزم به ذخیره ی قالب رشته ی برای استفاده ی بعدی می باشد، و تابع render() کار واقعی را انجام می دهد.

مانند فیلترهای template، این توابع باید به جای ایجاد خطا به طور بی صدا خطاهای ایجاد شده را رد کنند. تنها زمانی که تگ های template اجازه دارند خطاها را ایجاد کنند زمان کامپایل می باشد.

معرفی تگ

در پایان، نیاز به معرفی تگ با نمونه ی ماژول Library می باشد. معرفی تگ های سفارشی بسیار شبیه به معرفی فیلترهای سفارشی (همانطور که توضیح داده شد) می باشد. تنها یک نمونه template.Library را معرفی کرده و متد tag() آن را فراخوانی کنید. برای مثال:

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

متد tag() دو آرگومان دریافت می کند:

  • نام تگ template (رشته).
  • تابع کامپایل.

همانند معرفی فیلتر، امکان این وجود دارد که از register.tag به صورت یک decorator در پایتون 2.4 و بالاتر استفاده کرد:

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

در صورت حذف آرگومان name، همانند مثال دوم، جنگو از نام تابع برای نام تگ استفاده خواهد کرد.

تنظیم یک متغیر در Context

مثال بخش قبلی به سادگی یک مقدار را بر می گرداند. خیلی اوقات قرار دادن متغیرها به جای برگشت دادن مقادیر مفید خواهد بود. در این روش، نویسندگان template می توانند تنها متغیرهایی که تگ های template شما قرار داده اند را استفاده کنند.

برای قرار دادن یک متغیر در context، از اختصاص دادن دیکشنری برای شیء context در متد render() استفاده می شود. در زیر نسخه ی تغییر کرده ی CurrentTimeNode مشاهده می کنید:

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

(ساختن یک تابع do_current_time2، به اضافه ی معرفی آن تابع به تگ template، current_time2 انجام نشده است، تا خواننده آن ها را به عنوان تمرین انجام داد.)

توجه داشته باشید که render() یک رشته ی خالی را بر می گرداند. render() همواره باید یک رشته بر گرداند، بنابراین در صورتیکه تمام تگ های template یک متغیر را قرار دهند، render() باید یک رشته ی خالی بر گرداند.

در اینجا نحوه ی استفاده ی نسخه ی جدید از تگ نشان داده شده است:

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

ولی یک مشکل با CurrentTimeNode2 وجود دارد: نام متغیر current_time به صورت مستقیم استفاده شده است. این بدان معناست که شما نیاز است اطمینان حاصل کنید که template شما از {{ current_time }} هیچ جای دیگری استفاده نکرده است، زیرا {% current_time2 %} مقدار آن متغیر را باز نویسی خواهد کرد.

راهکار درست این است که تگ template نامی برای متغیر قرار داده شده تعیین نماید:

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

برای انجام چنین کاری، نیاز به تغییر هر دو تابع کامپایل و کلاس Node به صورت زیر می باشد:

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

حالا do_current_time() قالب رشته و نام متغیر را به CurrentTimeNode3 ارسال می کند.

Parse کردن تا تگ Template دیگر

تگ های template می توانند به صورت بلاک های حاوی تگ های دیگر (مانند {% if %} و {% for %}) کار کنند. برای ساختن یک تگ template مانند این، در تابع کامپایل خود از parser.parse() استفاده کنید.

در زیر نحوه ی کار تگ {% comment %} انجام شده است:

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

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 %} را به حروف بزرگ تبدیل می کند:

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

همانند مثال قبلی، از parser.parse() استفاده کرده ایم. این بار، نتیجه ی nodelist را به Node ارسال کرده ایم:

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

تنها مفهوم جدید در کد فوق 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 را با حالت نوشته شده ی فوق در زیر مشاهده می کنید:

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

در پایتون 2.4 و بالاتر، می توان از decorator استفاده کرد:

توجه به چند نکته ضروری می باشد:

  • تنها یک آرگومان (تک) داخل تابع ما ارسال شده است.
  • بررسی برای تعداد آرگومان های مورد به وسیله تعداد فراخوانی تابع انجام شده است، بنابراین نیازی به انجام آن نیست.
  • کتیشن اطراف آرگومان (در صورت وجود) حذف شده است، بنابراین ما یک رشته ی یونیکد عادی دریافت می کنیم.

تگ های Inclusion

تگ template رایج دیگر نوعی است که برخی داده ها را از طریق ارائه ی template دیگر نمایش می دهد. برای مثال، رابط مدیر جنگو از تگ های template سفارشی برای نمایش دکمه ها زیر فرم صفحات "add/change" استفاده می کند. آن دکمه های همواره دارای ظاهر یکسان می باشند، ولی نشانه های لینک بسته به ویرایش شیء تغییر می کند. آن ها یک مورد عالی برای استفاده از یک template کوچک می باشد که با جزئیات فرم شیء فعلی پر شده اند.

این قبیل از تگ ها، تگ های inclusion نامیده می شوند. نوشتن تگ های inclusion بهتر موضوعات دیگر با مثال قابل نشان دادن است. اجازه دهید یک تگ بنویسیم که یک لیست از کتاب ها برای یک شیء Author داده شده تولید می کند. ما از تگ به صورت زیر استفاده می کنیم:

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

نتیجه چیزی شبیه به کد زیر خواهد بود:

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

ابتدا، تابعی تعریف می شود که آرگومانی دریافت کرده و یک دیکشنری از داده ها برای نتیجه تولید کند. تقدت داشته باشید که نیاز به برگرداندن تنها یک دیکشنری می باشد، نه چیز پیچیده تر دیگری. این به صورت context برای قطعه ی template استفاده شده است:

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

در قدم بعدی، template ای با استفاده جهت ارائه ی خروجی تگ ساخته می شود. مثال زیر، template ای بسیار ساده می باشد:

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

در پایان، تگ inclusion با استفاده از فراخوانی متد inclusion_tag() در یک شیء Library ساخته و معرفی می شود.

مثال زیر، در صورتیکه template قبلی در یک فایل با نام book_snipper.html باشد، به صورت زیر معرفی می شود:

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

همچنین در پایتون 2.4 به بالا می توان به شکل زیر نیز کار کرد:

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

گاهی اوقات، تگ های inclusion شما نیاز به دسترسی به مقادیری از context مربوط به template پدر دارد. برای حل این موضوع، جنگو یک انتخاب takes_context را برای تگ ها inclusion ارائه کرده است. در صورتیکه در ساختن یک تگ inclusion از takes_context استفاده کنید، تگ آرگومان های الزامی نخواهد داشت، و تابع زیرین پایتون یک آرگومان خواهد داشت: template context تا زمانیکه تگ فراخوانی شده بود.

برای مثال، فرض کنید شما یک تگ inclusion می نویسید که همواره در یک context که حاوی متغیرهای home_link و home_title می باشد استفاده خواهد شد که به صفحه ی اصلی اشاره می کند. تابع پایتون آن به شکل زیر خواهد بود:

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

(توجه داشته باشید که اولین پارامتر باید context نامیده شود.)

template مورد نظر یعنی link.html باید حاوی کد زیر باشد:

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

سپس، هر زمان که بخواهید از آن تگ سفارشی استفاده کنید، کتابخانه ی آن را بارگذاری کرده و آن را بدون هیچ آرگومانی فراخوانی کنید:

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

نوشتن Template Loader های سفارشی

template loader های داخلی جنگو (که در بخش "داخل بارگذاری Template" توضیح داده شد) معمولا تمام احتیاجات بارگذاری template های شما را پوشش می دهند، ولی در صورتی که نیاز به منطق ویژه ی بارگذاری باشد، نوشتن Template loader برای خودتان واقعا ساده می باشد. برای مثال، می توان template ها را از یک پایگاه داده، یا به طور مستقیم از یک Subversion repository و یا (همانطور که کمی بعد توضیح داده خواهد شد) از یک ZIP archive بارگذاری کرد.

انتظار می رود یک template loader (هر آیتم در تنظیم TEMPLATE_LOADERS می باشد) یک شیء قابل فراخوانی با رابط زیر باشد:

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

آرگومان 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 هایی باشد:

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

تنها قدم کج در صورتیکه بخواهیم از این 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() را با هر تنظیمی که می خواهید تعیین کنید فراخوانی کنید.

لینک به دیدگاه
  • 2 هفته بعد...

مدل های پیشرفته در جنگو (Django)

شیء های مرتبط

مدل های book در آموزش مدل جنگو را بخاطر بیاورید:

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

همانطور که در آموزش مدل جنگو توضیح داده شد، دسترسی به مقدار یک فیلد خاص در یک شیء پایگاه داده به سادگی استفاده از یک attribute می باشد. برای مثال، جهت معلوم کردن عنوان یک کتاب با ID پنجاه، به شکل زیر عمل نمودیم:

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

ولی چیزی که قبلا ذکر نشده است شیء های مرتبط می باشد – فیلدهای بیان شده به صورت یک ForeignKey یا ManyToManyField – که کمی متفاوت عمل می کنند.

دسترسی به مقادیر Foreign Key

هنگامی که به یک فیلد ForeignKey دسترسی پیدا می کنید، شما شیء مدل مرتبطی دریافت خواهید کرد. برای مثال:

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

با فیلدهای ForeignKey، با روش دیگری مواجه خواهیم بود، ولی این کمی متفاوت است، به دلیل اینکه ارتباط نامتقارن می باشد. جهت بدست آوردن لیست کتاب ها برای ناشر داده شده، از publisher.book_set.all() استفاده کنید:

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

در پشت صحنه، book_set تنها یک QuerySet می باشد (همانطور که در آموزش مدل جنگو توضیح داده شد)، و می تواند همانند QuerySet های دیگر فیلتر یا برش داده شود. برای مثال:

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

نام attribute فوق یعنی book_set با استفاده از اضافه کردن نام مدل به صورت حروف کوچک به _set تولید شده است.

دسترسی به مقادیر Many-to-Many

مقادیر Many-to-Many همانند مقادیر foreign-key کار می کنند، با این تفاوت که ما با مقادیر QuerySet به جای نمونه های مدل سر کار داریم. برای مثال، در اینجا نحوه ی مشاهده ی نویسندگان برای یک کتاب وجود دارد:

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

در کد فوق، همانند فیلدهای foreignKey، book_set با اضافه کردن نام مدل به صورت حروف کوچک به _set تولید شده است.

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

Here, as with ForeignKey fields, the attribute name book_set is generated by appending the lower case model name to _set.

ایجاد تغییرات برای یک طرح پایگاه داده

هنگامی که دستور syncdb را در آموزش مدل جنگو معرفی کردیم، اشاره شد که syncdb تنها جداولی را که هنوز در پایگاه داده وجود ندارند را ایجاد می کند – این تغییرات همزمان و یا انجام حذف مدل ها نیست. در صورتیکه شما فیلد مدل را اضافه یا تغییر دهید، یا اگر یک مدل را حذف کنید، نیاز خواهید داشت تغییر را به صورت دستی در پایگاه داده اعمال کنید. این فصل نحوه ی انجام این کار را توضیح می دهد.

هنگام سر و کار داشتن با تغییرات طرح، به خاطر داشتن چند نکته درباره نحوه ی کار لایه پایگاه داده اهمیت دارد:

  • جنگو با صدای بلند شکایت خواهد کرد اگر یک مدل حاوی فیلدی باشد که هنوز در جدول پایگاه داده ساخته نشده باشد. بار اولی که از API پایگاه داده برای کوئری جدول داده شده استفاده کنید باعث بروز خطا خواهد شد (این اتفاق زمان اجرای کد رخ می دهد، نه در زمان کامپایل)
  • جنگو اهمیتی نمی دهد اگر جدول پایگاه داده حاوی ستون هایی باشد که در مدل تعریف نشده اند.
  • جنگو اهمیتی نمی دهد اگر یک پایگاه داده حاوی جدولی باشد که با یک مدل نشان داده نشده باشد.

ایجاد تغییرات schema موضوعی از تغییر قسمت های مختلف می باشد – کد پایتون و خود پایگاه داده.

اضافه کردن فیلدها

هنگامی که یک فیلد به جدول/مدل اضافه می کنید، حقه این است که از این خاصیت که فریم ورک یا چارچوب جنگو به یک جدول حاوی ستون ها که در مدل تعریف نشده اند اهمیت نمی دهد استفاده کنیم. استراتژی اضافه کردن ستون در پایگاه داده می باشد، و سپس به روز رسانی مدل برای یک فیلد جدید.

هر چند این مشکل که کدام عمل اول انجام شود در اینجا وجود دارد، زیرا به منظور دانستن نحوه ی ستون جدید پایگاه داده که باید در SQL بیان شود، نیاز است خروجی دستور manage.py sqlall را نگاه کنید که نیازمند این است که فیلد در مدل وجود داشته باشد. (توجه داشته باشید که برای ساختن ستون خود لازم نیست دقیقا از SQL یکسانی که جنگو از آن استفاده می کند استفاده کنید، ولی ایده ی خوبی است که از آن استفاده کنید، تنها برای مطمئن شدن اینکه همه چیز به طور همزمان است.)

راهکار برای این مشکل که کدام عمل اول انجام شود، استفاده از یک توسعه ی محیطی به جای ایجاد تغییرات در production سرور است. (شما در حال استفاده از یک testing/development محیطی می باشید، درست است؟) در اینجا مراحل دقیق وجود دارد.

ابتدا، این مراحل در development environment استفاده کنید (نه بر روی production server)

  • فیلد را به مدل خود اضافه کنید.
  • دستور manage.py sqlall [yourapp] را جهت مشاهده ی عبارت جدید CREATE TABLE برا مدل اجرا کنید. تعریف ستون برای فیلد جدید را توجه کنید.
  • interactive shell پایگاه داده ی خود را اجرا کرده (psql یا mysql و یا می توانید از manage.py dbshell استفاده کنید). یک عبارت ALTER TABLE را که ستون جدید شما را اضافه می کند اجرا کنید.
  • interactive shell پایتون را از طریق manage.py shell اجرا نموده و مطمئن شوید که فیلد جدید به درستی اضافه شده است از طریق import کردن مدل و واکشی از جدول (مانند MyModel.objects.all()[:5]). در صورتیکه پایگاده داده را به درستی به روز رسانی کرده باشید، عبارت باید بدون خطا کار کند.

سپس بر روی production server این مراحل را انجام دهید:

  • interactive shell پایگاه داده را اجرا کنید.
  • عبارت ALTER TABLE ای که در مرحله ی سوم development environment استفاده شد را اجرا کنید.
  • فیلد را به مدل خود اضافه کنید. در صورتیکه در دست ترجمه ...
  • وب سرور را برای اعمال تغییرات دوباره راه اندازی کنید.

برای مثال، تصور کنید یک فیلد با نام num_pages را به مدل Book که در آموزش مدل جنگو توضیح داده شد اضافه کرده ایم. ابتدا، درون development environment مدل خود را مانند زیر تغییر می دهیم:

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

سپس دستور manage.py sqlall books را برای مشاهده ی عبارت CREATE TABLE اجرا می کنیم. بسته به پایگاه داده ی شما، خروجی چیزی شبیه به کد زیر است:

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

ستون جدید بدین شکل نشان داده شده است:

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

در قدم بعدی، interactive shell پایگاه داده را برای توسعه ی پایگاه داده با تایپ کردن psql (برایPostgreSQL) اجرا کرده، و عبارت زیر را درون آن اجرا می کنیم:

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

اضافه کردن ستون های NOT NULL

در اینجا زیرکی قابل ذکری وجود دارد. هنگامی که فیلد num_pages را به مدل خود اضافه کردیم، امکان های blank=True و null=True را نیز استفاده کردیم. این کار به این دلیل انجام شده است که یک ستون پایگاه داده هنگام ساختن آن در با اول حاوی مقادیر NULL باشد.

هرچند، این امکان نیز وجود دارد که ستون هایی را اضافه کنیم که نتوانند حاوی مقادیر NULL باشند. برای انجام چنین کاری، باید ستون به صورت NULL ساخته شود، سپس مقادیر ستون با استفاده از برخی پیشفرض ها مقدار دهی شود، و پس از آن ستون را به NOT NULL تغییر داد. برای مثال

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

در صورتیکه از این روش استفاده می کنید، بخاطر داشته باشید که باید blank=True و null=True را در مدل خود حذف کنید.

بعد از عبارت ALTER TABLE، با اجرای shell پایتون و اجرای کد زیر در آن مطمئن می شویم که تغییر به درستی کار می کند:

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

در صورتیکه کد فوق موجب بروز هیچ خطایی نشود، بر روی production server رفته و عبارت ALTER TABLE را در پایگاه داده ی production اجرا می کنیم. سپس، مدل موجود در production environment را به روز رسانی کرده و وب سرور را دوباره راه اندازی می کنیم.

حذف فیلدها

حذف یک فیلد از یک مدل بسیار ساده تر از اضافه کردن آن می باشد. برای حذف یک فیلد، تنها کافیست مراحل زیر را دنبال کنید:

  • فیلد مورد نظر را از مدل خود حذف کرده و وب سرور را دوباره راه اندازی کنید.
  • ستون مورد نظر را از پایگاه داده ی خود، با استفاده از دستور زیر حذف کنید:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

اطمینان حاصل کنید مراحل فوق را به ترتیب انجام می دهید. در صورتیکه ابتدا ستون را از پایگاه داده حذف کنید، جنگو (Django) به سرعت خطایی را ایجاد خواهد کرد.

حذف فیلدهای Many-to-Many

به دلیل آنکه فیلدهای many-to-many متفاوت از فیلدهای عادی می باشند، روند حذف آن ها نیز متفاوت می باشد:

  • ManyToManyField را از مدل خود حذف کرده و وب سرور را دوباره راه اندازی کنید.
  • جدول many-to-many را از پایگاه داده ی خود حذف با استفاده از دستور زیر حذف کنید:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

همانند بخش قبلی، اطمینان حاصل کنید که مراحل فوق را به ترتیب انجام می دهید.

حذف مدل ها

حذف کردن یک مدل کاملا ساده تر از حذف یک فیلد می باشد. برای حذف یک مدل، تنها کافیست مراحل زیر را دنبال کنید:

  • مدل مورد نظر را از فایل models.py خود حذف کرده و وب سرور را دوباره راه اندازی کنید.
  • جدول مورد نظر را از پایگاه داده ی خود به استفاده از کد زیر حذف کنید:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

توجه داشته باشد که، ممکن است نیاز باشد تا هر جدول وابسته به این جدول را درون پایگاه داده حذف کنید – مانند، هر جدولی که به books_book دارای کلید خارجی باشد.

همانند بخش قبلی، اطمینان حاصل کنید که مراحل فوق را به ترتیب انجام می دهید.

Managers

در عبارت Book.objects.all()، objects یک attribute ویژه از میان کوئری هایی که به پایگاه داده ارسال می شود می باشد. در آموزش مدل جنگو این attribute، به طور خلاصه به صورت manager مدل توضیح داده شد. حالا زمان آن است که کمی عمیق تر به هویت manager ها و اینکه چگونه می توان از آن ها استفاده کرد بپردازیم.

به طور خلاصه، manager مدل یک شیء می باشد که مدل های جنگو از طریق آن کوئری های پایگاه داده را اجرا می کنند. هر مدل جنگو دارای حداقل یک manager می باشد، و می توان manager های سفارشی ای را به منظور سفارشی سازی دسترسی به پایگاه داده ایجاد نمود.

دو دلیل که ممکن است شما بخواهید یک manager سفارشی ایجاد کنید وجود دارد: برای اضافه کردن متدهای manager اضافه، و یا برای تغییر QuerySet اولیه ای که manager بر می گرداند.

اضافه کردن متدهای اضافه Manager

اضافه کردن متدهای اضافه ی manager روشی مقدم برای اضافه کردن عملکرد در سطح جدول برای مدل های شما می باشد. (برای عملکرد در سطح ردیف – مانند توابعی که بر روی یک نمونه ی تکی از یک شیء مدل عمل می کنند – از متدهای مدل استفاده کنید، که پیش تر در این فصل توضیح داده شده است.)

برای مثال، اجازه دهید برای مدل Book یک manager متد title_count() که که یک keyword دریافت کرده و تعداد کتاب هایی که عنوان آن ها حاوی keyword می باشد را بر می گرداند بنویسیم. (این مثال کمی ساختگی می باشد، ولی نحوه کار manager ها را نشان می دهد.)

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

با وجود manager فوق، می توان:

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

در نکته هایی درباره ی کد فوق را مشاهده می کنید:

  • یک کلاس BookManager ایجاد شد که از کلاس django.db.models.Manager ارث بری کرده است. این کلاس دارای تنها یک متد title_count() می باشد که محاسبات را انجام می دهد. توجه داشته باشید که متد از self.filter() استفاده کرده است که self به خود manager مراجعه می کند.
  • BookManager() به objects در مدل اختصاص داده شده است. این دارای تاثیر از جابه جایی پیشفرض manager برای مدل می باشد که objects نامیده می شود و در صورتیکه یک manager سفارشی تعیین نشده باشد به طور خودکار ساخته می شود. ما آن را به جای هر چیز دیگری، objects می نامیم. در دست ترجمه ...

چرا می خواهیم یک متد مانند title_count() اضافه کنیم؟ برای ایجاد کدی عمومی برای اجرای کوئری ها که نیازی به کد تکراری نداشته باشیم.

تغییر QuerySet های اولیه manager

QuerySet پایه ی manager تمام شیء های سیستم را بر می گرداند. برای مثال، Book.objects.all() تمام کتاب های موجود در پایگاه داده ی book را بر می گرداند.

می توان از طریق بازنویسی متد Manager.get_query_set()، QuerySet پایه ی manager را بازنویسی کرد. get_query_set() باید یک QuerySet با ویژگی هایی که نیاز است را بر گرداند.

برای مثال، مدل زیر دارای دو manager می باشد – یکی تمام شیء ها را بر می گرداند و دیگری تنها کتاب های نوشته شده توسط Roald Dahl را بر می گرداند.

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

با مدل نمونه فوق، Book.objects.all() تمام کتاب های موجود در پایگاه داده را بر خواهد گرداند، ولی Book.dahl_objects.all() تنها کتاب هایی که توسط Roald Dahl نوشته شده اند را بر خواهد گرداند. توجه داشته باشید که به طور صریح objects را در نمونه Manager عادی قرار داده ایم، زیرا در غیر اینصورت، تنها manager قابل دسترس dahl_objects خواهد بود.

البته، به دلیل آنکه get_query_set() یک شیء QuerySet بر می گرداند، می توانید از filter()، exclude() و تمام متدهای QuerySet در آن استفاده کرد. بنابراین عبارت های زیر درست می باشند:

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

مثال فوق همچنین به تکنیک های جالب دیگری نیز اشاره می کند: استفاده از manager های چندگانه در یک مدل. می توان به صورت بسیاری از نمونه های Manager() به یک مدل همانطور که می خواهید attach کنید.

برای مثال:

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

مثال فوق به شما اجازه می دهد Person.men.all()، Person.women.all() و Person.people.all() را درخواست کنید. در دست ترجمه ...

در صورتیکه از شیء های Manager سفارشی استفاده می کنید، توجه داشته باشید که اولین Manager ای که جنگو با آن برخورد می کند (به ترتیبی که درون مدل تعریف شده اند) دارای وضعیت ویژه ای می باشد. جنگو اولین Manager تعریف شده در یک کلاس را به صورت Manager پیشفرض تلقی می کند، و چندین بخش از جنگو (بجز برنامه ی مدیر) منحصرا برای آن مدل از این Manager استفاده خواهند کرد. نتیجه این که، اغلب ایده ی خوبی است که با دقت بیشتری manager پیشفرض را انتخاب کنیم، به منظور اجتناب از وضعیتی که نتایج get_query_set() را به دلیل ناتوانی برای بازیابی شیء هایی که می خواهید با آن ها کار کنید بازنویسی کنید.

متدهای Model

متدهای سفارشی در یک مدل برای اضافه کردن عملکرد سفارشی در سطح ردیف به شیء های خودتان اضافه کنید. در حالیکه manager ها برای چیزهایی در سطح جدول در نظر گرفته شده اند، متدهای مدل باید در یک نمونه ی مدل خاص عمل کنند.

این یک تکنیک ارزشمند برای نگهداشتن business logic در یک مکان می باشد – مدل.

ذکر یک مثال ساده ترین راه برای توضیح این موضوع می باشد. در اینجا یک مدل با تعدادی متدهای سفارشی وجود دارد:

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

آخرین متد در مثال فوق یک "property" می باشد. برای اطلاعات بیشتر در مورد property ها به

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

کاربرد مثال فوق:

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

اجرای کوئری های خام SQL

گاهی اوقات می خواهید کوئری هایی را به صورت مستقیم در پایگاه داده ی خود اجرا کنید. به آسانی می توان این کار را از طریق دسترسی به شیء django.db.connection انجام داد که connection فعلی پایگاده داده را نشان می دهد. برای استفاده از آن، connection.cursor() را جهت بدست آوردن یک شیء cursor فراخوانی کنید، سپس جهت اجرای SQL و cursor.fetchone() یا cursor.fetchall برای برگرداندن نتیجه ی ردیف ها cursor.execute(sql, [params]) را فراخوانی کنید:

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

connection و cursor غالبا "DB-API" استاندارد پایتون را اجرا می کنند که می توانید برا اطلاعات بیشتر در مورد آن به

برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
مراجعه کنید. در صورتیکه با DB-API پایتون آشنایی ندارید، توجه داشته باشید که عبارت SQL در cursor.execute() از حفره های "%s" به جای اضافه کردن پارامتر های به صورت مستقیم درون SQL استفاده می کند. در صورتیکه از این تکنیک استفاده می کنید، کتابخانه ی زیرین پایگاه داده به طور خودکار کتیشن هایی در صورت نیاز اضافه خواهد کرد. در دست ترجمه ...

به جای آن که کد view شما به صورت درهم و برهم و پراکنده با عبارت های django.db.connection قرار بگیرد، ایده ی خوبی است که آن ها را در متدهای سفارشی مدل یا متدهای manager قرار دهیم. برای مثال، مثال فوق می تواند درون یک متد manager سفارشی مانند زیر جمع شود:

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

کاربرد مثال فوق:

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

 

لینک به دیدگاه
  • 2 هفته بعد...

View های Generic در جنگو

در بدترین شکل ممکن، توسعه وب کار خسته کننده ای خواهد بود، تاکنون، نحوه ی تلاش فریم ورک یا چارچوب جنگو (Django) برای انجام برخی از این کارهای یکنواخت، در مدل و لایه ی template گفته شده است، همچنین توسعه دهندگان، این یکنواختی را در سطح view نیز تجربه کرده اند.

view های generic جنگو، برای ساده کردن این یکنواختی ها توسعه داده شده اند. view های generic برخی از الگوها و روندهای مشترک یافت شده در توسعه ی view را، در نظر گرفته و آن ها را طوری طراحی کرده اند که شما بتوانید به سرعت view های مشترک را بدون اینکه مجبور باشید کد زیادی بنویسید ایجاد کنید. در حقیقت، تقریبا هر مثال view ای در فصل های گذشته بیان شده است، می توانند با استفاده از view های generic بازنویسی شوند.

آموزش view و urlconf پیشرفته به طور خلاصه نحوه ی ایجاد یک view generic را توضیح داده است. جهت مرور، می توانیم بعضی از وظایف مشترک را شناسایی کنیم، مانند نمایش یک لیست از شیء ها، و نوشتن کدی که یک لیست از هر شیءی رانمایش دهد. سپس مدل مورد نظر می تواند به صورت یک آرگومان اضافه به URLconf ارسال شود.

جنگو view های generic را برای انجام کارهای زیر ارائه کرده است:

  • انجام وظایف ساده و مشترک: تغییر مسیر به یک صفحه ی متفاوت، یا ارائه ی یک template داده شده.
  • نمایش صفحات "list" و "detail" برای یک شیء تک. view های event_list و entry_list از آموزش view و urlconf پیشرفته مثال های از لیست view ها می باشند. یک صفحه ی تک event یک مثال از آنچه را که ما "detail" view می نامیم می باشد.
  • ارائه دادن شیء های بر اساس تاریخ در صفحات بایگانی سال/ماه/روز، همراه با جزئیات و صفحات "latest". وبلاگ سال، ماه و روز (http://www.djangoproject.com/weblog/) بایگانی با این ها ساخته شده اند، به صورت نوعی بایگانی روزنامه خواهد بود.

روی هم رفته، این view ها، رابط های ساده ای برای انجام رایج ترین وظایفی که توسعه دهندگان با آن روبرو هستند تهیه شده اند.

استفاده از View های Generic

تمام این view ها با ساختن پیکربندی دیکشنری های در فایل URLconf شما، و ارسال کردن آن دیکشنری ها به صورت عضو سوم از تاپل URLconf برای الگوی داده شده استفاده می شوند. (بخش "ارسال انتخاب های اضافه برای توابع view" را در آموزش view و urlconf پیشرفته برای مرور کلی درباره ی این تکنیک مطالعه کنید.)

برای مثال، در اینجا یک URLconf ساده وجود دارد که شما می توانید برای ارائه ی یک صفحه ی استاتیک "about" از آن استفاده کنید:

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

هر چند کد فوق ممکن است در نگاه اول کمی جادویی به نظر برسد – نگاه کنید، یک view بدون هیچ کدی! – کد فوق دقیقا با مثال آموزش view و urlconf پیشرفته یکی می باشد: view مورد نظر در آن مثال یعنی direct_to_template به سادگی اطلاعات را از پارامترهای اضافه ی دیکشنری دریافت کرده و زمان render شدن view از آن اطلاعات استفاده می کند.

به این دلیل که view های generic مانند توابع view دیگر یک تابع view عادی می باشد، می توان آن ها را، درون view های خودمان دوباره استفاده کنیم. به عنوان مثال، اجازه دهید مثال "about" را برای مرتبط ساختن URL ها از حالت /about/<whatever>/ برای about/<whatever>.html به طور ثابت render شده گسترش دهیم. ما این کار را با اولین اصلاح URLconf برای اشاره به تابع view انجام خواهیم داد:

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

در قدم بعدی، view مورد نظر یعنی about_pages را خواهیم نوشت:

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

در اینجا با direct_to_template مانند توابع دیگر رفتار کرده ایم. به این خاطر که این تابع یک HttpResponse بر می گرداند، می توانیم به سادگی آن را همانطور که هست برگردانیم. تنها مقداری رفتار خاص در کد فوق وجود دارد که آن هم سر و کار داشتن با template های نا معلوم می باشد. ما قصد استفاده از یک template ای که وجود ندارد و باعث بروز یک خطای سرور می شود را نداریم، بنابراین خطاهای TemplateDoesNotExist را کنترل کرده و خطای 404 را به جای آن بر گردانده ایم.

آیا از نظر امنیتی یک آسیب پذیری در اینجا وجود دارد؟

خوانندگان تیزبین ممکن است متوجه حفره ی امنیتی شده باشند: ما با استفاده محتویاتی که در میان عبارات دیگر جا داده شده اند استفاده کرده ایم (template="about/%s.html"). در نگاه اول، این شبیه به یک آسیب پذیری کلاسیک directory traversal می باشد (که در آموزش امنیت به تفصیل درباره ی آن بحث شده است). ولی آیا واقعا اینطور است؟

نه دقیقا. بله، یک مقدار با هدف مخرب ساخته شده از page می تواند موجب directory traversal شود، درست است که page از URL درخواست گرفته است، ولی نه هر مقداری که قبول شده خواهد بود. نکته ی کلیدی در URLconf این است که: در مثال فوق، از regular expression \w برای تطبیق بخش page از URL استفاده شده است، و \w تنها حروف الفبا و اعداد را قبول می کند. در نتیجه، هر حروف مخربی (مانند نقطه ها و علامت های \) قبل از اینکه به خود view برسند طرد خواهند شد.

View های Generic شیء ها

view مورد نظر یعنی direct_to_template قطعا مفید می باشد، ولی view های generic هنگامی که برای ارائه دادن view ها در محتوای پایگاه داده استفاده می شوند بیشتر خواهند درخشید. به این دلیل که مانند یک وظیفه ی مشترک می باشد، جنگو تعدادی از view های generic داخلی را ارائه کرده است که تولید کردن لیست و جزئیات view های شیء ها را فوق العاده آسان کرده است.

اجازه دهید نگاهی به یکی از این view های generic با نام "object list" بیاندازیم. ما از شیء Publisher که در آموزش مدل جنگو از آن استفاده شده است، در اینجا استفاده کرده ایم:

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

جهت ساختن یک صفحه ی لیست از تمام ناشران، ما از یک URLconf مانند زیر استفاده می کنیم:

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

تمام چیزی که برای نوشتن نیاز است کد پایتون می باشد. ولی هنوز نیاز به نوشتن یک template داریم. می توان یک کلید دیگر با نام template_name در آرگومان های اضافه دیکشنری که شامل template مورد نظر می باشد به publisher_info اضافه کرد:

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

در صورت نبودن template_name، به هر حال generic view مورد نظر یعنی object_list از نام شیء یکی را استنباط خواهد کرد. در این مورد، template استنباط شده، "books/publisher_list.html" خواهد بود – جزء "books" از نام app گرفته شده است، هنگامی که جزء "publisher" تنها حروف کوچک از نام مدل است آن را تعریف می کند.

این template در مقابل یک context حاوی یک متغیر به نام object_list، render خواهد شد که حاوی تمام شیء های publisher است. یک template خیلی ساده ممکن است شبیه به چیزی مانند زیر باشد:

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

(توجه داشته باشید که template فوق فرض می کند یک base.html وجود دارد، همانطور که در آموزش template جنگو در یک مثال ایجاد کردیم.)

در دست ترجمه/تالیف ...

گسترش View های Generic

شکی وجود ندارد که استفاده از view های generic می تواند سرعت توسعه را به شکل قابل ملاحظه ای افزایش دهد. در اغلب پروژه ها، در دست ترجمه/تالیف .... در واقع، یکی از رایج ترین سوالات پاسخ داده شده توسط توسعه دهندگان جدید فریم ورک یا چارچوب جنگو، نحوه ی ایجاد کردن کنترل یک مجموعه ای از شرایط گسترده تر view های generic می باشد.

خوشبختانه، تقریبا در هر یک از این موارد، روش هایی برای گسترش ساده ی view های generic برای کنترل یک آرایه ی بزرگتر از موارد استفاده وجود دارد.

ایجاد Template Context های مساعد

ممکن است توجه کرده باشید لیست ناشران یعنی درون متغیری با نام object_list ذخیره شده است. زمانی این کد زیباتر خواهد شد که، این نام زمانی برای نویسندگان template مساعد خواهد بود که به جای نام object_list نام آن publisher_list باشد؛ محتویات این متغیر با این نام واضح تر خواهد بود.

نام این متغیر را می توان به سادگی با آرگومان template_object_name تغییر داد:

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

درون template، view جنریک یک _list به template_object_name اضافه می کند.

ایجاد یک template_object_name مفید همواره یک ایده ی خوب می باشد. همکاران شما کسانی که template ها را طراحی می کند از شما ممنون خواهند بود.

اضافه کردن context اضافه

گاهی اوقات، ممکن است نیاز باشد برخی اطلاعات اضافه فراتر از اطلاعات تهیه شده توسط view جنریک ارائه شوند. برای مثال، یک لیست از تمام ناشران دیگر در هر صفحه ی جزئیات هر ناشر را تصور کنید. view جنریک مورد نظر یعنی object_detail ناشر را برای context تهیه می کند، ولی به نظر می رسد هیچ راهی برای بدست آوردن یک لیست از تمام ناشران در آن template وجود ندارد.

ولی باید متذکر شد که راهی وجود دارد: تمام view های جنریک یک پارامتر اختیاری اضافه به نام extra_context دریافت می کنند. این که دیکشنری از شیء های اضافه می باشد که به context ارسال شده به template اضافه خواهد شد. بنابراین، جهت ایجاد لیست ناشران، از یک دیکشنری اطلاعات مانند زیر استفاده کرده ایم:

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

کد فوق یک متغیر {{ book_list }} در context موجود در template قرار می دهد. این الگو می تواند برای ارسال هر اطلاعاتی به درون template برای view جنریک استفاده شود که بسیار نیز مفید می باشد.

هر چند، در واقع یک اشکال ظریف در اینجا وجود دارد – می توانید آن را حدس بزنید؟

مشکل باید هنگامی که کوئری های درون extra_context ارزیابی شدند ایجاد شود. زیرا این مثال Book.objects.all() را درون URLconf قرار داده است، در این حالت تنها یک بار ارزیابی می شود (هنگامی که URLconf برای اولین بار بارگذاری می شود). هنگامی که شما ناشران را حذف یا اضافه می کنید، دقت خواهید داشت که view جنریک تا زمانی که وب سرور را دوباره بارگذاری نکرده اید، این تغییرات را منعکس نخواهد کرد.

نکته

این مشکل در مورد آرگومان view جنریک، queryset اعمال نمی شود. چرا که فریم ورک یا چارچوب جنگو می داند که QuerySet خاص نباید هرگز ذخیره سازی (cache) شود، view جنریک زمانی هر view می خواهد render شود cache مناسبی را انجام می دهد.

راه حل، استفاده از یک callback در extra_context به جای متغیر می باشد. هر چیز قابل فراخوانی (مانند یک تابع) ارسال شده به extra_context زمانی که view ارائه شود ارزیابی خواهد شد (به جای تنها یک بار). می توانید با تعریف یک تابع مشکل فوق را حل کنید:

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

یا می توانید از یک روش کوتاه تر که بر این واقعیت تکیه دارد که Book.objects.all خودش قابل فراخوانی می باشد استفاده کنید:

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

به Book.objects.all بدون پرانتز پایانی توجه کنید. این حالت به تابع در واقع بدون فراخوانی آن رجوع می شود.

تماشای زیر مجموعه ای از شیء ها

اکنون اجازه دهید نگاه نزدیک تری به کلید queryset که در طول این مسیر از آن استفاده کرده ایم داشته باشیم. اغلب view های جنریک از این آرگومان های queryset را دریافت می کنند – این نحوه ی فهم view می باشد که کدام مجموعه از شیء ها را نمایش دهد (بخش "انتخاب شیء ها" در فصل پنچم برای مقدمه ی شیء های Queryset را مطالعه کنید).

برای برگزیدن یک مثال ساده، می خواهیم یک لیست از کتاب ها را از با تاریخ انتشار چیدمان کنیم.

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

مثال فوق کاملا ساده می باشد، ولی یک با حالتی ظریف ایده ای را نشان می دهد. البته، معمولا بیشتر از چند بار شیء ها می خواهید که چیدمان کنید. در صورتی که می خواهید لیستی از کتاب ها با یک ناشر خواص ارائه دهید، می توانید از تکنیکی یکسان استفاده کنید:

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

دقت داشته باشید که به همراه یک queryset فیلتر شده، همچنین از یک نام template سفارشی استفاده کرده ایم. در صورتی که این کار را انجام ندهیم، view جنریک از template همسان به صورت شیء لیست "vanilla" استفاده خواهد کرد، که ممکن است آنچه که می خواهید نباشد.

همچنین دقت داشته باشید که روش خیلی ظریفی برای انجام کتاب های ناشر خاص نمی باشد. در صورتی که بخواهید صفحه ی ناشر دیگری را اضافه کنید، نیاز تعدادی خط دیگر در URLconf و بیش از چند ناشر می باشد. در بخش بعدی با این مشکل سر و کار خواهیم داشت.

فیلتر کردن پیچیده با توابع wrapper

نیاز رایج دیگر، فیلتر کردن شیء های داده شده در یک صفحه ی لیست از طریق برخی کلیدها در URL می باشد. کمی قبل تر، نام ناشر را به طور مستقیم درون URLconf قرار می دادیم، ولی چه می شد اگر می خواستیم یک view بنویسیم که تمام کتاب ها را از طریق برخی ناشران دلخواه نمایش دهد؟ راهکار wrap کردن جنریک view مورد نظر یعنی object_list جهت اجتناب از نوشتن مقدار زیادی کد به صورت دستی می باشد. به طور معمول، با نوشتن یک URLconf شروع می کنیم:

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

در قدم بعدی، view مورد نظر یعنی books_by_publisher را خواهیم نوشت:

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

کد فوق جواب می دهد، زیرا واقعا هیچ چیز خاصی درباره ی view های جنریک وجود ندارد – کد فوق تنها توابع پایتون می باشد. همانند هر تابع view ای، view های جنریک مجموعه ای خاص از آرگومان ها دریافت کرده و شیء های HttpResponse را بر می گردانند. در نتیجه، wrap کردن یک تابع در اطراف یک view جنریک به طور باور نکردنی ساده می باشد که کار اضافه قبل (یا بعد؛ به بخش بعدی را مطالعه کنید) view جنریک را کنترل می کند.

نکته

دقت داشته باشید که در مثال قبلی، publisher جاری در حال نمایش را در extra_context ارسال نمودیم. این معمولا فکر خوبی برای wrapper های از این نوع می باشد؛ که اجازه می دهد template شیء "parent" ای را که در حال حاضر در حال جستجو می باشد را بشناسد.

انجام کار اضافه

آخرین الگوی رایجی که مورد بحث قرار خواهیم داد، انجام کار اضافه قبل یا بعد از فراخوانی view جنریک می باشد.

تصور کنید یک فیلد last_accessed در شیء Author موجود می باشد که برای پیگیری آخرین زمانی که کسی author را نگاه کرده است استفاده می شود. view جنریک object_dtail، البته هیچ چیزی درباره ی این فیلد نمی داند، ولی یک بار دیگر به سادگی یک view سفارشی، برای نگه داشتن فیلد به روز رسانی شده می نویسیم.

در ابتدا، نیاز به اضافه کردن یک بخش جزئیات نویسنده در URLconf برای اشاره به یک view سفارشی داریم:

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

سپس تابع wrapper خودمان را می نویسیم:

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

نکته

کد فوق در واقع کار نخواهد کرد مگر این که شما یک فیلد last_accessed به مدل Author اضافه کنید و یک template با نام books/author_detail.html بسازید.

می توان یک روش همسان برای تغییر پاسخ برگدانده شده از طریق view جنریک استفاده کرد. در صورتیکه بخواهیم یک نسخه ی متنی از لیست نویسندگان با قابلیت دانلود تهیه کنیم، می توان از یک view شبیه به زیر استفاده کرد:

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

کد فوق جواب خواهد داد، چرا که view ها جنریک شیء های ساده ی HttpResponse را بر می گردانند که می توانند مانند دیکشنری برای مجموعه ای از HTTP header ها رفتار کنند. ضمنا Content-Disposition، به مرورگر یاد می دهد که صفحه به جای اینکه در مرورگر نمایش دهد، آن را دانلود و ذخیره کند.

لینک به دیدگاه
  • 2 هفته بعد...

تولید محتوای غیر html ای در جنگو

معمولا هنگامی که درباره ی deploy کردن وب سایت ها صحبت می شود، موضوع صحبت درباره ی تولید HTML می باشد. البته که موارد بسیار دیگری نیز وجود دارد؛ ما از وب برای توزیع داده در تمام قابل بندی ها استفاده می کنیم: RSS، PDF، عکس ها و غیره ....

تاکنون، تمرکز بر روی تولید HTML بوده است، ولی در این آموزش، از این مسیر منحرف شده و به استفاده ی جنگو برای تولید انواع دیگر محتویات به غیر از HTML خواهیم پرداخت.

جنگو دارای ابزار داخلی مناسبی می باشد که می توان برای تولید برخی محتوای به غیر از HTML از آن استفاده کرد:

  • Feed های پیوند RSS/Atom
  • Sitemap ها (یک قالب بندی XML توسعه داده شده توسط گوگل که به موتورهای جستجو راهنمایی هایی را می دهد)

هر کدام از ابزار فوق در این قسمت بررسی خواهند شد، ولی ابتدا قواعد اولیه را پوشش خواهیم داد.

اصول اولیه: view ها و MIME-type ها

آموزش view و urlconf جنگو را بخاطر بیاورید که یک تابع view به سادگی یک تابپ پایتون می باشد که یک درخواست وب را دریافت کرده و یک پاسخ وب را بر می گرداند. این پاسخ می تواند محتویات HTML از صفحه ی وب یا یک تغییر مسیر، یا یک خطای 404، یا یک سند XML، یا حتی یک تصویر ... و یا هر چیز دیگری باشد.

به طور رسمی تر، یک تابع view جنگو باید

  • قبول یک نمونه ی HttpRequest به صورت اولین آرگومان
  • برگرداندن یک نمونه ی HttpResponse

نکته ی کلیدی جهت برگرداندن محتوای غیر HTML ای از یک view درون کلاس HttpResponse قرار دارد، به ویژه آرگومان mimetype. با قرار دادن mimetype ، می توان به مرورگر نشان داد که یک پاسخ از قالب بندی متفاوت را بر مر گردانیم.

برای مثال، اجازه دهید به view زیر که یک تصویر با قالب بندی PNG را بر می گرداند را بررسی کنیم، تنها فایل را از حافظه خوانده ایم:

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

همین! در صورتی که شما مسیر تصویر را در فراخوانی open() با یک مسیر برای یک تصویر واقعی جا به جا کنید، می توانید از این view به سادگی برای نمایش یک تصویر استفاده کنید، و مرورگر به درستی آن را نمایش خواهد داد.

نکته ی مهم دیگر که باید در نظر داشت این است که، شیء های HttpResponse با API استاندارد پایتون یعنی "file like object" کار می کنند. این بدین معناست که می توان، از یک نمونه ی HttpResponse را در هر جای پایتون (یا یک کتابخانه ی third-party) که انتظار یک فایل را دارد استفاده کنید.

برای مثالی از نحوه ی کارکرد آن، اجازه دهید نگاهی به تولید CSV با جنگو بیاندازیم.

تولید CSV

CSV یک قالب بندی ساده می باشد که معمولا توسط نرم افزار spreadsheet استفاده می شود. اساسا یک سری از ردیف های جدول با هر سلول در ردیف جدا شده توسط یک علامت کاما می باشد (CVS مخفف comma‑seperated values می باشد). برای مثال، در زیر برخی داده های "unruly" مسافران هوایی در قالب بندی CSV می باشد.

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

نکته

لیست قبلی حاوی اعداد واقعی می باشد! این لیست از مدیریت هواپیمایی فدرال آمریکا آمده است.

هر چند CSV ساده به نظر می رسد، قالب بندی جزئیات آن دارای توافق جهانی نمی باشد. قسمت های مختلف نرم افزار CVS های مختلفی را تولید و استفاده می کند. خوشبختانه، پایتون دارای یک کتابخانه ی استاندار CSV با نام cvs می باشد.

به دلیل آنکه ماژول csv به صورت file-like objects کار می کند، استفاده از آن ساده تر از HttpResponse می باشد:

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

کد فوق باید بسیار واضح باشد، ولی یک نکاتی ویژه برای ذکر کردن وجود دارد:

  • Response به جای mimetype پیشفرض یعنی text/html با mimetype مورد نظر یعنی text/csv معین شده است.
  • متغیر response یک Content-Disposition header اضافه دریافت می کند که حاوی نام فایل CSV می باشد. این header (بخش "attachment") مرورگر را برای یک جا جهت ذخیره کردن فایل به جای نمایش آن بر می انگیزد. نام این فایل دلخواه می باشد؛ آن را هر چه که می خواهید نام گذاری کنید. این توسط مرورگر در دیالوگ "Save As" استفاده خواهد شد.
  • جهت اختصاص دادن یک header در یک HttpResponse ، تنها کافیست به صورت یک دیکشنری و مجموعه ای از کلید و ارزش ها با آن رفتار کنید.
  • در دست ترجمه/تالیف ... به داخل API، CSV_generation ساده می باشد: تنها response را به صورت اولین آرگومان به csv.writer ارسال کنید.
  • برای هر ردیف در فایل CSV، فراخوانی writer.writerow مانند شیءی به صورت یک لیست یا تاپل آن را ارسال می کند.
  • ماژول CSV برای گذاشتن کتیشن برای ما محتاط می باشد، بنابراین نگرانی ای درباره ی رد کردن رشته های با کتیشن یا کاما در آن ها نخواهید داشت. تنها اطلاعات را برای writerow() ارسال می کند.

این الگوی کلی ای می باشد که شما در هر زمان که نیاز به برگرداندن محتوای غیر HTML ای دارید از آن استفاده خواهید کرد: ساختن یک شیء HttpResponse (با یک mimetype ویژه)، ارسال کردن آن به چیزی که انتظار یک فایل را دارد، و سپس برگرداند یک response.

اجازه دهید به نگاهی تعداد بیشتری از مثال بیاندازیم.

ساختن PDF

Portable Document Format (PDF) یک قالب بندی توسعه یافته توسط Adobe می باشد که برای نمایش اسناد قابل چاپ استفاده می شود، قالب بندی pixel-perfect کامل، فونت های جاسازی شده، و گرافیک دو بعدی. می توان یک PDF را معادل دیجیتال یک سند چاپ شده دانست؛ در واقع، PDF ها اغلب در توزیع اسناد به قصد چاپ آن ها استفاده می شود.

می توان به سادگی PDF ها را با پایتون و جنگو، با تشکر از کتابخانه ی عالی منبع باز ReportLab تولید کرد (htt://www.reportlab.org/rl_toolkit.html). مزیت تولید فایل های PDF به طور پویا این است که، می توانید PDF های سفارشی را برای اهداف مختلف ایجاد کنید.

نصب ReportLab

قبل از تولید PDF نیاز می باشد ReportLab را نصب کنید که معمولا خیلی ساده می باشد: تنها کافیست کتابخانه ی آن را از

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

نکته

در صورتیکه توزیع مدرن لینوک استفاده می کنید، ممکن است package management خود را قبل از نصب ReportLab بررسی کنید. اغلب repository های پکیج دارای ReportLab اضافه شده می باشند.

برای مثال، در صورتی که از ubuntu استفاده می کنید، به سادگی دستور apt-get install python-reportlab این کار را انجام خواهد داد.

راهنمای کاربر (به طور طبیعی تنها به صورت یک فایل PDF در دسترس می باشد) در

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

می توان نصب بودن ReportLab را با import کردن آن در interactive interpreter پایتون آزمایش کرد:

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

در صورتیکه دستور فوق هیچ خطایی را ایجاد نکند، نصب درست انجام شده است.

نوشتن view

همانند CSV، تولید PDF ها به صورت پویا با جنگو ساده می باشد، چرا که API مخصوص ReportLab به صورت file‑like object عمل می کند.

در زیر مثال "Hello World" را مشاهده می کنید:

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

نکاتی در مورد کد فوق:

  • در کد فوق از mimetype مخصوص PDF یعنی application/pdf استفاده شده است. این حالت به مرورگرها می گوید که سند به جای یک فایل HTML، یک فایل PDF می باشد. در صورتی که این اطلاعات را جا بیاندازید، مرورگرها پاسخ را به صورت HTML تفسیر می کنند، نتیجه ی به هم ریخته و نا مفهومی در پنجره ی مرورگر ایجاد خواهد کرد.
  • در دست ترجمه/تالیف ...: تنها کافیست response را به صورت آرگومان اول برای canvas.Canvas ارسال کنید. کلاس Canvas انتظار یک file-like object را دارد، و شیء های HttpResponse نیز همینطور.
  • تمام متدهای در دست ترجمه/تالیف ...
  • در پایان، فراخوانی showPage() و save() در فایل PDF با اهمیت می باشد – در غیر این صورت در فایل PDF خراب خواهد شد.

PDF های پیچیده

در صورتی که یک سند پیچیده ی PDF (یا هر قطعه داده ی بزرگ) را ایجاد می کنید، استفاده کردن از کتابخانه ی cStringIO به صورت یک محل نگهداری موقت برای فایل PDF خودتان را بررسی کنید. کتابخانه ی cStringIO یک رابط file-like object را تهیه می کند که برای حداکثر بهره وری در C نوشته شده است.

در زیر مثال "Hello World" قبلی با استفاده از cStringIO باز نویسی شده است:

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

امکانات دیگر

مجموعه ی کاملی از انواع دیگر محتویاتی که می توان در پایتون تولید کرد وجود دارد:

  • فایل های ZIP: کتابخانه ی استاندارد پایتون حاوی ماژول zipfile می باشد، که می تواند فایل های ZIP را هم بنویسد و هم بخواهند. می توان برای تولید بایگانی کردن تعدادی از فایل های مورد نیاز یا فشرده ساختن اسناد بزرگ از آن استفاده کرد. همچنین می توان فایل های TAR را با استفاده از ماژول tarfile در کتابخانه ی استاندارد پایتون تولید نمود.
  • تصاویر پویا: کتابخانه ی تصویر پایتون (PIL؛
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    یک جعبه ابزار خارق العاده برای تولید تصاویر (PNG، JPEG، GIF و مقدار بیشتری) می باشد. شما می توانید برای کاهش اندازه ی تصاویر به عکس های ریز به طور خودکار، تصاویر چندگانه ی مرکب در یک فریم و یا حتی برای انجام پردازش تصویر تحت وب از آن استفاده کنید.
  • طرح ها و نمودارها: تعدادی کتابخانه ی قدرتمند برای طرح ها و نمودارها در پایتون وجود دارد که می توان برای تولید نقشه های مورد نیاز، نمودارها، طرح ها و گرافیک ها از آن استفاده می کرد. مسلما نمی توان تمام آن ها را در اینجا لیست کرد، بنابراین تعداد از موارد برجسته در زیر نام برده شده اند:
  • matplotlib (http://matplotlib.sourceforge.net/) می تواند برای تولید نوعی از طرح های با کیفیت بالا که معمولا توسط MatLab یا Matematica تولید شده اند استفاده شود.
  • pygraphviz (http://networkx.lanl.gov/pygraphviz/)، یک رابط برای لایه ی ابزار گرافیک Graphviz (http://graphviz.org/) می باشد، که می تواند برای تولید نمودارهای ساخت یافته از گرافیک ها و شبکه ها استفاده شود.

به طور کلی، هر کتابخانه ی پایتون قابل ایجاد درون یک فایل می تواند درون جنگو استفاده شود. امکانات عظیم می باشند.

اکنون که با قواعد اولیهه ی تولید محتوای غیر HTML ای، آشنا شدیم، اجازه دهید تصورمان را یک درجه افزایش دهیم. جنگو تعداد ابزار بسیار جذاب برای تولید انواع رایج محتوای غیر HTML ای ارائه می دهد.

فریم ورک یا چارچوب Syndication Feed

جنگو یک فریم ورک یا چارچوب سطح بالا syndication-feed-generating را ارائه می کند که ایجاد RSS و Atom را ساده می کند.

RSS چیست؟ Atom جیست؟

RSS و Atom هر دو XML-based می باشند، که شما می توانید برای تهیه ی به روز رسانی به طور خودکار "feed" های محتویات سایت از آن استفاده کنید. برای اطلاعات بیشتر در مورد RSS می توانید به

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

برای ایجاد هر syndication feed، همه باید یک کلاس کوچک پایتون بنویسند. می توانید هر مقدار "feed" که می خواهید بسازید.

فریم ورک یا چارچوب سطح بالای feed-generating یک view می باشد که به طور قرارداد به /feeds/ وصل شده است. جنگو از باقی مانده ی URL (هر چیزی بعد از /feeds/) برای تعیین feed برای برگرداندن استفاده می کند.

برای ایجاد یک feed، شما یک Feed Class خواهید نوشت و در URLconf خود به آن اشاره می کنید.

مقدار دهی اولیه

برای فعال کردن syndication feed ها روی سایت جنگوی خود، URLconf زیر را اضافه کنید:

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

این خط به جنگو می گوید؛ از فریم ورک RSS جهت کنترل تمام URL هایی که با "feeds/" شروع می شوند استفاده کند. (می توانید پیشوند "feeds/" را برای متناسب ساختن با نیازهای خودتان تغییر دهید.)

این خط URLconf دارای یک آرگومان اضافی می باشد: {'feed_dict': feeds}. از این آرگومان اضافه جهت ارسال feed هایی که باید تحت URL منتشر شده باشند استفاده کنید.

به طور خاص، feed_dict باید یک دیکشنری باشد که نام URL را به کلاس Feed مرتبط می کند. می توانید feed_dict را درون خود URLconf تعریف کنید. در زیر مثال کامل URLconf را ملاحظه می کنید:

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

مثال قبلی دو feed را در نظر گرفته است:

  • feed اول که با LatestEntries نشان داده شده است و در feeds/latest/ قرار خواهد گرفت.
  • دومین feed که با latestEntriesByCategory نمایش داده شده و در feeds/categories قرار خواهد گرفت.

زمان راه اندازی، نیاز خواهید داشت خود کلاس های Feed را تعریف کنید.

یک کلاس Feed یک کلاس ساده ی پایتون می باشد که یک syndication feed را نشان می دهد. یک feed می تواند ساده باشد (مانند یک feed "سایت اخبار"، یا یک feed اولیه که آخرین ورودی های یک بلاگ را نمایش می دهد) یا بسیار پیچیده (مانند یک feed که تمام ورودی های بلاگ در یک طبقه بندی خاص جایی که طبقه بندی متغیر می باشد نمایش می دهد).

کلاس های Feed باید از کلاس django.contrib.syndication.feeds.Feed مشتق شوند. آن ها می توانند در هر جایی از درخت کد شما قرار بگیرند.

یک Feed ساده

مثال ساده ی زیر یک Feed از پنج ورودی آخر برای بلاگ داده شده را توضیح می دهد:

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

نکات قابل توجه در کد فوق از این قرار می باشند:

  • کلاس فوق از کلاس django.contrib.syndication.feeds.Feed مشتق شده است.
  • title، link و description به ترتیب با المان های <title>، <link> و <description> برابر می باشند.
  • items() یک متد می باشد که به سادگی یک لیست از شیء هایی که باید به صورت المان های <item> شامل شده در feed باشند بر می گرداند. اگر چه مثال فوق شیء های Entry را که از API پایگاه داده ی جنگو استفاده می کنند بر می گرداند، item() نباید نمونه های مدل را بر گرداند.

تنها یک گام بیشتر وجود دارد. در یک RSS feed، هر <item> دارای یک <title>، <link> و <description> می باشد. نیاز است داده ای که قرار است برای درون آن المنت ها قرار گیرد، به فریم ورک گفته شود.

  • برای تعیین محتویات <title> و <description>، template های جنگو را با نام های feeds/latest_title.html و feeds/latest_description.html ایجاد کنید، جایی که latest، slug تعیین شده در URLconf برای feed داده شده می باشد. توجه داشته باشید که پسوند .html الزامی می باشد.
  • سیستم RSS آن template را برای هر آیتم render می کند، ارسال آن دو متغیر template:
  • obj: شیء فعلی (هر کدام از شیء هایی که در items() بر گردانده شده است).
  • site: نمایش یک شیء django.models.core.sites.Site سایت فعلی. این برای {{ site.domain }} یا {{ site.name }} مفید می باشد.
  • در صورتیکه یک template برای title یا description ایجاد نکنید، فریم ورک به صورت پیشفرض از template، {{ obj }} استفاده می کند – این template نمایش رشته ی معمولی شیء می باشد. (برای شیء های مدل، این متد __unicode__() خواهد بود.)
  • همچنین می توان نام این دو template را از طریق تعیین title_template و description_template به صورت attribute های کلاس Feed تغییر داد.
  • برای تعیین محتویات <link>، دو option وجود دارد. برای هر آیتم در items()، جنگو ابتدا سعی می کند یک متد get_absolute_url() روی آن شیء اجرا کند. در صورتی که متد وجود نداشته باشد، سعی می کند یک متد item_link() در کلاس Feed فراخوانی کند، ارسال آن یک پارامتر تنها، item، که خود شیء می باشد.
  • هر دوی get_absolute_url() و item_link() باید URL ایتم را به صورت یک رشته ی معمولی پایتون بر گردانند.
  • برای مثال LatestEntries قبلی، می توانیم template های بسیار ساده ی feed را داشته باشیم. latest_title.html حاوی:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

و latest_description.html حاوی:

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

این اغلب بسیار ساده می باشد ...

یک Feed پیچیده تر

فریم ورک همچنین feed های پیچیده تر را نیز از طریق پارامترها پشتیبانی می کند.

برای مثال، تصور کنید بلاگ شما یک RSS feed برای هر تگ مجزایی که شما برای طبقه بندی ورودی های خودتان استفاده کرده اید ارائه می کند. ساختن یک کلاس Feed جدا برای هر تگ احمقانه می باشد؛ نقض قانون "Don't Repeat Yourself" (DRY) می باشد.

در عوض، چارچوب syndication به شما اجازه می دهد feed های جنریکی ایجاد کنید که آیتم های متکی بر اطلاعات در آدرس feed ها را گردانند.

feed های تعیین تگ شما می تواند مانند زیر URL ها را استفاده کنند:

  • برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    ورودی های فعلی علامت زده شده با "python" را بر می گرداند
  • برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    ورودی های فعلی علامت زده شده با "cats" را بر می گرداند

slug در اینجا "tags" می باشد. فریم ورک syndication، bit های URL اضافه ی بعد از slug را می بیند – 'python' و 'cats' – و به شما جهت گفتن معنی آن bit های URL و نحوه ی تاثیر آن ها در دست ترجمه/تالیف ...

یک مثال این موضوع را واضح تر می کند. در کد زیر feed های تگ تعیین را ملاحظه می کنید:

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

در کد فوق اصل اولیه ی الگوریتم فریم ورک RSS وجود دارد، به توجه به این کلاس و یک درخواست برای URL مورد نظر یعنی /feeds/tags/python/:

  • فریم ورک آدرس /feeds/tags/python/ را دریافت می کند و ملاحظه می کند که یک تکه ی اضافی از URL بعد از slug وجود دارد. فریم ورک رشته ی باقی مانده را از توسط حرف ("/") جدا کرده و متد کلاس Feed یعنی get_object را فراخوانی کرده و bit ها را به آن ارسال می کند.
  • در این مورد، bits مورد نظر ['python'] می باشد. برای یک درخواست به /feeds/tags/python/django/، bit ها ['python', 'django'] می باشند.
  • get_object() مسئول بازیابی شیء Tag داده شده از bit های داده شده می باشد.
  • در این مورد، get_object() برای بازیابی Tag از API پایگاده داده ی جنگو استفاده می کند. توجه داشته باشید که get_object() در صورتیکه پارامتر های غیر معتبر داده شود، باید خطای django.core.exceptions.ObjectDoesNotExist ایجاد کند. هیچ try/except ای در اطراف فراخوانی Tag.DoesNotExist وجود ندارد، زیرا این کار ضروری نمی باشد. آن تابع زمان شکست Tag.DoesNotExist ایجاد می کند، و Tag.DoesNotexist از OjbectDoesNotexist مشتق شده است. بروز objectDoesNotexist در get_object()تولید یک خطای 404 برای آن درخواست را به جنگو می گوید.
  • برای تولید <title>، <link> و <description> مربوط به feed، جنگو متدهای title()، link() و Description() را مورد استفاده قرار می دهد. در مثال قبلی، آن ها attribute های ساده ی کلاس رشته بودند، ولی این مثال نشان می دهد که، آن ها می تواند هم رشته و هم متد باشند. برای هر title، link و description، جنگو الگوریتم زیر را دنبال می کند:

 

  1. سعی می کند یک متد را فراخوانی کند، ارسال آرگومان obj، جایی که obj شیءی می باشد که توسط get_object() بر گردانده شده است.
  2. در صورت شکست، جنگو تلاش می کند یک متد با هیچ آرگومانی را فراخوانی کند.
  3. در صورت شکست، جنگو attribute کلاس را استفاده می کند.
  • در پایان، توجه داشته باشید که items() در این مثال همچنین آرگومان obj را نیز دریافت می کند. الگوریتم برای items همانند الگوریتم توضیح داده شده در گام قبلی می باشد – ابتدا، سعی می کند items(obj)، سپس items() و در پایان یک attribute کلاس items (که باید یک لیست باشد).
  • مستندات کامل از تمام متدها و attribute ها از کلاس های Feed همواره از اسناد رسمی جنگو قابل دسترسی می باشد (http://docs.djangoproject.com/en/dev/ref/contrib/syndication)

تعیین نوع Feed

به طور پیشفرض، فریم ورک syndication، RSS 2.0 را تهیه می کند. برای تغییر آن، یک attribute، feed_type برای کلاس Feed خودتان اضافه کنید:

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

توجه داشته باشید که feed_type را برای یک شیء کلاس در نظر گرفته اید، نه یک نمونه، در حال حاضر انواع feed های در دسترس در جدول زیر نشان داده شده اند.

کلاس Feed Format
Django.utils.feedgenerator.Rss201rev2Feed RSS 2.01 (default)
Django.utils.feedgenerator.RssUserland091Feed RSS 0.91
Django.utils.feedgenerator.Atom1Feed Atom 1.0

Enclosures

برای تعیین enclosure ها (مانند منابع media مرتبط با یک آیتم feed مانند feed های MP3 podcast)، item_enclosure_url، item_enclosure_length و item_enclosure_mime_type را استفاده کنید، به عنوان مثال:

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

البته که شما یک شیء Song با فیلدهای song_url و song_length (مانند اندازه ی بایت ها) ساخته اید.

Language

Feed های ساخته شده توسط فریم ورک syndication به طور خودکار شامل تگ مناسب <language> (RSS 2.0) یا attribute، xml:lang (Atom) می باشند. این به طور مستقیم از تنظیم LANGUAGE_CODE آمده است.

URLs

لینک متد/attribute می تواند هم یک URL مستقل (مانند "/blog/") یا یک URL با آدرس کامل دامنه و پروتکل (مانند "http://www.example.com/blog/") باشد. در صورتی که link دامنه را بر نگرداند، فریم ورک syndication دامنه ی سایت فعلی را به همراه تنظیم SITE_ID درج خواهد کرد. (برای اطلاعات بیشتر در مورد SITE_ID و چارچوب سایت ها به پکیج django.contrib مراجعه کنید.)

feed های Atom نیازمند یک <link rel="self"> می باشند که مکان فعلی feed را تعریف کند. فریم ورک syndication به طور خودکار این را قرار می دهد.

انتشار feed های Atom و RSS پشت سر هم

برخی از توسعه دهندگان تمایل دارند، فیلد های هر دو نسخه ی Atom و RSS در دسترس قرار دهند. انجام این کار با جنگو بسیار آسان می باشد: تنها کافیست یک کلاس فرزند از کلاس feed خود ایجاد کرده و feed_type را برای چیزی متفاوت قرار دهید. سپس URLconf خود را جهت اضافه کردن نسخه های اضافه به روز رسانی کنید. در زیر یک مثال کامل را ملاحظه می کنید:

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

و در زیر URL همراه:

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

فریم ورک یا چارچوب نقشه ی سایت

نقشه ی سایت یک فایل XML در وب سایت شما می باشد که به indexer های موتور جستجو نحوه ی تغییر مکرر و نحوه ی ارتباط برخی صفحات مهم با دیگر صفحات سایت شما را می گوید. این اطلاعات به موتورهای جستجوی فهرست سایت شما کمک می کند.

به عنوان مثال، در زیر یک تکه از نقشه ی سایت برای وب سایت جنگو وجود دارد (http://www.djangoproject.com/sitemap.xml):

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

برای مشاهده ی نقشه ی سایت های بیشتر به

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

فریم ورک نقشه ی سایت جنگو، ساختن این فایل XML را با اجازه دادن به شما جهت بیان این اطلاعات در کد پایتون خودکار می کند. جهت ساختن یک نقشه ی سایت، تنها نیاز به نوشتن یک کلاس Sitemap و اشاره ی به آن درون URLconf می باشد.

نصب

جهت نصب برنامه ی نقشه ی سایت، مراحل زیر را دنبال کنید:

  • 'django.contrib.sitemaps' را به تنظیم INSTALLED_APPS اضافه کنید.
  • 'django.template.loaders.app_directories.load_template_source'باید در تنظیم TEMPLATE_LOADERS وجود داشته باشد. این حالت به طور پیشفرض وجود دارد، بنابراین در صورتی که این تنظیم را تغییر داده اید، تنها کافیست آن را به حالت اول خود بر گردانید.
  • اطمینان حاصل کنید که سایت های چارچوب را نصب کرده اید (به پکیج django.contrib مراجعه کنید).

نکته

برنامه ی نقشه ی سایت در هر جدول دیتابیسی نصب نمی شود. تنها دلیلی که برای رفتن به داخل INSTALLED_APPS نیاز دارد این است که template loader مورد نظر یعنی load_template_source بتواند template های پیشفرض را پیدا کند.

مقدار دهی اولیه

جهت فعال کردن تولید نقشه ی سایت در سایت جنگو، خط زیر را درون URLconf اضافه کنید:

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

خط فوق برای ساختن یک نقشه ی سایت هنگامی که یک کلاینت به /sitemap.xml دسترسی پیدا می کند در نظر گرفته شده است. توجه داشته باشید که حرف نقطه در sitemap.xml با یک علامت ("\") میسر شده است، زیرا نقطه ها معنی خاصی در regular expression ها دارند.

نام فایل نقشه ی سایت اهمیتی ندارد، ولی مکان آن مهم می باشد. موتورهای جستجو تنها لینک ها را در نقشه ی سایت شما برای سطح URL فعلی و پایین فهرست می کنند. برای مثال، در صورتی که sitemap.xml در دایرکتوری ریشه ی شما می باشد، ممکن است به هر URL ای در سایت شما رجوع کند. در صورتی که نقشه ی سایت در /content/sitemap.xml باشد، ممکن است تنها به URL هایی که با /content/ شروع می شوند رجوع کند.

view نقشه ی سایت یک آرگومان اضافه ی الزامی دریافت می کند: {'sitemaps': sitemaps}. نقشه های سایت باید یک دیکشنری باشند که بخش کوتاه لیبلی (مانند blog یا news) را به کلاس Sitemap آن (مانند BlogSitemap یا NewsSitemap) مرتبط می سازد. همچنین ممکن است به یک instance از یک کلاس Sitemap (مانند BlogSitemap(some_var)) مرتبط سازد.

کلاس های Sitemap

یک کلاس Sitemap یک کلاس ساده ی پایتون می باشد که یک بخش از ورودی های نقشه ی سایت شما را نشان می دهد. برای مثال، یک کلاس Sitemap هنگامی که دیگری می تواند تمام رخدادها در در رخدادهای تقویم نشان دهد تمام ورودی های وبلاگ شما را نشان دهد.

در ساده ترین مورد، تمام این بخش ها با یکدیگر داخل یک sitemap.xml بدست می آیند، ولی استفاده از فریم ورک برای تولید یک فهرست نقشه ی سایت که به فایل های نقشه ی سایت منحصر به فرد نیز ممکن است، یکی برا هر بخش.

کلاس های Sitemap باید از کلاس django.contrib.sitemaps.Sitemap مشتق شوند. آن ها می توانند هر جایی در درخت کد پایتون شما قرار بگیرند.

برای مثال، فرض می کنیم شما دارای یک سیستم بلاگ می باشید، با یک مدل Entry، و می خواهید نقشه ی سایت تمام لینک ها به ورودی منحصر به فرد بلاگ را شامل شود. در زیر کلاس Sitemap مورد نظر وجود دارد:

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

declare کردن نقشه ی سایت باید بسیار شبیه به یک Feed باشد. در دست ترجمه/تالیف ...

همانند کلاس های Feed، اعضای Sitemap می توانند متد یا attribute ها باشند. به بخش کمی قبل در این آموزش از کتاب، "یک مثال پیچیده" برای نحوه ی انجام این اعمال مراجعه کنید.

یک کلاس نقشه ی سایت می تواند متد/attribute های زیر را تعریف کند:

  • items(required): لیستی از شیء ها تهیه می کند. فریم ورک نوع شیء هایی که هستند را زیر نظر ندارد؛ تمام موضوع این است که این شیء ها به متدهای location()، lastmod()، changefreq() و priority() ارسال شده باشند.
  • location(optional): URL مستقل برای شیء داده شده را می دهد. در اینجا، "URL مستقل" به معنی یک URL می باشد که شامل پروتکل یا دامنه نباشد. تعدادی مثال را در زیر مشاهده می کنید:
  1. خوب: '/foo/bar/'
  2. بد: 'example.com/foo/bar/'
  3. بد: 'http://example.com/foo/bar/'
  4. در صورتیکه location تهیه نشده باشد، فریم ورک متد get_absolute_url() را در هر شیء به صورت برگشت داده شده توسط items() فراخوانی خواهد کرد.
  • lastmod(optional): شیء های "آخرین اصلاح" زمان و تاریخ، به صورت یک شیء datetime پایتون.
  • changefreq(optional): هر چند وقت یک بار شیء را تغییر می دهد. مقادیر ممکن (به صورت داده شده توسط تعیین نقشه های سایت) به قرار زیر می باشند:
  1. 'always'
  2. 'hourly'
  3. 'daily'
  4. 'weekly'
  5. 'monthly'
  6. 'yearly'
  7. 'never'
  • priority(optional): یک پیشنهاد اولویت فهرست سازی بین 0.0 و 1.0. اولویت پیشفرض یک صفحه 0.5 می باشد؛ برای اطلاعات بیشتر درباره ی عمکرد اولویت به
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    مراجعه کنید.

میانبرها

فریم ورک نقشه ی سایت کلاس های مناسبی برای موارد مشترک تهیه کرده است. این کلاس ها در بخش های زیر توضیح داده شده اند.

FlatPageSitemap
کلاس django.contrib.sitemaps.FlatPageSitemap به تمام صفحات مسطح تعریف شده برای سایت فعلی نگاه می کند و یک ورودی در نقشه ی سایت ایجاد می کند. این ورودی ها تنها شامل attribute مورد نظر یعنی location می شوند – نه lastmod، changefreq یا priority.

GenericSitemap

کلاس GenericSitemap با هر view جنریکی کار می کند که شما آن ها را فرا گرفته اید.

برای استفاده از آن، یک نمونه بسازید، ارسال در info_dict یکسان که شما به view های جنریک ارسال می کنید. تنها نیازمندی این است که دیکشنری یک ورودی queryset داشته باشد. ممکن است همچنین یک ورودی date_field داشته باشد که یک فیلد تاریخ برای شیء های بازیابی شده از queryset را تعیین می کند. این برای attribute، lastmode در نقشه ی سایت تولید شده مورد استفاده قرار خواهد گرفت. شما ممکن است آرگومان های کیورد priority و changefreq را برای سازنده ی GenericSitemap جهت تعیین این attribute ها برای همه ی URL ها ارسال کنید.

در زیر مثالی از یک URLconf که از هر دوی FlatPageSitemap و GenericSiteMap (با شیء فرضی Entry از پیش) استفاده می کند وجود دارد:

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

ساخت فهرست نقشه ی سایت
فریم ورک نقشه ی سایت همچنین دارای توانایی برای ساختن یک فهرست نقشه ی سایت می باشد که به فایل های منحصر به فرد نقشه ی سایت رجوع می کند، یکی برای هر بخش تعریف شده در دیکشنری sitemaps. تنها تفاوت ها در کاربرد هستند:

  • شما از دو view در URLconf خود استفاده می کنید: django.contrib.views.index و django.conrib.sitemaps.views.sitemap.
  • django.contrib.sitemaps.views.sitemap باید یک آرگومان کیورد section دریافت کند.

در زیر خطوط URLconf مربوط برای مثال قبلی وجود دارد:

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

کد فوق به طور اتوماتیک یک فایل sitemap.xml تولید می کند که به هر دوی sitemap-flatpages.cml و sitemap-blog.xml رجوع می کند. کلاس های Sitemap و دیکشنری sitemaps هیچگاه تغییر نمی کنند.

پینگ کردن گوگل

ممکن است بخواهید گوگل را هنگامی که نقشه ی سایت شما تغییر می کند پینگ کنید، تا به آن اجازه دهید دوباره فهرست گذاری سایت شما را بداند. چارچوب یک تابع تنها برای فقط برای این کار تهیه کرده است: django.contrib.sitemaps.ping_google().

ping_google() یک آرگومان اختیاری دریافت می کند، sitemap_url، که باید URL مستقل از نقشه ی سایت سایت شما باشد (مانند '/sitemap.xml'). در صورتی که آرگومان تهیه نشده باشد، ping_google() تلاش می کند نقشه ی سایت شما را توسط انجام یک جستجوی بر عکس در URLconf بسنجد.

ping_google() در صورتی که نتواند URL نقشه ی سایت را تعیین کند، خطای django.contrib.sitemaps.SitemapNotFound را ایجاد خواهد کرد.

روش مفید برای فراخوانی ping_google به شکل یک متد save() مدل می باشد:

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

راهکار موثر تر، فراخوانی ping_google() از یک اسکریپت cron یا برخی وظایف برنامه ریزی شده ی دیگر می باشد. تابع یک HTTP request برای سرور های گوگل ایجاد می کند، بنابراین شما ممکن نیست بخواهید برای معرفی در هر بار فراخوانی save() بار اضافه ی شبکه را داشته باشید.

در پایان، در صورتی که 'django.contrib.sitemaps' درون تنظیم INSTALLED_APPS می باشد، manage.py شما شامل یک دستور جدید، ping_google خواهد بود. این برای دسترسی به پینگ کردن درون خط فرمان مفید می باشد. برای مثال:

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

 

لینک به دیدگاه

Session ها، کاربران، و عضویت

مرورگر هایی که سایت های ما را مورد هدف قرار می دهند که در پشت خود انسان های واقعی دارند (حداقل بیشتر اوقات). این نکته ی بزرگی برای رد کردن است: بهترین حالت اینترنت زمانی می باشد که برای وصل شدن به مردم خدمات می دهد، نه ماشین ها. در صورتی که بخواهیم به درستی سایت ها را وادار کنیم، سر انجام باید با افراد در پشت مرورگر ها سر و کار داشته باشیم.

متاسفانه، این موضوع ساده ای نمی باشد. HTTP طوری طراحی شده است که بی حالت باشد – بدین معنی که، هر درخواست در یک فضای تهی اتفاق می افتد. دوامی بین یک درخواست و درخواست بعدی وجود ندارد، و ما نمی توانیم هر یک از جنبه ها درخواست (آدرس IP، مرورگر و غیره ...) را که به طور مداوم توسط یک شخص به طور پی در پی ارسال می شود را شمارش کنیم.

در این آموزش شما نحوه ی کنترل این فقدان حالت را خواهید آموخت. با پایین ترین سطح (کوکی ها) شروع خواهیم کرد، و به سمت ابزار سطح بالاتر برای کنترل session ها، کاربران و عضویت حرکت خواهیم کرد.

کوکی ها (Cookies)

توسعه دهندگان مرورگر مدت ها پیش متوجه این موضوع شدند که وضعیت statelessness یک مشکل بزرگ برای توسعه دهندگان وب به شمار می رود، و در نتیجه کوکی ها چشم به جهان گشودند. کوکی یک تکه ی کوچک از اطلاعات می باشد که مرورگرها از طرف وب سرورها ذخیره می کنند. هر بار درخواست های یک مرورگر از یک صفحه ی فرم و از یک سرور خاص، به کوکی که در ابتدا دریافت شده است پس داده می شوند.

اجازه دهید نگاهی به نحوه ای که این عمل ممکن است انجام شود بیاندازیم. هنگامی که شما مرورگر خود را باز می کنید و درون آن google.com را تایپ می کنید، مرورگر شما یک درخواست HTTP را به گوگل می فرستد که با چیزی شبیه به این شروع می شود:

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

زمانی که گوگل پاسخ می دهد، یک پاسخ HTTP شبیه به پاسخ زیر خواهد بود:

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

به هدر Set_Cookie دقت کنید. مرورگر شما مقدار کوکی که از این قرار است ذخیره خواهد کرد (PREF=ID=5b14f22bdafle81c:TM=1167000671:LM=1167000671) و در هر بار که شما به سایت دسترسی پیدا کنید آن را به گوگل بر می گرداند. بنابراین در مرتبه ی بعدی که به گوگل دسترسی پیدا می کنید، مرورگر شما یک درخواست مانند زیر را ارسال خواهد کرد:

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

سپس گوگل می تواند از آن مقدار کوکی برای دانستن این که شما با کسی که قبل تر دسترسی پیدا کرده است یک فرد همسان می باشید استفاده می کند. این مقدار ممکن است برای مثال، یک کلید درون یک پایگاه داده که اطلاعات کاربر را ذخیره می کند باشد. گوگل می تواند (انجام می دهد) از آن برای نمایش نام کاربری حساب شما در صفحه استفاده کند.

قرار دادن و گرفتن کوکی ها

هنگامی که در جنگو با ماندگاری سر و کار دارید، بیشتر اوقات از seesion سطح بالا و یا از فریم ورک یا چارچوب های کاربر بحث شده در این آموزش از کتاب، استفاده خواهید کرد. با این حال، ابتدا به نحوه ی خواندن و نوشتن کوکی ها در سطح پایین نگاهی بیاندازید. این باید به فهم بقیه ی ابزار بحث شده در آموزش و در صورتی که شما بخواهید به صورت مستقیم از کوکی ها استفاده کنید به شما کمک خواهد کرد.

خواندن کوکی ها ساده می باشد. هر شیء HttpRequest دارای یک شیء COOKIES می باشد که مانند دیکشنری عمل می کند؛ می توانید برای خواند هر کوکی که مرورگر به view ارسال می کند از آن استفاده کنید:

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

نوشتن کوکی ها قدری پیچیده تر می باشد. نیاز است که از متد set_cookie() در یک شیء HttpResponse استفاده کنید. در زیر مثالی وجود دارد که کوکی favorite_color مستقر در یک پارامتر GET را قرار می دهد:

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

می توانید همچنین یک تعداد از آرگومان های اختیاری را به response.set_cookie() که جنبه هایی از کوکی را کنترل می کند ارسال کنید. همانطور که در جدول زیر مشاهده می کنید.

پارامتر پایگاه داده توضیح
Max_age None عمر (بر حسب ثانیه) که کوکی باید زنده باشد. در صورتی که این پارامتر None باشد، کوکی تنها تا زمانی که مرورگر بسته شود زنده خواهد ماند.
expire None زمان و تاریخ واقعی ای، که کوکی باید از بین برود که باید در قالب بندی "Wdy, DD-Mth-YY HH:MM:SS GMT" باشد. در صورت داده این پارامتر، پارامتر max_age باز نویسی می شود.
path "/" پیشوند مسیری که کوکی برای آن معتبر است. مرورگرها کوکی را تنها به صفحاتی که تحت این مسیر پیشوند می باشد بر می گرداند، بنابراین شما می توانید از این برای جلوگیری از ارسال کوکی ها به بخش های دیگر سایت استفاده کنید.
این پارامتر به ویژه هنگامی که سطح زیادی زا دامنه های سایت را کنترل نمی کنید مفید است.
domain None دامنه ای که این کوکی برای آن معتبر است. می توانید از این پارامتر برای قرار دادن یک کوکی cross-domain استفاده کنید. برای مثال، domain=".example.com" یک کوکی قرار خواهد داد که توسط دامنه های www.example.com، www2.example.com و sub.domain.example.com قابل خواندن می باشد.
در صورتی که این پارامتر None قرار داده شود، یک کوکی تنها توسط دامنه ای که آن را قرار داده قابل خواندن خواهد بود.
secure False در صورتی که مقدار این پارامتر را True قرار داده شود، این پارامتر به مرورگر خواهد گفت که این کوکی را تنها برای صفحاتی که HTTPS می باشند در دسترس قرار بده.

The Mixed Blessing of Cookies

ممکن است دقت کرده باشید که یک تعداد از مشکلات بالقوه روش کار با کوکی ها وجود دارند. اجازه دهید برخی از این مشکلات را مورد بررسی قرار دهیم:

  • ذخیره سازی کوکی ها ارادی می باشد؛ یک کلاینت ملزم به قبول یا ذخیره کوکی نمی باشد. در واقع، تمام مرورگرها کاربران را قادر به کنترل سیاست خود برای قبول کردن کوکی ها می کند در صورتی که می خواهید تنها کوکی های واجبی که در وب وجود دارند را تماشا کنید، option مرورگر خود یعنی "prompt to accept every cookie" را روشن کنید.
  • با وجود تقریبا استفاده ی جهانی آنها، کوکی ها هنوز غیر قابل اطمینان تلقی می شوند. این بدان معنی است که توسعه دهندگان باید بررسی کنند که یک کاربر در واقع کوکی ها را قبل اعتماد به آن ها قبول کرده است.
  • کوکی ها (به ویژه آن هایی که در HTTPS ارسال نمی شوند) امن نمی باشند. چرا که داده ی HTTP به صورت متن ساده ارسال می شود، کوکی ها به شدت در برابر حملات جاسوسی آسیب پذیر می باشند. این بدین معنی است که، یک مهاجم جاسوس می تواند یک کوکی را قطع کرده و آن را بخواند. این بدان معنی است که نباید هرگز اطلاعات حساس را در یک کوکی ذخیره کرد.
  • حتی یک حمله ی مخرب تر که بک حمله ی man-in-the-middle معروف است وجود دارد، در جایی که یک مهاجم یک کوکی را قطع می کند و از برای نشان دادن خود به صورت یک کاربر دیگر استفاده می کند. آموزش امنیت به تفصیل این مسائل و راه ها جلوگیری از آن را توضیح داده است.
  • کوکی ها حتی از دریافت کنندگان در نظر گرفته ی خود نیز امن نمی باشد. اغلب مرورگر ها روشی ساده برای ویرایش محتوای کوکی های منحصر به فرد تهیه می کنند، و کاربران خبره می توانند همواره با استفاده از ابزاری مانند mechanize (http://wwwsearch.sourceforge.net/mechanize) درخواست های HTTP به صورت دستی بسازند.
  • بنابراین شما نمی توانید داده ی درون کوکی ها را که برای دستکاری حساس می باشند را ذخیره کنید. اشتباه رایج در این سناریو ذخیره چیزی شبیه به IsLoggedIn=1 در یک کوکی در هنگامی که کاربر وارد شده است می باشد. متعجب می شوید وقتی تعدادی از سایت ها این اشتباه طبیعی را انجام می دهند؛ فریب دادن سیستم های امنیتی این سایت ها تنها یک ثانیه طول می کشد.

فریم ورک یا چارچوب Session جنگو

با تمام این محدودیت ها و حفره های امنیتی بالقوه، واضح است، آن کوکی ها و session های مقاوم مثال هایی از نقطه های درد در توسعه ی وب می باشند. البته، هدف جنگو داروی مسکن موثر بودن می باشد، بنابراین جنگو حاوی یک فریم روک session طراحی شده جهت تسکین دادن این سختی ها برای شما می باشد.

این چارچوب session به شما اجازه می دهد داده ی دلخواه را بر اساس هر بازدید کننده ی سایت ذخیره و بازیابی کنید. این فریم ورک داده را در سمت سرور ذخیره می کند و ارسال و دریافت کوکی ها را حذف می کند. کوکی ها تنها از یک ID هش شده استفاده می کنند – نه خود داده – در نتیجه شما را از مشکلات رایج کوکی محافظت می کند.

اجازه دهید به نحوه ی فعال کردن session ها و استفاده کردن از آن ها در view ها بپردازیم.

فعال کردن Session ها

session ها از طریق قسمتی از middleware (به فرم مراجعه کنید) و مدل جنگو اجرا می شوند. جهت فعال کردن session ها، نیاز است مراحل زیر را انجام دهید:

  • تنظیم MIDDLEWARE_CLASSES را ویرایش کرده و اطمینان حاصل کنید که MIDDLEWARE_CLASSES حاوی 'django.contrib.sessions.middleware.SessionMiddleware'می باشد.
  • اطمینان حاصل کنید که 'django.contrib.sessions' درون تنظیم INSTALLED_APPS وجود دارد (و در صورتیکه ملزم به اضافه کردن آن هستید دستور manage.py syncdb را اجرا کنید).

اسکلت بندی پیشفرض تنظیمات ایجاد توسط startproject دارای هر دوی این قسمت ها فوق می باشد، بنابراین در صورتی که آن ها را حذف نکرده باشید، برای کار کردن session ها نیازی به تغییر چیزی نیست.

در صورتی که نمی خواهید از session ها استفاده کنید، ممکن است بخواهید خط SessionMiddleware را از MIDDLEWARE_CLASSES و 'django.contrib.sessions'از INSTALLED_APPS خود حذف کنید. این شما را تنها از مقدار کمی از بار اضافی حفظ می کند، ولی هر قسمت کوچکی شمارش می شوند.

استفاده از Session ها در view ها

هنگامی که SessionMiddleware فعال شده است، هر شیء HttpRequest – اولین آرگومان برای هر تابع view جنگو – دارای یک attribute، seesion خواهد بود که یک شیء شبیه به دیکشنری می باشد. می تواند درست مثل یک دیکشنری معمولای از آن استفاده کنید. برای مثال در یک view می توانید کاری شبیه به زیر را انجام دهید:

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

همچنین می توان از متدهای دیگر دیکشنری همانند keys() و items() در request.session استفاده کنید.

تعدادی قوانین ساده برای استفاده ی موثر از session های جنگو وجود دارد:

  • از رشته های معمولی پایتون به صورت کلیدهای دیکشنری در request.session (مخالف integer ها، شیء ها و غیره ...) استفاده کنید.
  • کلیدهای دیکشنری session که با یک خط تیره شروع می شوند، برای استفاده ی داخلی توسط جنگو رزرو شده اند. در عمل، فریم ورک تنها از تعداد کمی از متغیرهای session با خط تیره شروع شده استفاده می کند، ولی در صورتی که در مورد این قبیل متغیرهای اطلاعاتی ندارید (و اگر تمایل دارید با هر تغییراتی در خود جنگو مطابق باشید)، استفاده از پیشوندهای خط تیره، از تداخل جنگو با برنامه ی شما جلوگیری می کند.
  • برای مثال، از کلید session با نام _fav_color، مانند زیر استفاده نکنید:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.

request.session را با یک شیء جدید جایگزین نکنید، و به attribute های آن دسترسی پیدا نکرده و چیزی در آن ها قرار ندهید. از آن مانند یک دیکشنری پایتون استفاده کنید. مثال:

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

اجازه دهید مثال های کوچکی را ذکر کنیم. view ساده ی زیر بعد از این که کاربر یک کامنت را پست می کند مقدار True را در یک متغیر has_commented قرار می دهد. جلوگیری از پست کردن بیشتر از یک کامنت توسط کاربر ساده می باشد:

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

view ساده ی ورودی یک عضو در سایت:

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

و کد زیر خروج یک عضو از سایت که از طریق login() فوق وارد سایت شده است:

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

نکته

در عمل، روش فوق روشی مناسبی برای ورودی کاربران نمی باشد. فریم ورک authentication که به طور مختصر بحث شده است این وظیفه را برای شما بسیار قدرمتند و با روشی مفید انجام می دهد. این مثال ها عمدا ساده می باشند، به طوری که شما بتوانید به راحتی در جریان کار قرار بگیرید.

آزمون کوکی های ارسال شده

همانطور که در بالا ذکر شد، نمی توان به هر کوکی ارسال شده ی مرورگر اطمینان کرد. بنابراین، برای راحتی کار، جنگو روشی ساده برای آزمون کوکی های ارسال شده ی مرورگر تهیه کرده است. تنها کافیست request.session.set_test_cookie() را در یک view فراخوانی کرده و request.session.test_cookie_worked() را در view بعدی بررسی کنید – نه در فراخوانی view همسان.

این جدایی بین set_test_cookie() و test_cookie_worked به دلیل روش کار کوکی ها ضروری می باشد. زمانی که شما یک کوکی را قرار می دهید، در واقع شما نمی توانید تا درخواست بعدی مرورگر بگویید یک مرورگر آن را ارسال کرده است.

استفاده از delete_test_cookie() برای تمیز کردن بعد از خودتان تمرین خوبی می باشد. این عمل را بعد از تایید کارکرد کوکی آزمون انجام دهید.

در زیر مثال کاربرد معمولی موضوع فوق وجود دارد:

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

نکته

یک بار دیگر، توابع داخلی authentication این بررسی را برای شما انجام می دهند.

استفاده از Session ها خارج از view ها

به طور داخلی، هر session تنها یک مدل جنگوی معمولی تعریف شده در django.contrib.sessions.models می باشد. هر session توسط هش تصادفی کمتر یا بیشتر 32 حرفی در یک کوکی شناسایی می شود. به این دلیل که session یک مدل معمولی می باشد، می توان با استفاده از API معمولی پایگاه داده ی جنگو به session ها دسترسی پیدا کرد:

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

برای بدست آوردن داده ی session واقعی، نیاز به فراخوانی get_decoded() می باشد. این موضوع ضروری می باشد، چرا که دیکشنری در قالب بندی رمزی شده ذخیره شده است:

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

زمانی که Session ها ذخیره شده اند

به طور پیشفرض، جنگو تنها در صورتی که session تغییر کند آن ها را درون پایگاه داده ذخیره می کند – این بدین معنی است که اگر هر کدام از مقادیر دیکشنری آن اختصاص داده شود یا حذف شود:

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

برای تغییر این رفتار پیشفرض، مقدار SESSION_SAVE_EVERY_REQUEST را True قرار دهید. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، جنگو در هر درخواست تنها درون پایگاه داده ذخیره می کند، حتی اگر تغییر نکرده باشد.

توجه داشته باشید که کوکی session تنها زمانی که یک session ساخته شده یا تغییر کرده باشد فرستاده می شود. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، کوکی session در هر درخواست فرستاده خواهد شد. به طور یکسان، بخش expires از یک کوکی session در هر بار که کوکی session فرستاده شود به روز رسانی می شود.

Session های Browser-Length در مقابل Session های مقاوم

ممکن است متوجه شده باشید که کوکی گوگل فرستاده شده به ما در ابتدای این آموزش حاوی expires=Sun، 17-Jan-2038 19:14:07 GMT; بود. کوکی ها به طور اختیاری می توانند حاوی یک تاریخ انقضا باشند که مرورگر را در هنگام حذف کوکی آگاه می سازد. در صورتی که یک کوکی حاوی مقدار انقضا نباشد، زمانی که کاربر پنجره ی مرورگر را ببندد از بین می رود. می توان رفتار فریم ورک یا چارچوب session را رابطه با تنظیم SESSION_EXPIRE_AT_BROWSER_CLOSE کنترل کرد.

به طور پیشفرض، مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، False در نظر گرفته شده است، که بدین معنی می باشد که کوکی های session در مرورگرهای کاربران برای SESSION_COOKIE_AGE ثانیه (که پیشفرض آن دو هفته یا 1,209,600 ثانیه) می باشد. در صورتی که نمی خواهید مردم در هر بار که مرورگر را باز می گنند به سایت log in نشوند از این تنظیم می توانید استفاده کنید.

در صورتی که مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، True باشد، جنگو از کوکی های browser-length استفاده خواهد کرد.

تنظیمات دیگر Session

در کنار تنظیمات ذکر شده، چند تنظیم دیگر بر نحوه ی استفاده ی فریم ورک session جنگو از کوکی ها تاثیر می گذارد، همانطور که در جدول زیر نشان داده شده است.

تنظیم توضیح پیشفرض
SESSION_COOKIE_DOMAIN دامنه ی مورد استفاده برای کوکی های session. برای این تنظیم یک رشته مانند ".example.com" برای کوکی های cross‑domain قرار دهید، یا برای یک کوکی استاندارد از None استفاده کنید. None
SESSION_COOKIE_NAME نام کوکی ای که برای session ها استفاده می شود. این می تواند هر رشته ای باشد. "sessionid"
SESSION_COOKIE_SECURE در دست ترجمه/تالیف .... در صورتی که این تنظیم True باید، کوکی به صورت "امن" علامت گذاری خواهد شد، که بدین معناست که مرورگر ها مطمئن خواهند بود که کوکی تنها از طریق HTTPS ارسال شده است. False

جزئیات فنی

محض کنجکاوی، در زیر چند نکته ی فنی درباره ی کار عملکرد داخلی فریم ورک session بیان شده است:

  • در دست ترجمه/تالیف .... برای کسب اطلاعات درمورد کارکرد ماژول داخلی پایتون یعنی pickle مستندات پایتون مراجعه کنید.
  • داده ی session در جدول پایگاه داده با نام django_session ذخیره می شود.
  • در دست ترجمه/تالیف .... در صورتی که هرگز به request.session دسترسی ندارید، جنگو به آن جدول پایگاه داده مراجعه می کند.
  • جنگو در صورت نیاز یک کوکی ارسال می کند. در صورتی که هیچ داده ی session ای قرار نگرفته باشد، جنگو کوکی session ای ارسال نخواهد کرد (مگر اینکه مقدار SESSION_SAVE_EVERY_REQUEST، True قرار داده شده باشد).
  • فریم ورک session جنگو به طور کلی و تنها بر اساس کوکی می باشد. در دست ترجمه/تالیف ....
  • این یک تصمیم طراحی بین المللی می باشد. قرار دادن session ها در URL ها نه تنها باعث زشت شدن URL ها می شود، بلکه همچنین سایت شما را برای یک فرم خاص از سرقت هدر Referer آسیب پذیر می سازد.

در صورتی که هنوز کنجکاو هستید، منبع بسیار آسان می باشد؛ برای اطلاعات بیشتر به django.contrib.sessions نگاهی بیاندازید.

کاربران و تصدیق

session ها راه تداوم داده را از بین چندین درخواست مرورگر به ما می دهند؛ دومین قسمت معادله استفاده از آن session ها برای ورود کاربر می باشد. البته، نمی توانیم به راحتی به هر کاربری که وارد می شود اعتماد کنیم، بنابراین نیاز به تصدیق کردن آن ها در طول مسیر می باشد.

به طور طبیعی، جنگو ابزاری را جهت این وظایف مشترک (و بسیاری دیگر) تهیه کرده است. سیستم تصدیق کاربر جنگو حساب ها کاربر، حق دسترسی ها، و session های بر پایه ی کوکی کاربر را کنترل می کند. این سیستم اغلب به صورت یک سیستم auth/auth (تصدیق و تصدیق) مورد مراجعه قرار گرفته است. آن نام عدم اعتبار کاربران را اغلب با یک پردازش دو مرحله ای تشخیص می دهد. نیاز به نکات زیر می باشد:

  • تصدیق کاربری که ادعای کاربر بودن می کند (معمولا توسط بررسی یک نام کاربری و رمز عبور در مقابل یک پایگاه داده از کاربران)
  • تصدیق این که کاربر مجاز به اجرای برخی اعمال داده شده (معمولا توسط بررسی در یک جدول از حق دسترسی ها) می باشد.

در ادامه ی این نیازمندی ها، سیستم auth/auth جنگو حاوی تعدادی از بخش ها می باشد:

  • کاربران: افرادی که در سایت شما عضویت دارند
  • حق دسترسی ها: پرچم های دودویی (yes/no) طراحی شده برای مشخص کردن اینکه آیا یک کاربر می تواند یک وظیفه ی خاص را انجام دهد
  • گروه ها: روشی عمومی جهت بکار بردن لیبل ها و حق دسترسی به بیشتر از یک کاربر
  • پیام ها: روشی ساده برای به صف کردن و نمایش سیستم پیام ها به کاربران

در صورتی که از ابزار مدیر استفاده کرده اید، شما بسیاری از این ابزار را مشاهده کرده اید، و در صورتی که کاربران و گروه ها را در ابزار مدیر ویرایش کرده باشید، شما در واقع در جدول پایگاه داده ی سیستم auth داده ها را ویرایش کرده اید.

فعال ساختن پشتیبانی تصدیق

همانند ابزار session، پشتیبانی تصدیق به صورت یک برنامه ی جنگو در django.contrib که نیاز به نصب شدن دارد همراه است. همچنین مانند ابزار session، باید به طور پیشفرض نصب شده باشد، ولی در صورتی که شما آن را حذف کرده باشید، نیاز است مراحل زیر را برای نصب آن دنبال کنید:

  • اطمینان حاصل کنید فریم ورک session همانطور که قبلا در این آموزش از کتاب توضیح داده شد نصب شده باشد. پیگیری کاربران بدیهی است که مستلزم کوکی ها می باشد، و در نتیجه در فریم ورک session ساخته می شود.
  • 'django.contrib.auth' را در تنظیم INSTALLED_APPS قرار داده و دستور manage.py syncdb را جهت نصب جداول پایگاه داده ی مناسب اجرا کنید.
  • از وجود 'django.contrib.auth.middleware.AuthenticationMiddleware'درون تنظیم MIDDLEWARE_CLASSES اطمینان حاصل کنید – بعد از SessionMiddleware.

با انجام مراحل فوق، همه چیز برای سر و کار داشتن با کاربران در توابع view آماده می باشد. رابط اصلی که شما برای دسترسی به کاربران در یک view از آن استفاده خواهید کرد request.user می باشد؛ این یک شیء است که کاربر فعلی وارد شده به سایت را نشان می دهد. در صورتی که کاربر وارد نشده باشد، به جای آن یک شیء AnonymousUser خواهد بود (برای جزئیات بیشتر به ادامه ی این بخش نگاه بیاندازید).

می توانید به سادگی در صورتی که یک کاربر وارد شده است، با متد is_authenticated() تشخیص دهید:

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

استفاده از کاربران

هنگامی که شما یک کاربر دارید – اغلب از request.user، ولی از طریق یکی از روش های مختصر توضیح داده شده – تعدادی از فیلدها و متدهای در دسترس در آن شیء دارید. شیء های AnonymousUser برخی از این رابط ها را تقلید کرده است، ولی نه تمام آن را، بنابراین باید همواره user.is_authenticated() را قبل از آنکه کاربری را که با آن سر و کار دارید را با حسن نیت تصور کنید بررسی کنید.. جدول 3-14 و 4-14 فیلد ها و متد ها را به ترتیب در شیء های User لیست کرده است.

فیلد توضیح
username الزامی؛ 30 حرف یا کمتر. تنها حروف الفبایی (حروف الفبا، عددها، و خط تیره)
First_name اختیاری؛ 30 حرف یا کمتر
Last_name اختیاری؛ 30 حرف یا کمتر
email اختیاری؛ آدرس پست الکترونیک
password الزامی؛ یک هش و ابر داده از رمز عبور (جنگو رمز عبور خام را ذخیره نمی کند). برای اطلاعات بیشتر به بخش "رمزهای عبور" مراجعه کنید.
Is_staff Boolean. اینکه کاربر می تواند به سایت مدیر دسترسی پیدا کند یا خیر را مشخص می کند.
Is_active Boolean. این که این حساب می تواند برای ورود استفاده شده باشد یا خیر را مشخص می کند. این پرچم را به جای حذف حساب ها مقدار False قرار دهید.
Is_superuser Boolean. اینکه این کاربر دارای تمام دسترسی ها بدون اختصاص دادن آن ها به طور واضح می باشد یا خیر را مشخص می کند.
Last_login یک datetime از آخرین ورود کاربر. به طور پیشفرض زمان/تاریخ فعلی در آن قرار دارد.
date_joined تشخیص datetime زمانی که حساب ساخته شده است. به طور پیشفرض زمانی که حساب ساخته شده است مقدار آن زمان/تاریخ فعلی می باشد.

 

 

متد توضیح
is_authenticated() همواره شیء های User "واقعی" مقدار True بر می گرداند. در صورتی که کاربر تصدیق شده باشد این روشی برای گفتن می باشد. این هیچ اشاره ای به حق دسترسی ها نداشته و فعال بودن کاربر را نیز بررسی نمی کند. تنها این را نشان می دهد که کاربر با موفقیت تصدیق شده است.
is_anonymous() تنها برای شیء های AnonylousUser مقدار True بر می گرداند (و مقدار False برای شیء های User "واقعی"). عموما، باید استفاده از is_authenticated() را برای این متد ترجیح دهید.
get_full_name() first_name را بعلاوه ی last_name، با یک فاصله در بین آن ها بر می گرداند.
set_password(passwd) رمز عبور کاربر را برای رشته ی خام داده شده قرار می دهد، در دست ترجمه/تالیف .... در واقع این متد شیء User را ذخیره نمی کند.
check_password(passwd) در صورتی که رشته ی خام داده شده رمز عبور درست کاربر باشد مقدار True بر می گرداند. در دست ترجمه/تالیف ....
get_group_permissions() لیستی از رشته های حق دسترسی که کاربر از طریق گروهی که به آن تعلق دارد در اختیار دارد را بر می گرداند.
get_all_permissions() لیستی از رشته ی حق دسترسی که کاربر دارد، هم حق دسترسی گروه هم کاربر.
has_perm(perm) در صورتی که کاربر دارای حق دسترسی مشخص شده باشد مقدار True بر می گرداند و perm قالب بندی "package.codename" می باشد. در صورتی که کاربر غیر فعال باشد، این متد همواره مقدار False بر می گرداند.
has_perms(perm_list) در صورتی که کاربر دارای تمام حق دسترسی های تعیین شده باشد مقدار True بر می گرداند. در صورتی که کاربر غیر فعال باشد، هموراه این متد مقدار False بر می گرداند.
has_module_perms(app_lable) در صورتی که کاربر هیچ حق دسترسی ای در app_lable داده شده نداشته باشد مقدار True بر می گرداند. در صورتی که کاربر غیر فعال باشد، این متد هموراه مقدار False بر می گرداند.
get_and_delete_messages() لیستی از شیء های Message در صف کاربر بر می گرداند و پیام های صف را حذف می کند.
email_user(subj, msg) یک پست الکترونیکی به کاربر می فرستد. این پست الکترونیکی از تنظیم DEFAULT_FROM_EMAIL فرستاده می شود. همچنین می توانید یک آرگومان سوم به نام from_email جهت override آدرس From در پست الکترونیکی ارسال کنید.

 

 

در پایان، شیء های User دارای دو فیلد many-to-many می باشند: groups و permissions. شیء های User می توانند همانند فیلدهای many-to-many دیگر به شیء های مربوط به خود دسترسی پیدا کنند:

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

وارد و خارج شدن از سایت

جنگو برای کنترل ورود و خروج از سایت، برخی توابع داخلی (و چند فوت و فن جذاب) را ارائه کرده است، ولی قبل از آن، اجازه دهید نگاهی به نحوه ی ورود و خروج از سایت را "به صورت دستی" بیاندازیم. جنگو جهت انجام این اعمال در django.contrib.auth دو تابع با نام های authenticate() و login() را ارائه کرده است.

جهت تصدیق یک نام کاربری و رمز عبور داده شده، از authenticate() استفاده کنید. این تابع دو آرگومان کیورد username و password را دریافت می کند، و در صورتی که رمز عبور برای نام کاربری داده شده معتبر باشد، یک شیء User بر می گرداند. در صورتی که رمز عبور معتبر نباشد، authenticate() مقدار None بر می گرداند:

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

authenticate() تنها اعتبار کاربر را تایید می کند. جهت ورود کاربر، از تابع loging() استفاده کنید. این تابع یک شیء HttpRequest و یک شیء User دریافت کرده و با استفاده از فریم ورک session، ID کاربر را درون session ذخیره می کند.

مثال زیر نحوه ی استفاده از هر دوی تابع authenticate() و login() را درون یک تابع view نشان می دهد:

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

جهت خروج یک کاربر، از django.contrib.auth.logout() داخل view خود استفاده کنید. این تابع یک شیء HttpRequest دریافت کرده و هیچ مقداری بر نمی گرداند:

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

دقت داشته باشید که auth.logout() در صورتی که کاربر وارد سایت نشده باشد، هیچ خطایی ایجاد نمی کند.

در عمل، نیازی به نوشتن توابع login/logout خودتان نخواهید داشت؛ سیستم تصدیق مجموعه ای از view ها برای کنترل ورود و خروج به طور عمومی ارائه کرده است. اولین گام در استفاده از این view های تصدیق، وصل کردن آن ها به URLconf می باشد. نیاز است تکه کد زیر را اضافه کنید:

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

/accounts/login/ و /accounts/logout/، URL های پیشفرض می باشند که جنگو برای این view استفاده می کند.

به طور پیشفرض view، login یک template را در registeration/login.html، render می کند (می توانید نام این template را از طریق ارسال یک آرگومان view اضافه تغییر دهید، ``template_name``). این فرم نیاز دارد حاوی یک فیلد username و یک فیلد password باشد. یک template ساده ممکن است چیزی شبیه به کد زیر باشد:

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

در صورتی که وارد شدن موفقیت آمیز باشد، کاربر به طور پیشفرض به /accounts/profile/ تغییر مسیر داده خواهد شد. می توان این حالت را توسط یک فیلد hidden به نام next با URL جهت تغییر مسیر بعد از ورود override کرد. همچنین می تواند این مقدار را به صورت یک پارامتر GET به view، login ارسال کرده و آن به صورت خودکار به صورت متغیر next به context اضافه خواهد شد که می توانید درون آن فیلد hidden آن را درج کنید.

view، logout کمی متفاوت تر عمل می کند. به طور پیشفرض این view یک template در registration/loggedout.html (که معمولا حاوی یک پیام "you've successfully logged out" می باشد) را render می کند. می توان view را با یک آرگومان اضافه با نام next_page فراخوانی کرد، که view را جهت تغییر مسیر بعد از خروج راهنمایی خواهد کرد.

محدودیت دسترسی برای کاربران وارد شده

به طور ساده، راه خام جهت محدود کردن دسترسی برای صفحات، بررسی request.user.is_authenticated() و تغییر مسیر به یک صفحه ی ورود می باشد:

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

یا شاید نمایش یک پیام خطا:

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

به صورت یک میانبر، می توان از decorator مناسب login_required استفاده کرد:

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

login_required به شکل زیر عمل می کند:

  • در صورتی که کاربر وارد نشده باشد، به /accounts/login/ تغییر مسیر داده می شود، ارسال شدن مسیر URL فعلی در رشته ی کوئری به صورت next، برای مثال: /accounts/login/?next=/polls/3/.
  • در صورتی که کاربر وارد شده باشد، view به صورت معمول اجرا می شود. کد view می تواند.

محدودیت دسترسی برای کاربرانی که یک آزمون را رد می کنند

محدودیت دسترسی بر پایه ی حق دسترسی ها یا برخی آزمون های دیگر، یا ارائه ی یک مکان مختلف برای view ورود اساسا به یک روش کار می کند.

روش خام اجرای آزمون در request.user به طور مستقیم درون view می باشد. برای مثال، view زیر برای اطمینان از این که کاربر، وارد شده و دارای حق دسترسی polls.can_vote می باشد یا خیر:

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

بار دیگر، جنگو یک میانبر با نام user_passes_test ارائه کرده است. این میانبر آرگومان هایی دریافت کرده و یک decorator تخصص یافته برای وضعیت خاص شما تولید می کند:

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

user_passes_test یک آرگومان الزامی دریافت می کند: یک قابل فراخوانی که یک شیء User دریافت کرده و در صورتی که کاربر اجازه ی تماشای صفحه را داشته باشد مقدار True بر می گرداند. توجه داشته باشید که user_passes_test به طور اتوماتیک تصدیق شدن کاربر را بررسی نمی کند؛ شما باید آن را برای خودتان انجام دهید.

همچنین در این مثال آرگومان دوم (اختیاری) نشان داده شده است، که اجازه ی تعیین URL برای صفحه ی خودتان را می دهد (/accounts/login/ به طور پیشفرض). در صورتی که کاربر آزمون را نگذرانده باشد؛ سپس decorator، user_passes_test کاربر را به login_url تغییر مسیر خواهد داد.

به دلیل آن که بررسی این که یک کاربر دارای یک حق دسترسی خاص است یا خیر یک وظیفه ی نسبتا مشترک می باشد، جنگو یک میانبر برای آن ارائه کرده است که یک decorator با نام permission_required() می باشد.

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

دقت داشته باشد که همچنین permission_required() یک پارامتر اختیاری login_url دریافت می کند؛ که این نیز به طور پیشفرض '/accounts/login/' می باشد.

محدود کردن دسترسی برای view های جنریک

یکی از سوالات تکراری پرسیده شده در لیست کاربران جنگو در مورد محدود کردن دسترسی برای یک view جنریک می باشد. برای جواب به این سوال؛ نیاز به نوشتن یک thin wrapper در اطراف view و اشاره URLconf به wrapper خود به جای خود generic view خواهید داشت:

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

البته که می توانید، login_required را با هر decorator محدودیت دیگری جا به جا کنید.

مدیریت کاربران، حق دسترسی ها و گروه ها

ساده ترین را برای مدیریت سیستم auth تاکنون، از طریق رابط مدیر بوده است. سایت مدیر در مورد نحوه ی استفاده از سایت مدیر جنگو را جهت ویرایش کاربران و کنترل حق دسترسی آن ها بحث کرده است، و اغلب اوقات شما فقط از این رابط استفاده خواهید کرد.

هر چند API های سطح پایین ای وجود دارند که شما هنگامی که نیاز به کنترل مستقل دارید از آن ها استفاده کنید، و این API ها را در بخش های بعدی توضیح داده ایم.

ساختن کاربران

ساختن کاربران با تابع کمکی create_user:

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

در این نقطه، user یک رابط نمونه ی User حاضر برای ذخیره شدن در پایگاه داده (create_user() در واقع save() خودش را فراخوانی نمی کند) می باشد. همچنین می توانید قبل از ذخیره attribute های آن را تغییر دهید:

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

تغییر رمزهای عبور

می توان یک رمز عبور را با set_password() تغییر داد:

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

attribute، password را به طور مستقیم قرار ندهید، مگر اینکه کاملا بدانید که چه کار می کنید. رمز عبور در واقع به صورت یک salted hash دخیره شده و در نتیجه نمی تواند به طور مستقیم ویرایش شود.

به طور رسمی تر، attribute، password از یک شیء User یک رشته در این قالب بندی می باشد:

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

آن یک نوع hash، salt و خود hash، جدا شده توسط حرف ($) می باشد.

hashtype همچنین sha1 (پیشفرض) یا md5 می باشد، الگوریتم استفاده شده برای انجام یک hash یک طرفه از رمز عبور. salt یک رشته ی تصادفی استفاده شده برای اضافه شدن به رمز عبور خام برای ساختن hash برای مثال:

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

توابع User.set.password() و User.check_password() این مقادیر را در پشت صحنه بررسی کرده و قرار می دهند.

hash های اضافه شده

hash تابع یک طرفه ی پنهانی می باشد – بدین معنی که، می توان به سادگی hash مقدار داده شده را محاسبه کرد، ولی گرفتن یک hash و برگرداندن آن به مقدار اصلی آن تقریبا غیر ممکن است.

در صورتی که رمزهای عبور را به صورت متن ساده ذخیره کرده باشیم، هر کسی که درون پایگاه داده ی دست داشته باشد به سرعت می تواند رمز عبور همه را بفهمد. ذخیره رمزهای عبور به صورت hash ها احتمال به خطر افتادن اطلاعات پایگاه داده را کاهش می دهد.

هر چند که، یک حمله کنند با رمز عبور پایگاه داده همچنان می تواند یک حمله ی brute-force را اجرا کرده و میلیون ها رمز عبور را به صورت hash در آورده و آن hash ها را با مقادیر ذخیره شده مقایسه کند. این مدتی طول می کشد ولی کمتر آن که شما ممکن است فکر کنید.

بدتر از آن rainbow table ها می باشند که به صورت عمومی در دسترس هستند، یا پایگاه های داده ی قبل از محاسبه ی hash ها از میلیون ها رمز عبور. با یک rainbow table، یک مهاجم با تجربه می تواند در چند ثانیه تمام رمزهای عبور را بشکند.

اضافه کردن یک salt – اساسا یک مقدار اولیه تصادفی – برای hash ذخیره شده یک لایه ی دیگری را جهت سختی در شکستن رمزهای عبور اضافه می کند. زیرا salt ها در هر رمزعبوری متفاوت می باشند، آن ها همچنین از استفاده ی rainbow table جلوگیری می کنند؛ در نتیجه مهاجمان مجبور به سقوط در یک حمله ی brute-force می شوند، در دست ترجمه/تالیف ....

هنگامی hash های salt شده امن ترین روش برای ذخیره رمزهای عبور نیستند، یک حد وسط بین امنیت و راحتی می باشند.

کنترل عضویت

می توان از این ابزار سطح پایین برای ساختن view هایی که به کاربر جهت عضو شدن برای حساب های جدید اجازه می دهند استفاده کرد. توسعه دهندگان مختلف عضویت را به طور متفاوتی انجام می دهند، بنابراین جنگو نوشتن یک view را برای شما ترک کرده است. خوشبختانه، این کار بسیار ساده می بشاد.

در ساده طرین حالت، می توان یک view کوچک برای اطلاعات الزامی کاربر و ساختن آن کاربران تهیه کرد. جنگو یک فرم داخلی ارائه کرده است که می توان برای این منظور از آن استفاده کرد، که در مثال زیر استفاده خواهیم کرد:

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

این فرم یک template با نام registration/register.html را فرض می کند. در اینجا یک مثال از template مورد نظر وجود دارد:

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

استفاده از تصدیق داده در Template ها

کاربر وارد شده ی فعلی و حق دسترسی های وی، زمانی که از RequestContext (آموزش template پیشرفته را نگاه کنید) استفاده می کنید در template context در دسترس می باشد.

نکته

از نظر فنی، این متغیرها تنها در صورتی که شما از RequestContext استفاده کنید و تنظیم TEMPLATE_CONTEXT_PROCESSORS حاوی "django.core.context_processors.auth" باشد (که به طور پیشفرض این طور است) در template context در دسترس می باشند. بار دیگر برای اطلاعات بیشتر می توانید به آموزش template پیشرفته مراجعه کنید.

هنگامی که از RequestContext استفاده می کنید، کاربر فعلی (که می تواند هم یک نمونه از User یا یک نمونه ی AnonymousUser باشد) در متغیر template {{ user }} ذخیره می شود:

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

حق دسترسی های این کاربر در متغیر template {{ perms }} ذخیره شده اند. این یک پروکسی template‑friendly برای تعدادی از متدهای حق دسترسی می باشد که به طور خلاصه توضیح داده شده است.

برای استفاده از شیء perms دو روش وجود دارد. می توان در صورتی که کاربر دارای هیچ حق دسترسی برای برخی برنامه های داده شده نداشته باشد، برای بررسی آن از چیزی شبیه به این {% if perms.polls %} استفاده کرد، یا می توان در صورتی که کاربر دارای حق دسترسی خاصی می باشد برای بررسی آن از چیزی شبیه به این {% if perms.polls.can_vote %} استفاده کرد.

در نتیجه، می توان حق دسترسی ها را در عبارت template {% if %} بررسی کرد:

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

حق دسترسی ها، گروه ها و پیام ها

چند قسمت دیگر از فریم ورک authentication وجود دارد که تنها به طور روزنامه وار از کنار آن عبور کردیم. در ادامه نگاهی نزدیک تر به آن ها خواهیم داشت.

حق دسترسی ها

حق دسترسی ها روشی ساده برای "علامت گذاری" کاربران و گروه ها جهت نشان دادن این که کاربر یا گروه علامت گذاری شده دارای توانایی اجرای برخی کارها می باشد. حق دسترسی ها معمولا توسط سایت مدیر جنگو استفاده شده اند، ولی می توان به سادگی در کد خود نیز از آن ها استفاده کرد.

سایت مدیر جنگو به صورتی که در زیر بیان شده است از حق دسترسی ها استفاده کرده است:

  • دسترسی به view فرم "add"، و اضافه کردن یک شیء که محدود به کاربران با حق دسترسی add برای آن نوع از شیء.
  • دسترسی به view لیست تغییر، view فرم "change"، و تغییر یک شیء که به کاربران با حق دسترسی change برای آن نوع شیء محدود شده است.
  • دسترسی به حذف یک شیء محدود شده به کاربران با حق دسترسی delete برای آن نوع از شیء.

حق دسترسی ها به صورت globally برای هر نوع از آجکت، نه هر نمونه ی خاص از شیء قرار داده شده اند. برای مثال، می توان گفت "Mary قادر است اخبار را تغییر دهد" ولی حق دسترسی ها اجازه نمی دهد که به عنوان مثال بگویید "Mary قادر است اخبار تغییر دهید، ولی تنها آنهایی را که خود او ساخته است" یا "Mary قادر است تنها اخباری را تغییر دهید که دارای یک وضعیت خاصی، انتشار یا ID خاص باشد."

این ها سه حق دسترسی اساسی می باشد – اضافه کردن، تغییر دادن، و حذف کردن – که به طور خودکار برای هر مدل جنگو ایجاد شده اند. در پشت صحنه، این حق دسترسی ها هنگامی که شما دستور manage.py sycdb را اجرا می کنید درون جدول پایگاه داده با نام auth_permission اضافه می شوند.

این حق دسترسی ها فرمی از "._" می باشند. بدین معنی که اگر دارای یک برنامه ی polls (نظرسنجی) با یک مدل Choice می باشید، حق دسترسی های "polls.add_choice"، "polls.change_choice" و "polls.delete_choice" را بدست خواهید آورد.

درست مثل کاربران، حق دسترسی ها در یک مدل جنگو موجود در django.contrib.auth.models اجرا شده اند. این یعنی این که می توان در صورت تمایل ارتباط با permission ها، به طور مستقیم از API پایگاه داده ی جنگو استفاده کرد.

گروه ها

گروه ها یک view جنریک از طبقه بندی کاربران می باشد، بنابراین می توان حق دسترسی ها، یا برخی چیزهای دگر را برای آن کاربران بکار برد. یک کاربر می تواند به هر تعدادی از گروه ها تعلق داشته باشد.

یک کاربر در یک گروه به طور خودکار دارای حق دسترسی های داده به آن گروه می باشد. برای مثال، در صورتی که گروه Site editors دارای حق دسترسی can_edit_home_page باشد، هر کاربر در آن گروه آن حق دسترسی را خواهد داشت.

گروه ها همچنین روش مناسبی برای طبقه بندی کاربران برای دادن برخی لیبل ها یا قابلیت تمدید به آن ها می باشد. برای مثال، می توان یک گروه با نام 'Special users' ایجاد نمود، و آن قبیل از کاربرانی را که می خواهیم در یک بخش از سایت که تنها برای دسترسی کاربران در نظر گرفته شده است فعالیت کنند را در آن قرار دهیم، یا پیام هایی که تنها برای کاربران در نظر گرفته ایم را به آن ها ارسال کنیم.

همانند کاربران، ساده ترین روش برای مدیریت گروه ها، از طریق رابط مدیر می باشد. هر چند، گروه ها نیز تنها مدل های جنگو می باشند که در django.contrib.auth.models وجود دارند. بنابراین شما می توانید همواره برای سرو کار داشتن با گروه ها در سطح پایین از API پایگاه داده ی جنگو استفاده کنید.

پیام ها

سیستم پیام یک روش سبک جهت به صف کردن پیام برای کاربران داده شده می باشد. یک پیام، مرتبط با یک User می باشد. هیچ مفهومی از انقضا یا برچسب زمانی وجودد ندارد.

پیام ها توسط رابط مدیر جنگو بعد از اعمال موفقیت آمیز استفاده می شوند. زمانی که شما یک آجکت ایجاد می کند، متوجه ی یک پیام "The object was created successfully" در بالای صفحه ی مدیر خواهید شد.

می توانید از یک API هم شکل برای صف بندی و نمایش پیام ها در برنامه ی خودتان استفاده کنید. API ساده می باشد:

  • جهت ایجاد یک پیام جدید، از user.message_set.create(message='message_text') استفاده کنید.
  • جهت بازیابی/حدف پیام ها، از user.get_and_delete_messages() استفاده کنید، که یک لیست از شیء های Message در صف کاربران (در صورت وجود) بر می گرداند و پیام های از صف را حذف می کند.

در مثال view زیر، سیستم یک پیام برای کاربر بعد از ساختن یک playlist ذخیره می کند:

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

هنگامی که شما از RequestContext استفاده می کند، کاربر وارد شده ی فعلی و پیام او در template context به صورت متغیر template {{ message }} در دسترس هستند. در زیر یک مثال از کد template وجود دارد که پیام ها را نمایش می دهد:

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

دقت داشته باشید که RequestContext در پشت صحنه get_and_delete_messages را فراخوانی می کند، بنابراین هر پیامی حذف شده خواهد بود حتی اگر آن ها را نمایش ندهید.

در پایان، دقت داشته باشید که این فریم ورک پیام ها تنها با کاربران در پایگاه داده ی کاربر کار می کنند. برای ارسال پیام ها به کاربران anonymous، از چارچوب session به طور مستقیم استفاده کنید.

لینک به دیدگاه
  • 4 هفته بعد...

Caching در جنگو

هر بار که یک کاربر یک صفحه را درخواست می کند، وب سرور تمام محاسبات را ایجاد می کند – از کوئری های پایگاه داده جهت render کردن template برای business logic – برای ساختن صفحه ای که بازدید کنندگان سایت می بینند. این حرکت از نظر بار اضافی بسیار پر خرج تر و سنگین تر از خواندن فایل از filesystem می باشد.

برای اغلب برنامه های وب، این بار اضافی یک درگیری بزرگی به حساب نمی آید. اغلب برنامه های وب washingtonpost.com یا slashdot.org نیستند؛ آن ها وب سایت هایی یا اندازهای کوچک و متوسط و با ترافیکی به همین شکل می باشند. ولی برای سایت های با ترافیک بالا، حذف بارهای اضافی تا حد ممکن یک ضرورت به حساب می آید.

در آنجا بود که cashing بوجود آمد.

cache کردن چیزی، ذخیره ی نتیجه ی یک محاسبه ی پر خرج به طوری که مجبور نباشید محاسبه را در بار بعدی انجام دهید می باشد. در زیر تعدادی شبه کد وجود دارد که نحوه ی این عمل را برای یک صفحه ی وب به طور پویا تولید شده توضیح می دهد:

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

فریم ورک یا چارچوب جنگو یک سیستم قدرتمند cache را ارائه می کند که اجازه می دهد صفحات پویا را به طوری که اجباری برای مورد محاسبه قرار دادن برای هر درخواست نداشته باشید ذخیره کنید. برای راحتی، جنگو (Django) سطح های متفاوتی از cache به صورت دانه دانه را ارائه می دهد: می توان خروجی view های خاص را cache کرد، می توان تنها قسمت هایی که برای تولید مشکل می باشند را cache کرد، یا می توان تمام سایت را cache کرد.

همچنین جنگو با cache ها "upstream" به خوبی کار می کند، مانند Squid (http://www.squid-cache) و cache های بر پایه ی مرورگر. این ها انواعی از cache هایی هستند که به طور مستقیم کنترل نمی شوند ولی می توان تذکراتی (از طریق HTTP headers) درباره ی قسمت هایی از سایت که باید cache شده باشد و نحوه ی آن تهیه کرد.

نصب کردن Cache

سیستم cache نیازمند نصب کردن اندکی از مقدادیر می باشد. به عبارت دیگر، باید جایی که داده ی cache شده ی شما باید وجود داشته باشد را به آن بگویید – خواه در یک پایگاه داده، در filesystem یا مستقیما در حافظه. این یک تصمیم مهم می باشد که بر روی اجرای cache شما تاثیر می گذارد؛ بله، برخی از انواع cache ها از انواع دیگر سریع تر می باشند.

اولویت cache شما در تنظیم CACHE_BACKEND درون فایل تنظیمات می باشد. در زیر توضیحی از تمام مقادیر قابل دسترس برای CACHE_BACKEND وجود دارد.

Memcached

تاکنون سریع ترین، موثرترین نوع cache در دسترس برای جنگو، Memcached یک فریم ورک cache بر پایه ی حافظه می باشد که در ابتدا برای کنترل بارگذاری های بالا در LiveJournal.com و به دنبال آن (در دست ترجمه ...). این نوع cache توسط سایت هایی از قبیل Facebook و Wikipedia جهت کاهش دسترسی پایگاه داده و به طور چشمگیری افزایش کارایی سایت استفاده شده است.

Memcached به صورت آزاد و مجانی در

برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
در دسترس می باشد. این cache به صورت daemon اجرا شده و مقدار مشخص از RAM را اختصاص داده است. تمام کاری که این نوع cache انجام می دهد، تهیه ی یک رابط سریع برای اضافه کردن، بازیابی و حذف داده دلخواه در cache می باشد. تمام داده به طور مستقیم در حافظه ذخیره شده است، بنابراین هیچ بار اضافه ای برای پایگاه داده یا استفاده filesystem وجود ندارد.

بعد از نصب خود Memchached، نیاز به نصب اتصالات پایتون Memcached خواهیم داشت، که به طور مستقیم همراه جنگو نمی باشند. دو نسخه از این قابل دسترس می باشند. یکی از ماژول های زیر را انتخاب و نصب کنید:

  • سریع ترین آپشن در دسترس یک ماژول با نام cmemchache می باشد، که در لینک
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    در دسترس می باشد.
  • در صورتی که نمی توانید cmemcache را نصب کنید، می توانید python‑memcached را که در لینک
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    در دسترس می باشد را نصب کنید. در صورتی که URL دیگر معتبر نباشد، تنها کافیست به وب سایت Memcached مراجعه کرده (http://www.danga.com/memcached/) و اتصالات پایتون را از بخش "Client APIs" به دست آورید.

جهت استفاده Memcached با جنگو، CACHE_BACKEND را با مقدار memcached://ip:port/ تنظیم کنید، جایی که ip آدرس IP Memcached daemon و port، پورت Memcached ای می باشد که در حال اجرا است.

در مثال زیر، Memcached در localhost (127.0.0.1) پورت 11211 در حال اجرا می باشد:

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

یکی از ویژگی های بسیار خوب Memcached، توانایی آن برای به اشتراک گذاشتن cache در سرتاسر چندین سرور می باشد. بدین معنی که شما می توانید Memcached daemon ها را در چندین ماشین اجرا کرده و برنامه با گروهی از ماشین به صورت یک cache تنها رفتار خواهد کرد، بدون نیاز به مقادیر cache تکراری در هر ماشین. جهت بهره بردن از این خصوصیت، تمام آدرس های سرورها را در CACHE_BACKEND که با علامت (;) از هم جدا شده اند قرار دهید.

در مثال زیر، cache در سرتاسر نمونه های Memcachedd در حال اجرا در آدرس IP های 172.19.26.240 و 172.19.26.242 هر دو در پورت 11211 به اشتراک گذاشته شده اند.

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

در مثال زیر، cache در سرتاسر نمونه های Memcached در حال اجرا در آدرس IP های 172.19.26.240 (پورت 11211) و 172.19.26.242 (پورت 11212) و 172.19.26.244 (پورت 11213) به اشتراک گذاشته شده است.

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

نکته آخر درباره ی Memcached این است که، cache بر پایه ی حافظه دارای یک اشکال نیز می باشد: به این دلیل که داده cache شده درون حافظه ذخیره می شود، در صورتی که سرور شما crash کند داده مورد نظر از بین خواهد رفت. واضح است که، حافظه برای ذخیره سازی داده به طور دائمی در نظر گرفته نشده است، بنابراین به cache کردن بر پایه حافظه برای تنها ذخیره داده اعتماد نکنید. بدون هیچ شکی، باطن هیچکدام از سیستم های cache جنگو برای ذخیره سازی دائمی در نظر گرفته نشده اند – آن ها به طور کلی راهکارهایی برای cache کردن داده می باشند، نه ذخیره سازی – ولی به این موضوع در اینجا اشاره کردیم، چرا که cache کردن بر پایه حافظه به طور خاص موقتی می باشد.

Cache کردن پایگاه داده

جهت استفاده از یک جدول پایگاه داده برای cache، ابتدا یک جدول cache درون پایگاه داده خود توسط اجرای دستور زیر ایجاد کنید:

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

.. جایی که [cache_table_name] نام جدول پایگاه داده ای می باشد که ساخته خواهد شد. (این نام می تواند هر چیزی که می خواهید باشد، تا زمانی که یک نام جدول معتبر باشد و درون پایگاه داده شما وجود نداشته باشد.) این دستور یک جدول تنها در پایگاه داده شما ایجاد می کند که در قالب بندی مناسبی که سیستم cache پایگاه داده انتظار دارد می باشد.

هنگامی که شما جدول پایگاه داده را ایجاد کردید، تنظیم CACHE_BACKEND را با "db://tablename" تنظیم کنید، جایی که tablename نام جدول پایگاه داده می باشد. در مثال زیر، نام جدول cache نام my_cache_table می باشد:

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

cache پایگاه داده از پایگاه داده همسان به صورتی که درون فایل تنظیمات تعیین شده است استفاده می کند. شما نمی توانید از پایگاه داده متفاوتی برای جدول cache خود استفاده کنید.

cache پایگاه داده در صورتی که دارای یک پایگاه داده ی سریع باشید بسیار عالی کار خواهد کرد.

Cache کردن Filesystem

جهت ذخیره ی آیتم های cache شده در یک filesystem، از "file://" در CACHE_BACKEND استفاده کنید. به عنوان مثال، جهت ذخیره داده ی cache شده در /var/tmp/django_cache از تنظیم زیر استفاده کنید:

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

توجه داشته باشید که سه علامت (/) در شروع مثال فوق وجود دارد. دوتای اول برای file://، و سومی، اولین حرف مسیر دایرکتوری /var/tmp/django_cache می باشد. در صورتی که در سیستم عامل ویندوز هستید، حرف درایو را بعد از file:// مانند زیر قرار دهید:

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

مسیر دایرکتوری باید کامل باشد – بدین بدان معنی است که، باید از ریشه filesystem شروع شود. گذاشتن یا نذاشتن علامت (/) در پایان تنظیم اهمیتی ندارد.

اطمینان حاصل کنید دایرکتوری اشاره شده توسط این تنظیم وجود داشته و قابل نوشتن و خواندن توسط کاربر سیستمی که وب سرور درون آن اجرا می شود باشد. در ادامه مثال فوق، در صورتی که سرور شما به صورت کاربر apache اجرا می شود، اطمینان حاصل کنید که دایرکتوری /var/tmp/django_cache وجود داشته و قابل نوشتن و خواندن توسط کاربر apache باشد.

هر مقدار cache ای به صورت یک فایل جدا ذخیره شده خواهد بود که محتویات داده cach ذخیره شده در یک قالب بندی سریال شده ("pickled") توسط ماژول pickle پایتون هستند. هر نام فایل کلید cache رها شده برای استفاده امن filesystem می باشد.

Cache کردن حافظه ی داخلی

اگر مزایای سرعت cache در حافظه را بدون قابلیت اجرای Memcached می خواهید، cache حافظه داخلی را ملاحظه کنید. این cache چند پردازشی و thread-safe می باشد. برای استفاده از آن، تنظیم CACHE_BACKEND را با "locmem:///" تنظیم کنید. برای مثال:

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

توجه داشته باشید که هر پردازش دارای نمونه cache خصوصی خود می باشد، که بدین معنی است که cache به صورت cross‑process ممکن خواهد بود. همچنین واضح است که حافظه ی داخلی cache منحصرا حافظه ی کار آمد به حساب نمی آید، بنابراین شاید برای محیط های تولید انتخاب مناسبی نباشد. این نوع cache برای توسعه عالی می باشد.

Cache کردن ساختگی (برای توسعه)

در پایان، جنگو یک cache با نام "dummy" ارائه کرده است که در واقع cache نیست – این تنها بدون انجام چیزی رابط cache را اجرا می کند.

این نوع cache در صورتی که شما دارای یک سایت تولید باشید که از cache سنگینی را در مکان های گوناگون استفاده کند مفید است. ولی مکان ها یک محیط توسعه/آزمون جایی که نمی خواهید cache انجام شود و نمی خواهید لزوما کد شما برای مورد خاص اخیر تغییر کند. جهت فعال کردن dummy cache، تنظیم CACHE_BACKEND را مانند زیر تنظیم کنید:

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

استفاده از یک Cache سفارشی

هنگامی که جنگو پشتیبانی از تعداد از cache ها بدون هیچ تنظیمی را ارائه می کند. ممکن است بخواهید از یک cache سفارشی شده استفاده کنید. برای استفاده از یک cache خارجی با جنگو، از مسیر import پایتون به صورت قسمت طرح (قسمتی قبل از تعریف علامت کالن ":") از URL، CACHE_BACKEND مانند زیر استفاده کنید:

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

در صورتی که cache مخصوص خود را می سازید، می توانید از cache استاندارد به صورت پیاده سازی مرجع استفاده کنید. درون دایرکتوری django/core/cache/backends/ از منبع جنگو کد را خواهید یافت.

نکته: بدون هیچ دلیل قانع کننده ای، مانند به عنوان مثال پشتیبانی نکردن یک میزبانی از آیتمی، شما باید به cache های درون جنگو (Django) وجود دارند استفاده کنید. آن ها بخوبی مورد آزمون قرار گرفته و استفاده از آن ها ساده می باشد.

آرگومان های CACHE_BACKEND

هر نوع cache ای ممکن است آرگومان هایی دریافت کند. آن ها به شکل query-string در تنظیم CACHE_BACKEND داده شده می باشند. آرگومان های معتبر از این قرار می باشند:

  • timeout: timeout پیشفرض بر حسب ثانیه، برای cache استفاده می شود. این آرگومان به طور پیشفرض 300 ثانیه (5 دقیقه) می باشد.
  • max_entries: برای cache های locmem، filesystem و پایگاه داده می باشد، حداکثر تعداد از ورودی های مجاز در cache قبل از مقادیر قدیمی که حذف شده اند. این آرگومان به صورت پیشفرض 300 می باشد.
  • cull_percentage: زمانی که max_entries برسد، درصد ورودی که جمع آوری شده اند می باشد. (در دست ترجمه ...).
  • یک مقدار از 0 برای cull_percentage بدین معنی است که تمام cache زمانی که max_entries برسد خالی شده خواهد بود. (در دست ترجمه ...).

در مثال زیر، timeout مقدار 60 می باشد:

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

در مثال زیر، timeout مقدار 30 بوده و max_entires مقدار 400 می باشد:

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

آرگومان های نا معتبر بدون هیچ خطایی رد می شوند، به طوری که مقادیر نا معتبر آرگومان های شناخته شده رد می شوند.

Cache در هر سایت

هنگامی که cache راه اندازی شده است، ساده ترین روش جهت استفاده از cache، cache کردن کل سایت می باشد. نیاز به اضافه کردن 'django.middleware.cache.UpdateCacheMiddleware' و 'django.middleware.cache.FetchFromCacheMiddleware' به تنظیم مورد نظر یعنی MIDDLEWARE_CLASSES خواهید داشت، مانند مثال زیر:

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

نکته

نه، این اشتباه تایپ نیست: middleware مربوط به "update" باید در ابتدای لیست باشد، و middleware مربوط به "fetch" باید آخرین باشد. جزئیات کمی مبهم می باشند، ولی در صورتی که می خواهید داستان کامل را بدانید ترتیب MIDDLEWARE_CLASSES زیر را ببینید.

سپس، نیازمندی های زیر را به فایل تنظیمات جنگو خود اضافه کنید:

  • CACHE_MIDDLEWARE_SECONDS – تعداد ثانیه هایی که هر صفحه باید cache شده باشد.
  • CACHE_MIDDLEWARE_KEY_PREFIX – در صورتی که cache در میان چندین سایت با استفاده از نصب جنگو یکسان به اشتراک گذاشته شده باشد. این تنظیم برای نام سایت قرار دهید، یا برخی رشته های دیگر که برای این نمونه جنگو منحصر به فرد می باشند، جهت جلوگیری برخوردهای کلید. در صورتی که اهمیتی نمی دهید از یک رشته ی خالی استفاده کنید.

middleware مربوط به cache، هر صفحه ای که دارای پارامتر GET یا POST نباشد را cache می کند. به طور اختیاری، در صورتی که تنظیم CACHE_MIDDLEWARE_ANONYMOUS_ONLY مقدار True را داشته باشد، تنها درخواست های anonymous (نه آن هایی که توسط یک کاربر وارد شده ساخته شده باشند) cache شده خواهند بود. این یک روش ساده و موثر از از کار انداختن عمل cache برای صفحات هر کاربر خاص (شمال رابط مدیر جنگو) می باشند. دقت داشته باشید، اگر از CACHE_MIDDLEWARE_ANONYMOUS_ONLY استفاده می کنید، باید اطمینان حاصل کنید AuthenticationMiddleware فعال کرده اید.

علاوه بر این، cache middleware به طور خودکار تعدادی header در هر HttpResponse قرار می دهد:

  • یک header به نام last-Modified برای تاریخ/زمان فعلی هنگامی که یک نسخه ی cache نشده از صفحه درخواست شده است قرار می دهد.
  • header ای با نام Expires برای تاریخ/زمان فعلی به علاوه ی CACHE_MIDDLEWARE_SECONDS تعریف شده قرار می دهد.
  • header ای با نام Cache-Control جهت دادن یک حداکثر عمر برای صفحه – بار دیگر، از تنظیم CACHE_MIDDLEWARE_SECONDS.

برای اطلاعات بیشتر در مورد middleware به مبحث middleware مراجعه کنید.

در صورتی که یک view زمان انقضای (به عنوان مثال دارای یک بخش max-age در هدر Cache-Control خود باشد) خود را قرار دهد، سپس صفحه تا زمان انقضا cache شده خواهد بود، به جای CACHE_MIDDLEWARE_SECONDS. استفاده از decorator ها در django.views.decorators.cache می توان به سادگی یک زمان انقضای view (با استفاده از decorator، cache_control) قرار داد یا cache برای یک view را غیر فعال کرد (با استفاده از decorator، never_cache). برای اطلاعات بیشتر در مورد این decorator ها به بخش "استفاده از header های دیگر مراجعه کنید.

Cache در ازای هر View

یک روش cache در مقیاس کوچک تر برای استفاده از فریم ورک یا چارچوب cache به شکل cache کردن خروجی view های منحصر به فرد می باشد. django.views.decorators.cache یک decorator با نام cache_page تعریف می کند که به طور خودکار پاسخ view را برای شما cache می کند. این روش برای استفاده ساده می باشد:

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

همچنین می توانید از دستور زبان پایتون 2.4 به بالا استفاده کنید:

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

cache_page یک آرگومان تنها دریافت می کند: timeout مربوط به cache، بر حسب ثاینه. در مثال بالا، نتیجه view مورد نظر یعنی my_view() برای 15 دقیقه cache شده خواهد بود. (توجه داشته باشید که جهت خوانایی بیشتر به صورت 60 * 15 نوشته شده است. 60 * 15 به صورت 900 ارزیابی خواهد شد – این بدان معنی است که، 15 دقیقه توسط ضرب 60 ثانیه در هر دقیقه بدست می آید.)

cache به ازای هر view، مانند cache به ازای هر سایت، (در دست ترجمه ...). در صورتی که چندین URL به یک view همسان اشاره کنند، هر URL به صورت جداگانه cache خواهد شد. در ادامه مثال my_view، در صورتی که URLconf شما مانند زیر باشد:

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

سپس درخواست های به /foo/1/ و /foo/23/ به طور جداگانه cache خواهند شد، به صورتی که ممکن است انتظار داشته باشید. ولی هنگامی که یک URL خاص (مانند /foo/23/) درخواست شده باشد، درخواست های بعدی به آن URL از cache استفاده خواهند کرد.

تعیین به ازای هر Cache View در URLconf

مثال های بخش قبلی دارای کد مسقیم زده شده در view ای که مورد cache قرار می گرفت بودند، زیرا cache_page تابع my_view را در محل تغییر می دهد. این رویکرد view شما را به سیستم cache جفت می کند، که به دلایلی ایده آل نمی باشد. به عنوان مثال، ممکن است بخواهید از توابع view در جایی دیگر، سایت بدون cache استفاده کنید، یا ممکن است view ها را به افرادی توزیع کنید که ممکن است بخواهند از آن ها بدون cache شدن استفاده کنند. راهکار برای این مشکلات، تعیین cache به ازای هر view به جای قرار گرفتن در خود توابع view درون URLconf می باشد.

انجام این کار ساده می باشد: به سادگی عبارت cache_page که درون تابع view قرار گرفته است را درون URLconf اشاره کننده به این تابع view قرار دهید. در زیر URLconf قبلی را مشاهده می کنید:

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

در زیر کدی همسان وجود دارد، با این تفاوت که cache_page درون URLconf قرار گرفته است:

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

در صورتی که از این رویکرد استفاده می کنید، قرار داده cache_page را درون URLconf فراموش نکنید.

Template Fragment Caching

در صورتی که خواستار کنترل بیشتر می باشید، همچنین می توانید قطعه های template را با استفاده تگ template ای با نام cache، cache کنید. جهت دادن دسترسی templateبه این تگ، {% load cache %} را در بالای template خود قرار دهید.

تگ {% cache %} محتویات بلاک برای مقدار زمان داده شده را cache می کند. این تگ حداقل دو آرگومان دریافت می کند: cache timeout بر حسب ثانیه، و نام برای دادن قطعه cache. برای مثال:

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

گاهی اوقات ممکن است بخواهید چندین کپی از یک قطعه را بسته به برخی داده های پویا که داخل قطعه ظاهر می شوند cache کنید. برای مثال، ممکن است یک کپی جدای cache شده از نوار کناری استفاده شده در مثال قبلی برای هر کاربر از سایت خود را بخواهید. توسط ارسال آرگومان های اضافه به تگ {% cache %} برای تشخیص قطعه cache به طور منحصر به فرد این کار را انجام دهید:

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

تعیین بیشتر از یک آرگومان برای تشخیص قطعه کاملا خوب می باشد. به سادگی برخی آرگومان ها را به {% cache %} همان طور که نیاز دارید ارسال کنید.

cache timeout می تواند یک متغیر template باشد، تا زمانی که متغیر template یک مقدار integer باشد. برای مثال، در صورتی که متغیر my_timeout مقدار 600 برایش قرار گرفته باشد، سپس دو مثال زیر با هم برابر هستند:

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

این خصوصیت برای اجتناب از تکرار در template ها مفید می باشد. می توان timeoutرا درون یک متغیر قرار داد، در یک جا، و تنها از آن مقدار دوباره استفاده کرد.

API سطح پایین Cache

گاهی اوقات، cache کردن تمام صفحه ی render شده فایده ی خیلی زیادی برای شما ندارد، در واقع بیش از حد نا مناسب می باشد.

ممکن است، برای مثال، سایت شما حاوی یک view باشد که بسته به چندین کوئری پر خرج نتیجه دهد، نتایج از آن تغییر در فواصل مختلف. در این مورد، استفاده از cache تمام صفحه ایده آل نمی باشد که به ازای هر سایت یا هر view استراتژی های ارائه شده cache، زیرا شما نمی خواهید تمام نتیجه (از آنجایی که برخی از داده ها اغلب تغییر می کنند) را cache کنید، ولی همچنان می خواهید نتایجی که به ندرت تغییر می کنند را cache کنید.

برای موارد شبیه به این، جنگو یک cache API سطح پایین ساده را ارائه می کند. می توان از این API جهت ذخیره ی شیء هایی در cache با هر سطحی که می خواهید استفاده کرد. می توان هر شیء پایتونی که می تواند به طور امن pickled باشد را cache کرد: رشته ها، دیکشنری ها، لیست شیء های مدل، و غیره ... (اغلب شیء های رایج پایتون می توانند pickled باشند؛ برای اطلاعات بیشتر درباره pickling به مستندات پایتون مراجعه کنید.)

ماژول cache، django.core.cache دارای یک شیء cache می باشد که به طور خودکار از تنظیم CACHE_BACKEND ساخته شده است:

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

رابط اصلی set(key, value, timeout_seconds) و get(key):

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

آرگومان timeout_seconds اختیاری می باشد و به آرگومان timeout در تنظیم CACHE_BACKEND بر می گردد.

در صورتی که شیء در cache مورد نظر یعنی cache.get() وجود نداشته باشد None بر می گرداند:

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

توصیه می شود مقدار واقعی None را در cache ذخیره نکنید، چرا که قادر به تشخیص مقدار ذخیره کرده ی None خود و دیگر مقادیر None نخواهید بود.

cache.get() می تواند یک آرگومان default دریافت کند. این آرگومان مقدار برگشت داده شده، در صورت عدم وجود شیء در cache را تعیین می کند:

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

جهت اضافه کردن یک کلید تنها در صورتی که وجود نداشته باشد، از متد add() استفاده کنید. این متد پارامترهایی همانند set() دریافت می کند، ولی این متد در صورتی که کلید تعیین شده حاضر باشد (وجود داشته باشد) تلاشی برای به روز رسانی نخواهد کرد:

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

در صورتی که نیاز به دانستن این موضوع دارید که آیا add() یک مقدار در cache ذخیره کرده است، می توانید مقدار برگشتی را بررسی کنید. این مقدار در صورتی که مقدار ذخیره شده باشد True و در غیر این صورت مقدار False بر می گرداند.

همچنین یک رابط بانام get_many() وجود دارد که تنها یک بار (در دست ترجمه ...). get_many() یک دیکشنری با تمام کلیدهایی که خواسته اید و در واقع در cache وجود داشته باشد بر می گرداند.

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

در پایان، می توان به طور واضح با delete() کلیدها را حذف کرد. این یک روش ساده از حذف cache برای یک شیء خاص می باشد:

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

همچنین می توان با استفاده از متدهای incr() و decr() یک کلید موجود را به ترتیب افزایش و کاهش داد. به طور پیشفرض، مقدار cache موجود توسط مقدار 1، افزایش یا کاهش داده خواهد شد. مقادیر دیگر افزایش/کاهش می توانند توسط تهیه ی یک آرگومان برای فراخوانی افزایش/کاهش تعیین شده باشند. در صورتی که تلاش کنید یک کلید cache ای که وجود ندارد را افزایش یا کاهش دهید یک خطا ایجاد خواهد شد.:

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

نکته

متدهای incr()/decr() برای atomic بودن تضمین نشده اند. در آن cache ها که افزایش/کاهش atomic را پشتیبانی می کنند (که مهمترین آن ها، memcached می باشد)، اعمال افزایش و کاهش atomic خواهند بود. هر چند، در صورتی که cache یک عمل افزایش/کاهش را ذاتا تهیه نکند، با استفاده از یک پروسه ی دو مرحله ای بازیابی/به روز رسانی انجام شده خواهد بود.

Cache های بالا دست

تا کنون این آموزش از کتاب، بر روی cache داده های خودتان تمرکز داشته است. ولی نوع دیگری از cache، مربوط به توسعه ی وب می باشد که توسط cache های "بالا دست" انجام می شود. این ها سیستم هایی هستند که صفحات را برای کاربران حتی قبل از رسیدن درخواست به وب سایت شما، cache می کنند.

در اینجا مثال از cache های بالا دست وجود دارد:

  • ISP شما ممکن است بعضی صفحات را cache کند، بنابراین اگر یک صفحه را از
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    درخواست کرده باشید، ISP شما صفحه را بدون داشتن دسترسی مستقیم به example.com به شما ارسال می کند. maintainer های example.com دارای هیچ دانشی از این cache نمی باشند؛ ISP بین example.com و مرورگر وب شما نشسته و تمام cache را به طور روشن کنترل می کند.
  • وب سایت جنگوی شما ممکن است، پشت یک cache پروکسی از قبیل وب پروکسی Squid (http://www.squid‑cache.org/) نشسته و صفحات را برای نمایش cache نماید. در این مورد، هر درخواستی ابتدا توسط پروکسی کنترل می شده و تنها در صورت لزوم به برنامه ی شما ارسال می شود.
  • مرورگر وب شما نیز همچنین صفحات را cache می کند. در صورتی که یک صفحه ی وب header های مناسب را ارسال کند، مرورگر شما، کپی cache های داخلی را برای درخواست های بعدی به آن صفحه استفاده می کند، بدون حتی اتصال دوباره به صفحه ی وب جهت دیدن این که آیا تغییر کرده است یا خیر.

cache بالا دست یک افزایش بهره وری خوب می باشد، ولی یک خطر در آن وجود دارد: بسیاری از محتویات صفحات وب از لحاظ authentication و میزبانی از متغیرهای دیگر متفاوت می باشند، و سیستم های cache به طور کورکورانه صفحات مستقر در URL ها را می توانند به طور نادرس نشان دهند یا داده های حساس را به بازدیدکنندگان بعدی از آن صفحات نشان دهند.

به عنوان مثال، تصور کنید یک سیستم وب پست الکترونیکی را اداره می کنید، و محتویات صفحه ی "inbox" واضح است که بسته به کاربر وارد شده می باشد. در صورتی که یک ISP کورکورانه سایت شما را cache کند، سپس اولین کاربری که از طریق آن ISP وارد شود صفحه ی inbox، cache شده برای بازدید کنندگان بعدی از آن سایت نمایش داده خواهد شد که این اصلا جالب نیست.

خوشبختانه، HTTP یک راهکار برای این مشکل ارائه می کند. تعدادی از HTTP header ها برای راهنمایی کردن cache های بالا دست جهت متمایز کردن محتویات cache بسته به متغیرهای تعیین شده وجود دارند. و برای گفتن مکانیسم های cache که نباید صفحات خاصی را cache کنند. به برخی از این header ها در بخش های بعدی خواهیم پرداخت.

Using Vary Headers

(در دست ترجمه ...). برای مثال، در صورتی که محتویات یک صفحه ی به زبان مورد ترجیح کاربر وابستگی داشته باشد، صفحه "vary on language" گفته می شود.

به طور پیشفرض، سیستم cache جنگو کلیدهای cache خود را با استفاده از مسیر درخواست شده (مانند "/stories/2005/jun/23/bank_robbed/") ایجاد می کنید. این یعنی هر درخواست به آن URL از یک نسخه cache همسان استفاده خواهد کرد، بدون در نظر گرفتن تفاوت های user-agent از قبیل کوکی ها یا تنظیمات زبان. هر چند، اگر این صفحه محتویات متفاوتی بر اساس آن تفاوت در header های درخواست تولید کند – از قبیل یک کوکی، یا یک زبان، یا یک user-agent – شما نیاز خواهید داشت جهت گفتن مکانیسم های cache که خروجی صفحه به آن چیزها بستگی دارد، از Vary header استفاده کنید.

برای انجام این کار در جنگو، از decorator برای view با نام vary_on_headers مانند زیر استفاده کنید:

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

در این مورد، یک مکانیسم cache (مانند cache middleware خود جنگو) یک نسخه ی جدا از صفحه را برای هر user-agent منحصر به فرد cache خواهد کرد.

مزیت استفاده از vary_on_headers به جای دستی قرار دادن Vary header (با استفاده از چیزی شبیه به response['Vary'] = 'user-agent') این است که decorator به Vary header اضافه می کند (که ممکن وجود داشته باشد)، به جای (در دست ترجمه ...) و به طور بالقوه هر چیزی که در آن جا وجود داشته باشد را override می کند.

می توان چندین header را به vary_on_headers() ارسال کرد:

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

این به cache های بالا دست تغییر کردن هر دو را می گوید، که یعنی ترکیب user-agent و cookie مقدار cache خود را بدست خواهند آورد. برای مثال، یک درخواست با user-agent ای مانند Mozilla و مقدار کوکی foo=bar از یک درخواست با user-agent ای با نام Mozilla و مقدار کوکی foo=ham متفاوت در نظر گرفته خواهند شد.

به این دلیل که vary در کوکی بسیار رایج می باشد، یک decorator با نام vary_on_cookie وجود دارد. این دو view برابر می باشند:

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

header هایی که به vary_on_header ارسال می شوند به حروف بزرگ و کوچک حساس نیستند؛ "User‑gent" هیچ فرقی با "user-agent" نخواهد داشت.

همچنین می توان از یک تابع کمکی با نام django.utils.cache.patch_var_headers به طور مستقیم استفاده کرد. این تابع Vary header را قرار داده یا اضافه می کند. برای مثال:

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

patch_vary_headers یک نمون ی HttpResponse به صورت اولین آرگومان و یک لیست/تاپل از نام های header حساس به حروف بزرگ و کوچک به عنوان آرگومان دوم دریافت می کند.

کنترل Cache: با استفاده از Header ها

مشکلات دیگر cache حریم شخصی داده و سوال از جایی که داده باید در یک آبشاری از cache ها در آن ذخیره شده باشد.

یک کاربر معمولا با دو نوع از cache ها رو به رو می باشد: cache مرورگر خود کاربر (cache خصوصی) و ارائه دهنده ی cache کاربر (یک cache عمومی). cache عمومی توسط چندین کاربر و استفاده می شود و توسط برخی دیگر کنترل می شود. (در دست ترجمه ...). بنابراین برنامه های وب نیاز به یک روش برای گفتن cache ها دارند که کدام داده خصوصی و بوده و کدام عمومی می باشد.

راهکار، نشان دادن cache صفحه باید "خصوصی" باشد. برای انجام این کار در جنگو، از decorator مورد نظر برای view با نام cache_control استفاه کنید:

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

این decorator مراقب فرستادن HTTP header مناسب در پشت صحنه می باشد.

چند روش دیگر برای کنترل پارامترهای cache وجود دارد. برای مثال، HTTP به برنامه ها اجازه ی انجام کارهای زیر را می دهد:

  • تعریف حداکثر زمانی که یک صفحه باید cache شده باشد.
  • تعیین اینکه یک cache باید همواره برای نسخه های جدیدتر بررسی شود، تنها تحویل محتوای cache شده هنگامی که هیچ تغییری وجود ندارد. (برخی cache ها ممکن است محتوای cache شده را حتی اگر صفحه ی سرور تغییر کرده باشد تحویل دهند، فقط به خاطر این که کپی cache هنوز منقضی نشده است.)

در جنگو، از decorator، cache_control برای تعیین این پارامترهای cache استفاده کنید. در این مثال، cache_control جهت دوباره معتبر ساختن cache در هر دسترسی و جهت ذخیره ی نسخه های cache برای حداکثر 3600 ثانیه به cache ها می گوید:

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

هر رهنمود HTTP کنترل cache معتبری در cache_control() معتبر می باشد. در زیر لیست کامل وجود دارد:

public=True
private=True
no_cache=True
no_transform=True
must_revalidate=True
proxy_revalidate=True
max_age=num_seconds
s_maxage=num_seconds
(توجه داشته باشید که caching middleware پیش از این cache هدر max-age را با مقدار تنظیم CACHE_MIDDLEWARE_SETTINGS قرار داده شده است. در صورتی که از یک max_age سفارشی در یک decorator، cache_control استفاده می کنید، decorator اولیت خواهد گرفت، و مقادیر header به درستی ادغام خواهند شد.)

در صورتی که می خواهید جهت غیر فعال کردن الگوریتم cache کردن از هدرها استفاده کنید، django.view.decorators.cache.never_cache یک decorator برای view می باشد که جهت اطمینان از پاسخی که توسط مرورگر یا دیگر cache ها cache نخواهد شد هدرها را اضافه می کند. مثال:

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

بهینه سازی های دیگر

جنگو چند قسمت دیگر از middleware را که می تواند کارایی app های شما را بهینه کند ارائه می کند.

django.middleware.http.ConditionalGetMiddleware مرورگرهای مدرن را برای پاسخ های GET بر پایه ی هدرهای ETag و Last-Modified پشتیبانی می کند.
django.middleware.gzip.GZipMiddleware پاسخ های تمام مرورگرها را فشرده کرده و پنهای باند و زمان انتقال را ذخیره می کند.

ترتیب MIDDLEWARE_CLASSES

در صورتی که از cashing middleware استفاده می کنید، قرار دادن هر نیمه در جای راست داخل تنظیم MIDDLEWARE_CLASSES اهمیت دارد. چرا که cache middleware نیاز دارد بداند هدرها توسط کدام ذخیره سازی vary cache می شود. middleware همواره هنگامی که بتواند چیزی را به پاسخ هدر Vary اضافه می کند.

UpdateCacheMiddleware فاز پاسخ را اجرا می کند، جایی که middleware به طور برعکس می باشد، بنابراین یک آیتم در بالای لیست آخرین فاز پاسخ را اجرا می کند. در نتیجه، نیاز می باشد اطمینان حاصل کنید که UpdateCacheMiddleware قبل از هر middleware دیگر ظاهر می شود که ممکن است چیزی را به هدر Vary اضافه کند. ماژول های middleware این کار را انجام می دهند:

  • SessionMiddleware، Cookie اضافه می کند
  • GZipMiddleware، Accept-Encoding اضافه می کند
  • LocaleMiddleware، Accept-Language اضافه می کند

FetchFromCacheMiddleware، از سوی دیگر فاز درخواست را اجرا می کند، جایی که middleware به صورت اول به آخر بکار برده شده است، بنابراین یک آیتم در بالای لیست اولی فاز درخواست را اجرا می کند. همچنین FetchFromCacheMiddleware لازم است بعد از به روز رسانی های هدر Vary، middleware دیگر اجرا شود، بنابراین FetchFromCacheMiddleware باید بعد از هر آیتمی باشد که این کار را انجام می دهد.

لینک به دیدگاه
  • 3 هفته بعد...

آموزش پکیج django.contrib

یکی از نقاط قدرت پایتون فلسفه ی "batteries included" پایتون می باشد: هنگامی که پایتون را نصب می کنید، پایتون درون خود دارای یک کتابخانه بزرگ از پکیج هایی می باشد که می توان بلافاصله از آن ها استفاده کرد می باشد، بدون نیاز به دانلود هیچ چیز دیگری. جنگو سعی می کند این فلسفه را دنبال کند، و حاوی کتابخانه ی استاندارد خودش می باشد از add-on های مفید برای وظایف مشترک توسعه ی وب می باشد. این آموزش از کتاب، مجموعه ای از این add-on ها را پوشش می دهد.

کتابخانه استاندارد جنگو

کتابخانه ی استاندارد جنگو، درون پکیج django.contrib قرار گرفته است. درون هر ساب پکیج یک قسمت جدا از عملکرد add-on می باشد. این قسمت ها لزوما وابسته نمی باشند، ولی برخی ساب پکیج های django.contrib نیازمند دیگر پکیج ها می باشند.

هیچ نیازمندی سختی برای انواع عمکردها در django.contrib وجود ندارد. برخی از پکیج ها شامل ماژول هایی می باشند (و از این رو نیازمند نصب جداول پایگاه داده ی آن ها درون پایگاه داده ی شما می باشد.)، ولی بقیه تنها عبارتند از middleware یا تگ های template.

تنها مشخصه ی مشترکی بین پکیج های django.contrib وجود دارد این است: در صورتی که پکیج django.contrib را به کلی حذف کنید، شما همچنان می توانید از خصوصیات اساسی جنگو بدون هیچ مشکلی استفاده کنید. هنگامی که توسعه دهندگان جنگو عملکرد جدیدی را به فریم ورک یا چارچوب اضافه می کنند، آن ها از این قانون تجربی در تصمیم اینکه آیا عمکرد جدید باید درون django.contrib باشد یا جای دیگری استفاده می کند.

django.contrib عبارت است از این پکیج ها:

  • admin: سایت مدیر جنگو.
  • admindocs: مستندات خودکار برای سایت مدیر جنگو.
  • auth: فریم ورک تصدیق جنگو.
  • comments: یک برنامه ی نظرات.
  • contenttypes
  • csrf: محافظت در برابر Cross-Site Request Forgery (CSRF).
  • databrowse: برنامه ی جنگو ای که اجازه می دهد داده ها را مرور کنید.
  • flatpages: یک فریم ورک برای مدیریت ساده ی محتوای "flat" HTML در یک پایگاه داده.
  • formtools: تعدادی از کتابخانه های مفید سطح بالا برای سر و کار داشتن با الگوهای مشترک در فرم ها.
  • gis: ضمیمه هایی برای جنگو که پشتیبانی از GIS (Geographic Information Systems) را پشتیبانی می کند. برای مثال، به مدل های جنگو جهت ذخیره داده جغرافیایی اجازه می دهد و کوئری های جغرافیایی را نمایش می دهد.
  • humanize: مجموعه ای از فیلترهای مفید template جنگو برای اضافه کردن یک "human touch" به داده می باشد.
  • localflavor: قسمت های مناسبی از کد که برای کشور یا فرهنگ های خاص مفید می باشند. برای مثال، شامل روش هایی برای تایید اعتبار کدهای ZIP ایالات متحده یا شماره های شناسایی ایسلند.
  • marup: مجموعه ای از فیلترهای template جنگو که تعدادی از زبان های مشترک markup را اجرا می کنند.
  • redirects: فریم ورکی برای مدیریت تغییر مسیرها.
  • sessions: فریم session جنگو.
  • sitemaps: فریم ورکی برای تولید فیل XML نقشه ی سایت.
  • sites: فریم ورکی که به شما اجازه می دهد چندین وب سایت را با یک پایگاه داده و یک نصب جنو اداره کنید.
  • syndication: فریم ورکی برای تولید خوراک پیوند در RSS و Atom.
  • webdesign: add-on های جنگو که به طور خاص برای طراحان وب (مخالف توسعه دهندگان) مفید می باشد.

Sites

سیستم sites جنگو یک فریم ورک یا چارچوب جنریک می باشد که به شما اجازه می دهد چندین وب سایت را با پایگاه داده و پروژه جنگوی همسان اداره کنید. این یک مفهوم تصوری می باشد، بنابراین با چند سناریو شروع می کنیم که این مفهوم می تواند در آنجا مفید باشد.

سناریو 1: استفاده دوباره از داده در سایت های چندگانه

همانطور که در مقدمه توضیح داده شد، سایت های LJWorld.com و Lawrence.com ساخته شده با جنگو توسط سازمان اخبار یکسان اداره شده است: روزنامه ی Lawrence Journal-World در Lawrence، Kansas. LJWorld.com بر روی اخبار تمرکز می کند، در حالی که Lawrence.com بر روی سرگرمی های محلی. ولی اغلب مقاله نویسان می خواهند یک مقاله را بر روی هر دو سایت منتشر کنند.

روش احمقانه برای حل مشکل، استفاده از پایگاه داده ی جدا برای هر سایت و نیاز داشتن تولید کنندگان سایت جهت دو مرتبه انتشار یک داستان همسان: یک مرتبه برای LJWorld.com و دوباره یک بار دیگر برای Lawrence.com. ولی این حرکت برای تولید کنندگان سایت نا کارآمد می باشد، و ذخیره ی چندین کپی از یک داستان در پایگاه داده کاری زائد و افزونه می باشد.

راهکار بهتر؟ هر دو سایت از یک پایگاه داده استفاده کنند، و یک مقاله از طریق یک رابطه ی چند به چند با یک یا چند سایت مشترک باشد. فریم ورک یا چارچوب sites جنگو جدول پایگاه داده ای برای آن دسته از مقالاتی که می توانند مرتبط باشند ارائه می کند. این یک قلاب برای پیوستن داده با یک یا بیشتر سایت ها می باشد.

سناریو 2: ذخیره نام/دامنه سایت شما در یک مکان

LJWorld.com و Lawrence.com هر دو دارای قابلیت هشدار پست الکترونیک می باشند، که به خوانندگان اجازه می دهد جهت دست یابی به اطلاعات در هنگام بروز اخبار عضو شوند. این بسیار اساسی است: یک خواننده در یک وب عضو می شود، و بلا فاصله یک پست الکترونیک با پیام "Thanks for you subscription" در یافت می کند.

اجرا شدن این پروسه ی عضویت به صورت دو مرتبه کد زدن یکسان نا کارآمد و زائد می باشد، بنابراین سایت ها از در پشت صحنه از یک کد همسان استفاده می کنند. ولی پیام "Thanks you for your subscription" برای هر سایت باید متفاوت باشد. با استفاده از شیء های Site، می توان پیام thank-you را با استفاده از مقادیر نام سایت فعلی (مانند 'LJWorld.com') و نام دامنه (مانند 'www.ljworld.com') جدا کرد.

فریم ورک sites جنگو مکانی برای شما جهت ذخیره name و domain برای هر سایت در پروژه ی جنگو ارائه می کند، که بدین معنی است که می توانید از مقدایر در یک روش جنریک دوباره استفاده کرد.

نحوه استفاده از فریم ورک یا چارچوب Sites

فریم ورک sites بیشتر یک سری از قرارداد ها می باشد تا یک چارچوب. همه چیز بر اساس دو مفهوم ساده می باشد:

  • مدل Site، درون django.contrib.sites، دارای فیلدهای domain و name.
  • تنظیم SITE_ID که ID پایگاه داده را از شیء Site وابسته شده با آن تنظیمات خاص فایل تعیین می کند.

نحوه استفاده از این دو مفهوم به عهده ی خودتان می باشد، ولی جنگو از طریق قراردادهای ساده ای در چند روش به طور خودکار استفاده می کند.

جهت نصب برنامه sites، مراحل زیر را دنبال کنید:

  • 'django.contrib.sites' را به تنظیم INSTALLD_APPS اضافه کنید.
  • دستور manage.py syncdb را جهت نصب جدول django_site داخل پایگاه داده ی خود اجرا کنید. همچنین این یک شیء site پیشفرض، با دامنه ی example.com ایجاد می کند.
  • سایت example.com را به دامنه ی خودتان تغییر داده، و هر شیء Site دیگری را اضافه کنید، هم از طریق سایت مدیر جنگو یا از طریق API پایتون. یک شیء Site برای هر سایت/دامنه ای که این پروژه ی جنگو ایجاد کرده بسازید.
  • تنظیم SITE_ID در هر یک از فایل های تنظیم خودتان تعریف کنید. این مقدار باید ID پایگاه داده برای شیء Site، جهت سایت ایجاد شده توسط آن فایل تنظیمات باشد.

قابلیت های فریم ورک یا چارچوب Sites

بخش های زیر کارهای مختلفی را که می توانید با فریم ورک sites انجام دهید را توضیح می دهد.

استفاده دوباره از داده در چندین سایت

جهت استفاده ی دوباره از داده در چندین سایت، همانطور که در سناریو 1 توضیح داده شد، تنها کافیست یک ManyToManyField برای Site در مدل های خود ایجاد کنید، برای مثال:

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

این زیر ساختی است که برای مربوط ساختن مقالات با چندین سایت در پایگاه داده نیاز می باشد. با قرار گرفتن آن، می توان از کد view جنگوی همسان برای چندین سایت دوباره استفاده کرد. مثال مدل Article زیر، view مورد نظر یعنی article_detail به شکل زیر خواهد بود:

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

تابع view فوق قابل استفاده ی دوباره می باشد چرا که سایت مقاله را به صورت پویا بررسی می کند، بر حسب مقدار تنظیم SITE_ID.

برای مثال، تصور کنید فایل تنظیمات LJWorld.com دارای یک تنظیم SITE_ID با مقدار 1 می باشد، و فایل تنظیمات Lawrence.com دارای یک SITE_ID با مقدار 2 می باشد. در صورتی که این view زمانی که فایل تنظیمات LJWorld.com فعال است فراخوانی شده است، سپس (در دست ترجمه ...).

مربوط ساختن محتوی با یک سایت تنها

به طور همسان، می توان یک مدل را در یک رابطه ی چند به یک با استفاده از ForeignKey به مدل Site مرتبط کرد.

برای مثال، در صورتی که هر مقاله تنها با یک سایت تنها مرتبط باشد، می توانید از مدلی شبیه به مدل زیر استفاده کنید:

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

کد فوق دارای مزیت همسان با بخش قبلی می باشد که توضیح داده شد.

Hooking Into the Current Site from Views

در یک سطح پایین، می توان کارهای خاصی را بر اساس سایت با استفاده از فریم ورک sites در view های جنگو در view ای که فراخوانی شده است انجام داد، برای مثال:

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

البته، قرار دادن ID های سایت مانند کد فوق کاری شلخته می باشد. روشی تمیزتر از انجام کاری یکسان برای بررسی دامنه ی سایت:

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

روش بازیابی شیء Site برای مقدار settings.SITE_ID بسیار رایج می باشد، بنابراین مدیر مدل Site (Site.objects) دارای یک متد get_current() می باشد. مثال زیر برابر با مثال قبلی می باشد:

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

نکته
در مثال آخر، نیاز به import کردن django.conf.settings نمی باشد.

بدست آوردن دامنه فعلی برای نمایش

برای یک رویکرد DRY (Don't Repeat Yourself)، حذف کد های تکراری، جهت ذخیره ی نام سایت و دامنه، همانطور که کمی قبل تر در "سناریو 2: ذخیره نام/دامنه سایت در یک مکان" توضیح داده شد، تنها کافیست به name و domain شیء Site فعلی مراجعه کنید. برای مثال:

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

در ادامه مثال در حال پیشرفت LJWorld.com و Lawrence.com در Lawrence.com این آدرس پست الکترونیک دارای خط موضوع "Thanks for subscribing to Lawrence.com alerts" می باشد. در LJWorld.com، آدرس پست الکترونیکی دارای خط موضوع "Thanks … to LJworld.com alerts" می باشد. این رفتار همسان مختص سایت برای بدنه ی پیام آدرس پست الکترونیک بکار برده می شود.

یک روش حتی بیشتر انعطاف پذیر (ولی سنگین تر) برای انجام این استفاده از سیستم template جنگو می باشد. با فرض اینکه Lawrence.com و LJWorld.com دارای دایرکتوری های template متفاوت (TEMPLATE_DIRS)، می توان به سادگی به سیستم template محول کرد، مانند زیر:

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

در این مورد، شما ملزم به ساخت template های subject.txt و message.txt در هر دو دایرکتوری template برای LJWorld.com و Lawrence.com می باشد. همانطور که قبلا ذکر شد، این روش منعطف تر از روش قبلی، ولی پیچیده تر نیز می باشد.

این ایده ی خوبی برای بکار بردن شیء های Site تا آنجا که ممکن است برای حذف پیچیدگی زائد و اضافه می باشد.

CurrentSiteManager

در صورتی که شیء ها نقش کلیدی را در برنامه ی شما ایفا می کنند، استفاده از CurrentSiteManager را در مدل های خود مورد ملاحظه قرار دهید. CurrentSiteMannager یک مدیر مدل است که به طور خودکار کوئری های خود را جهت بر داشتن تنها شیء های مرتبط شده با Site فعلی فیلتر می کند.

با اضافه کردن CurrentSiteManager به مدل خود از آن استفاده کنید. برای مثال:

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

با مدل فوق، Photo.objects.all() تمام شیء های Photo را در پایگاه داده بر می گرداند. ولی Photo.on_site.all() تنها شیء های Photo مرتبط شده با سایت فعلی را بر می گرداند، بر حسب تنظیم SITE_ID.

به عبارت دیگر، دو عبارت زیر با یکدیگر برابر می باشند:

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

چطور CurrentSiteManager می داند کدام فیلد از Photo، Site بوده است؟ به طور پیشفرض CurrentSiteMananger به دنبال یک فیلد با نام site می گردد. در صورتی که مدل شما دارای یک ForeignKey یا ManyToManyField فراخوانی شده ی چیزی به غیر از site باشد، نیاز خواهید داشت آن را به صورت پارامتر به CurrentSiteManager ارسال کنید. مدل زیر، که دارای یک فیلد با نام publish_on می باشد، این را توضیح می دهد:

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

در صورتی که در تلاش برای استفاده از CurrentSiteManager و ارسال یک نام فیلدی که وجود ندارد می باشد، جنگو یک خطای ValueError ایجاد خواهد کرد.

نکته

احتمالا یک Manager معمولی (non-site-specific) در مدل خود نگه خواهید داشت، حتی اگر از CurrentSiteManager استفاهد کنید. همانطور که در Appendix B توضیح داده شده است، در صورتی که به صورت دستی یک مدیر تعریف کنید، جنگو مدیر خودکار objects = models.Manager() را برای شما ایجاد نخواهد کرد.

همچنین بعضی قسمت های جنگو – برای مثال، سایت مدیر جنگو و view های جنریک – از مدیر در ابتدا تعریف شده در مدل استفاده می کنند، بنابراین در صورتی که می خواهید سایت مدیر شما دارای دسترسی به تمام شیء ها باشد (نه فقط آن هایی که site-specific هستند)، objects = models.Manager() را در مدل خود قرار دهید، قبل از تعریف CurrentSiteManager.

طریقه استفاده جنگو از فریم ورک Sites

اگر چه استفاده از فریم ورک sites الزامی نمی باشد، استفاده از آن تشویق شده است، چرا که جنگو از آن در چند جا سود می برد. حتی اگر نصب جنگو شما تنها یک سایت را پشتیبانی کند، شما باید چند ثانیه برای ساختن شیء site با domain و name خودتان و اشاره به ID آن در تنظیم SITE_ID وقت صرف کنید.

در زیر نحوه استفاده جنگو از فریم ورک sites وجود دارد:

  • در فریم ورک تغییر مسیرها (به بخش "تغییر مسیرها" مراجعه کنید)، هر شیء redirect با یک سایت خاص مرتبط می باشد. هنگامی که جنگو برای یک تغییر مسیر جستجو می کند.
  • در فریم ورک comments، هر comment با یک سایت خاص مرتبط شده است. هنگامی که یک comment ارسال می شود، site آن در SITE_ID فعلی قرار داده می شود، و هنگامی که comment ها از طریق تگ template مناسب لیست شده اند، comment ها تنها برای سایت فعلی نمایش داده می شوند.
  • در فریم ورک flatpages، هر flatpage یا یک سایت خاص مرتبط شده است. هنگامی که یک flatpage ساخته شده است، شما site آن را تعیین می کند، و flatpage middleware، SITE_ID فعلی را در بازیابی flatpage ها برای نمایش بررسی می کند.
  • در فریم ورک syndication template ها برای title و description به طور خودکار دارای دسترسی به یک متغیر {{ site }} می باشند که نمایش شیء Site، سایت فعلی می باشد. همچنین، hook برای تهیه ی URL های آیتم در صورتی که یک دامنه ی کاملا واجد شرایط تعیین نکنید از domain شیء Site فعلی استفاده خواهد کرد.
  • در فریم ورک تصدیق، django.contrib.auth.views.login نام Site فعلی برای template به صورت {{ site_name }} و شیء Site فعلی را به صورت {{ site }} ارسال می کند.

Flatpages

اغلب شما یک برنامه ی وب پایگاه داده محور خواهید داشت، ولی به تعدادی از صفحات استاتیک نیز نیاز خواهید داشت، از قبیل یک صفحه ی About یا یک صفحه ی Privacy Policy. این امکان وجود دارد که برای این نوع صفحات از وب سرور های استانداردی مانند Apache به صورت فایل های flat HTML استفاده کرد، اما این سطح اضافه ای از پیچیدگی درون برنامه ی شما ایجاد می کند، چرا که شما باید نسبت به تنظیم Apache نیز نگرانی هایی داشته باشید. باید برای تیم خود جهت ویرایش آن فایل ها دسترسی بر پا کنید، و همچنین نمی توانید از سیستم template جنگو برای طراحی این صفحات استفاده کنید.

راهکار برای این مشکل، برنامه ی flatpages جنگو می باشد، که در پکیج django.contrib.flatpages وجود دارد. این برنامه به شما اجازه می دهد صفحات استاتیک را از طریق سایت مدیر جنگو مدیریت کنید، همچنین این برنامه به شما اجازه می دهد template هایی برای آن ها با استفاده از سیستم template جنگو تعیین کنید، درست مثل بقیه ی داده های شما، و شما می توانید با API استاندارد پایگاه داده ی جنگو به flatpages دسترسی پیدا کنید.

استفاده از Flatpages

جهت نصب برنامه ی flatpages، مراحل زیر را دنبال کنید:

  • 'django.contrib.flatpages' را به تنظیم INSTALLED_APPS اضافه کنید. django.contrib.flatpages به django.contrib.sites وابسته است، بنابراین اطمینان حاصل کنید که هر دو پکیج درون INSTALLED_APPS وجود دارند.
  • 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' را به تنظیم MIDDLEWARE_CLASSES اضافه کنید.
  • جهت نصب دو جدول الزامی درون پایگاه داده ی خود دستور manage.py sycndb را اجرا کنید.

برنامه ی flatpages دو جدول درون پایگاه داده ی شما ایجاد می کند: django_flatpage و django_flatpage_sites. django_flatpage به سادگی یک URL را به عنوان و دسته ای از متن محتوا مرتبط می کند. django_flatpage_sites یک جدول چند به چند می باشد که یک flatpage را با یک یا بیشتر سایت ها مرتبط می سازد.

برنامه دارای یک تنها یک مدل FlatPage می باشد، که در django/contrib./flatpages/models.py تعریف شده است. که چیزی شبیه به کد زیر می باشد:

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

اجازه دهید فیلدهای فوق را مورد بررسی قرار دهیم:

  • url: URL ای می باشد که درون flatpage قرار دارد، به استثنای نام دامنه ولی دارای علامت "/" ابتدا (/about/contact/).
  • title: عنوان flatpage. فریم ورک هیچ کار خاصی با این فیلد انجام می دهد. این مسئولیت با عهده ی شما می باشد که آن را درون temtplate خود نمایش دهید.
  • content: محتوای flatpage (مانند HTML صفحه). فریم ورک هیچ کار خاصی با این فیلد انجام نمی دهد. این به عهده ی شما می باشد که آن را درون template نمایش دهید.
  • enable_comments: فعال کردن یا نکردن comment ها در این flatpage می باشد. فریم ورک هیچ کار خاصی با این فیلد انجام نمی دهد. می توان این مقدار را درون template بررسی کرده و در صورت نیاز یک comment نمایش داد.
  • template_name: نام template جهت استفاده برای render کردن این flatpage. این فیلد اختیاری می باشد؛ در صورتی که داده نشده باشد یا در صورتی که این template وجود نداشته باشد، فریم ورک به template عقب نشینی خواهد کرد flatpages/default.html.
  • registration_required: عضویت برای نمایش این flatpage الزامی است یا خیر. این فیلد با فریم ورک authentication/user کامل می شود.
  • sites: سایت هایی که این flatpage در آن ها وجود دارد. این فیلد با فریم sites جنگو متحد می شود ، که در بخش "Sites" همین فصل توضیح داده شده است.

می توان flatpages را از طریق رابط مدیر جنگو یا API پایگاه داده ی جنگو نیز ایجاد نمود. برای اطلاعات بیشتر در این مورد، بخش "اضافه کردن، تغییر دادن و حذف Flatpages" مشاهده کنید.

هنگامی که flatpages ساخته شد، FlatpageFallbackMiddleware تمام کار را انجام می دهد. هر بار که هر برنامه ی جنگو یک خطای 404 را ایجاد می کند، این middleware برای URL درخواست شده به صورت آخرین چاره middleware را بررسی می کند. به طور خاص، برای یک flatpage با URL داده شده یا یک ID سایت که با تنظیم SITE_ID مطابقت دارد بررسی می کند.

در صورتی که یک مطابق پدا کند، template مربوط به flatpage و یا در صورتی که flatpage دارای یک template سفارشی نباشد flatpages/default.html را بار گذاری می کند. این middleware آن template را تنها به یک متغیر context با نام flatpage ارسال می کند که شیء FlatPage می باشد، و در render کردن template از RequestContext استفاده می کند.

در صورتی که FlatpageFallbackMiddleware هیچ مطابقی پیدا نکند، درخواست برای پردازش به صورت معمول به کارش ادامه می دهد.

نکته

این middleware تنها برای خطای 404 (page not found) فعال می شود – نه برای 500 (server error) یا خطاهای دیگر. همچنین به مسائل ترتیب MIDDLEWARE_CLASSES دقت داشته باشید که، به طور معمول FlatpageFallbackMiddleware در آنتها یا نزدیک به انتهای لیست قرار دهید، چرا که استفاده از آن چاره ی آخر می باشد.

اضافه کردن، تغییر دادن و حذف Flatpages

می توان flatpages را به دو روش اضافه کرده، تغییر داده و یا حذف کرد:

از طریق رابط مدیر

در صورتی که رابط خودکار مدیر جنگو فعال کرده اید، باید به قسمت "Flatpages" در صفحه ی index مدیر مشاهده کنید. flatpages را به همانطور که هر شیء دیگری را در سیستم ویرایش می کند ویرایش کنید.

از طریق API پایتون

همانطور که قبلا ذکر شد، flatpages توسط یک مدل استاندارد جنگو که درون django/contrib./flatpages/models.py وجود دارد نشان داده می شود. از این رو، می توان به شیء های flatpage از طریق API پایگاه داده ی جنگو دسترسی پیدا کرد، برای مثال:

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

استفاده از Template های Flatpage

به طور پیشفرض، flatpages توسط template، flatpages/default.html، render می شود، ولی می توان آن را برای یک flatpage خاص، با فیلد template_name در شیء FlatPage، override کرد.

ساختن flatpages/default.html به عهده ی شما می باشد. درون دایرکتوری template خودتان، تنها کافیست یک دایرکتوری flatpages حاوی فایل default.html بسازید.

template های flatpage یک متغیر تنهای context با نام flatpage ارسال می کنند که شیء flatpage می باشد.

در زیر یک نمونه template، flatpages/default.html وجود دارد:

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

توجه داشته باشید که در کد فوق از فیلتر template، safe برای اجازه به flatpage.content جهت (در دست ترجمه ...) استفاده شده است.

Redirects

فریم ورک redirects جنگو به شما اجازه می دهد تغییر مسیرها را به سادگی توسط ذخیره ی آن ها در یک پایگاه داده و رفتار کردن با آن ها همانند هر شیء مدل دیگری کنترل کنید. برای مثال، می توان از فریم ورک redirects جنگو برای گفتن "هر درخواست به /music/ را به /sections/arts/music/ تغییر مسیر بده" استفاده کرد. هنگامی که نیاز به حرکت دادن برخی لینک در سایت خود داشته باشید این فریم ورک بسیار سودمند می باشد؛ توسعه دهندگان وب باید جهت اجناب از لینک های شکسته شده در صورت لزوم هرکاری انجام دهند.

استفاده از فریم ورک چارچوب Redirects

جهت نصب برنامه ی redirects، مراحل زیر را دنبال کنید:

  • 'django.contrib.redirects' را به تنظیم INSTALLED_APPS اضافه کنید.
  • 'django.contrib.redirects.middleware.RedirectFallbackMiddleware' به تنظیم MIDDLEWARE_CLASSES اضافه کنید.
  • دستور manage.py sycdb را به نصب تنها جدول الزامی درون پایگاه داده اجرا کنید.

manage.py syncdb یک جدول django_redirect در پایگاه داده ی شما ایجاد می کند. این یک جدول جستجو با فیلدهای site_id، old_path و new_path می باشد.

می توان redirects را هم از طریق رابط مدیر جنگو و یا از API پایگاه داده ی جنگو ایجاد نمود. برای اطلاعات بیشتر بخش "اضافه کردن، تغییر دادن و حذف Redirects" مراجعه کنید.

هنگامی که redirects ایجاد شد، کلاس RedirectFallbackMiddleware تمام کار را انجام می دهد. هر زمان که هر برنامه ی جنگو یک خطای 404 ایجاد می کند، این middleware پایگاه داده ی redirects را برای URL درخواست شده به عنوان آخرین چاره بررسی می کند. به طور خاص، این middleware برای یک تغییر مسیر با old_path داده شده با یک ID سایت که مطابق با تنظیم SITE_ID می باشد بررسی می کند.سپس middleware مذکور مراحل زیر را دنبال می کند:

  • در صورتی که یک مطابق پیدا کند، و new_path خالی نباشد، به new_path تغییر مسیر می دهد.
  • در صورتی که یک مطابق پیدا کند، وی new_path خالی باشد، یک HTTP header، 410 ("Gone") و یک پاسخ خالی (بدون محتوی) ارسال می مکند.
  • در صورتی که یک مطابق پیدا نکند، درخواست برای پردازش معمولی به کارش ادامه می دهد.

middleware تنها برای خطاهای 404 فعال شده است – نه برای خطاها یا پاسخ های 500 از هر کد وضعیت دیگر.

به موضوع ترتیب قرار گیری MIDDLEWARE_CLASSES توجه کنید. به طور کلی، می توان RedirectFallbackMiddleware را تقریبا در انتهای لیست قرار داد، زیرا آخرین چاره می باشد.

نکته

در صورتی که از هر دو middleware های redirect و flatpage استفاده می کند، توجه کنید که می خواهید کدام یک (redirect یا flatpage) در ابتدا بررسی شود. پیشنهاد می کنیم flatpages را قبل از redirects قرار دهید (از این رو flatpage را قبل از redirect قرار می دهید)، ولی ممکن است نظر شما چیز دیگری باشد.

اضافه کردن، تغییر دادن و حذف Redirects

می توان redirects را به دو روش اضافه کرده، تغییر داده و حذف کرد:

از طریق رابط مدیر

در صورتی که رابط مدیر جنگو را فعال کرده باشید، می بایست یک بخش "Redirects" در صفحه ی index مدیر مشاهده کنید. همانطور که هر شیء دیگری را در این سیستم ویرایش می کنید، redirects را نیز ویرایش کنید.

از طریق API پایتون

redirects توسط یک مدل استاندارد جنگو که درون django/contrib./redirects/models.py قرار دارد نشان داده شده است. از این رو، می توان از طریق API پایگاه داده ی جنگو به شیء های redirect دسترسی پیدا کرد، برای مثال:

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

محافظت CSRF

پکیج django.contrib.csrf در مقابل Cross-Site Request Forgery (CSRF) حفاظت می کند.

CSRF، همچنین به صورت "session riding" نیز شناخته می شود، که یک سایت امنیت بهره برداری می باشد. CSRF زمانیکه یک وب سایت مخرب (در دست ترجمه ...).

یک مثال ساده CSRF

فرض کنید درون یک حساب webmail در example.com وارد شده اید. این سایت webmail دارای یک دکمه ی log out می باشد که به آدرس example.com/logout اشاره می کند – این بدین معناست که، تنها عملی که برای خروج لازم است رفتن به آدرس example.com/logout می باشد.

یک می سایت مخرب می تواند توسط آن آدرس به صورت یک <iframe> پنهان در ون صفحه ی خود شما را ناگذیر به بازدید از آدرس example.com/logout کند. در نتیجه، در صورتی که درون حساب webmail در example.com باشید و از صفحه ی مخربی که دارای <iframe> به example.com/logout بازدید کنید، عمل بازدید از صفحه ی مخرب شما را از example.com خارج می کند.

واضح است، خروج از یک سایت webmail بر خلاف میل خود یک شکاف وحشتناک امنیتی نمی باشد، ولی این نوع از رفتار می تواند بر هر سایت که به کاربران اعتماد می کند اتفاق بیافتد، سایت هایی از قبیل یک سایت بانک آنلاین یا یک سایت تجارت الکترونیک، جایی که این رفتار می تواند یک سفارش یا پرداخت بدون خبر داشتن کاربر آغاز کند.

یک مثال پیچیده در از CSRF

در مثال قبلی، example.com تا حدی مقصر می باشد، زیرا به یک تغییر وضعیت (مانند خرج کاربر) اجازه داده است تا از طریق روش GET درخواست شود. خیلی بهتر است برای هر درخواست از روش POST استفاده شود که وضعیت را در سرور تغییر می دهد. ولی حتی وب سایت هایی که روش POST را برای تغییر وضعیت لازم می دانند نیز برای CSRF آسیب پذیر می باشند.

فرض کنید example.com دارای عملکرد ارتقا پیدا کرده ی خروج می باشد، به طوری که یک دکمه ی <form> از طریق روش POST به آدرس example.com/logout ارسال می کند. بعلاوه، خروج <form> حاوی این فیلد hidden می باشد:

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

این موضوع مطمئن می سازد که یک POST ساده به آدرس example.com/logout یک کاربر را خارج نمی سازد؛ به منظور خروج یک کاربر، کاربر باید example.com/logout را از طریق POST در خواست کرده و متغیر POST به نام confirm را با یک مقدار 'true' ارسال کند.

خوب، با وجود امنیت اضافه، این تنظیم می تواند همچنان توسط CSRF مورد سو استفاده قرار گیرد – صفحه ی مخرب تنها نیاز به یک مقدار کار بیشتر دارد. مهاجمان می توانند یک فرم کامل را به هدف سایت شما ساخته، آن را در یک <iframe> غیر قابل مشاهده مخفی کرده، سپس با استفاده از جاوا اسکریپت آن فرم را به طور خود کار ارسال کنند.

جلوگیری از CSRF

بنابراین، چطور یک سایت می تواند خود را از این سو استفاده حفاظت کند؟

این موضوع درخواست های POST را کنار می گذارد. دومین قدم دادن یک فیلد hidden به هر <form> از نوع POST می باشد که مقدار این فیلد باید محرمانه بوده و از session ID کاربر تولید شده باشد. سپس، هنگام پردازش فرم در سمت سرور، برای آن فیلد محرمانه بررسی به عمل آمده و در صورت معتبر نبودن یک خطا ایجاد می شود.

این دقیقا آن چیزی است که لایه ی جلوگیری CSRF جنگو انجام می دهد، همانطور که در بخش های زیر توضیح داده شده است.

استفاده از CSRF Middleware

پکیج django.contrib.csrf حاوی تنها یک ماژول با نام middleware.py می باشد. این ماژول حاوی یک کلاس middleware جنگو با نام CSRFMiddleware می باشد که حفاظت CSRF را انجام می دهد.

جهت فعال کردن حفاظت CSRF، 'django.contrib.csrf.middleware.CsrfMiddleware'به تنظیم MIDDLEWARE_CLASSES درون فایل تنظیمات خود اضافه کنید. این middleware نیاز است که بعد از SessionMiddleware پاسخ را پردازش کند، بنابراین CSRFMiddleware باید قبل از SessionMiddleware در لیست قرار گرفته باشد (زیرا middleware پاسخ از آخر به اول پردازش شده است). همچنین این middleware باید پاسخ را قبل از آن که پاسخ فشرده شده باشد یا مسائل دیگر پردازش شود، بنابراین CSRFMiddleware باید بعد از GZipMiddleware بیاید. هنگامی که آن را به تنظیم MIDDLEWARE_CLASSES خود اضافه کنید، کار را انجام داده اید.

در صورتی که علاقمند می باشید، در زیر نحوه ی عملکرد CsrfMiddleware وجود دارد. دو کار زیر انجام می شود:

  • درخواست های خروجی را توسط اضافه کردن یک فیلد hidden به تمام فرم های POST، با نام csrfmiddlewaretoken و یک مقدار هش session ID به اضافه ی یک کلید امنیت می باشد، ویرایش می کند. middleware در صورتی که هیچ session ID ای وجود نداشته باشد هیچ ویرایشی انجام نمی دهد، بنابراین کاهش کارایی برای درخواست هایی که از session ها استفاده نمی کنند قابل اغماض است.
  • در تمام درخواست های ورودی POST که دارای کوکی session نیستند، بررسی می کند که csrfmiddlewaretoken حاضر و درست باشد. در صورتی که نباشد، کاربر یک خطای 403 HTTP دریافت می کند. محتوای صفحه ای خطای 403 در آن نشان داده می شود پیام "Cross Site Request Forgery detected. Request aborted." می باشد.

این اطمینان حاصل می کند که تنها فرم هایی که از سایت شما سرچشمه گرفته اند می توانند از داده ها در برگشت استفاده کنند.

این middleware به طور عمد، تنها درخواست های HTTP POST را هدف گیری می کند (و فرم های POST متناظر). همانطور که توضیح داده شد، درخواست های GET هرگز نباید دارای تاثیرات غیر قابل انتظار باشند؛ این بر عهده ی شما می باشد که از این موضوع مطمئن شوید.

درخواست های POST با یک کوکی session محافظت نشده همراه نمی باشند، ولی آن ها نیاز به محافظت ندارند، زیرا یک وب سایت مخرب در هر صورت می تواند این نوع درخواست ها را بسازد.

جهت اجتناب از تغییر درخواست های غیر HTML ای، middleware نوع header محتویات پاسخ را قبل از ویرایش آن بررسی می کند. تنها صفحاتی که به صورت text/html یا application/xml xhtml باشند ویرایش می شوند.

محدودیت های CSRF Middleware

CsrfMiddleware نیازمند عملکرد فریم ورک session جنگو می باشد. در صورتی که از یک session سفارشی یا فریم ورک authentication ای استفاده می کنید که به صورت دستی کوکی های session را مدیریت می کند، این middleware به شما کمکی نخواهد کرد.

در صورتی که برنامه ی شما صفحات HTML و فرم ها را در برخی روش های غیر معمول ایجاد می کند (مثلا، در صورتی که قطعات HTML در عبارت های document.write جاوا اسکریپت ارسال می شوند)، ممکن است فیلتری که فیلد hidden به فرم اضافه می کند دور بزنید. در این مورد، ارسال فرم همواره شکست خواهد خورد. (این به این دلیل اتفاق می افتد که CsrfMiddleware برای اضافه کردن فیلد csrfmiddlewaretoken به HTML شما قبل از آن که صفحه به کلاینت ارسال شود از یک regular expression استفاده می کند. و regular expression گاهی اوقات این نوع HTML ها را نمی تواند کنترل کند.) در صورتی که شک دارید که این موضوع اتفاق می افتد یا خیر، تنها کافیست source را در مرورگر خود تماشا کنید که آیا csrfmiddlewaretoken درون <form> قرار گرفته است یا خیر.

برای اطلاعات و مثال های بیشتر درباره ی CSRF به

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

Humanizing Data

پکیج django.contrib.humanize مجموعه ای از فیلترهای مفید template جنگو را برای اضافه کردن یک "human touch" به داده نگه می دارد. جهت فعال کردن این فیلترها، 'django.contrib.humanize' به تنظیم INSTALLED_APPS اضافه کنید. هنگامی که این کار را انجام داده شد، در یک template از {% load humanize %} استفاده کنید، تا به فیلترهای توضیح داده شده در بخش های بعدی دسترسی پیدا کنید.

apnumber

برای شماره های 1 تا 9، این فیلتر شماره به حروف را بر می گرداند. در غیر اینصورت، عدد را بر می گرداند.

Examples:

  • 1 می شود "one".
  • 2 می شود "two".
  • 10 می شود "10".

می توان نمایش یک integer را در یک integer یا رشته ارسال کرد.

intcomma

این فیلتر یک integer را به یک رشته ی حاوی کاما برای هر سه رفم تبدیل می کند.

مثال ها:

  • 4500 می شود "4,500".
  • 45000 می شود "45,000".
  • 450000 می شود "450,000".
  • 4500000 می شود "4,500,000".

می توان نمایش یک integer را در یک integer یا رشته ارسال کرد.

intword

این فیلتر یک large integer را به یک نمایش متن مناسب تبدیل می کند.

مثال ها:

  • 1000000 می شود "1.0 million".
  • 1200000 می شود "1.2 million".
  • 1200000000 می شود "1.2 milliard".

مقادیر تا یک کادریلیون (عدد 1 با پانزده عدد صفر 1000000000000000) پشتیبانی می شوند.

می توان نمایش یک integer را در یک integer یا رشته ارسال کرد.

ordinal

این فیلتر یک integer را به ordinal آن به صورت رشته تبدیل می کند.

مثال ها:

  • 1 می شود "1st".
  • 2 می شود "2nd".
  • 3 می شود "3rd".
  • 254 می شود "254th".

می توان نمایش یک integer را در یک integer یا رشته ارسال کرد.

فیلترهای Markup

پکیج django.contrib.markup شامل چند فیلتر template جنگو می باشند که هرکدام یک زبان markup رایج را اجرا می کنند.

  • textile: Textile را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    مراجعه کنید.
  • markdown: Markdown را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    مراجعه کنید.
  • restructuredtext: Restructured Text را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    مراجعه کنید.

در هر مورد، فیلتر انتظار قالب بندی markup را به صورت یک رشته را داشته و یک نمایش متن markup شده را بر می گرداند. برای مثال، فیلتر textile متنی را که در قالب بندی Textile، mark up شده است را به HTML تبدیل می کند:

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

جهت فعال کردن این فیلترها، 'django.contrib.markup'را به تنظیم INSTALLED_APPS اضافه کنید. هنگامی که این کار را انجام داده شد، از {% load markup %} در یک template استفاده کنید تا به این فیلترها دسترسی پیدا کنید.

لینک به دیدگاه

آموزش Middleware

در مواردی، نیاز به اجرای یک قطعه کد در هر درخواستی که جنگو کنترل می کند خواهید داشت. این کد ممکن است نیاز باشد درخواست را قبل از آن که view آن را کنترل کند، ویرایش کند. این کد ممکن است نیاز باشد اطلاعات درباره ی درخواست برای اهداف debug و غیره ثبت کند.

می توان این کار را با فریم ورک یا چارچوب middleware انجام داد، که مجموعه ای از hook ها درون پردازش request/response می باشند. این چارچوب یک سیستم "plug-in" سطح پایین و light قادر به تغییر سراسری ورودی و خروجی جنگو (Django) می باشد.

هر جزء middleware مسئول انجام برخی کارهای خاص می باشد. در صورتی که این کتاب را کامل خوانده باشید، middleware را بارها و بارها دیده اید:

  • تمام ابزار session و کاربر که در بخش کاربران عضویت و session مشاهده شد توسط چند تکه ی کوچک از middleware ممکن می شود (بیشتر به خصوص، middleware، request.session و request.user را برای شما در view ها قابل دسترس می کند).
  • sitewide cache بحث شده در مبحث caching در واقع تنها یک قطعه از middleware می باشد که در صورتی که پاسخ برای view مورد نظر cache شده باشد، فراخوانی آن تابع view را دور می زند.
  • برنامه های flatpages، redirects و csrf در پکیج django.contrib تمام کارهای خارق العاده خود را از طریق اجزای middleware انجام می دهند.

در این قسمت از آموزش، ماهیت middleware و نحوه کارکرد آن به طور عمیق تر بحث شده و نحوه نوشتن middleware را شرح می دهد.

Middleware چیست؟

اجازه دهید یا یک مثال خیلی ساده شروع کنیم.

سایت های پر ترافیک اغلب نیاز به قرار دادن جنگو در پشت یک پروکسی load-balancing دارند. این می تواند موجب پیچیدگی های کوچکی شود و یکی از این پیچیدگی ها این است که IP از راه دور هر درخواست (request.META["REMOTE_IP"]) متعادل کننده ی بار آن خواهد بود، نه در واقع IP ای که request ایجاد می کند. متعادل کننده های بار (load balancers) توسط یک header ویژه به نام X-Forwarded-For جهت درخواست آدرس IP واقعی با این موضوع سر و کار دارند.

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

(توجه: اگر چه HTTP header با نام X-Forwarded-For وجود دارد، جنگو آن را به صورت request.META['HTTP_X_FORWARDED_FOR'] قابل دسترس می کند. به استثنای content-length و content-type، هر HTTP header ای درون درخواست با تبدیل تمام کاراکتر به حروف بزرگ، به کلید های request.META تبدیل می شود، به جای هر hyphen با خط تیره و یک پیشوند HTTP_ به نام آن.)

در صورتی که این middleware نصب شده باشد، مقدار هر X‑Forwarded‑For به طور خودکار درون request.META['REMOTE_ADDR'] درج می شود. این بدین معنی است که برنامه ی جنگوی شما نسبت به این موضوع که آیا آن ها پشت یک پروکسی load-blancing هستند یا خیر نگرانی نخواهد داشت، آن ها می توانند به سادگی به request.META['REMOT_ADDR'] دسترسی پیدا کنند و آن در صورتی که یک پروکسی استفاده شده باشد یا خیر کار خواهد کرد.

در واقع، این یک نیاز به اندازه ی کافی رایج می باشد که این قسمت از middleware در یک بخش داخلی جنگو (Django) باشد. آن در django.middleware.http قرار دارد.

نصب Middleware

در صورتی که این کتاب را به صورت کامل خوانده باشید، مثال هایی از نصب middleware را تاکنون مشاهده کرده اید؛ بسیاری از مثال ها در آموزش های قبلی کتاب، دارای بعضی middleware های ضروری می باشد. برای کامل کردن، در زیر نحوه ی نصب middleware وجود دارد.

جهت فعال کردن یک جزء middleware، آیتم یا جزء مورد نظر را به تاپی MIDDLEWARE_CLASSES موجود در ماژول تنظیمات (settings.py) اضافه کنید. درون MIDDLEWARE_CLASSES، هر جزء middleware توسط یک رشته نشان داده شده است: مسیر کامل نام کلاس middleware. برای مثال، در زیر MIDDLEWARE_CLASSES پیشفرض ساخته شده توسط django‑admin startproject وجود دارد:

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

نصب جنگو نیاز به هیچ middleware ای ندارد – در صورت تمایل، MIDDLEWARE_CLASSES می تواند خالی باشد – ولی توصیه می شود که CommonMiddleware را که به طور مختصر توضیح می دهیم فعال کنید.

ترتیب قرار گیری مهم می باشد. درون درخواست و فازهای view، جنگو middlewre را با توجه به ترتیب داده شده ی آن ها در MIDDLEWARE_CLASSES بکار می برد، و در پاسخ و فازهای exception، جنگو middleware را با طور برعکس ترتیب بکار می برد. بدین معنی که، جنگو با MIDDLEWARE_CLASSES به صورت یک نوعی از "wrapper" در اطراف تابع view رفتار می کند: در درخواست به سمت پایین لیست به view حرکت کرده، و در پاسخ بر می گردد.

متدهای Middleware

اکنون که ماهیت middleware و نحوه عمکرد آن را می دانید، اجازه دهید به تمام متدهایی که کلاس های middleware می توانند تعریف کنند نگاهی بیاندازیم.

مقدار ده اولیه: __init__(self)

از __init__() جهت راه اندازی systemwide برای یک کلاس middleware داده شده استفاده کنید.

هر کلاس middleware فعال شده ای تنها یک بار در هر پردازش سرور instantiated شده است. این بدین معنی است که __init__() تنها یک بار فراخونی می شود – در زمان راه اندازی سرور – نه برای درخواست های فردی.

دلیل رایج برای اجرای یک متد __init__() بررسی این موضوع می باشد که middleware واقعا ضروری می باشد یا خیر. در صورتی که __init__()، django.core.exceptions.MiddlewareNotUsed را بوجود آورد، در آن هنگام جنگو middleware را از توده ی middleware حذف خواهد کرد. ممکن است از خصوصیت برای بررسی برخی قسمت های نرم افزار که کلاس middleware ضروری می باشد استفاده کنید، یا بررسی این که آیا سرور در حالت debug اجرا می شود یا خیر، (در دست ترجمه ...).

در صورتی که یک کلاس middleware یک متد __init__() تعریف کند، متد نباید هیچ آرگومانی بیشتر از self دریافت کند.

پردازشگر درخواست: process_request(self, request)

این متد به محض این که درخواست دریافت شود فراخوانی می شود – قبل از آن که جنگو URL را برای تعیین view ای که باید اجرا شود تجزیه کند. این متد شیء HttpRequest را ارسال می کند، که شما ممکن است در صورت تمایل آن را اصلاح کنید.

process_request() باید یا یک شیء HttpResponse و یا None بر گرداند.

  • در صورتی که None بر گرداند، جنگو پردازش این request را ادامه خواهد داد، اجرای هر middleware دیگر و سپس view متناظر.
  • در صورتی که یک شیء HttpResponse بر گرداند، جنگو هر دوی هر middleware دیگر (از هر نوع) یا view متناظر را فراخوانی نخواهد کرد. جنگو بلافاصله آن HttpResponse را بر خواهد گرداند.

پردازشگر View: process_view(self, request, view, args, kwargs)
این متد بعد از آن که پردازشگر درخواست فراخوانی شده و تعیین view جهت اجرا توسط جنگو صورت گرفت فراخوانی می شود، ولی قبل آن view واقعا اجرا می شود.

آرگومان های ارسال شده به این view در جدول زیر نشان داده شده اند.

آرگومان توضیح
request شیء HttpRequest.
view تابع پایتون که جنگو برای کنترل این request فراخوانی خواهد کرد. این آرگومان واقعا خود شیء تابع می باشد، نه نام تابع به صورت یک رشته.
args لیست آرگومان های موضعی ه به view ارسال خواهد شد، شامل آرگومان request نمی شود (که همواره اولین آرگومان view می باشد).
kwargs دیکشنری آرگومان های کیورد که به view ارسال خواهند شد

 

درست مثل process_request()، process_view() باید یک شیء HttpResponse یا None بر گرداند.

  • در صورتی که None بر گرداند، جنگو پردازش این درخواست اجرای هر middleware دیگر و سپس view متناظر را ادامه خواهد داد.
  • در صورتی که یک شیء HttpResponse بر گرداند، جنگو هر دوی هر middleware دیگر (از هر نوع) یا view متناظر را فراخوانی نخواهد کرد. جنگو بلافاصله آن HttpResponse را بر خواهد گرداند.

پردازشگر پاسخ: process_response(self, request,response)

این متد بعد از فراخوانی شدن تابع view و تولید شدن response فراخوانی می شود. در اینجا، پردازشگر محتوای یک پاسخ را تغییر داده یا اصلاح می کند. یک مورد استفاده ی آشکار، فشرده سازی محتوا می باشد، از قبیل gzip کردن HTML درخواست.

پارامترها باید کاملا واضح و آشکار باشند: request شیء درخواست می باشد، و response شیء پاسخ برگردانده شده از view است.

بر خلاف درخواست و پردازشگرهای view که ممکن است None برگردانند، process_response() باید یک شیء HttpResponse برگرداند. آن reponse می تواند همان اصل ارسال شده به تابع (احتمالا اصلاح شده) باشد یا یک نمونه ی جدید باشد.

پردازشگر خطا: process_exception(self, request,exception)

این متد تنها در صورتی که چیزی اشتباه شود و view یک خطای cache نشده ایجاد کند فراخوانی می شود. می توان از این hook برای ارسال تذکرهای خطا استفاده کرد،

پارامترهای این تابع همسان با شیء های request ای می باشد که تا کنون با آن سر و کار داشته ایم، و exception شیء واقعی Exception ایجاد توسط تابع view می باشد.

process_exception() باید یا یک None و یا یک شیء HttpResponse برگرداند.

  • در صورتی که None برگرداند، جنگو پردازش این درخواست را با کنترل خطای داخلی فریم ورک ادامه خواهد داد.
  • در صورتی که یک شیء HttpResponse برگرداند، جنگو از آن پاسخ به جای کنترل خطای داخلی چارچوب استفاده می کند.

نکته

جنگو تعداد کلاس های middleware (توضیح داده شده در بخش بعدی) ارائه می کند که مثال های خوبی را ایجاد می کند. خواندن کد آن ها احساس خوبی نسبت به قدرت middleware به شما می دهد.

همچنین می توانید تعدادی مثال های ایجاد شده توسط جامعه ی جنگو توزیع شده است را در wiki جنگو بیابید:

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

Middleware داخلی

فریم ورک یا چارچوب جنگو (Django) برخی middleware های داخلی را که با مشکلات رایج سر و کار دارند را ارائه میکند، که در بخش های زیر بحث می کنیم.

Middleware پشتیبانی Authentication

کلاس Middleware: django.contrib.ath.middleware.AuthenticationMiddleware.

این middleware پشتیبانی authentication را فعال می کند. این middleware اتریبیوت request.user نشان داده شده در کاربر وارد شده ی فعلی به هر شیء HttpRequest ورودی اضافه می کند.

"Common" Middleware

کلاس middleware: django.middleware.common.CommonMiddleware.

این middleware، تهسیلاتی برای افراد کمال گرا اضافه کرده است:

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

به import re توجه داشته باشید، چرا که DISALLOWED_USER_AGENT نیازمند مقادیر آن جهت کامپایل regex ها می باشد (مثلا، خروجی re.compile()). فایل تنظیمات پایتونی می باشد، بنابراین برای بکار بردن عبارات import درون آن کاملا مناسب می باشد.

باز نویسی بر اساس تنظیمات APPEND_SLASH و PREPEND_WWW انجام می دهد: در صورتی که APPEND_SLASH دارای مقدار True باشد، URL هایی که علامت "/" جلو را نداشته باشند به URL همسان دارای علامت "/" جلو تغییر مسیر داده خواهند شد، مگر آن که قسمت آخر مسیر حاوی یک پایان باشد. بنابراین foo.com/bar به foo.com/bar/ تغییر مسیر داده می شود، ولی foo.com/bar/file.txt بدون تغییر ارسال می شود.
در صورتی که PREPEND_WWW مقدار True داشته باشد، URL هایی که "www." ابتدایی را نداشته باشند، به URL همسان با "www." در ابتدای آن تغییر مسیر داده می شوند.

هر دوی این option ها به معنای عادی سازی URL ها می باشند. فلسفه این است که هر URL باید در یک – و تنها یک – مکان وجود دارشته باشد. از نظر فنی آدرس example.com/bar متفاوت از example.com/bar/ می باشد، که به نوبه ی خود متفاوت از www.example.com/bar/ می باشد. یک indexer موتور جستجو با این ها به صورت آدرس های جدا رفتار می کند، که برای رتبه ی موتور جستجوی سایت مضر می باشد، بنابراین عادی سازی URL ها بهترین عمل می باشد.

ETag ها را بر اساس تنظیم USE_ETAGS کنترل می کند: ETag ها یک سطح بهینه سازی HTTP برای cache صفحات به طور مشروط می باشد. در صورتی که USE_ETAGS مقدار True باشد، جنگو توسط محتوای صفحه ی MD5-hashing یک Etag برای هر درخواست محاسبه می کند، و در صورت لزوم مواظب ارسال پاسخ های Not Modified خواهد بود.
همچنین توجه داشته باشید که یک GET middleware شرطی، که به طور اختصار توضیح داده شده، ETag ها و یک مقدار بیشتر را کنترل می کند.

فشرده سازی Middleware

کلاس middleware: django.middleware.gzip.GZipMiddleware.

این middleware به طور خودکار محتوا را برای مرورگرهایی که فشرده سازی gzip را می فهمند (تمام مرورگرهای مدرن) فشرده می سازد. این middleware می تواند تا حد زیادی مقدار bandwidth ای که یک وب سرور مصرف می کند را کاهش دهد. در عوض مقداری زمان جهت فشرده سازی صفحات خواهد برد.

ما معمولا سرعت را bandwith ترجیح می دهیم، ولی در صورتی که شما بر عکس این را ترجیح می دهید، تنها کافیست این middleware را فعال کنید.

GET Middleware شرطی

کلاس middleware: django.middleware.http.ConditionalGetMiddleware.

این middleware اعمال شرطی GET را پشتیبانی می کند. در صورتی که پاسخ دارای یک Last-Modified یا Etag یا header باشد، و درخواست دارای If-None-Match یا If-Modified-Since باشد، پاسخ توسط یک پاسخ 304 ("Not modified") جایگزین می شود. پشتیبانی ETag وابسته به تنظیم USE_ETAGS می باشد و انتظار دارد که header پاسخ Etag قبلا قرار گرفته باشد. همانطور که در بالا بحث شد، هدر ETag توسط common middleware قرار گرفته است.

همچنین محتوای هر پاسخ به یک درخواست HEAD را نیز حذف می کند و هدرهای پاسخ DATE و Content-Length را برای هر درخواستی قرار می دهد.

پشتیبانی پروکسی معکوس (X-Forwarded-For Middleware)

کلاس middleware: django.middleware.http.SetRemoteAddrFromForwardedFor.

این مثالی است که در بخش "Middleware چیست؟" بررسی شد. این middleware، request.META['REMOTE_ADDR'] بر اساس request.META['HTTP_X_FORWARDED_FOR'] قرار می دهد، در صورتی که دومی قرار بگیرد. این در صورتی که در پشت یک پروکسی معکوس نشسته باشین که موجب شود هر REMOTE_ADDR درخواست به 127.0.0.1 قرار گرفته باشد مفید است.

خطر!

این middleware، HTTP_X_FORWARDED_FOR را معتبر نمی سازد.

در صورتی که در پشت یک پروکسی معکوس نمی باشید که به طور خودکار HTTP_X_FORWARDED_FOR قرار گرفته باشد، از این middleware استفاده نکنید. هر کسی می تواند مقدار HTTP_X_FORWARDED_FOR فریب دهد، و به دلیل آنکه این REMOTE_ADDR بر اساس HTTP_X_FORWARDED_FOR قرار داده شده است، بدین معنی است که هر کسی می تواند آدرس IP خود را جا بزند.

تنها زمانی که کاملا به مقدار HTTP_X_FORWARDED_FOR اعتماد دارید از این middleware استفاده کنید.

Middleware پشتیبانی Session

کلاس middleware: django.contrib.sessions.middleware.SessionMiddleware.

Sitewide Cache Middleware

کلاس middleware: django.middleware.cache.UpdateCacheMiddleware و django.middleware.cache.FetchFromCacheMiddleware.

این middleware ها با یکدیگر برای cache هر صفحه ی تولید شده توسط جنگو کار می کنند. این موضوع به تفصیل در مبحث caching بحث شده است.

Transaction Middleware

کلاس middleware: django.middleware.transaction.TransactionMiddleware.

این middleware، COMMIT یا ROLLBACK پایگاه داده را به فاز request/response پیوند می دهد. در صورتی که یک تابع view با موفقیت اجرا شود، یک COMMIT صادر می شود. در صورتی که view یک خطا ایجاد کند، یک ROLLBACK صادر می شود.

ترتیب این middleware درون لیست اهمیت دارد. ماژول های middleware خارج از اجرای آن با commit‑on‑save اجرا می شوند – رفتار پیشفرض جنگو. ماژول های middleware داخل آن (دیرتر درون لیست می آیند) که در زیر کنترل transaction همسان به صورت توابع view خواهد بود اجرا می شوند.

لینک به دیدگاه

یکپارچه سازی برنامه ها و دیتابیس های Legacy

جنگو مناسب ترین برای به اصطلاح توسعه ی green-field می باشد – بدین معنی که، شروع پروژه های از ابتدا، مثل این که شما یک ساختمان در یک دشت از علف سبز ساخته اید. ولی با وجود این واقعیت که جنگو (Django) از پروژه های از ابتدا طرفداری می کند، یکپارچه سازی فریم ورک یا چارچوب به پایگاه داده ها و برنامه های legacy ممکن است. این بخش از آموزش، تعدادی از استراتژی های یکپارچه سازی را توضیح می دهد.

یکپارچه سازی با یک پایگاه داده Legacy

لایه ی پایگاه داده ی جنگو الگوی SQL را از کد پایتون تولید می کند – ولی با یک پایگاه داده ی legacy، شما قبلا دارای الگوی SQL می باشید. در چنین مواردی، نیاز به ساخت مدل ها برای جداول پایگاه داده موجود خواهید بود. برای این هدف، جنگو با یک ابزاری که می تواند کد مدل را توسط خواندن لایه های جدول پایگاه داده تولید کند ارائه کرده است. این ابزار inspectdb نامیده می شود، و می توان توسط اجرای دستور manage.py inspectdb آن را فراخوانی کرد.

استفاده از inspectdb

مزیت inspectdb پایگاه داده ی اشاره شده توسط فایل تنظیمات درون گرایی می کند، هر نمایش مدل جنگو را برای هر جدول شما تعیین می کند، و کد مدل پایتون به خروجی استاندارد پرینت می کند.

در زیر دستور عمل یکپارچه سازی پردازش از ابتدا وجود دارد. تنها فرض این است که جنگو نصب شده و شما یک پایگاه داده ی legacy دارید.

یک پروژه ی جنگو را توسط اجرای دستور django‑admin startproject mysite (جایی که mysite نام پروژه ی شما می باشد) ایجاد کنید. ما در این مثال از mysite برای نام پروژه استفاده خواهیم کرد.

فایل تنظیمات در آن پروژه یعنی mysite/settings.py را برای تنظیم پارامترهای کانکشن پایگاه داده و نام پایگاه داده ویرایش کنید. به طور خاص، تنظیمات DATABASE_NAME، DATABASE_ENGINE، DATABASE_USER، DATABASE_PASSWORD، DATABASE_HOST و DATABASE_PORT را آماده کنید.

توسط اجرای دستور python mysite/manage.py startapp myapp یک برنامه ی جنگو درون پروژه خود ایجاد کنید (جایی که myapp نام برنامه ی شما می باشد). ما در اینجا از myapp به صورت نام برنامه استفاده خواهیم کرد.

دستور python mysite/manage.py inspectdb را اجرا کنید. این دستور جداول درون پایگاه داده ی DATABASE_NAME را بازرسی کرده و کلاس مدل تولید شده برای هر جدول را پرینت می کند. برای دریافت آگاهی از آنچه که inspectdb می تواند انجام دهد نگاهی به خروجی بیاندازید.

خروجی را درون فایل models.py درون برنامه خود، با استفاده از تغییر مسیر خروجی استاندارد shell ذخیره کنید.

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

فایل mysite/myapp/models.py برای تمیز کردن مدل های تولید شده و در صورت لزوم سفارشی سازی ویرایش کنید. برخی اشاره ها برای این موضوع را در بخش آینده توضیح داده ایم.

تمیز کردن مدل های تولید شده

همانطور که ممکن است انتظار داشته باشید، درون گرایی پایگاه داده عالی نمی باشد، و نیاز خواهید داشت برخی پاکسازی های سبک در کد مدل نتیجه شده انجام دهید. در زیر تعدادی اشاره کننده ها برای سر و کار داشتن با مدل های تولید شده وجود دارند:

هر جدول پایگاه داده به یک کلاس مدل تبدیل می شود (مثلا، یک رابطه ی یک به یک بین جداول پایگاه داده و کلاس های مدل وجود دارد). این بدین معناست که نیاز خواهید داشت مدل ها را برای هر کدام از جداول join چند به چند به شیء های ManyToManyField تغییر دهید.

هر مدل تولید شده دارای یک attribute برای هر فیلد می باشد، شامل id فیلدهای کلید اصلی. هر چند، فراخوانی دوباره ی آن جنگو در صورتی که یک مدل دارای یک کلید اصلی نباشد، به طور خودکار یک فیلد id کلید اصلی اضافه می کند. در نتیجه، هر خطی که شبیه به زیر باشد را حذف خواهید نمود:

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

نه تنها این خط ها اضافی می باشند، بلکه می توانند در صورتی که برنامه شما بخواهد رکوردهای جدیدی به جدول ها اضافه کند موجب بروز مشکلاتی می شود.

هر نوع فیلد (مانند، CharField، DateField) توسط نگاه به نوع ستون پایگاه داده (مانند VARCHAR، DATE) تعیین شده است. در صورتی که inspectdb نتواند نوع ستون را به یک نوع فیلد مدل ارتباط دهد،

از TextField استفاده خواهد کرد و کامنت پایتون 'This field type is a guess' در کنار فیلد در مدل توضیح داده شده درج می کند. با دقت به آن نگاه کنید، و بنابراین در صورت لزوم نوع فیلد را تغییر دهید.

در صورتی که یک فیلد در پایگاه داده ی شما دارای هیچ معادل خوبی نباشد، می توانید با خیال راحت از آن دست بکشید. لایه ی مدل جنگو ملزم به شامل شدن هر فیلد درون جدول (ها) نمی باشد.

در صورتی که یک نام ستون پایگاه داده یک کلمه ی رزرو شده ی پایتون باشد (از قبیل page، class یا for)، inspectdb، '_field' را به نام attribute اضافه کرده و attribute، db_column را به نام فیلد واقعی قرار می دهد (مانند، pass، class یا for).

برای مثال، در صورتی که یک جدول دارای یک ستون INT با نام for باشد، مدل تولید شده دارای یک فیلد شبیه به زیر خواهد بود:

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

inspectdb کامنت پایتون زیر را در کنار فیلد درج خواهد کرد 'Field renamed because it was a Python reserved world.'.

در صورتی که پایگاه داده ی شما حاوی جداولی باشد که به جدول های دیگر رجوع می کند (همانطور که اغلب پایگاه های داده انجام می دهند)، ممکن است نیاز باشد ترتیب مدل های تولید شده را دوباره چیدمان کنید، به طوری که مدل هایی که به مدل های دیگر رجوع می کنند به درستی منظم شوند. برای مثال، در صورتی که مدل Book دارای یک ForeignKey به مدل Author باشد، مدل Author باید قبل از مدل Book تعریف شده باشد. در صورتی که نیاز به ساخت یک رابطه در یک مدلی که هنوز تعریف نشده می باشید، می توانید از یک رشته حاوی نام مدل به جای خود شیء مدل استفاده کنید.

inspectdb برای PostgreSQL، MySQL و SQLite کلیدهای اصلی را شناسایی می کند. این بدین معناست که، inspectdb برای حداقل یک فیلد در هر مدل primary_key=True درج می کند، زیرا مدل های ملزم به داشتن یک فیلد primary_key=True می باشند.

شناسایی Foreign-key تنها با PostgreSQL و با بعضی نوع از جدول های MySQL کار می کند.

یکپارچه سازی با یک سیستم Authentication

امکان یکپارچه سازی فریم ورک یا چارچوب جنگو با یک سیستم authentication موجود امکان پذیر است – منبع دیگر نام های کاربری و رمزهای عبور یا متدهای authentication.

برای مثال، شرکت شما ممکن است قبلا دارای یک راه اندازی LDAP باشد که یک نام کاربری و رمز عبور را برای هر کارمند ذخیره می کند. در صورتی که کاربران دارای حساب های جدا در LDAP و برنامه های بر اساس جنگو باشند، این برای هر دوی مدیر شبکه و خود کاربران عذاب خواهد بود.

جهت کنترل وضعیت شبیه به این، سیستم authentication اجازه می دهد منابع دیگر authentication را وصل کنید. می توان الگوی پایگاه داده محور پیشفرض جنگو را override کرد، یا می توان از سیستم پیشفرض جفت با سیستم های دیگر استفاده کرد.

تعیین Authentication Backend ها

در پشت صحنه، فریم ورک یا چارچوب جنگو یک لیست از "authentication backend" ها نگه داشته است که authentication را بررسی می کند. هنگامی که شخصی django.contrib.auth.authenticate() را فراخوانی می کند، جنگو سعی می کند تمام authentication backend ها را تصدیق کند. در صورتی که اولین متد authentication شکست بخورد، جنگو برای دومی تلاش می کند، و به همین ترتیب پیش می رود، تا تمام backend ها امتحان شوند.

لیست authentication backend ها درون تنظیم AUTHENTICATON_BAKCENDS جهت استفاده تعیین شده اند. این تنظیم یک تاپل از مسیر نام های پایتون می باشد که به کلاس های پایتونی که می دانند چطور تصدیق کنند اشاره می کند. این کلاس ها می توانند هرجایی از مسیر پایتون شما باشند.

به طور پیشفرض، AUTHENTICATION_BACKENDS مانند زیر قرار گرفته است:

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

کد فوق اصول پایه ای الگوی authentication می باشد که پایگاه داده ی کاربران جنگو را بررسی می کند.

موضوع نحوه ی ترتیب AUTHENTICATION_BACKENDS اهمیت دارد، بنابراین درصورتی که نام کاربری و رمز عبور همسان در چند backend معتبر باشد، جنگو پردازش را در اولین تطابق مثبت متوقف خواهد کرد.

نوشتن یک Authentication Backend

authentication backend یک کلاس است که دو متد را اجرا می کند: get_user(id) و authentication(**credentials).

متد get_user یک id در یافت می کند – که می تواند یک نام کاربری، id پایگاه داده یا هر چیز دیگری باشد – و یک شیء User بر می گرداند.

متد authenticate، credential هایی را به صورت آرگومان های کیورد دریافت می کند. اغلب اوقات چیزی شبیه به کد زیر می باشد:

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

ولی همچنین می تواند یک token را تصدیق کند، مانند زیر:

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

در هر صورت، authenticate باید credential هایی را که دریافت می کند را بررسی کند، و باید یک شیء User مطابق با آن credential ها می باشد بر گرداند، در صورتی که credential ها معتبر باشند. در صورتی که معتبر نباشند، باید مقدار None را بر گرداند.

سیستم مدیر جنگو به طور محکم همراه شیء User، database-backend خود جنگو می باشد که در بخش کاربران عضویت و session مورد توضیح داده شده است. بهترین روش برای سر و کار داشتن با این موضوع ساختن یک شیء User برای هر کاربر می باشد که برای backend شما وجود دارد (مثلا، درون دایرکتوری LDAP، پایگاه داده ی SQL خارجی شما). همچنین می توان یک اسکریپت برای انجام این موضوع پیشاپیش نوشت یا متد authenticate شما می تواند اولین باری که یک کاربر وارد میشود آن را انجام دهد.

در زیر یک مثال backend وجود دارد که یک متغیر نام کاربری و رمز عبور تعریف شده در فایل settings.py را تصدیق کرده و اولین بار که یک کاربر تصدیق می شود یک شیء User ایجاد می کند:

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

یکپارچه سازی با برنامه های وب legacy

این امکان وجود دارد که یک برنامه ی جنگو در وب سرور همسان را که به صورت یک برنامه ایجاد شده توسط تکنولوژی دیگر می باشد را اجرا کنید. ساده ترین روش برای انجام این کار استفاده از فایل configuration آپاچی یعنی httpd.conf جهت محول کردن الگوهای URL دیگر به تکنولوژی های دیگر می باشد.

کلید این که جنگو (Django) برای یک الگوی URL خاص تنها در صورتی که فایل httpd.con چنین می گوید فعال شده خواهد بود. گسترش پیشفرض فرض می کند می خواهید ایجاد هر صفحه را در دامنه ی خاص انجام دهید:

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

در اینجا، خط <location "/"> بدین معنی است که "هر URL، در root شروع شود،" با جنگو.

محدود کردن این دایرکتیو <location> برای بعضی دایرکتوری ها کاملا خوب می باشد. برای مثال، تصور کنید شما یک برنامه ی legacy PHP دارید که اغلب صحات در یک دامنه ایجاد شده اند و می خواهید یک سایت مدیر جنگو در /admin/ بدون مزاحمت برای کد PHP ایجاد کنید. برای انجام این کار، تنها کافیست دایرکتیو <location> را با /admin/ تنظیم کنید:

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

با این کار، تنها URL هایی که با /admin/ شروع می شوند جنگو را فعال خواهند کرد. هر صفحه ی دیگری طبق زیرساخت قبلی خود، کار خواهد کرد.

توجه داشته باشید که پیوستن جنگو به URL واجد شرایط (از قبیل /admin/ در مثال این بخش) تاثیری بر روی URL تجزیه شده ی جنگو نمی گذارد. جنگو با URL مستقل خود کار می کند (مانند، /admin/people/person/add/)، نه یک نسخه ی "محروم" از URL (مانند، /people/person/add/). این بدین معنی است که URLconf ریشه باید شامل /admin/ آغازین باشد.

لینک به دیدگاه

آموزش بین المللی سازی (Internationalization)

جنگو در ابتدا درست در ایالات متحده ی میانی توسعه یافت – در معنای واقعی کلمه، در لارنس، کنزاس، کمتر از 40 مایلی مرکز جغرافیای قاره ایالات متحده. همانند اغلب پروژه های منبع باز (open source)، جنگو در بین مردم سرتاسر زمین بزرگ شد. همانطور که جامعه جنگو به طور افزاینده ای متنوع می شدند، internationalization و localization به طور افزاینده ای دارای اهمیت شد. به دلیل آن که بسیاری از توسعه دهندگان درک مبهمی از این دوره ها دارند، به طور اختصار آنها را تعریف می کنیم.

internationalization به پردازش طراحی برنامه ها برای استفاده ی بالقوه از هر مکان رجوع می کند. این شامل علامت گذاری متن (از قبیل المان های UI و پیام های خطا) برای ترجمه ی آینده، abstract کردن نمایش تاریخ ها و زمان ها به طوری که استاندارهای مختلف محلی ممکن observed باشه، تهیه ی پشتیبانی برای منطقه های زمانی مختلف، و به طور کلی اطمینان از اینکه کد حاوی هیچ فرضی در باره ی محل آن کاربران نباشد. اغلب "internationalization" را به صورت اختصار I18N می بینید. ("18" به تعداد حروف حذف شده بین حرف اول "I" و حرف آخر "N" می باشد.)

localization به پردازش ترجمه ی واقعی یک برنامه ی internationalize شده برای استفاده در یک محل خاص رجوع می کند. گاهی اوقات "localization" به صورت اختصار L10N می بینید.

خود جنگو به طور کامل internationalize شده می باشد؛ تمام رشته ها برای ترجمه علامت گذاری شده اند، و تنظیمات نمایش مقادیر local-dependent مانند تاریخ ها و زمان ها را کنترل می کنند. جنگو همچنین دارای بیشتر از 50 فایل localization می باشد. در صورتی که زبان مادری شما انگلیسی نمی باشد، یک شانس خوب وجود دارد که جنگو قبلا به زبان اصلی شما ترجمه شده است.

فریم ورک همسان internationalization برای این localization ها برای شما جهت استفاده از کد و template های خودتان در دسترس می باشند.

جهت استفاده از این فریم ورک یا چارچوب، نیاز به اضافه کردن حداقل hook ها به کد پایتون و template های خود دارید. این hook ها رشته های ترجمه نام دارند. آن ها به جنگو می گویند، "این متن باید به زبان کاربر ترجمه شود، در صورتی که یک ترجمه برای این متن در آن زبان در دسترس باشد."

فریم ورک یا چارچوب جنگو (Django) مراقب استفاده از این hook ها برای ترجمه ی برنامه های وب می باشد، بسیار سریع، بر طبق تنظیمات زبان کاربر.

اساسا، جنگو دو کار را انجام می دهد:

  • به توسعه دهندگان و نویسندگان template اجازه می دهد که بخش های برنامه ی خودشان که باید قابل ترجمه باشد را تعیین کنند.
  • از آن اطلاعات جهت ترجمه ی برنامه های وب برای کاربران خاص بر طبق زبان مورد ترجیح استفاده می کند.

نکته

دستگاه ترجمه ی جنگو از GNU gettext (http://www.gnu.org/software/gettext/) از طریق ماژول gettext که در پایتون ارائه شده است استفاده می کند.

در صورتی که به internationalization نیازی نداشته باشید:

hook های internationalization جنگو به طور پیشفرض فعال می باشند، که موجب کمی بار اضافی می شود. در صورتی که از internationalization استفاده نمی کنید، باید درون فایل تنظیمات USE_I18N = False را قرار دهید. در صورتی که USE_I18N مقدار False باشد، جنگو بهینه سازی هایی را برای بارگذاری نشدن دستگاه internationalization انجام می دهد.

همچنین شاید بخواهید 'django.core.context_processors.i18n' از تنظیم TEMPLATE_CONTEXT_PROCESSORS حذف کنید.

سه مرحله برای internationalize کردن برنامه جنگو به قرار زیر می باشند:

  • رشته های ترجمه را در کد پایتون خود را template قرار دهید.
  • ترجمه هایی برای آن رشته های بدست آورید، در هر زبانی که برای پشتیبانی می خواهید.
  • locale middleware را در تنظیمات جنگو خود فعال کنید.

هر کدام از این مراحل را به تفصیل پوشش خواهیم داد.

1. نحوه تعیین رشته های ترجمه

رشته های ترجمه "این متن باید ترجمه شده باشد" تعیین می کنند این رشته ها می توانند در کد پایتون و template های شما ظاهر شوند. علامت گذاری رشته های ترجمه بر عهده ی شما می باشد؛ سیستم تنها می تواند رشته هایی را که می شناسد ترجمه کند.

در کد پایتون
ترجمه استاندارد

تعیین یک رشته ی ترجمه با استفاده از تابع ugettext() می باشد. به صورت قرار است که این تابع به صورت یک نام مستعار کوتاه تر یعنی "_" import شود.

در مثال زیر، متن "Welcome to my site." به صورت رشته ی ترجمه علامت گذاری شده است:

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

واضح است، می توانید بدون استفاده از نام مستعار کد فوق را بنویسید. مثال زیر برابر با مثال فوق می باشد:

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

ترجمه با مقدار محاسبه شده کار می کند. مثال زیر برابر با دو مثال قبلی می باشد:

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

ترجمه با متغیرها نیز کار می کند. برای بار دیگر، مثال زیر برابر با مثال های قبلی می باشد:

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

(اخطار در مورد استفاده از متغیرها یا مقادیر محاسبه شده، همانطور که در دو مثال قبلی مشاهده کردید، این است که مزیت تشخیص رشته ی ترجمه یعنی django-admin.py makemessages، قادر به یافتن این رشته ها نخواهد بود. در مورد makemessage کمی بعد بیشتر توضیح خواهیم داد.)

رشته هایی که به _() یا ugettext() ارسال می کنید، می توانند placeholder هایی دریافت کنند، که درون زبان پایتون وجود دارند. مثال:

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

این تکنیک به ترجمه های زبان خاص اجازه می دهد متن placeholder را دوباره چیدمان کنند. برای مثال، یک ترجمه ی انگلیسی ممکن است "Today is November 26." باشد، هنگام ترجمه ی اسپانیایی ممکن است "Hoy es 26 de Noviembre." باشد – با placeholder ها (ماه و روز) با موقعیت های عوض شدهی آنها.

به همین دلیل، شما باید از رشته های نام گذاری شده (مانند %(day)s) به جای رشته های موضعی (مانند، %s یا %d) هر زمان که بیشتر از یک پارامتر وجود دارد استفاده کنید. در صورتی که از رشته های موضعی استفاده می کنید قادر نمی باشد placeholder متن را دوباره چیدمان کنید.

علامت گذاری رشته ها به صورت No-Op

از تابع django.utils.translation.ugettext_noop() جهت علامت گذاری یک رشته به صورت یک رشته ی ترجمه بدون ترجمه کردن آن استفاده کنید.

در صورتی که رشته های ثابتی دارید که باید در ریشه ی زبان ذخیره شده باشند چرا که آن ها در سرتاسر سیستم ها و کاربران رد و بدل می شوند – مانند رشته های درون یک پایگاه داده – ولی باید در آخرین نقطه ی ممکن ترجمه شوند، مانند زمانی که رشته به کاربر نشان داده می شود، از این استفاده کنید.

ترجمه ی Lazy

از تابع django.utils.translation.ugettext_lazy() جهت ترجمه ی رشته های به صورت lazy استفاده کنید – زمانی که مقدار در دسترس است، به جای هنگامی که تابع ugettext_lazy() فراخوانی شده است.

برای مثال، جهت ترجمه ی مدل help_text کد زیر را عمل کنید:

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

در مثال فوق، ugettext_lazy() یک بازگشت lazy به رشته را ذخیره می کند – نه ترجمه ی واقعی را. خود ترجمه، هنگامی که رشته در یک رشته ی context استفاده شده باشد، مانند render کردن template در سایت مدیر جنگو، انجام خواهد شد.

نتیجه فراخوانی یک ugettext_lazy() می تواند هر جایی که شما یک رشته ی یونیکد (یک شیء از نوع unicode) در پایتون استفاده می کند استفاده شده باشد. در صورتی که سعی کنید در جایی که انتظار یک bytestring (شیء str) از آن استفاده کنید، هیچ چیز آن طوری که انتظار آن می رود کار نخواهد کرد، چون یک شیء uggettext_lazy() نحوه ی تبدیل خودش به یک bytestring را نمی داند. نمی توانید از یک رشته ی یونیکد درون یک bytestring استفاده کنید، بنابراین این رفتار عادی پایتون می باشد. برای مثال:

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

در صورتی که همواره خروجی شبیه به "hello <django.utils.functional...>" مشاهده می کند، باید سعی به درج نتیجه ی ugettext_lazy() به درون یک bytestring کنید. آن یک اشکال در کد شما می باشد.

در صورتی که نام طولانی ugettext_lazy را نمی پسندید، می توانید از نام مستعار "_" (خط تیره)، مانند زیر استفاده کنید:

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

همواره از ترجمه های lazy در مدل های جنگو استفاده کنید. نام های فیلد و نام جدول باید برای ترجمه علامت گذاری شده باشند(در غیر این صورت، آن ها درون رابط مدیر ترجمه نخواهند شد). این بدین معنی است که نوشتن صریح option های verbose_name و verbose_name_plural در کلاس Meta، به جای اعتماد به تعیین پیشفرض verbose_name و verbose_name_plural جنگو توسط نگاه به نام کلاس مدل:

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

جمع بندی

برای تعیین پیام های جمع بندی شده از تابع django.utils.translation.ungettext() استفاده کنید. مثال:

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

ungettext سه آرگومان دریافت می کند: رشته ی ترجمه ی تک، رشته ی ترجمه ی جمع و تعداد شیء ها (که به زبان های ترجمه به صورت متغیر count ارسال شده است).

در کد Template

ترجمه در template های جنگو از دو تگ template و یک syntax کمی متفاوت تر از کد پایتون استفاده می کند. برای دادن دسترسی به این تگ ها به template خود، {% load i18n %} را نزدیک بالای template خود قرار دهید.

تگ template {% trans %} هر دوی رشته ی ثابت (احاطه شده درون دابل کتیشن یا تک کتیشن) یا محتوای متغیر را ترجمه می کند:

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

در صورتی که آپشن noop موجود باشد، variable lookup همچنان اتفاه می افتد ولی ترجمه انجام نمی شود.

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

امکان ترکیب یک متغیر template درون یک رشته ی داخل {% trans %} امکان پذیر نمی باشد. در صورتی که ترجمه های شما نیازمند رشته هایی با متغیرها (placeholder ها) باشد، از {% blocktrans %} استفاده کنید:

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

جهت ترجمه ی یک عبارت template – تصور کنید، از فیلترهای template استفاده می کند – نیاز چسباندن عبارت به متغیر محلی برای استفاده درون بلاک ترجمه می باشد:

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

در صورتی که نیاز به چسباندن بیشتر از یک عبارت داخل یک تگ blocktrans دارید، قسمت ها را با and جدا کنید:

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

جهت جمع بستن، هر دو فرم های تک و جمع را با تگ {% plural %} تعیین کنید، که درون {% blocktrans %} و {% endblocktrans %} ظاهر می شوند. مثال:

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

به طور داخلی، تمام بلاک و ترجمه های درون خطی از فراخوانی مناسب ugettext / ungettext استفاده می کنند.

هر RequestContext دارای دسترسی به سه متغیر ترجمه ی خاص می باشد:

  • LANGUAGE لیستی از تاپل ها می باشد که در المان اول کد زبان و دومی نام زبان (ترجمه شده به locale فعال فعلی) می باشد.
  • LANGUAGE_CODE زبان مورد ترجیح کاربر فعلی به صورت یک رشته می باشد. مثال: en-us. (به بخش "نحوه ی پی بردن جنگو به زبان مورد ترجیح" در همین آموزش مراجعه کنید.)
  • LANGUAGE_BIDI جهت locale فعلی می باشد. در صورتی که True باشد، یک زبان راست به چپ، مانند: فارسی، عربی می باشد. در صورتی که False باشد یک زبان چپ به راست مانند : انگلیسی، فرانسوی و آلمانی می باشد.

در صورتی که از RequestContext extension استفاده نمی کنید، می توان آن مقادیر را با سه تگ زیر بدست آورد:

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

این تگ ها همچنین به یک {% load i18n %} نیاز دارند

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

نکته

در این مثال، زیر ساخت ترجمه، رشته ی "yes,no" را ارسال می کند، نه رشته های فردی "yes" و "no". رشته ی ترجمه شده نیازمند علامت کاما "," می باشد، به طوری که تجزیه کننده ی کد فیلتر نحوه ی جدا کردن آرگومان ها را بداند. برای مثال، یک مترجم آلمانی ممکن است رشته ی "yes,no" را به صورت "ja,nein" ترجمه کند (نگهداشتن کاما دست نخورده).

کار با شیء های ترجمه ی Lazy

استفاده از ugettext_lazy() و ungettext_lazy() برای علامت گذاری رشته ها در مدل ها و توابع یک عمل رایج می باشد. هنگامی که با این شیء ها در جای دیگر کد خود کار می کنید، باید مطمئن باشید که به طور تصادفی آن ها را رشته ها تبدیل نمی کنید، زیرا آن ها باید در آخرین زمان ممکن تبدیل شوند (در دست ترجمه ...). این موضوع ایجاب می کند که از چند تابع کمکی استفاده کنید.

متصل کردن رشته ها: string_concat()

join های رشته ی استاندارد پایتون (''.join([...])) بر روی لیست های حاوی شیء های ترجمه ی lazy کار نخواهند کرد. در عوض، می توانید از django.utils.translation.string_concat() استفاده کنید، که یک شیء lazy ایجاد می کند که محتویات آن را به هم وصل کرده و آن ها را تنها هنگامی که نتیجه در یک رشته شامل شده باشد، به هم متصل می کند. برای مثال:

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

در این مورد، ترجمه ی lazy در result تنها زمانی که خود result در یک رشته استفاده شده باشد به رشته ها تبدیل می شود (معمولا زمان render شدن template).

دکوریتور allow_lazy()

جنگو بسیاری توابع سودمند (به ویژه در django.utils) ارائه می کند که یک رشته را به صورت اولین آرگومان دریافت می کنند و کاری را بر روی آن رشته انجام می دهند. این توابع توسط فیلترهای template همچنین به طور مستقیم در کد دیگر استفاده می شوند.

در صورتی که توابع مشابه خود را بنویسید و با ترجمه های سر و کار داشته باشید، زمانی که اولین آرگومان یک شیء ترجمه ی lazy باشد با مشکل چه باید بکنم رو به رو می شوید. شما نمی خواهید آن را بلا فاصله به یک رشته تبدیل کنید، چرا که ممکن است تابع را بیرون از view نیز استفاده کنید (در دست ترجمه ...).

برای موارد شبیه به این، از دکوریتور django.utils.functional.allow_lazy() استفاده کنید. این دکوریتور تابع اصلاح می کند، به طوری که اگر با یک ترجمه lazy به عنوان اولین آرگومان فراخوانی شود، ارزیابی تابع تا زمانی که نیاز باشد به یک رشته تبدیل شود به تاخیر می افتد.

برای مثال:

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

استفاده از این دکوریتور بدین معناست که شما می توانید تابع خود را نوشته و فرض کنید که ورودی یک رشته ی مناسب می باشد، سپس پشتیبان برای شیء های ترجمه ی lazy در پایان اضافه کنید.

2. نحوه ساختن فایل های زبان

هنگامی رشته های خود را برای ترجمه ی بعدی ضمیمه کردید، نیاز به نوشتن (بدست آوردن) خود ترجمه های زبان دارید. در زیر نحوه ی این عمل وجود دارد.

محدودیت های Locale

جنگو localize کردن برنامه ی شما را به داخل یک locale که خود جنگو ترجمه نکرده است را پشتیبانی نمی کند. در این مورد، فایل های ترجمه ی شما نادیده گرفته می شوند. در صورتی که برای این مورد تلاش کرده اید و جنگو از آن پشتیبانی کرده است، ناگذیر به یک مخلوط از رشته های ترجمه خود (از برنامه خود) رشته های انگلیسی (از خود جنگو) نگاه کنید. در صورتی که می خواهید یک locale برای برنامه ی خود را پشتیبیانی کنید که قبلا بخشی از جنگو نمی باشد، نیاز به ساختن حداقل یک ترجمه از هسته جنگو خواهید داشت.

فایل های پیام

اولین قدم برای یک زبان جدید ساختن یک فایل پیام می باشد. فایل پیام یک فایل متنی ساده برای نشان دادن یک زبان است، که حاوی تمام رشته های در دسترس برای ترجمه و چگونگی نمایش آن ها در زبان داده شده است. فایل های پیام دارای پسوند .po می باشند.

جنگو ابزاری به نام django‑admin.py makemessages ارائه می کند، که ساختن و نگهداری این فایل ها را خودکار می کند. جهت ساخت و به روز رسانی یک فایل پیام، دستور زیر را اجرا کنید:

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

de کد زبان برای فایل پیامی که می خواهید بسازید می باشد. کد زبان، در این مورد در قابل بندی locale می باشد. برای مثال، pt_BR برای برزیلی پرتغالی و de_AT برای اتریشی آلمانی می باشد.

اسکریپت باید از یکی از این سه مکان اجرا شود:

  • دایرکتوری ریشه ی پروژه ی جنگوی شما.
  • دایرکتوری ریشه ی app جنگوی شما.

این اسکریپت در سرتاسر درخت منبع پروژه یا درخت منبع برنامه اجرا می شود و تمام رشته های علامت گذاری شده برای ترجمه را بیرون می کشد. این اسکریپت یک فایل پیام در دایرکتوری locale/LANG/LC_MESSAGES می سازد (یا بروز رسانی می کند). در مثال de، فایل locale/de/LC_MESSAGES/django.po خواهد بود.

به طور پیشفرض django-admin.py makemessages هر فایلی که دارای پسوند .html باشد را بررسی می کند. در مواردی که می خواهید آن پیشفرض را override کنید، از آپشن –extension یا –e برای تعیین پسوندهای فایل جهت بررسی استفاده کنید:

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

چندین پسوند را با کاما و / یا با استفاده از –e یا ––extension چندین بار جدا کنید:

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

زمان ساختن کاتالوگ های ترجمه ی جاوا اسکریپت (که کمی بعد در این آموزش از کتاب پوشش داده خواهد شد،) نیاز به استفاده از دامنه ی خاص 'djangojs' دارید، نه –e js.

gettext نه؟

در صورتی که فواید gettext را نصب ندارید، django‑admin.py makemessages فایل های خالی خواهد ساخت. اگر چنین است می توانید gettext را نصب کنید و یا فایل پیام (locale/en/LC_MESSAGES/django.po) انگلیسی را که فقط یک فایل خالی ترجمه است در صورت دسترس کپی کنید و از آن به عنوان نقطه شروع استفاده کنید.

با سیستم عامل ویندوز کار می کنید؟

در صورتی که از ویندوز استفاده می کنید و نیاز به نصب فواید GNU gettext برای عملکرد django‑admin makemessages دارید، به بخش "gettext در ویندوز" در همین فصل مراجعه کنید.

قالب بندی فایل های .po ساده می باشد. هر فایل .po حاوی یک قسمت کوچک از metadata، از قبیل اطلاعات تماس maintainer ترجمه، توده ی فایل یک لیست از پیام ها می باشد – ارتباط های ساده بین رشته های ترجمه و متن ترجمه شده ی واقعی برای زبان خاص.

برای مثال، در صورتی که app جنگو شما حاوی یک رشته ی ترجمه برای متن "Welcom to my site." مانند زیر می باشد:

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

سپس django-admin.py makemessages یک فایل .po حاوی تکه کد های زیر را خواهد ساخت – یک پیام:

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

یک توضیح سریع:

  • msgid رشته ی ترجمه می باشد، که در منبع وجود دارد. آن را تغییر ندهید.
  • msgstr جایی است که ترجمه ی زبان خاص را در آن جا قرار می دهید. که با مقدار خالی شروع می کند، بنابراین تغییر دادن آن به عهده ی شما می باشد. اطمینان حاصل کنید که ترجمه ی شما درون کتیشن می باشد.
  • برای راحتی، هر پیام به صورت شکلی از خط کامنت شروع شده با علامت "#" می باشد و در بالای خط msgid قرار گرفته است، filename و شماره خط از رشته ی ترجمه شده جمع شده اند.

پیام های طولانی یک مورد خاص می باشند. در آن جا، اولین رشته به طور مستقیم بعد از msgstr (یا msgid) یک رشته ی خالی می باشد. سپس خود محتوا خودش سرتاسر چند خط بعدی به صورت یک رشته در هر خط نوشته شده خواهند بود. آن رشته ها به صورت مستقیم متصل شده اند. space های عقبی درون رشته ها را فراموش نکنید؛ در غیر اینصورت، آن ها بدون فاصله به یکدیگر وصل می شوند!

جهت بررسی دوباره ی تمام کد منبع و template ها برای رشته های ترجمه ی جدید و به روز رسانی تمام فایل های پیام برای تمام زبان ها، دستور زیر را اجرا کنید:

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

کامپایل فایل های Message

بعد از این که فایل پیام خود را ایجاد نمودید – و هر بار که تغییراتی در آن انجام دهید – نیاز به کامپایل آن درون یک فرم موثرتر خواهید داشت، جهت استفاده با gettext. این کار را با مزیت django‑admin.py compilemessages انجام دهید.

این ابزار سرتاسر تمام فایل های .po در دسترس اجرا می شود و فایل های .mo را ایجاد می کند که فایل های باینری بهینه شده برای استفاده توسط gettext می باشند. درون دایرکتوری همسانی که دستور django‑admin.py makemessages را اجرا کردید، دستور django-admin.py compilemessages را مانند زیر اجرا کنید:

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

همین. ترجمه های شما آماده برای استفاده می باشند.

3. طریقه پی بردن جنگو به ترجیح زبان

هنگامی ترجمه های خود را آماده می کنید – یا، در صورتی که تنها می خواهید از ترجمه های موجود در خود جنگو استفاده کنید – تنها نیاز خواهید داشت ترجمه را برای app خود فعال کنید.

در پشت صحنه، جنگو دارای یک مدل خیلی منعطف از تصمیم گیری در مورد زبانی که باید استفاده شود می باشد – installation‑wide برای یک کاربر خاص، یا هر دو.

برای قرار دادن preference زبان installation-wide، LANGUAGE_CODE را قرار دهید. جنگو از این زبان به صورت ترجمه ی پیشفرض استفاده می کند – تلاش نهایی در صورتی که مترجم دیگری یک ترجمه پیدا کند.

در صورتی که بخواهید جنگو را با زبان مادری خود اجرا کنید، و یک فایل زبان برای زبان شما در دسترس می باشد، نیاز به قرار دادن LANGUAGE_CODE دارید.

در صورتی که می خواهید برای هر کاربر منحصر به فرد زبان مورد ترجیح خودش را مشخص کنید، از LocaleMiddleware استفاده کنید. LocaleMiddleware انتخاب زبان بر اساس داده ی درخواست را فعال می کند. LocaleMiddleware محتوا را برای کاربر سفارشی می کند.

جهت استفاده از LocaleMiddlewareT، 'django.middleware.locale.localeMiddleware' را به تنظیم MIDDLEWARE_CLASSES اضافه کنید. به دلیل آن که ترتیب middleware مهم می باشد، باید دستور العمل های زیر را دنبال کنید:

  • اطمینان حاصل کنید آن اولین middleware نصب شده می باشد.
  • باید بعد از SessionMidedleware بیاید، زیرا LocaleMiddleware از داده session استفاده می کند.
  • در صورتی که از CacheMiddleware استفاده می کند، LocaleMiddleware را بعد از آن قرار دهید.

به عنوان مثال، MIDDLEWARE_CLASSES ممکن است شبیه به زیر باشد:

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

LocaleMiddleware تلاش می کند preference زبان کاربر را توسط الگوریتم زیر تعیین کند:

  • ابتدا، به دنبال کلید django_language در session کاربر فعلی می گردد.
  • در صورت نبود، به دنبال یک کوکی می گردد.
  • در صورت نبود، به HTTP هدر Accept-Language نگاه می کند. این هدر توسط مرورگر شما ارسال شده است و به سرور زبانی را که شما ترجیح می دهید را می گوید. جنگو برای هر زبان در هدر تلاش می کند تا یک را با ترجمه های در دسترس بایبد.
  • در صورت نبود، از تنظیم سراسری LANGUAGAE_CODE استفاده می کند.

نکته ها:

  • در هر کدام از این مکان ها، preference زبان، انتظار می رود در قالب بندی استاندارد زبان باشد، به صورت یک رشته. برای مثال، برزیلی پرتغالی، pt-br می باشد.
  • در صورتی که زبان پایه در دسترس باشد ولی sublanguage مشخص نباشد، جنگو از زبان پایه استفاده می کند. برای مثال، در صورتی که کاربرد de-at تعیین کرده باشد (اتریشی آلمانی) ولی جنگو تنها دارای de در دسترس باشد، جنگو از de استفاده می کند.
  • تنها زبان های لیست شده در تنظیم LANGUAGES می توانند انتخاب شوند. در صورتی که می خواهید انتخاب زبان را به یک زیر مجموعه از زبان های تهیه شده محدود کنید (چرا که برنامه ی شما تمام آن زبان ها را تهیه نمی کند)، LANGUAGE را به یک لیستی از زبان ها محدود کنید. برای مثال:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
  • مثال فوق زبان هایی را که برای انتخاب خودکار به آلمانی و انگلیسی در دسترس می باشند را محدود می کند (و هر sublanguage، مانند de-ch یا en-us).
  • در صورتی که یک تنظیم LANGUAGE سفارشی تعریف می کند، همانطور که در bullet قبلی توضیح داده شد، علامت گذاری زبان ها به صورت رشته های ترجمه صحیح می باشد – ولی استفاده از یک تابع ugettext() ساختگی، نه یکی در django.utils.translation. هرگز نباید django.utils.translation را درون فایل تنظیمات import کنید، چرا که آن ماژول در خودش وابسته به تنظیمات می باشد، و موجب یک import دایره ای می شود.
  • راهکار استفاده از یک تابع ugettext() ساختگی می باشد. در زیر یک نمونه از فایل تنظیمات می باشد:
برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
  • با این ترتیب، django‑admin.py makemessages همچنان این رشته های علامت گذاری شده برای ترجمه را پیدا می کند، ولی ترجمه در زمان اجرا اتفاق نمی افتد – بنابراین باید بخاطر داشته باشید که زبان ها را در ugettext() واقعی در هر کدی که در زمان اجرا از LNAGUAGE استفاده می کند wrap کنید.
  • LocalMiddleware می تواند تنها زبان هایی تهیه شده توسط جنگو برای ترجمه را انتخاب کند. در صورتی که می خواهید ترجمه هایی را برای برنامه ی خود تهیه کنید که قبلا در ترجمه های منبع جنگو قرار داده نشده اند، شما می خواهید حداقل اصول اولیه ترجمه ها را برای آن زبان تهیه کنید. برای مثال، جنگو ID های پیام فنی برای ترجمه ی قالب بندی های تاریخ و زمان استفاده می کند – بنابراین نیاز به حداقل آن ترجمه های برای سیستم، جهت عملکرد درتس خواهید داشت.
  • نقطه ی شروع خوب کپی فایل انگلیسی .po و ترجمه ی حداقل پیام های فنی می باشد – شاید پیام های validation، همچنین.
  • ID های فنی پیام به سادگی تشخیص داده شده اند؛ آن ها تماما حروف بزرگ می باشند. شما ID پیام را به صورت پیام های دیگر ترجمه نمی کنید، شما local گوناگون صحیح در مقدار انگلیسی تهیه شده تهیه می کنید. برای مثال، با DATETIME_FORMAT (یا DATE_FORMATE یا TME_FORMAT)، این قالبندی رشته است که می خواهید برای زبان خود استفاده کنید. قالب بندی جهت قالب بندی رشته هایی که توسط تگ template، now استفاده شده اند یکسان است.

هنگامی که LocalMiddleware، preference کاربر را تعیین می کند، این preference را به صورت request.LANGUAGE_CODE برای هر HttpRequest قابل دسترس می کند. احساس آزادی کندی برای خواند این مقدار در کد view خود. در زیر یک مثال ساده وجود دارد:

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

توجه داشته باشید که، با ترجمه استاتیک (بدون middleware)، زبان دز settings.LANGUAGE_CODE می باشد، در حالی که با ترجمه ی پویا (middleware)، در request.LANGUAGE_CODE می باشد.

استفاده از ترجمه ها در پروژه خودتان

جنگو توسط الگوریتم زیر به دنبال ترجمه می گردد:

  • ابتدا، به دنبال دایرکتوری locale در دایرکتوری برنامه (در دست ترجمه ...). در صورتی که یک ترجمه برای زبان انتخاب شده پیدا کند، ترجمه نصب خواهد شد.
  • سپس، به دنبال دایرکتوری locale در دایرکتوری پروژه می گردد. در صورتی که یک ترجمه پیدا کند، ترجمه نصب خواهد شد.
  • در پایان، پایگاه ترجمه ی تهیه شده توسط جنگو را در django/conf/locale را بررسی می کند.

می توانید برنامه هایی که شامل ترجمه های خودشان باشند را بنویسید، و می توانید پایگاه ترجمه ها را در مسیر پروژه ی خود override کنید. یا، می توانید تنها یک پروژهی بزرگ خارج از چندین app ساخته و تمام ترجمه ها را درون یک پروژه ی بزرگ فایل پیام قرار دهید. انتخاب با خودتان می باشد.

تمامی مخازن (repositories) دارای ساخت به روش یکسان می باشند، آن ها:

  • $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
  • $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
  • تمام مسیرهای لیست شده در LOCALE_PATHS در فایل تنظیمات شما (در دست ترجمه ...).
  • $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

جهت ساخت فایل های پیام، از ابزار همسان django-admin.py makemessages همانطور که فایل های پیام جنگو با آن بود استفاده می کنید. تنها نیاز است د رجای مناسب باشید – در دایرکتوری conf/locale ((در دست ترجمه ...)) یا locale/ ((در دست ترجمه ...)) دایرکتوری قرار دارند. همچنین از دستور همسان django-admin.py compilemessages برای تولید فایل های باینری django.mo که توسط gettext استفاده می شوند استفاده می شود.

همچنین می توانید django-admin.py compilemessages --settings=path.to.settings را برای ایجاد پردازش کامپایلر تمام دایرکتوری های موجود در تنظیم LOCALE_PATHS اجرا کرد.

فایل های پیام برنامه برای پی بردن کمی پیچیده می باشند – آن ها نیاز به LocaleMiddleware دارند. در صورتی که از middleware استفاده نمی کنید، تنها فایل های پیام جنگو و فایل های پیام پروژه پردازش خواهند شود.

در صورتی که برنامه ها نیاز به تحویل داده شدن به کاربران دیگر داشته و در پروژه های دیگر استفاده خواهد شد، ممکن است بخواهید ترجمه های app-specific را استفاده کنید. ولی استفاده از ترجمه های app‑specific و ترجمه های پروژه می تواند مشکلات مرموزی با makemessages زتولید کند: makemessages از تمام دایرکتوری های زیر مسیر فعلی عبور کرده و بنابراین ممکن است ID های پیام را درون فایل پیام پروژه قرار دهد که قبلا در فایل های پیام برنامه وجود داشته اند.

ساده ترین راه ذخیره کردن برنامه هایی است که بخشی از پروژه نیستند (و بنابراین ترجمه های خودشان را حمل می کنند) خارج از درخت پروژه. django-admin.py makemessages در سطح پروژه تنها رشته هایی که پروژه ی صریح شما متصل هستند را ترجمه می کند و نه رشته هایی که به طور مستقل توزیع شده هستند.

view تغییر مسیر set_language

برای راحتی، جنگو یک view به نام django.views.i18n.set_language ارائه می کند، که یک preference زبان کاربر را قرار می دهد و به صفحه ی قبلی تغییر مسیر می دهد.

این view را توسط اضافه کردن خط زیر به URLconf فعال کنید:

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

(توجه داشته باشید که این مثال view را در /i18n/setlang/ قابل دسترس می کند.)

view انتظار دارد توسط روش POST فراخوانی شود، با یک پارامتر langage در درخواست. در صورتی که پشتیبانی session فعال شده باشد، view انتخاب زبان در session کاربر را ذخیره می کند. در غیر اینصورت، زبان انتخاب در یک کوکی که به طور پیشفرض با نام django_language می باشد را ذخیره می کند. (نام می تواند از طریق تنظیم LANGUAGE_COOKIE_NAME تغییر کند.)

بعد از تنظیم انتخاب زبان، جنگو با الگوریتم زیر کاربر را تغییر مسیر می دهد:

  • جنگو به دنبال یک پارامتر next در داده POST می گردد.
  • در صورتی که وجود نداشته باشد، یا خالی باشد، جنگو URL درون هدر Referrer را امتحان می کند.
  • در صورتی که خالی باشد – تصور کنید، در صورتی که یک مرورگر مانع آن هدر شود – سپس کاربر به / (ریشه ی سایت) تغییر مسیر داده خواهد شد.

در زیر مثال کد HTML template وجود دارد:

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

ترجمه ها و جاوا اسکریپت

اضافه کردن ترجمه ها به جاوا اسکریپت برخی مشکلات را موجب می شود:

  • کد جاوا اسکریپت دارای دسترسی به انجام gettext نمی باشد.
  • کد جاوا اسکریپت دارای دسترسی به فایل های .po یا .mo نمی باشد؛ آن ها نیاز به تحویل داده شدن توسط سرور را دارند.
  • کاتالوگ های ترجمه برای جاوا اسکریپت باید در کوچکترین حد ممکن نگهداری شوند.

جنگو یک راهکار یکپارچه سازی شده برای این مشکلات تهی می کند: ترجمه ها را به جاوا استکریپت اراسل می کند، بنابراین می توانید gettext و غیره را از درون جاوا اسکریپت فراخوانی کنید.

javascript_catalog

راهکار اصلی برای این مشکلات، view ای با نام javascript_catalog می باشد، که یک کتابخانه ی کد جاوا اسکریپت با توابعی است که رابط gettext را تقلید کرده است، به اضافه ی یک آرایه از رشته های ترجمه. آن رشته های ترجمه گرفته شده از برنامه، پروژه یا هسته ی جنگو می باشد، مطابق آن چه را که شما در info_dict یا URL تعیین کرده اید.

به شکل زیر آن را مرتبط می کنید:

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

هر رشته در packages باید در syntax پایتون dotted-package باشد (قالب بندی همسان به صورت رشته ها در INSTALLED_APPS) و باید به یک پکیجی که حاوی دایرکتوری locale می باشد رجوع کند. در صورتی که چندین پکیج تعیین می کنید، تمام آن کاتالوگ ها در یک کاتالوگ ادغام می شوند. در صورتی که جاوا اسکریپتی دارید که از رشته های برنامه های مختلف استفاده می کند این مفید می باشد.

می توانید view را توسط قرار دادن پکیج هایی داخل الگوی URL پویا کنید:

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

با این موضوع، شما پکیج هایی را به صورت یک لیست از نام های پکیج محدود شده توسط علامت های ' 'در URL تعیین می کنید. این بویژه در صورتی که صفحات شما کدهایی از app های مختلف استفاده می کند و این اغلب تغییر می کند و نمی خواهید در یک فایل کاتالوگ بزرگ بکشید مفید است. به عنوان یک اقدام امنیتی، این مقادیر تنها django.conf یا هر پکیجی از تنظیم INSTALLED_APPS می باشند.

استفاده از کاتالوگ ترجمه ی جاوا استکریپت

جهت استفاده از کاتالوگ، تنها کافیست

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

این نحوه ای است که مدیر ترجمه ی کاتالوگ سرور را واکشی می کند. هنگامی که کاتالوگ بارگذاری شد، کد جاوا اسکریپت شما می تواند از رابط gettext استاندارد برای دسترسی به آن استفاده کند:

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

همچنین یک رابط ngettext نیز وجود دارد:

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

و حتی یک تابع رشته ی interpolation:

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

interpolation syntax قرض گرفته شده از پایتون می باشد، بنابراین تابع interpolate هر دوی interpolation موضعی و نام گذاری شده را پشتیبانی می کند:

interpolation موضعی: obj حاوی یک شیء آرایه ی جاوا اسکریپت می باشد که مقادیر المان ها به ترتیب در placeholder های متناظر ftm در ترتیب یکسانی که ظاهر شده اند قرار می گیرند:

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

interpolation های نام گذاری شده: این حالت توسط ارسال پارامتر boolean اختیاری name به صورت true انتخاب شده است. obj حاوی یک شیء جاوا اسکریپت یا آرایه ی associative می باشد. برای مثال:

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

شما نباید بالا با رشته ی interpolation بروید: این همچنان جاوا اسکریپت می باشد، بنابراین، کد باید تعویض های تکرار شده ی regular-expression را ایجاد کند. این به همان سرعت رشته ی interpolation در جاوا نمی باشد، بنابراین آن را برای مورادی که واقعا به آن نیاز دارید نگه دارید (برای مثال، در رابطه با ngettet برای تولید جمع بندی های مناسب).

ساختن کاتالوگ های ترجمه ی جاوا اسکریپت

شما کاتالوگ های ترجمه را به همان روشی که کاتالوگ های ترجمه ی دیگر جنگو را ساخته و به روز رسانی می کردید – با ابزار django-admin.py makemessages – انجام می دهید. تنها تفاوت این است که نیاز به تهیه ی یک پارامتر –d djangojs مانند زیر خواهید داشت:

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

دستور فوق کاتالوگ ترجمه برای جاوا اسکریپت برای آلمانی را ساخته یا به روز رسانی می کند. بعد از به روز رسانی کاتالوگ های ترجمه، تنها کافیست دستور django-admin.py compilemessages همانند کاتالوگ های عادی جنگو اجرا کنید.

نکته هایی برای کاربران آشنا به gettext

در صورتی که gettext را می شناسید، ممکن است متوجه این تخصص های روش جنگو برای انجام ترجمه شده باشید:

  • رشته ی دامنه، django یا djangojs است. این رشته ی دامنه برای فرق قائل شدن بین برنامه های مختلف می باشد، که داده ی آن ها در یک کتابخانه ی مشترک فایل پیام ذخیره می شود (معمولا /usr/share/locale/). دامنه ی جنگو برای پایتون و رشته های ترجمه ی template استفاده شده است و به کاتالوگ های سراسری ترجمه بارگذاری شده است. دامنه ی djangojs تنها برای کاتالوگ های ترجمه ی جاوا اسکریپت جهت اطمینان اینکه آن ها تا حد ممکن کوچک هستند استفاده شده است.
  • جنگو از xgettext تنها استفاده نمی کند. از wrapper پایتون در اطراف xgettext و msgfmt استفاده می کند. این بیشتر برای راحتی است.

gettext در ویندوز

این تنها برای افرادی که می خواهند ID های پیام را استخراج کنند یا فایل های پیام را کامپایل کنند ضروری می باشد (.op). خود کار ترجمه تنها با ویرایش فایل های موجود از این نوع درگیر است، ولی در صورتی که می خواهید فایل های پیام خود را ایجاد کنید، یا می خواهید یک فایل پیام تغییر کرده را آزمون یا کامپایل کنید، نیاز به مزیت های gettext خواهید داشت:

فایل های zip زیر را از

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

  • gettext-runtime-X.bin.woe32.zip
  • gettext-tools-X.bin.woe32.zip
  • libiconv-X.bin.woe32.zip

3 فایل را فولدر همسان از حالت zip خارج کنید (مانند C:\Program Files\gettext‑utils)
PATH سیستم را به روز رسانی کنید:

  • Control Panel > System > Advanced > Environment Variables
  • در لیست System variables، بر روی Path کلید کنید، بر روی Edit کلیک کنید
  • ;C:\Program Files\gettext‑utils\bin به انتهای فیلد Variable value اضافه کنید

همچنین می توانید از باینری های gettect نیز که در جای دیگر بدست آورده شده است استفاده کنید، مادامی که دستور xtgett –version به درستی کار می کند. برخی نسخه های باینری 0.14.4 این دستور را پشتیبانی نمی کنند. در صورتی که دستور xgettext –version وارد شده در یک command prompt ویندوز موجب یک پنجره ی popup شده است "xgettext.exe has generated errors and will be closed by Windows" برای استفاده از مزیت های ترجمه ی جنگو با پکیج gettext تلاش نکنید.

لینک به دیدگاه
  • 2 هفته بعد...

امنیت در جنگو

اینترنت می تواند یک مکان ترسناک باشد.

این روزها، به نظر می رسد اشتباهات امنیتی خجالت آوری اتفاق می افتند. مشاهده می کنیم که ویروس ها با سرعت شگفت انگیزی گسترش می یابند، بسیاری از کامپیوترهای مورد تهاجم ویروس ها به صورت تسلیحات بکار برده می شوند، تسلیحات بی پایان در برابر فرستندگان spam، و بسیاری، بسیاری از گزارش های دزدی از وب سایت های هک شده مسابقه می دهند.

به عنوان توسعه دهندگان وب، ما وظیفه داریم راه هایی را که می توان با این نیروهای تاریکی مبارزه کرد را بیان کنیم. هر توسعه دهنده ی وب نیاز دارد با امنیت به عنوان یک جنبه اساسی از برنامه نویسی وب رفتار کند. متاسفانه، پیاده سازی امنیت سخت است – مهاجمان نیاز است تنها یک آسیب پذیری پیدا کنند، ولی مدافعان باید همه ی این آسیب پذیری ها را محافظت کنند.

جنگو تلاش می کند این سختی را سبک تر کند. جنگو جهت محافظت شما به طور خودکار از بسیاری از این اشتباهات امنیتی طراحی شده است که توسعه دهندگان جدید (و حتی با تجربه ها) ایجاد می کنند. ولی همچنان اطلاع کسب کردن از ماهیت این مشکلات، نحوه ی محافظت شما توسط جنگو با اهمیت می باشد، و – از همه مهم تر – مراحلی است که می توان برای امنیت بیشتر کد خود انجام داد.

قبل از هر چیز، یک سلب مسئولیت مهم: ما قصد ارائه ی یک راهنمای قطعی برای هر امنیت وب را نداریم، و همچنین تلاشی سعی نداریم هر آسیب پذیری را در یک روش جامع توضیح دهیم. در عوض، یک خلاصه ی کوتاه از مشکلات امنیتی را که در جنگو بکار می روند را ارائه خواهیم کرد.

موضوع امنیت (Security) وب

اگر قرار است تنها یک چیز از این آموزش از کتاب یاد بگیرید، اجازه دهید آن این باشد:

هرگز – تحت هیچ شرایطی – به داده های از سمت مرورگر اعتماد نکنید.
هرگز کسی را که آن طرف اتصال HTTP می باشد را نمی شناسید. ممکن است یکی از کاربران شما باشد، ولی به سادگی می تواند یک cracker نابکار بدنبال یک موقعیت باشد.

هر داده ای از هر نوعی که از سمت مرورگر می آید، باید با آن به چشم یک داده ی مخرب نگاه کرد. این شامل هر دوی داده های "in band" (مانند، داده های ارسالی از فرم ها وب) و "out of band" (مانند، HTTP header ها، کوکی ها، و اطلاعات دیگر درخواست) می باشند. این ها مسائل جزئی ای درخواست می باشند که مرورگر های معمولا به طور خودکار اضافه می کنند.

هر یک از آسیب پذیری های بحث شده در این آموزش از کتاب، به طور مستقیم جلوی اجرای داده ای را که از آن سوی سیم می آید را گرفته و سپس قبل از استفاده از آن، داده ی مورد نظر را بررسی می کنند. باید همواره این سوال را از خودتان بپرسید که "این داده از کجا آمده است؟".

SQL Injection

SQL injection یک سو استفاده ی رایج می باشد که در آن مهاجم پارامترهای صفحه ی وب (از قبیل داده ی GET/POST یا URL ها) را جهت درج تکه کد های دلخواه SQL که یک برنامه وب ساده به طور مستقیم در پایگاه داده ی خود اجرا می کند را تغییر می دهد. این احتمال دارد خطرناک ترین حالت آسیب پذیری باشد – و متاسفانه، یکی از رایج ترین آن می باشد.

این آسیب پذیری، هنگامی که با دست از ورودی کاربر SQL ایجاد می کنید بیشتری احتمال رخ دادن را دارد. برای مثال، تصور کنید، یک تابع جهت جمع آوری یک لیست از اطلاعات تماس، یک صفحه جستجوی تماس می نویسید. جهت جلوگیری از spammer ها از خواندن هر پست الکترونیکی در سیستم، کاربر را مجبور به تایپ نام کاربری کسی قبل از تهیه آدرس الکترونیکی خود می کنیم:

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

نکته

در این مثال و همه مثال های دیگر که نوشته ایم "این کار را نکن" فقط هدف آموزش بوده است و شما نباید برای پروژه عملی از آن استفاده کنید. این فانکشن ها فقط کار می کنند.

هر چند، در ابتدا این خطرناک به نظر نمی آمد، ولی واقعا خطرناک است.

ابتدا، تلاش ما در حفاظت از کل لیست پست الکترونیک با یک کوئری به طور هوشمندانه ساخته شده شکست خواهد خورد. فرض کنید، اگر یک مهاجم در box کوئری تایپ کند "' OR 'a'='a" چه اتفاقی می افتد. در این مورد، کوئری که رشته ی مورد نظر درون آن قرار داده شود بدین گونه خواهد بود:

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

به این دلیل که به SQL نا امن را درون رشته اجازه دادیم، عبارت OR مهاجم مطمئن است که هر ردیف تنها برگردانده شده است.

هر چند، که حداقل حمله ترسناک می باشد. تصور کنید، اگر مهاجم عبارتی شبیه به این را ارسال کنید چه اتفاقی خواهد افتاد "'; DELETE FROM user_contacts WHERE 'a' = 'a". کوئری کامل چیزی شبیه به این خواهد بود:

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

اوه! تمام لیست به سرعت حذف می شود.

راهکار

اگرچه این مشکل موذیانه و گاهی اوقات کشف آن سخت می باشد، راهکار آن ساده می باشد: هرکز به داده ی ارسال شده از سمت کاربر اعتماد نکنید، و همواره از ارسال آن به SQL اجتناب کنید.

API پایگاه داده ی جنگو این کار را برای شما انجام می دهد. این API به طور خودکار تمام پارامترهای ویژه ی SQL را رد می کند، با توجه به قراردادهای به نقل از سرور پایگاه داده ای که از آن استفاده می کنید (مانند PostgreSQL یا MySQL).

برای مثال، در این فراخوانی API:

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

جنگو بر این اساس از ورودی را رد می کند، نتیجه چیزی شبیه به این خواهد بود:

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

کاملا بی ضرر.

این در تمام API پایگاه داده ی جنگو بکار برده می شود، با تعدادی استثنا:

  • آرگومان where به متد extra(). (Appendix C را مشاهده کنید.) آن پارامتر SQL خام را توسط design قبول می کند.
  • کوئری های زده شده "به صورت دستی" با استفاده از API پایگاه داده ی سطح پایین تر. (آموزش مدل پیشرفته را مشاهده کنید.)

در هر یک از این موارد، محافظت از خودتان ساده می باشد. در هر مورد، از رشته ی interpolation دوری کنید وقتی می توانید از ارسال پارامترهای bind استفاده کنید. مثالی که در این بخش شروع کردیم باید به صورت زیر باشد:

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

متد سطح پایین execute یک رشته ی SQL با placeholder یعنی %s دریافت می کند و به طور خودکار پارامترهایی را که از لیست ارسال شده به صورت آرگومان دوم را رد و درج می کند. می توان همواره با این روش SQL سفارشی ساخت.

متاسفانه، نمی توان از پارامترهای bind در هر جایی از SQL استفاده کرد؛ آن ها به صورت identifier ها مجاز نمی باشند (مانند، جدول یا نام های ستون). در نتیجه، در صورت نیاز، تصور کنید، به صورت پویا یک لیست از جداول از یک متغیر POST ساخته شده است، نیاز خواهید داشت آن نام را در کد کد escape کنید. جنگو یک تابع با نام django.db.connection.ops.quote_name ارائه می دهد، که identifier را بر طبق الگوی quoting پایگاه داده ی فعلی escape می کند.

اسکریپت نویسی Cross-Site (XSS)

اسکریپت نویسی cross-site (XSS)، کشف شده در برنامه های وب می باشد که جهت escape کردن محتوای ارسالی کاربر قبل از render کردن آن درون HTML می باشد. این موضوع به مهاجم اجازه می دهد HTML دلخواه خود را درون صفحه ی وب شما درج کند، معمولا در شکل تگ های <script>.

مهاجمان اغلب از حملات XSS جهت دزدیدن اطلاعات session و کوکی استفاده می کنند، یا جهت فریب کاربران در دادن اطلاعات شخصی به یک شخص غلط (که نام دیگر آن phishing می باشد).

این نوع حمله می تواند یک تعداد از فرم های مختلف دریافت کرده و اغلب دارای تغییر اساسی نامحدود می باشد، بنابراین تنها یک مثال نمونه را بررسی نگاه می کنیم. به view بی نهایت ساده ی "Hello, World" ملاحظه کنید:

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

این view به سادگی نام پارامتر GET را خوانده و آن نام را به HTML تولید شده ارسال می کند. بنابراین، در صورتی که به

برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
دسترسی پیدا کنیم، صفحه حاوی کد زیر خواهد بود:

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

اما صبر کنید – چه اتفاقی می افتد اگر به

برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
دسترسی پیدا کنید؟ در اینصورت کد زیر اتفاق می افتد:

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

البته، یک مهاجم از چیز ابتدایی مانند تگ های <i> استفاده نمی کند؛ وی یک مجموعه ی کامل از HTML که صفحه ی شما را به محتوای دلخواه سرقت کند در آنجا قرار می دهد. این نوع حمله جهت فریب دادن کاربر برای وارد کردن داده به وب سایت هایی مانند بانک و غیره استفاده می شود، ولی در واقع یک XSS-hijacked، اطلاعات برگشتی حساب را به یک مهاجم ارسال می کند.

مشکل وخیم تر می شود اگر این داده را درون پایگاه داده ذخیره کنید و بعد آن را درون سایت خود نمایش دهید. برای مثال، MySpace یک بار برای یک حمله ی XSS از این نوع آسیب پذیر بوده است. یک کاربری که جاوا اسکریپت درون پروفایل شما درج کرده است که به طور خودکار او را به صورت دوست شما اضافه می کند زمانی که صفحه پروفایل او را بازدید می کنید. در عرض چند روز، او میلیون ها دوست خواهد داشت.

حالا، این ممکن است نسبتا ملایم باشد، ولی به خاطر داشته باشید که این مهاجمان برای بدست آوردن کد خود مدیریت می کنند – نه MySpace – اجرا کردن در کامپیوتر شما. این تجاوزات مورد اعتماد فرض شده اند که تمام کد در MySpace واقعا توسط MySpace نوشته شده است.

MySpace به شدت شانس آورد که این مخربان کد به طور خودکار حساب های کاربران را حذف، رمز عبور آن ها را تغییر، سایت را با spam غرق، یا هر سناریو خوفناک دیگری که احتمال داشت را انجام ندادند.

راهکار

راهکار ساده است: همواره از هر محتوایی را که ممکن است از سمت یک کاربر قبل از درج آن درون HTML باشد دوری کنید.

جهت محافظت در برابر این، سیستم template جنگو به طور خودکار تمام مقادیر متغیر را escape می کند. اجازه دهید ببینیم چه اتفاقی می افتد اگر مثال خودمان را با استفاده از سیستم template بنویسیم:

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

با استفاده از این سیستم، یک درخواست به

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

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

auto-escaping جنگو در آموزش template جنگو مورد پوشش قرار داده شده است، همراه با راه های خاموش کردن آن. ولی حتی اگر از این خصوصیت استفاده کنید، باید همچنان این عادت را داشته باشید که همواره از خود سوال را کنید "این داده از کجا آمده است؟" هیچ راهکار خودکاری هرگز سایت شما را از حملات XSS به طور 100% حفاظت نمی کند.

درخواست ساختگی Cross-Site

درخواست ساختگی cross-site (cross-site request forgery، CSRF)، هنگامی که یک مخرب کاربران را در بارگذاری ندانسته یک URL از یک سایت که آنها قبلا در آن authenticate شدن اتفاق می افتد – از این رو از وضعیت authenticate شده آن ها سود می برند.

جنگو (Django) دارای ابزار داخلی برای محافظت از این قبلی حملات می باشد.

ربودن/جعل کردن Session

این یک حمله ی خاص نیست، بلکه یک کلاس عمومی از حملات در یک داده session کاربر می باشد. می تواند به شکل های مختلف باشد:

  • حمله ی man-in-the-middle، جایی که یک مهاجم در داده session به صورت پیمودن در سرتاسر شبکه ی سیمی (یا بی سیم) تجسس می کند.
  • session forging، جایی که یک مهاجم از یک session ID (که ممکن است از طریق یک حمله ی man-in-the-middle به دست آورده باشد) برای وانمود کردن این که کاربر دیگری می باشد استفاده می کند.
  • یک مثال از این دوتای اول، یک مهاجم می باشد که در یک کافی شاپ، از شبکه ی بی سیم برای گرفتن یک کوکی session استفاده می کند. وی سپس می تواند از آن کوکی جهت جعل هویت خود به عنوان یک کاربر اصلی استفاده کند.
  • حمله ی cookie-forging، جایی که یک مهاجم داده ی به ظاهر read-only ذخیره شده در یک کوکی را override می کند. بخش کاربران عضویت و session نحوه ی عملکرد کوکی را به تفصیل توضیح داده است، و یکی از نکات برجسته، تغییر کوکی به سادگی و بدون اطلاع شما توسط کاربران مخرب می باشد.
  • یک تاریخچه ی طولانی از وب سایت هایی که دارای یک کوکی ذخیره شده مانند IsLoggedIn=1 یا حتی LoggedInAsUser=Jacob بوده اند وجود دارد. سو استفاده از این نوع کوکی های بسیار ساده می باشد.
  • در سطح زیرکانه تر، اگر چه، اعتماد کردن از هر آنچه که درون کوکی ها ذخیره می شود هرگز ایده ی خوبی نیست، شما هرگز کسی را که با آن ها سر و کار دارد را نمی شناسید.
  • session fixation، هنگامی که یک مهاجم یک کاربر را در تنظیمات یا دوباره قرار دادن session ID فریب می دهد.
  • برای مثال، PHP به identifier های session اجازه می دهد درون URL ارسال شوند (مانند،
    برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
    یک مهاجم کسی که یک کاربر را فریب می دهد تا بر روی یک لینک با یک session ID به طور مستقیم قرار داده شده کلیک کند که موجب خواهد شد کاربر آن session را دوباره قرار دهد.
  • session fixation در حملات phishing جهت فریب کاربران برای وارد کردن اطلاعات شخصی درو یک حساب که متعلق به یک مهاجم است استفاده می شود. او می تواند بعدا درون آن حساب وارد شده و داده را بازیابی کند.
  • session poisoning، جایی که یک مهاجم به طور بالقوه داده خطرناک را بدون session کاربر تزریق می کند – معمولا از طریق یک وب که کاربر برای قرار دادن session اطلاعات را ارسال می کند.
  • یک مثال استاندارد یک سایت می باشد که یک preference ساده ی کاربر (مانند یک رنگ زمینه ی صفحه) را در یک کوکی ذخیره می کند. یک مهاجم می تواند یک کاربر را برای کلیک بر روی یک لینک که یک "color" را ارسال می کند فریب دهد که واقعا حاوی یک حمله ی XSS می باشد. در صورتی که رنگ escape نشده باشد، کاربر می تواند دوباره کد مخرب خود را درون محیط کاربر تزریق کند.

راهکار

تعدادی اصول کلی وجود دارد که می تواند شما را از این حملات محافظت کنند:

  • هرگز اجازه ندهید اطلاعات session در URL قرار بگیرند.
  • فریم ورک session جنگو به سادگی اجازه نمی دهد که session های درون URL قرار بگیرند.
  • داده را به طور مستقیم درون کوکی ذخیره نکنید، در عوض یک session ID را که به داده session ذخیره شده در backend مرتبط است را ذخیره کنید.
  • در صورتی که از فریم ورک یا چارچوب داخلی session (مانند، request.session) استفاده می کنید، این به طور خودکار برای شما کنترل شده است. تنها کوکی ای که فریم ورک session استفاده می کند یک session ID تک می باشد؛ تمام داده ی session درون پایگاه داده ذخیره شده است.
  • فراموش نکنید، در صورتی که داده ی session را در template نمایش می دهید آن را escape کنید. بخش XSS قبلی را مشاهده کرده، و به یاد داشته باشید که هر محتوای ساخته شده توسط کاربر را به همان خوبی داده های آمده از سمت مرورگر بکار می برد. می توان با اطلاعات session به مانند اطلاعات ساخته شده توسط کاربر رفتار کرد.
  • جلوی مهاجمان را از فریب دادن session ID ها هر زمان که ممکن است بگیرید.
  • اگرچه تقریبا شناسانی کسانی که یک session ID را سرقت می کنند ممکن است، جنگو دارای محافظت های داخلی در برابر یک حمله بی رحم session می باشد. session ID ها به صورت hashe ها ذخیره می شوند (به جای اعداد ترتیبی)، که یک حمله ی بی رحمانه را منع می کنند، و یک کاربر همواره در صورتی که برای یکی که وجود ندارد تلاش کند، یک session ID جدید دریافت خواهد کرد، که از session fixation جلوگیری می کند.

توجه کنید که هیچ از آن اصول و ابزارها حملات man-in-the-middle را جلوگیری نمی کنند. شناسایی این نوع از حملات تقریبا غیر ممکن است. در صورتی که سایت شما اجازه می دهد کاربران برای مشاهده ی هر گونه از داده های حساس وارد شوند، باید همواره آن سایت درون HTTPS خدمات دهد. علاوه بر این، در صورتی که دارای یک سایت SSL-enabled هستید، باید تنظیم SESSION_COOKIE_SECURE را مقدار True قرار دهید؛ این باعث می شود جنگو کوکی های session را از درون HTTPS ارسال کند.

E-mail Header Injection

SQL injection نسبت به برادر خود e-mail header injection کم تر معروف است، که فرم های وب ارسال شده در پست الکترونیکی را سرقت می کند. یک مهاجم می تواند از این تکنیک برای ارسال spam از طریق mail سرور ما استفاده کند. هر فرمی که هدرهای ایمیل را از داده فرم وب می سازد برای این نوع حمله آسیب پذیر می باشد.

اجازه دهید نگاهی به تماس canonical که در بسیاری از سایت ها پیدا شده است بیاندازیم. معمولا این یک پیام به صورت کد مستقیم به یک آدرس ایمیل ارسال می کند و از این رو، در نگاه اول برای سو استفاده ی spam آسیب پذیر ظاهر نمی شود.

هرچند، اغلب این فرم ها همچنین به کاربر برای تایپ در subject خودش برای ایمیل اجازه می دهند (به همراه یک آدرس "from"، بدنه، و گاهی اوقات فیلدهای دیگر). این فیلد subject برای ساخت هدر "subject" از پیام ایمیل استفاده می شوند.

در صورتی که آن هدر هنگام ساختن پیام ایمیل escape نشده باشد، یک مهاجم می تواند چیزی مانند "hello\ncc:spamvictim@example.com" ارسال کند (جایی که "\n" یک کارکتر خط جدید می باشد). آن ایمیل ساخته شده را به شکل زیر تغیری می دهد:

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

همانند SQL injection، در صورتی که به خط subject داده شده توسط کاربر اعتماد کنیم، اجازه خواهیم داد به او که یک مجموعه از هدرهای مخرب را بسازد، و او می تواند از contact خود برای ارسال spam استفاده کند.

راهکار

می توان با روش همسانی که برای جلوگیری از SQL injection استفاده می کردیم برای جلوگیری از این حمله استفاده کرد: همواره محتوای ارسال شده توسط کاربر را escape یا validate کنید.

توابع mail داخلی جنگو (در django.core.mail) به سادگی به خط های جدید در هر فیلد اجازه نمی دهد برای ساختن هدر استفاده شوند (from و address، به اضافه ی subject). در صورتی که سعی به استفاده از django.core.mail.send_mail با یک subject دارید که حاوی خط های جدید می باشد، جنگو یک خطای BadHeaderError ایجاد می کند.

در صورتی که از توابع داخلی mail جهت ارسال ایمیل استفاده نمی کند، نیاز خواهید داشت، اطمینان حاصل کنید که خط های جدید در هدرها موجب بروز خطا شده یا حذف شوند. ممکن است بخواهید کلاس SafeMIMETest را در django.core.mail برای مشاهده ی انجام این عمل توسط جنگو را بررسی کنید.

پیمایش دایرکتوری

پیمایش دایرکتوری یکی دیگر از حملات به شکل injection می باشد، جایی که یک کاربر مخرب کد filesystem را برای خواند و/یا نوشتن فایل هایی که وب سرور نباید به آن ها دسترسی داشته باشد فریب می دهند.

یک مثال ممکن یک view باشد که فایل ها را بدون دقت نام آن فایل می خواند:

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

هر چند به نظر می رسد که view دسترسی فایل را به فایل های زیر BASE_PATH (توسط استفاده از os.path.join) محدود کرده است، در صورتی که مهاجم در یک filename محتویات .. (مختصر نویسی دایکرتوری پدر) را ارسال کند، می تواند به فایل های بالای BASE_PATH دسترسی پیدا کند. در دست ترجمه ....

هر چیزی که فایل ها را بدون escape مناسب بخواند برای این مشکل آسیب پذیر است. view هایی که فایل ها را به این شکل آسیب پذیر می نویسند، دارای عواقب دو چندان وخیم می باشند.

تغییر دیگر از این مشکل در کدی قرار دارد که به طور پویا مازول های بر اساس URL یا اطلاعات درخواست دیگر را بارگذاری می کند. یک مثال شایع در مورد دنیای Ruby on Rails می باشد. قبل از اواسط سال 2006، Rails از URL های شبیه به

برای مشاهده این محتوا لطفاً ثبت نام کنید یا وارد شوید.
به طور مستقیم برای بارگذاری ماژول های و فراخوانی متدها استفاده می کرد. نتیجه آن بود که یک URL به دقت ساخته شده می توانست به طور خودکار کد دلخواه را بارگذاری کنید، شامل یک اسکریپت دوباره راه اندازی کننده ی دیتابیس!

راهکار

در صورتی که کد شما هموراه نیاز به خواندن یا نوشتن فایل ها بر اساس ورودی کاربر دارد، نیاز به تمیز کردن مسیر درخواست شده به طور بسیار با دقت جهت اطمینان از این که یک مهاجم قادر به escape از دایرکتوری پایه ای که شما دسترسی به آن را محدود کرده اید دارید.

نکته

نیازی به گفتن نیست، که شما هرگز نباید کدی بنویسید که بتواند از هر منطقه ای از دیسک بخواند!

یک مثال خوب از نحوه ی انجام این escape در محتوی ارائه شده ی استاتیک view می باشد (در django.views.static). در اینجا کد مرتبط وجود دارد:

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

جنگو فایل ها را نمی خواند (مگر این که از تابع static.serve استفاده کنید، ولی آن با کدی که نشان داده شد محافظت شده است)، بنابراین این آسیب پذیری بر هسته ی کد ندارد.

علاوه بر این، استفاده از URLconf abstraction بدین معناست که جنگو هرگز کدی را که به طور واضح نگفته اید بارگذاری شود بارگذاری نمی کند. هیچ راهی برای ساختن یک URL ای که موجب می شود جنگو چیزی را که در یک URLconf ذکر نشده است را بارگذاری کند وجود ندارد.

پیام های خطای محافظت نشده

در طول توسعه، قادر بودن برای مشاهده ی traceback ها و خطا هایی که در مرورگر شما وجود دارند به شدت مفید می باشد. فریم ورک یا چارچوب جنگو (Django) دارای پیام های debug خوب و آموزنده ای جهت debug کردن آسان تر می باشد.

البته در صورتی که، این خطا ها یک بار در سایتی نمایش داده شود، می تواند جنبه هایی از کد شما یا پیکربندی را فاش کند که می تواند به یک مهاجم کمک کند.

بعلاوه، خطا ها و traceback ها همیشه برای کاربران مفید نمی باشد. فلسفه ی جنگو این است که بازدید کنندگان سایت نباید هرگز پیام های خطای مرتبط به برنامه را مشاهده کنند. در صورتی که کد شما یک خطای handle نشده را ایجاد کند، یک بازدید کننده ی سایت نباید traceback کامل را مشاهده کند – یا هر اشاره ای از تکه های کد یا پیام های خطای پایتون. در عوض، بازدید کننده باید یک پیام دوستانه "این صفحه قابل دسترسی نمی باشد" دریافت کند.

البته به طور طبیعی، توسعه دهندگان نیاز دارند traceback ها برای debug برنامه ها در کد آن ها مشاهده کنند. بنابراین فریم ورک یا چارچوب باید تمام پیام های خطا را برای عموم مخفی کند، ولی باید آن ها را برای توسعه دهندگان مورد اعتماد سایت نمایش دهد.

راهکار

تنظیم DEBUG جنگو (Django) نمایش این پیام های خطا را کنترل می کند. اطمینان حاصل کنید که این تنظیم زمانی که برای deploy آماده می باشید False است.

کاربرانی که می خواهند deploy را در Apache و mod_python انجام دهند، همچنین باید اطمینان حاصل کنند که دارای PythonDebug Off در فایل های conf آپاچی می باشند؛ این موضوع هر خطایی که قبل از آنکه جنگو دارای یک شانس برای بارگذاری باشد را سرکوب خواهد کرد.

حرف آخر درباره امنیت

ما امیدواریم تمام این صحبت های درباره ی مشکلات امنیتی رعب آور نبوده باشد. درست است که وب می تواند یک دنیای وحشی باشد، ولی با کمی دور اندیشی، می توانید یک وب سایت امن داشته باشید.

به خاطر داشته باشید که امنیت وب یک تغییر فیلد به طور دائم است: در صورتی که در حال خواندن نسخه ی dead-tree این کتاب می باشید، مطمئن شوید که بررسی های به روز تری از منابع امنیتی برای هر آسیب پذیری جدید که کشف شده است انجام داده اید. در واقع، همواره ایده ی خوبی است که مدت زمانی را در هفته یا ماه برای جستجو و تحقیق در مورد امنیت برنامه ی وب کنار بگذارید. این یک سرمایه گذاری کوچک است، ولی حفاظتی که برای سایت خود و کاربرانتان بدست می آورید بدون خرج خواهد بود.

لینک به دیدگاه

به گفتگو بپیوندید

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

مهمان
ارسال پاسخ به این موضوع ...

×   شما در حال چسباندن محتوایی با قالب بندی هستید.   حذف قالب بندی

  تنها استفاده از 75 اموجی مجاز می باشد.

×   لینک شما به صورت اتوماتیک جای گذاری شد.   نمایش به صورت لینک

×   محتوای قبلی شما بازگردانی شد.   پاک کردن محتوای ویرایشگر

×   شما مستقیما نمی توانید تصویر خود را قرار دهید. یا آن را اینجا بارگذاری کنید یا از یک URL قرار دهید.

×
×
  • اضافه کردن...