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

 

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

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

جستجو در تالار: در حال نمایش نتایج برای برچسب های 'آموزش فریم ورک جنگو'.



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

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

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

نوع محتوا


تالار ها

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

وبلاگ‌ها

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

دسته ها

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

دسته ها

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

دسته ها

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

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

  1. این تاپیک پشتیبانی برای آموزش است آموزش پکیج django.contrib در جنگو (جلسه پانزدهم ) 12/05/96 14:42 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  2. آموزش پکیج 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: فریم ورک تصدیق جنگو. توضیح داده شده در بخش کاربران عضویت و session. ***comments: یک برنامه ی نظرات. این کتاب این خصوصیت را پوشش نمی دهد. مستندات رسمی جنگو را بررسی کنید. ***contenttypes: (در دست ترجمه ...). ***csrf: محافظت در برابر Cross-Site Request Forgery (CSRF). به بخش "CSRF Protection" در همین فصل مراجعه کنید. ***databrowse: برنامه ی جنگو ای که اجازه می دهد داده ها را مرور کنید. این کتاب این خصوصیت را پوشش نمی دهد؛ مستندات رسمی جنگو را بررسی کنید. ***flatpages: یک فریم ورک برای مدیریت ساده ی محتوای "flat" HTML در یک پایگاه داده. به بخش "Flatpages" در همین فصل مراجعه کنید. ***formtools: تعدادی از کتابخانه های مفید سطح بالا برای سر و کار داشتن با الگوهای مشترک در فرم ها. این کتاب این خصوصیت را پوشش نمی دهد؛ مستندات رسمی جنگو را بررسی کنید. ***gis: ضمیمه هایی برای جنگو که پشتیبانی از GIS (Geographic Information Systems) را پشتیبانی می کند. برای مثال، به مدل های جنگو جهت ذخیره ی داده ی جغرافیایی اجازه می دهد و کوئری های جغرافیایی را نمایش می دهد. این یک کتابخانه ی بزرگ و پیچیده می باشد که در این کتاب پوشش داده نمی شود. برای اطلاعات بیشتر در این مورد به http://geodjango.org/ مراجعه کنید. ***humanize: مجموعه ای از فیلترهای مفید template جنگو برای اضافه کردن یک "human touch" به داده می باشد. به بخش "Humanizing Data" در همین فصل مراجعه کنید. ***localflavor: قسمت های مناسبی از کد که برای کشور یا فرهنگ های خاص مفید می باشند. برای مثال، شامل روش هایی برای تایید اعتبار کدهای ZIP ایالات متحده یا شماره های شناسایی ایسلند. ***marup: مجموعه ای از فیلترهای template جنگو که تعدادی از زبان های مشترک markup را اجرا می کنند. برای اطلاعات بیشتر به بخش "فیلترهای Marcup" مراجعه کنید. ***redirects: فریم ورکی برای مدیریت تغییر مسیرها. به بخش "تغییر مسیرها" مراجعه کنید. ***sessions: فریم session جنگو. توضیح داده شده در بخش کاربران عضویت و session. ***sitemaps: فریم ورکی برای تولید فیل XML نقشه ی سایت. توضیح داده شده در بخش کاربران عضویت و session. ***sites: فریم ورکی که به شما اجازه می دهد چندین وب سایت را با یک پایگاه داده و یک نصب جنو اداره کنید. به بخش بعدی "sites" مراجعه کنید. ***syndication: فریم ورکی برای تولید خوراک پیوند در RSS و Atom. توضیح داده شده در تولید محتوای غیر html ای. ***webdesign: add-on های جنگو که به طور خاص برای طراحان وب (مخالف توسعه دهندگان) مفید می باشد. تا زمان نوشتن این کتاب، این آیتم تنها شامل یک تگ template با نام {% lorem %} می باشد. برای اطلاعات بیشتر مستندات جنگو را بررسی کنید. باقی مانده ی این فصل به جزئیات تعدادی از پکیج های django.contrib خواهد پرداخت که تاکنون درباره ی آن ها در این کتاب بحث نشده است. 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 در مدل های خود ایجاد کنید، برای مثال: from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(max_length=200) # ... sites = models.ManyToManyField(Site) این زیر ساختی است که برای مربوط ساختن مقالات با چندین سایت در پایگاه داده نیاز می باشد. با قرار گرفتن آن، می توان از کد view جنگوی همسان برای چندین سایت دوباره استفاده کرد. مثال مدل Article زیر، view مورد نظر یعنی article_detail به شکل زیر خواهد بود: from django.conf import settings from django.shortcuts import get_object_or_404 from mysite.articles.models import Article def article_detail(request, article_id): a = get_object_or_404(Article, id=article_id, sites__id=settings.SITE_ID) # ... تابع view فوق قابل استفاده ی دوباره می باشد چرا که سایت مقاله را به صورت پویا بررسی می کند، بر حسب مقدار تنظیم SITE_ID. برای مثال، تصور کنید فایل تنظیمات LJWorld.com دارای یک تنظیم SITE_ID با مقدار 1 می باشد، و فایل تنظیمات Lawrence.com دارای یک SITE_ID با مقدار 2 می باشد. در صورتی که این view زمانی که فایل تنظیمات LJWorld.com فعال است فراخوانی شده است، سپس (در دست ترجمه ...). مربوط ساختن محتوی با یک سایت تنها به طور همسان، می توان یک مدل را در یک رابطه ی چند به یک با استفاده از ForeignKey به مدل Site مرتبط کرد. برای مثال، در صورتی که هر مقاله تنها با یک سایت تنها مرتبط باشد، می توانید از مدلی شبیه به مدل زیر استفاده کنید: from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(max_length=200) # ... site = models.ForeignKey(Site) کد فوق دارای مزیت همسان با بخش قبلی می باشد که توضیح داده شد. Hooking Into the Current Site from Views در یک سطح پایین، می توان کارهای خاصی را بر اساس سایت با استفاده از فریم ورک sites در view های جنگو در view ای که فراخوانی شده است انجام داد، برای مثال: from django.conf import settings def my_view(request): if settings.SITE_ID == 3: # Do something. else: # Do something else. البته، قرار دادن ID های سایت مانند کد فوق کاری شلخته می باشد. روشی تمیزتر از انجام کاری یکسان برای بررسی دامنه ی سایت: from django.conf import settings from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get(id=settings.SITE_ID) if current_site.domain == 'foo.com': # Do something else: # Do something else. روش بازیابی شیء Site برای مقدار settings.SITE_ID بسیار رایج می باشد، بنابراین مدیر مدل Site (Site.objects) دارای یک متد get_current() می باشد. مثال زیر برابر با مثال قبلی می باشد: from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get_current() if current_site.domain == 'foo.com': # Do something else: # Do something else. نکته : در مثال آخر، نیاز به import کردن django.conf.settings نمی باشد. بدست آوردن دامنه ی فعلی برای نمایش برای یک رویکرد DRY (Don't Repeat Yourself)، حذف کد های تکراری، جهت ذخیره ی نام سایت و دامنه، همانطور که کمی قبل تر در "سناریو 2: ذخیره نام/دامنه سایت در یک مکان" توضیح داده شد، تنها کافیست به name و domain شیء Site فعلی مراجعه کنید. برای مثال: from django.contrib.sites.models import Site from django.core.mail import send_mail def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... current_site = Site.objects.get_current() send_mail('Thanks for subscribing to %s alerts' % current_site.name, 'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % current_site.name, 'editor@%s' % current_site.domain, [user_email]) # ... در ادامه مثال در حال پیشرفت 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 محول کرد، مانند زیر: from django.core.mail import send_mail from django.template import loader, Context def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... subject = loader.get_template('alerts/subject.txt').render(Context({})) message = loader.get_template('alerts/message.txt').render(Context({})) send_mail(subject, message, 'do-not-reply@example.com', [user_email]) # ... در این مورد، شما ملزم به ساخت template های subject.txt و message.txt در هر دو دایرکتوری template برای LJWorld.com و Lawrence.com می باشد. همانطور که قبلا ذکر شد، این روش منعطف تر از روش قبلی، ولی پیچیده تر نیز می باشد. این ایده ی خوبی برای بکار بردن شیء های Site تا آنجا که ممکن است برای حذف پیچیدگی زائد و اضافه می باشد. CurrentSiteManager در صورتی که شیء ها نقش کلیدی را در برنامه ی شما ایفا می کنند، استفاده از CurrentSiteManager را در مدل های خود مورد ملاحظه قرار دهید. CurrentSiteMannager یک مدیر مدل (به آموزش مدل پیشرفته مراجعه کنید) است که به طور خودکار کوئری های خود را جهت بر داشتن تنها شیء های مرتبط شده با Site فعلی فیلتر می کند. با اضافه کردن CurrentSiteManager به مدل خود از آن استفاده کنید. برای مثال: from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(max_length=100) pub_date = models.DateField() site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager() با مدل فوق، Photo.objects.all() تمام شیء های Photo را در پایگاه داده بر می گرداند. ولی Photo.on_site.all() تنها شیء های Photo مرتبط شده با سایت فعلی را بر می گرداند، بر حسب تنظیم SITE_ID. به عبارت دیگر، دو عبارت زیر با یکدیگر برابر می باشند: Photo.objects.filter(site=settings.SITE_ID) Photo.on_site.all() چطور CurrentSiteManager می داند کدام فیلد از Photo، Site بوده است؟ به طور پیشفرض CurrentSiteMananger به دنبال یک فیلد با نام site می گردد. در صورتی که مدل شما دارای یک ForeignKey یا ManyToManyField فراخوانی شده ی چیزی به غیر از site باشد، نیاز خواهید داشت آن را به صورت پارامتر به CurrentSiteManager ارسال کنید. مدل زیر، که دارای یک فیلد با نام publish_on می باشد، این را توضیح می دهد: from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(max_length=100) pub_date = models.DateField() publish_on = models.ForeignKey(Site) objects = models.Manager() on_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 (بخش "Flatpages" را در همین فصل مشاهده کنید)، هر flatpage یا یک سایت خاص مرتبط شده است. هنگامی که یک flatpage ساخته شده است، شما site آن را تعیین می کند، و flatpage middleware، SITE_ID فعلی را در بازیابی flatpage ها برای نمایش بررسی می کند. ***در فریم ورک syndication (تولید محتوای غیر html ای را مشاهده کنید) template ها برای title و description به طور خودکار دارای دسترسی به یک متغیر {{ site }} می باشند که نمایش شیء Site، سایت فعلی می باشد. همچنین، hook برای تهیه ی URL های آیتم در صورتی که یک دامنه ی کاملا واجد شرایط تعیین نکنید از domain شیء Site فعلی استفاده خواهد کرد. ***در فریم ورک تصدیق (بخش کاربران عضویت و session را مشاهده کنید)، 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 تعریف شده است. که چیزی شبیه به کد زیر می باشد: from django.db import models from django.contrib.sites.models import Site class FlatPage(models.Model): url = models.CharField(max_length=100, db_index=True) title = models.CharField(max_length=200) content = models.TextField(blank=True) enable_comments = models.BooleanField() template_name = models.CharField(max_length=70, blank=True) registration_required = models.BooleanField() sites = models.ManyToManyField(Site) اجازه دهید فیلدهای فوق را مورد بررسی قرار دهیم: ***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 کامل می شود، که در بخش کاربران عضویت و session بیشتر درباره ی آن توضیح داده شده است. ***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 پایگاه داده ی جنگو دسترسی پیدا کرد، برای مثال: >>> from django.contrib.flatpages.models import FlatPage >>> from django.contrib.sites.models import Site >>> fp = FlatPage.objects.create( ... url='/about/', ... title='About', ... content='<p>About this site...</p>', ... enable_comments=False, ... template_name='', ... registration_required=False, ... ) >>> fp.sites.add(Site.objects.get(id=1)) >>> FlatPage.objects.get(url='/about/') <FlatPage: /about/ -- About> استفاده از 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 وجود دارد: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html> <head> <title>{{ flatpage.title }}</title> </head> <body> {{ flatpage.content|safe }} </body> </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 می باشد بررسی می کند. (برای اطلاعات بیشتر در مورد SITE_ID و فریم ورک sites به بخش "Sites" در همین فصل مراجعه کنید.) سپس 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 دسترسی پیدا کرد، برای مثال: >>> from django.contrib.redirects.models import Redirect >>> from django.contrib.sites.models import Site >>> red = Redirect.objects.create( ... site=Site.objects.get(id=1), ... old_path='/music/', ... new_path='/sections/arts/music/', ... ) >>> Redirect.objects.get(old_path='/music/') <Redirect: /music/ ---> /sections/arts/music/> محافظت 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 می باشد: <input type="hidden" name="confirm" value="true"> این موضوع مطمئن می سازد که یک 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 خود اضافه کنید، کار را انجام داده اید. بخش "ترتیب 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 مراجعه کنید.) در صورتی که از یک session سفارشی یا فریم ورک authentication ای استفاده می کنید که به صورت دستی کوکی های session را مدیریت می کند، این middleware به شما کمکی نخواهد کرد. در صورتی که برنامه ی شما صفحات HTML و فرم ها را در برخی روش های غیر معمول ایجاد می کند (مثلا، در صورتی که قطعات HTML در عبارت های document.write جاوا اسکریپت ارسال می شوند)، ممکن است فیلتری که فیلد hidden به فرم اضافه می کند دور بزنید. در این مورد، ارسال فرم همواره شکست خواهد خورد. (این به این دلیل اتفاق می افتد که CsrfMiddleware برای اضافه کردن فیلد csrfmiddlewaretoken به HTML شما قبل از آن که صفحه به کلاینت ارسال شود از یک regular expression استفاده می کند. و regular expression گاهی اوقات این نوع HTML ها را نمی تواند کنترل کند.) در صورتی که شک دارید که این موضوع اتفاق می افتد یا خیر، تنها کافیست source را در مرورگر خود تماشا کنید که آیا csrfmiddlewaretoken درون <form> قرار گرفته است یا خیر. برای اطلاعات و مثال های بیشتر درباره ی CSRF به Cross-site requ... مراجعه کنید. 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 را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به Textile (markup... مراجعه کنید. ***markdown: Markdown را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به Markdown - Wiki... مراجعه کنید. ***restructuredtext: Restructured Text را اجرا می کند که برای اطلاعات بیشتر در مورد آن می توانید به reStructuredTex... مراجعه کنید. در هر مورد، فیلتر انتظار قالب بندی markup را به صورت یک رشته را داشته و یک نمایش متن markup شده را بر می گرداند. برای مثال، فیلتر textile متنی را که در قالب بندی Textile، mark up شده است را به HTML تبدیل می کند: {% load markup %} {{ object.content|textile }} جهت فعال کردن این فیلترها، 'django.contrib.markup'را به تنظیم INSTALLED_APPS اضافه کنید. هنگامی که این کار را انجام داده شد، از {% load markup %} در یک template استفاده کنید تا به این فیلترها دسترسی پیدا کنید. برای مستندات بیشتر، کد منبع django/contrib./markup/templatetags/markup.py را بخوانید. خسته نباشید دوستان عزیز . جلسه پانزدهم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . برگرفته از سایت oruji.org ترجمه از سایت www.djangobook.com
  3. این تاپیک پشتیبانی برای آموزش است آموزش Caching در جنگو (جلسه چهاردهم ) 10/05/96 09:44 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  4. آموزش Caching در جنگو هر بار که یک کاربر یک صفحه را درخواست می کند، وب سرور تمام محاسبات را ایجاد می کند – از کوئری های پایگاه داده جهت render کردن template برای business logic – برای ساختن صفحه ای که بازدید کنندگان سایت می بینند. این حرکت از نظر بار اضافی بسیار پر خرج تر و سنگین تر از خواندن فایل از filesystem می باشد. برای اغلب برنامه های وب، این بار اضافی یک درگیری بزرگی به حساب نمی آید. اغلب برنامه های وب washingtonpost.com یا slashdot.org نیستند؛ آن ها وب سایت هایی یا اندازهای کوچک و متوسط و با ترافیکی به همین شکل می باشند. ولی برای سایت های با ترافیک بالا، حذف بارهای اضافی تا حد ممکن یک ضرورت به حساب می آید. در آنجا بود که cashing بوجود آمد. cache کردن چیزی، ذخیره ی نتیجه ی یک محاسبه ی پر خرج به طوری که مجبور نباشید محاسبه را در بار بعدی انجام دهید می باشد. در زیر تعدادی شبه کد وجود دارد که نحوه ی این عمل را برای یک صفحه ی وب به طور پویا تولید شده توضیح می دهد: given a URL, try finding that page in the cache if the page is in the cache: return the cached page else: generate the page save the generated page in the cache (for next time) return the generated page جنگو یک سیستم قدرتمند cache را ارائه می کند که اجازه می دهد صفحات پویا را به طوری که اجباری برای مورد محاسبه قرار دادن برای هر درخواست نداشته باشید ذخیره کنید. برای راحتی، جنگو سطح های متفاوتی از 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 به صورت آزاد و مجانی در http://danga.com/memchashed/ در دسترس می باشد. این cache به صورت daemon اجرا شده و مقدار مشخص از RAM را اختصاص داده است. تمام کاری که این نوع cache انجام می دهد، تهیه ی یک رابط سریع برای اضافه کردن، بازیابی و حذف داده دلخواه در cache می باشد. تمام داده به طور مستقیم در حافظه ذخیره شده است، بنابراین هیچ بار اضافه ای برای پایگاه داده یا استفاده filesystem وجود ندارد. بعد از نصب خود Memchached، نیاز به نصب اتصالات پایتون Memcached خواهیم داشت، که به طور مستقیم همراه جنگو نمی باشند. دو نسخه از این قابل دسترس می باشند. یکی از ماژول های زیر را انتخاب و نصب کنید: ***سریع ترین آپشن در دسترس یک ماژول با نام cmemchache می باشد، که در لینک http://gijsbert.org/cmemcache/ در دسترس می باشد. ***در صورتی که نمی توانید cmemcache را نصب کنید، می توانید python‑memcached را که در لینک ftp://ftp.tummy.com/pub/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 در حال اجرا می باشد: CACHE_BACKEND = 'memcached://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_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/' در مثال زیر، cache در سرتاسر نمونه های Memcached در حال اجرا در آدرس IP های 172.19.26.240 (پورت 11211) و 172.19.26.242 (پورت 11212) و 172.19.26.244 (پورت 11213) به اشتراک گذاشته شده است. CACHE_BACKEND = 'memcached://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 درون پایگاه داده خود توسط اجرای دستور زیر ایجاد کنید: python manage.py createcachetable [cache_table_name] ... جایی که [cache_table_name] نام جدول پایگاه داده ای می باشد که ساخته خواهد شد. (این نام می تواند هر چیزی که می خواهید باشد، تا زمانی که یک نام جدول معتبر باشد و درون پایگاه داده شما وجود نداشته باشد.) این دستور یک جدول تنها در پایگاه داده شما ایجاد می کند که در قالب بندی مناسبی که سیستم cache پایگاه داده انتظار دارد می باشد. هنگامی که شما جدول پایگاه داده را ایجاد کردید، تنظیم CACHE_BACKEND را با "db://tablename" تنظیم کنید، جایی که tablename نام جدول پایگاه داده می باشد. در مثال زیر، نام جدول cache نام my_cache_table می باشد: CACHE_BACKEND = 'db://my_cache_table' cache پایگاه داده از پایگاه داده همسان به صورتی که درون فایل تنظیمات تعیین شده است استفاده می کند. شما نمی توانید از پایگاه داده متفاوتی برای جدول cache خود استفاده کنید. cache پایگاه داده در صورتی که دارای یک پایگاه داده ی سریع باشید بسیار عالی کار خواهد کرد. Cache کردن Filesystem جهت ذخیره ی آیتم های cache شده در یک filesystem، از "file://" در CACHE_BACKEND استفاده کنید. به عنوان مثال، جهت ذخیره داده ی cache شده در /var/tmp/django_cache از تنظیم زیر استفاده کنید: CACHE_BACKEND = 'file:///var/tmp/django_cache' توجه داشته باشید که سه علامت (/) در شروع مثال فوق وجود دارد. دوتای اول برای file://، و سومی، اولین حرف مسیر دایرکتوری /var/tmp/django_cache می باشد. در صورتی که در سیستم عامل ویندوز هستید، حرف درایو را بعد از file:// مانند زیر قرار دهید: file://c:/foo/bar مسیر دایرکتوری باید کامل باشد – بدین بدان معنی است که، باید از ریشه filesystem شروع شود. گذاشتن یا نذاشتن علامت (/) در پایان تنظیم اهمیتی ندارد. اطمینان حاصل کنید دایرکتوری اشاره شده توسط این تنظیم وجود داشته و قابل نوشتن و خواندن توسط کاربر سیستمی که وب سرور درون آن اجرا می شود باشد. در ادامه مثال فوق، در صورتی که سرور شما به صورت کاربر apache اجرا می شود، اطمینان حاصل کنید که دایرکتوری /var/tmp/django_cache وجود داشته و قابل نوشتن و خواندن توسط کاربر apache باشد. هر مقدار cache ای به صورت یک فایل جدا ذخیره شده خواهد بود که محتویات داده cach ذخیره شده در یک قالب بندی سریال شده ("pickled") توسط ماژول pickle پایتون هستند. هر نام فایل کلید cache رها شده برای استفاده امن filesystem می باشد. Cache کردن حافظه ی داخلی اگر مزایای سرعت cache در حافظه را بدون قابلیت اجرای Memcached می خواهید، cache حافظه داخلی را ملاحظه کنید. این cache چند پردازشی و thread-safe می باشد. برای استفاده از آن، تنظیم CACHE_BACKEND را با "locmem:///" تنظیم کنید. برای مثال: CACHE_BACKEND = 'locmem:///' توجه داشته باشید که هر پردازش دارای نمونه cache خصوصی خود می باشد، که بدین معنی است که cache به صورت cross‑process ممکن خواهد بود. همچنین واضح است که حافظه ی داخلی cache منحصرا حافظه ی کار آمد به حساب نمی آید، بنابراین شاید برای محیط های تولید انتخاب مناسبی نباشد. این نوع cache برای توسعه عالی می باشد. Cache کردن ساختگی (برای توسعه) در پایان، جنگو یک cache با نام "dummy" ارائه کرده است که در واقع cache نیست – این تنها بدون انجام چیزی رابط cache را اجرا می کند. این نوع cache در صورتی که شما دارای یک سایت تولید باشید که از cache سنگینی را در مکان های گوناگون استفاده کند مفید است. ولی مکان ها یک محیط توسعه/آزمون جایی که نمی خواهید cache انجام شود و نمی خواهید لزوما کد شما برای مورد خاص اخیر تغییر کند. جهت فعال کردن dummy cache، تنظیم CACHE_BACKEND را مانند زیر تنظیم کنید: CACHE_BACKEND = 'dummy:///' استفاده از یک Cache سفارشی هنگامی که جنگو پشتیبانی از تعداد از cache ها بدون هیچ تنظیمی را ارائه می کند. ممکن است بخواهید از یک cache سفارشی شده استفاده کنید. برای استفاده از یک cache خارجی با جنگو، از مسیر import پایتون به صورت قسمت طرح (قسمتی قبل از تعریف علامت کالن ":") از URL، CACHE_BACKEND مانند زیر استفاده کنید: CACHE_BACKEND = 'path.to.backend://' در صورتی که cache مخصوص خود را می سازید، می توانید از cache استاندارد به صورت پیاده سازی مرجع استفاده کنید. درون دایرکتوری django/core/cache/backends/ از منبع جنگو کد را خواهید یافت. نکته: بدون هیچ دلیل قانع کننده ای، مانند به عنوان مثال پشتیبانی نکردن یک میزبانی از آیتمی، شما باید به cache های درون جنگو وجود دارند استفاده کنید. آن ها بخوبی مورد آزمون قرار گرفته و استفاده از آن ها ساده می باشد. آرگومان های 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 می باشد: CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60" در مثال زیر، timeout مقدار 30 بوده و max_entires مقدار 400 می باشد: CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400" آرگومان های نا معتبر بدون هیچ خطایی رد می شوند، به طوری که مقادیر نا معتبر آرگومان های شناخته شده رد می شوند. Cache در هر سایت هنگامی که cache راه اندازی شده است، ساده ترین روش جهت استفاده از cache، cache کردن کل سایت می باشد. نیاز به اضافه کردن 'django.middleware.cache.UpdateCacheMiddleware' و 'django.middleware.cache.FetchFromCacheMiddleware' به تنظیم مورد نظر یعنی MIDDLEWARE_CLASSES خواهید داشت، مانند مثال زیر: MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ) نکته : نه، این اشتباه تایپ نیست: 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 می کند. این روش برای استفاده ساده می باشد: from django.views.decorators.cache import cache_page def my_view(request): # ... my_view = cache_page(my_view, 60 * 15) همچنین می توانید از دستور زبان پایتون 2.4 به بالا استفاده کنید: @cache_page(60 * 15) def my_view(request): # ... 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 شما مانند زیر باشد: urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), ) سپس درخواست های به /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 قبلی را مشاهده می کنید: urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), ) در زیر کدی همسان وجود دارد، با این تفاوت که cache_page درون URLconf قرار گرفته است: from django.views.decorators.cache import cache_page urlpatterns = ('', (r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)), ) در صورتی که از این رویکرد استفاده می کنید، قرار داده cache_page را درون URLconf فراموش نکنید. Template Fragment Caching در صورتی که خواستار کنترل بیشتر می باشید، همچنین می توانید قطعه های template را با استفاده تگ template ای با نام cache، cache کنید. جهت دادن دسترسی templateبه این تگ، {% load cache %} را در بالای template خود قرار دهید. تگ {% cache %} محتویات بلاک برای مقدار زمان داده شده را cache می کند. این تگ حداقل دو آرگومان دریافت می کند: cache timeout بر حسب ثانیه، و نام برای دادن قطعه cache. برای مثال: {% load cache %} {% cache 500 sidebar %} .. sidebar .. {% endcache %} گاهی اوقات ممکن است بخواهید چندین کپی از یک قطعه را بسته به برخی داده های پویا که داخل قطعه ظاهر می شوند cache کنید. برای مثال، ممکن است یک کپی جدای cache شده از نوار کناری استفاده شده در مثال قبلی برای هر کاربر از سایت خود را بخواهید. توسط ارسال آرگومان های اضافه به تگ {% cache %} برای تشخیص قطعه cache به طور منحصر به فرد این کار را انجام دهید: {% load cache %} {% cache 500 sidebar request.user.username %} .. sidebar for logged in user .. {% endcache %} تعیین بیشتر از یک آرگومان برای تشخیص قطعه کاملا خوب می باشد. به سادگی برخی آرگومان ها را به {% cache %} همان طور که نیاز دارید ارسال کنید. cache timeout می تواند یک متغیر template باشد، تا زمانی که متغیر template یک مقدار integer باشد. برای مثال، در صورتی که متغیر my_timeout مقدار 600 برایش قرار گرفته باشد، سپس دو مثال زیر با هم برابر هستند: {% cache 600 sidebar %} ... {% endcache %} {% cache my_timeout sidebar %} ... {% endcache %} این خصوصیت برای اجتناب از تکرار در 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 ساخته شده است: >>> from django.core.cache import cache رابط اصلی set(key, value, timeout_seconds) و get(key): >>> cache.set('my_key', 'hello, world!', 30) >>> cache.get('my_key') 'hello, world!' آرگومان timeout_seconds اختیاری می باشد و به آرگومان timeout در تنظیم CACHE_BACKEND بر می گردد. در صورتی که شیء در cache مورد نظر یعنی cache.get() وجود نداشته باشد None بر می گرداند: # Wait 30 seconds for 'my_key' to expire... >>> cache.get('my_key') None توصیه می شود مقدار واقعی None را در cache ذخیره نکنید، چرا که قادر به تشخیص مقدار ذخیره کرده ی None خود و دیگر مقادیر None نخواهید بود. cache.get() می تواند یک آرگومان default دریافت کند. این آرگومان مقدار برگشت داده شده، در صورت عدم وجود شیء در cache را تعیین می کند: >>> cache.get('my_key', 'has expired') 'has expired' جهت اضافه کردن یک کلید تنها در صورتی که وجود نداشته باشد، از متد add() استفاده کنید. این متد پارامترهایی همانند set() دریافت می کند، ولی این متد در صورتی که کلید تعیین شده حاضر باشد (وجود داشته باشد) تلاشی برای به روز رسانی نخواهد کرد: >>> cache.set('add_key', 'Initial value') >>> cache.add('add_key', 'New value') >>> cache.get('add_key') 'Initial value' در صورتی که نیاز به دانستن این موضوع دارید که آیا add() یک مقدار در cache ذخیره کرده است، می توانید مقدار برگشتی را بررسی کنید. این مقدار در صورتی که مقدار ذخیره شده باشد True و در غیر این صورت مقدار False بر می گرداند. همچنین یک رابط بانام get_many() وجود دارد که تنها یک بار (در دست ترجمه ...). get_many() یک دیکشنری با تمام کلیدهایی که خواسته اید و در واقع در cache وجود داشته باشد بر می گرداند. >>> cache.set('a', 1) >>> cache.set('b', 2) >>> cache.set('c', 3) >>> cache.get_many(['a', 'b', 'c']) {'a': 1, 'b': 2, 'c': 3} در پایان، می توان به طور واضح با delete() کلیدها را حذف کرد. این یک روش ساده از حذف cache برای یک شیء خاص می باشد: >>> cache.delete('a') همچنین می توان با استفاده از متدهای incr() و decr() یک کلید موجود را به ترتیب افزایش و کاهش داد. به طور پیشفرض، مقدار cache موجود توسط مقدار 1، افزایش یا کاهش داده خواهد شد. مقادیر دیگر افزایش/کاهش می توانند توسط تهیه ی یک آرگومان برای فراخوانی افزایش/کاهش تعیین شده باشند. در صورتی که تلاش کنید یک کلید cache ای که وجود ندارد را افزایش یا کاهش دهید یک خطا ایجاد خواهد شد.: >>> cache.set('num', 1) >>> cache.incr('num') 2 >>> cache.incr('num', 10) 12 >>> cache.decr('num') 11 >>> cache.decr('num', 5) 6 نکته : متدهای incr()/decr() برای atomic بودن تضمین نشده اند. در آن cache ها که افزایش/کاهش atomic را پشتیبانی می کنند (که مهمترین آن ها، memcached می باشد)، اعمال افزایش و کاهش atomic خواهند بود. هر چند، در صورتی که cache یک عمل افزایش/کاهش را ذاتا تهیه نکند، با استفاده از یک پروسه ی دو مرحله ای بازیابی/به روز رسانی انجام شده خواهد بود. Cache های بالا دست تا کنون، این فصل بر روی cache داده های خودتان تمرکز داشته است. ولی نوع دیگری از cache، مربوط به توسعه ی وب می باشد که توسط cache های "بالا دست" انجام می شود. این ها سیستم هایی هستند که صفحات را برای کاربران حتی قبل از رسیدن درخواست به وب سایت شما، cache می کنند. در اینجا مثال از cache های بالا دست وجود دارد: ***ISP شما ممکن است بعضی صفحات را cache کند، بنابراین اگر یک صفحه را از http://example.com/ درخواست کرده باشید، 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 مانند زیر استفاده کنید: from django.views.decorators.vary import vary_on_headers # Python 2.3 syntax. def my_view(request): # ... my_view = vary_on_headers(my_view, 'User-Agent') # Python 2.4 decorator syntax. @vary_on_headers('User-Agent') def my_view(request): # ... در این مورد، یک مکانیسم cache (مانند cache middleware خود جنگو) یک نسخه ی جدا از صفحه را برای هر user-agent منحصر به فرد cache خواهد کرد. مزیت استفاده از vary_on_headers به جای دستی قرار دادن Vary header (با استفاده از چیزی شبیه به response['Vary'] = 'user-agent') این است که decorator به Vary header اضافه می کند (که ممکن وجود داشته باشد)، به جای (در دست ترجمه ...) و به طور بالقوه هر چیزی که در آن جا وجود داشته باشد را override می کند. می توان چندین header را به vary_on_headers() ارسال کرد: @vary_on_headers('User-Agent', 'Cookie') def my_view(request): # ... این به cache های بالا دست تغییر کردن هر دو را می گوید، که یعنی ترکیب user-agent و cookie مقدار cache خود را بدست خواهند آورد. برای مثال، یک درخواست با user-agent ای مانند Mozilla و مقدار کوکی foo=bar از یک درخواست با user-agent ای با نام Mozilla و مقدار کوکی foo=ham متفاوت در نظر گرفته خواهند شد. به این دلیل که vary در کوکی بسیار رایج می باشد، یک decorator با نام vary_on_cookie وجود دارد. این دو view برابر می باشند: @vary_on_cookie def my_view(request): # ... @vary_on_headers('Cookie') def my_view(request): # ... header هایی که به vary_on_header ارسال می شوند به حروف بزرگ و کوچک حساس نیستند؛ "User‑gent" هیچ فرقی با "user-agent" نخواهد داشت. همچنین می توان از یک تابع کمکی با نام django.utils.cache.patch_var_headers به طور مستقیم استفاده کرد. این تابع Vary header را قرار داده یا اضافه می کند. برای مثال: from django.utils.cache import patch_vary_headers def my_view(request): # ... response = render_to_response('template_name', context) patch_vary_headers(response, ['Cookie']) return response patch_vary_headers یک نمون ی HttpResponse به صورت اولین آرگومان و یک لیست/تاپل از نام های header حساس به حروف بزرگ و کوچک به عنوان آرگومان دوم دریافت می کند. کنترل Cache: با استفاده از Header ها مشکلات دیگر cache حریم شخصی داده و سوال از جایی که داده باید در یک آبشاری از cache ها در آن ذخیره شده باشد. یک کاربر معمولا با دو نوع از cache ها رو به رو می باشد: cache مرورگر خود کاربر (cache خصوصی) و ارائه دهنده ی cache کاربر (یک cache عمومی). cache عمومی توسط چندین کاربر و استفاده می شود و توسط برخی دیگر کنترل می شود. (در دست ترجمه ...). بنابراین برنامه های وب نیاز به یک روش برای گفتن cache ها دارند که کدام داده خصوصی و بوده و کدام عمومی می باشد. راهکار، نشان دادن cache صفحه باید "خصوصی" باشد. برای انجام این کار در جنگو، از decorator مورد نظر برای view با نام cache_control استفاه کنید: from django.views.decorators.cache import cache_control @cache_control(private=True) def my_view(request): # ... این decorator مراقب فرستادن HTTP header مناسب در پشت صحنه می باشد. چند روش دیگر برای کنترل پارامترهای cache وجود دارد. برای مثال، HTTP به برنامه ها اجازه ی انجام کارهای زیر را می دهد: ***تعریف حداکثر زمانی که یک صفحه باید cache شده باشد. ***تعیین اینکه یک cache باید همواره برای نسخه های جدیدتر بررسی شود، تنها تحویل محتوای cache شده هنگامی که هیچ تغییری وجود ندارد. (برخی cache ها ممکن است محتوای cache شده را حتی اگر صفحه ی سرور تغییر کرده باشد تحویل دهند، فقط به خاطر این که کپی cache هنوز منقضی نشده است.) در جنگو، از decorator، cache_control برای تعیین این پارامترهای cache استفاده کنید. در این مثال، cache_control جهت دوباره معتبر ساختن cache در هر دسترسی و جهت ذخیره ی نسخه های cache برای حداکثر 3600 ثانیه به cache ها می گوید: from django.views.decorators.cache import cache_control @cache_control(must_revalidate=True, max_age=3600) def my_view(request): # ... هر رهنمود 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 نخواهد شد هدرها را اضافه می کند. مثال: from django.views.decorators.cache import never_cache @never_cache def myview(request): # ... بهینه سازی های دیگر جنگو چند قسمت دیگر از 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 باید بعد از هر آیتمی باشد که این کار را انجام می دهد. خسته نباشید دوستان عزیز . جلسه چهاردهم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . برگرفته از سایت oruji.org ترجمه از سایت www.djangobook.com
  5. آموزش Session ها، کاربران، و عضویت در جنگو وقت آن رسیده است که اعترافی کنیم: ما تا اینجای کار به طور عمد، یک جنبه ی مهم از توسعه ی وب را عقب انداخته ایم. مرورگر هایی که سایت های ما را مورد هدف قرار می دهند که در پشت خود انسان های واقعی دارند (حداقل بیشتر اوقات). این نکته ی بزرگی برای رد کردن است: بهترین حالت اینترنت زمانی می باشد که برای وصل شدن به مردم خدمات می دهد، نه ماشین ها. در صورتی که بخواهیم به درستی سایت ها را وادار کنیم، سر انجام باید با افراد در پشت مرورگر ها سر و کار داشته باشیم. متاسفانه، این موضوع ساده ای نمی باشد. HTTP طوری طراحی شده است که بی حالت باشد – بدین معنی که، هر درخواست در یک فضای تهی اتفاق می افتد. دوامی بین یک درخواست و درخواست بعدی وجود ندارد، و ما نمی توانیم هر یک از جنبه ها درخواست (آدرس IP، مرورگر و غیره ...) را که به طور مداوم توسط یک شخص به طور پی در پی ارسال می شود را شمارش کنیم. در این فصل شما نحوه ی کنترل این فقدان حالت را خواهید آموخت. با پایین ترین سطح (کوکی ها) شروع خواهیم کرد، و به سمت ابزار سطح بالاتر برای کنترل session ها، کاربران و عضویت حرکت خواهیم کرد. کوکی ها (Cookies) توسعه دهندگان مرورگر مدت ها پیش متوجه این موضوع شدند که وضعیت statelessness یک مشکل بزرگ برای توسعه دهندگان وب به شمار می رود، و در نتیجه کوکی ها چشم به جهان گشودند. کوکی یک تکه ی کوچک از اطلاعات می باشد که مرورگرها از طرف وب سرورها ذخیره می کنند. هر بار درخواست های یک مرورگر از یک صفحه ی فرم و از یک سرور خاص، به کوکی که در ابتدا دریافت شده است پس داده می شوند. اجازه دهید نگاهی به نحوه ای که این عمل ممکن است انجام شود بیاندازیم. هنگامی که شما مرورگر خود را باز می کنید و درون آن google.com را تایپ می کنید، مرورگر شما یک درخواست HTTP را به گوگل می فرستد که با چیزی شبیه به این شروع می شود: GET / HTTP/1.1 Host: google.com ... زمانی که گوگل پاسخ می دهد، یک پاسخ HTTP شبیه به پاسخ زیر خواهد بود: HTTP/1.1 200 OK Content-Type: text/html Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com Server: GWS/2.1 ... به هدر Set_Cookie دقت کنید. مرورگر شما مقدار کوکی که از این قرار است ذخیره خواهد کرد (PREF=ID=5b14f22bdafle81c:TM=1167000671:LM=1167000671) و در هر بار که شما به سایت دسترسی پیدا کنید آن را به گوگل بر می گرداند. بنابراین در مرتبه ی بعدی که به گوگل دسترسی پیدا می کنید، مرورگر شما یک درخواست مانند زیر را ارسال خواهد کرد: GET / HTTP/1.1 Host: google.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ... سپس گوگل می تواند از آن مقدار کوکی برای دانستن این که شما با کسی که قبل تر دسترسی پیدا کرده است یک فرد همسان می باشید استفاده می کند. این مقدار ممکن است برای مثال، یک کلید درون یک پایگاه داده که اطلاعات کاربر را ذخیره می کند باشد. گوگل می تواند (انجام می دهد) از آن برای نمایش نام کاربری حساب شما در صفحه استفاده کند. قرار دادن و گرفتن کوکی ها هنگامی که در جنگو با ماندگاری سر و کار دارید، بیشتر اوقات از seesion سطح بالا و یا از فریم ورک های کاربر بحث شده در این فصل استفاده خواهید کرد. با این حال، ابتدا به نحوه ی خواندن و نوشتن کوکی ها در سطح پایین نگاهی بیاندازید. این باید به فهم بقیه ی ابزار بحث شده در فصل و در صورتی که شما بخواهید به صورت مستقیم از کوکی ها استفاده کنید به شما کمک خواهد کرد. خواندن کوکی ها ساده می باشد. هر شیء HttpRequest دارای یک شیء COOKIES می باشد که مانند دیکشنری عمل می کند؛ می توانید برای خواند هر کوکی که مرورگر به view ارسال می کند از آن استفاده کنید: def show_color(request): if "favorite_color" in request.COOKIES: return HttpResponse("Your favorite color is %s" % \ request.COOKIES["favorite_color"]) else: return HttpResponse("You don't have a favorite color.") نوشتن کوکی ها قدری پیچیده تر می باشد. نیاز است که از متد set_cookie() در یک شیء HttpResponse استفاده کنید. در زیر مثالی وجود دارد که کوکی favorite_color مستقر در یک پارامتر GET را قرار می دهد: def set_color(request): if "favorite_color" in request.GET: # Create an HttpResponse object... response = HttpResponse("Your favorite color is now %s" % \ request.GET["favorite_color"]) # ... and set a cookie on the response response.set_cookie("favorite_color", request.GET["favorite_color"]) return response else: return HttpResponse("You didn't give a favorite color.") می توانید همچنین یک تعداد از آرگومان های اختیاری را به response.set_cookie() که جنبه هایی از کوکی را کنترل می کند ارسال کنید. همانطور که در جدول 1-14 مشاهده می کنید. جدول ۱-۱۴ 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 ها، نیاز است مراحل زیر را انجام دهید: 1- تنظیم MIDDLEWARE_CLASSES را ویرایش کرده و اطمینان حاصل کنید که MIDDLEWARE_CLASSES حاوی 'django.contrib.sessions.middleware.SessionMiddleware'می باشد. 2- اطمینان حاصل کنید که '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 می توانید کاری شبیه به زیر را انجام دهید: # Set a session value: request.session["fav_color"] = "blue" # Get a session value -- this could be called in a different view, # or many requests later (or both): fav_color = request.session["fav_color"] # Clear an item from the session: del request.session["fav_color"] # Check if the session has a given key: if "fav_color" in request.session: ... همچنین می توان از متدهای دیگر دیکشنری همانند keys() و items() در request.session استفاده کنید. تعدادی قوانین ساده برای استفاده ی موثر از session های جنگو وجود دارد: ***از رشته های معمولی پایتون به صورت کلیدهای دیکشنری در request.session (مخالف integer ها، شیء ها و غیره ...) استفاده کنید. ***کلیدهای دیکشنری session که با یک خط تیره شروع می شوند، برای استفاده ی داخلی توسط جنگو رزرو شده اند. در عمل، فریم ورک تنها از تعداد کمی از متغیرهای session با خط تیره شروع شده استفاده می کند، ولی در صورتی که در مورد این قبیل متغیرهای اطلاعاتی ندارید (و اگر تمایل دارید با هر تغییراتی در خود جنگو مطابق باشید)، استفاده از پیشوندهای خط تیره، از تداخل جنگو با برنامه ی شما جلوگیری می کند. برای مثال، از کلید session با نام _fav_color، مانند زیر استفاده نکنید: request.session['_fav_color'] = 'blue' # Don't do this! ***request.session را با یک شیء جدید جایگزین نکنید، و به attribute های آن دسترسی پیدا نکرده و چیزی در آن ها قرار ندهید. از آن مانند یک دیکشنری پایتون استفاده کنید. مثال: request.session = some_other_object # Don't do this! request.session.foo = 'bar' # Don't do this! اجازه دهید مثال های کوچکی را ذکر کنیم. view ساده ی زیر بعد از این که کاربر یک کامنت را پست می کند مقدار True را در یک متغیر has_commented قرار می دهد. جلوگیری از پست کردن بیشتر از یک کامنت توسط کاربر ساده می باشد: def post_comment(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') if 'comment' not in request.POST: raise Http404('Comment not submitted') if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=request.POST['comment']) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!') view ساده ی ورودی یک عضو در سایت: view ساده ی ورودی یک عضو در سایت: def login(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') try: m = Member.objects.get(username=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponseRedirect('/you-are-logged-in/') except Member.DoesNotExist: return HttpResponse("Your username and password didn't match.") و کد زیر خروج یک عضو از سایت که از طریق login() فوق وارد سایت شده است: def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.") نکته : در عمل، روش فوق روشی مناسبی برای ورودی کاربران نمی باشد. فریم ورک authentication که به طور مختصر بحث شده است این وظیفه را برای شما بسیار قدرمتند و با روشی مفید انجام می دهد. این مثال ها عمدا ساده می باشند، به طوری که شما بتوانید به راحتی در جریان کار قرار بگیرید. آزمون کوکی های ارسال شده همانطور که در بالا ذکر شد، نمی توان به هر کوکی ارسال شده ی مرورگر اطمینان کرد. بنابراین، برای راحتی کار، جنگو روشی ساده برای آزمون کوکی های ارسال شده ی مرورگر تهیه کرده است. تنها کافیست request.session.set_test_cookie() را در یک view فراخوانی کرده و request.session.test_cookie_worked() را در view بعدی بررسی کنید – نه در فراخوانی view همسان. این جدایی بین set_test_cookie() و test_cookie_worked به دلیل روش کار کوکی ها ضروری می باشد. زمانی که شما یک کوکی را قرار می دهید، در واقع شما نمی توانید تا درخواست بعدی مرورگر بگویید یک مرورگر آن را ارسال کرده است. استفاده از delete_test_cookie() برای تمیز کردن بعد از خودتان تمرین خوبی می باشد. این عمل را بعد از تایید کارکرد کوکی آزمون انجام دهید. در زیر مثال کاربرد معمولی موضوع فوق وجود دارد: def login(request): # If we submitted the form... if request.method == 'POST': # Check that the test cookie worked (we set it below): if request.session.test_cookie_worked(): # The test cookie worked, so delete it. request.session.delete_test_cookie() # In practice, we'd need some logic to check username/password # here, but since this is an example... return HttpResponse("You're logged in.") # The test cookie failed, so display an error message. If this # were a real site, we'd want to display a friendlier message. else: return HttpResponse("Please enable cookies and try again.") # If we didn't post, send the test cookie along with the login form. request.session.set_test_cookie() return render_to_response('foo/login_form.html') نکته : یک بار دیگر، توابع داخلی authentication این بررسی را برای شما انجام می دهند. استفاده از Session ها خارج از view ها به طور داخلی، هر session تنها یک مدل جنگوی معمولی تعریف شده در django.contrib.sessions.models می باشد. هر session توسط هش تصادفی کمتر یا بیشتر 32 حرفی در یک کوکی شناسایی می شود. به این دلیل که session یک مدل معمولی می باشد، می توان با استفاده از API معمولی پایگاه داده ی جنگو به session ها دسترسی پیدا کرد: >>> from django.contrib.sessions.models import Session >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12) برای بدست آوردن داده ی session واقعی، نیاز به فراخوانی get_decoded() می باشد. این موضوع ضروری می باشد، چرا که دیکشنری در قالب بندی رمزی شده ذخیره شده است: >>> s.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' >>> s.get_decoded() {'user_id': 42} زمانی که Session ها ذخیره شده اند به طور پیشفرض، جنگو تنها در صورتی که session تغییر کند آن ها را درون پایگاه داده ذخیره می کند – این بدین معنی است که اگر هر کدام از مقادیر دیکشنری آن اختصاص داده شود یا حذف شود: # Session is modified. request.session['foo'] = 'bar' # Session is modified. del request.session['foo'] # Session is modified. request.session['foo'] = {} # Gotcha: Session is NOT modified, because this alters # request.session['foo'] instead of request.session. request.session['foo']['bar'] = 'baz' برای تغییر این رفتار پیشفرض، مقدار 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 جنگو از کوکی ها تاثیر می گذارد، همانطور که در جدول 2-14 نشان داده شده است. جزئیات فنی : محض کنجکاوی، در زیر چند نکته ی فنی درباره ی کار عملکرد داخلی فریم ورک 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 (تصدیق و تصدیق) مورد مراجعه قرار گرفته است. آن نام عدم اعتبار کاربران را اغلب با یک پردازش دو مرحله ای تشخیص می دهد. نیاز به نکات زیر می باشد: 1- تصدیق کاربری که ادعای کاربر بودن می کند (معمولا توسط بررسی یک نام کاربری و رمز عبور در مقابل یک پایگاه داده از کاربران) 2- تصدیق این که کاربر مجاز به اجرای برخی اعمال داده شده (معمولا توسط بررسی در یک جدول از حق دسترسی ها) می باشد. در ادامه ی این نیازمندی ها، سیستم auth/auth جنگو حاوی تعدادی از بخش ها می باشد: ***کاربران: افرادی که در سایت شما عضویت دارند ***حق دسترسی ها: پرچم های دودویی (yes/no) طراحی شده برای مشخص کردن اینکه آیا یک کاربر می تواند یک وظیفه ی خاص را انجام دهد ***گروه ها: روشی عمومی جهت بکار بردن لیبل ها و حق دسترسی به بیشتر از یک کاربر ***پیام ها: روشی ساده برای به صف کردن و نمایش سیستم پیام ها به کاربران در صورتی که از ابزار مدیر (بحث شده در سایت مدیر) استفاده کرده اید، شما بسیاری از این ابزار را مشاهده کرده اید، و در صورتی که کاربران و گروه ها را در ابزار مدیر ویرایش کرده باشید، شما در واقع در جدول پایگاه داده ی سیستم auth داده ها را ویرایش کرده اید. فعال ساختن پشتیبانی تصدیق همانند ابزار session، پشتیبانی تصدیق به صورت یک برنامه ی جنگو در django.contrib که نیاز به نصب شدن دارد همراه است. همچنین مانند ابزار session، باید به طور پیشفرض نصب شده باشد، ولی در صورتی که شما آن را حذف کرده باشید، نیاز است مراحل زیر را برای نصب آن دنبال کنید: 1- اطمینان حاصل کنید فریم ورک session همانطور که قبلا در این فصل توضیح داده شد نصب شده باشد. پیگیری کاربران بدیهی است که مستلزم کوکی ها می باشد، و در نتیجه در فریم ورک session ساخته می شود. 2- 'django.contrib.auth' را در تنظیم INSTALLED_APPS قرار داده و دستور manage.py syncdb را جهت نصب جداول پایگاه داده ی مناسب اجرا کنید. 3- از وجود 'django.contrib.auth.middleware.AuthenticationMiddleware'درون تنظیم MIDDLEWARE_CLASSES اطمینان حاصل کنید – بعد از SessionMiddleware. با انجام مراحل فوق، همه چیز برای سر و کار داشتن با کاربران در توابع view آماده می باشد. رابط اصلی که شما برای دسترسی به کاربران در یک view از آن استفاده خواهید کرد request.user می باشد؛ این یک شیء است که کاربر فعلی وارد شده به سایت را نشان می دهد. در صورتی که کاربر وارد نشده باشد، به جای آن یک شیء AnonymousUser خواهد بود (برای جزئیات بیشتر به ادامه ی این بخش نگاه بیاندازید). می توانید به سادگی در صورتی که یک کاربر وارد شده است، با متد is_authenticated() تشخیص دهید: if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users. استفاده از کاربران هنگامی که شما یک کاربر دارید – اغلب از request.user، ولی از طریق یکی از روش های مختصر توضیح داده شده – تعدادی از فیلدها و متدهای در دسترس در آن شیء دارید. شیء های AnonymousUser برخی از این رابط ها را تقلید کرده است، ولی نه تمام آن را، بنابراین باید همواره user.is_authenticated() را قبل از آنکه کاربری را که با آن سر و کار دارید را با حسن نیت تصور کنید بررسی کنید.. جدول 3-14 و 4-14 فیلد ها و متد ها را به ترتیب در شیء های User لیست کرده است. در پایان، شیء های User دارای دو فیلد many-to-many می باشند: groups و permissions. شیء های User می توانند همانند فیلدهای many-to-many دیگر به شیء های مربوط به خود دسترسی پیدا کنند: # Set a user's groups: myuser.groups = group_list # Add a user to some groups: myuser.groups.add(group1, group2,...) # Remove a user from some groups: myuser.groups.remove(group1, group2,...) # Remove a user from all groups: myuser.groups.clear() # Permissions work the same way myuser.permissions = permission_list myuser.permissions.add(permission1, permission2, ...) myuser.permissions.remove(permission1, permission2, ...) myuser.permissions.clear() وارد و خارج شدن از سایت جنگو برای کنترل ورود و خروج از سایت، برخی توابع داخلی (و چند فوت و فن جذاب) را ارائه کرده است، ولی قبل از آن، اجازه دهید نگاهی به نحوه ی ورود و خروج از سایت را "به صورت دستی" بیاندازیم. جنگو جهت انجام این اعمال در django.contrib.auth دو تابع با نام های authenticate() و login() را ارائه کرده است. جهت تصدیق یک نام کاربری و رمز عبور داده شده، از authenticate() استفاده کنید. این تابع دو آرگومان کیورد username و password را دریافت می کند، و در صورتی که رمز عبور برای نام کاربری داده شده معتبر باشد، یک شیء User بر می گرداند. در صورتی که رمز عبور معتبر نباشد، authenticate() مقدار None بر می گرداند >>> from django.contrib import auth >>> user = auth.authenticate(username='john', password='secret') >>> if user is not None: ... print "Correct!" ... else: ... print "Invalid password." authenticate() تنها اعتبار کاربر را تایید می کند. جهت ورود کاربر، از تابع loging() استفاده کنید. این تابع یک شیء HttpRequest و یک شیء User دریافت کرده و با استفاده از فریم ورک session، ID کاربر را درون session ذخیره می کند. مثال زیر نحوه ی استفاده از هر دوی تابع authenticate() و login() را درون یک تابع view نشان می دهد: from django.contrib import auth def login_view(request): username = request.POST.get('username', '') password = request.POST.get('password', '') user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: # Correct password, and the user is marked "active" auth.login(request, user) # Redirect to a success page. return HttpResponseRedirect("/account/loggedin/") else: # Show an error page return HttpResponseRedirect("/account/invalid/") جهت خروج یک کاربر، از django.contrib.auth.logout() داخل view خود استفاده کنید. این تابع یک شیء HttpRequest دریافت کرده و هیچ مقداری بر نمی گرداند: from django.contrib import auth def logout_view(request): auth.logout(request) # Redirect to a success page. return HttpResponseRedirect("/account/loggedout/") دقت داشته باشید که auth.logout() در صورتی که کاربر وارد سایت نشده باشد، هیچ خطایی ایجاد نمی کند. در عمل، نیازی به نوشتن توابع login/logout خودتان نخواهید داشت؛ سیستم تصدیق مجموعه ای از view ها برای کنترل ورود و خروج به طور عمومی ارائه کرده است. اولین گام در استفاده از این view های تصدیق، وصل کردن آن ها به URLconf می باشد. نیاز است تکه کد زیر را اضافه کنید: from django.contrib.auth.views import login, logout urlpatterns = patterns('', # existing patterns here... (r'^accounts/login/$', login), (r'^accounts/logout/$', logout), ) /accounts/login/ و /accounts/logout/، URL های پیشفرض می باشند که جنگو برای این view استفاده می کند. به طور پیشفرض view، login یک template را در registeration/login.html، render می کند (می توانید نام این template را از طریق ارسال یک آرگومان view اضافه تغییر دهید، ``template_name``). این فرم نیاز دارد حاوی یک فیلد username و یک فیلد password باشد. یک template ساده ممکن است چیزی شبیه به کد زیر باشد: {% extends "base.html" %} {% block content %} {% if form.errors %} <p class="error">Sorry, that's not a valid username or password</p> {% endif %} <form action="" method="post"> <label for="username">User name:</label> <input type="text" name="username" value='' id="username"> <label for="password">Password:</label> <input type="password" name="password" value='' id="password"> <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next|escape }}" /> </form> {% endblock %} در صورتی که وارد شدن موفقیت آمیز باشد، کاربر به طور پیشفرض به /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() و تغییر مسیر به یک صفحه ی ورود می باشد: from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/?next=%s' % request.path) # ... یا شاید نمایش یک پیام خطا: def my_view(request): if not request.user.is_authenticated(): return render_to_response('myapp/login_error.html') # ... به صورت یک میانبر، می توان از decorator مناسب login_required استفاده کرد: from django.contrib.auth.decorators import login_required @login_required def my_view(request): # ... login_required به شکل زیر عمل می کند: ***در صورتی که کاربر وارد نشده باشد، به /accounts/login/ تغییر مسیر داده می شود، ارسال شدن مسیر URL فعلی در رشته ی کوئری به صورت next، برای مثال: /accounts/login/?next=/polls/3/. ***در صورتی که کاربر وارد شده باشد، view به صورت معمول اجرا می شود. کد view می تواند. در دست ترجمه/تالیف .... محدودیت دسترسی برای کاربرانی که یک آزمون را رد می کنند محدودیت دسترسی بر پایه ی حق دسترسی ها یا برخی آزمون های دیگر، یا ارائه ی یک مکان مختلف برای view ورود اساسا به یک روش کار می کند. روش خام اجرای آزمون در request.user به طور مستقیم درون view می باشد. برای مثال، view زیر برای اطمینان از این که کاربر، وارد شده و دارای حق دسترسی polls.can_vote می باشد یا خیر: def vote(request): if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')): # vote here else: return HttpResponse("You can't vote in this poll.") بار دیگر، جنگو یک میانبر با نام user_passes_test ارائه کرده است. این میانبر آرگومان هایی دریافت کرده و یک decorator تخصص یافته برای وضعیت خاص شما تولید می کند: def user_can_vote(user): return user.is_authenticated() and user.has_perm("polls.can_vote") @user_passes_test(user_can_vote, login_url="/login/") def vote(request): # Code here can assume a logged-in user with the correct permission. ... user_passes_test یک آرگومان الزامی دریافت می کند: یک قابل فراخوانی که یک شیء User دریافت کرده و در صورتی که کاربر اجازه ی تماشای صفحه را داشته باشد مقدار True بر می گرداند. توجه داشته باشید که user_passes_test به طور اتوماتیک تصدیق شدن کاربر را بررسی نمی کند؛ شما باید آن را برای خودتان انجام دهید. همچنین در این مثال آرگومان دوم (اختیاری) نشان داده شده است، که اجازه ی تعیین URL برای صفحه ی خودتان را می دهد (/accounts/login/ به طور پیشفرض). در صورتی که کاربر آزمون را نگذرانده باشد؛ سپس decorator، user_passes_test کاربر را به login_url تغییر مسیر خواهد داد. به دلیل آن که بررسی این که یک کاربر دارای یک حق دسترسی خاص است یا خیر یک وظیفه ی نسبتا مشترک می باشد، جنگو یک میانبر برای آن ارائه کرده است که یک decorator با نام permission_required() می باشد. در دست ترجمه/تالیف ...: from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url="/login/") def vote(request): # ... دقت داشته باشد که همچنین permission_required() یک پارامتر اختیاری login_url دریافت می کند؛ که این نیز به طور پیشفرض '/accounts/login/' می باشد. محدود کردن دسترسی برای view های جنریک یکی از سوالات تکراری پرسیده شده در لیست کاربران جنگو در مورد محدود کردن دسترسی برای یک view جنریک می باشد. برای جواب به این سوال؛ نیاز به نوشتن یک thin wrapper در اطراف view و اشاره URLconf به wrapper خود به جای خود generic view خواهید داشت: from django.contrib.auth.decorators import login_required from django.views.generic.date_based import object_detail @login_required def limited_object_detail(*args, **kwargs): return object_detail(*args, **kwargs) البته که می توانید، login_required را با هر decorator محدودیت دیگری جا به جا کنید. مدیریت کاربران، حق دسترسی ها و گروه ها ساده ترین را برای مدیریت سیستم auth تاکنون، از طریق رابط مدیر بوده است. سایت مدیر در مورد نحوه ی استفاده از سایت مدیر جنگو را جهت ویرایش کاربران و کنترل حق دسترسی آن ها بحث کرده است، و اغلب اوقات شما فقط از این رابط استفاده خواهید کرد. هر چند API های سطح پایین ای وجود دارند که شما هنگامی که نیاز به کنترل مستقل دارید از آن ها استفاده کنید، و این API ها را در بخش های بعدی توضیح داده ایم. ساختن کاربران ساختن کاربران با تابع کمکی create_user: >>> from django.contrib.auth.models import User >>> user = User.objects.create_user(username='john', ... email='jlennon@beatles.com', ... password='glass onion') در این نقطه، user یک رابط نمونه ی User حاضر برای ذخیره شدن در پایگاه داده (create_user() در واقع save() خودش را فراخوانی نمی کند) می باشد. همچنین می توانید قبل از ذخیره attribute های آن را تغییر دهید: >>> user.is_staff = True >>> user.save() تغییر رمزهای عبور می توان یک رمز عبور را با set_password() تغییر داد: >>> user = User.objects.get(username='john') >>> user.set_password('goo goo goo joob') >>> user.save() attribute، password را به طور مستقیم قرار ندهید، مگر اینکه کاملا بدانید که چه کار می کنید. رمز عبور در واقع به صورت یک salted hash دخیره شده و در نتیجه نمی تواند به طور مستقیم ویرایش شود. به طور رسمی تر، attribute، password از یک شیء User یک رشته در این قالب بندی می باشد: hashtype$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 کوچک برای اطلاعات الزامی کاربر و ساختن آن کاربران تهیه کرد. جنگو یک فرم داخلی ارائه کرده است که می توان برای این منظور از آن استفاده کرد، که در مثال زیر استفاده خواهیم کرد: from django import forms from django.contrib.auth.forms import UserCreationForm from django.http import HttpResponseRedirect from django.shortcuts import render_to_response def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return HttpResponseRedirect("/books/") else: form = UserCreationForm() return render_to_response("registration/register.html", { 'form': form, }) این فرم یک template با نام registration/register.html را فرض می کند. در اینجا یک مثال از template مورد نظر وجود دارد: {% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <form action="" method="post"> {{ form.as_p }} <input type="submit" value="Create the account"> </form> {% endblock %} استفاده از تصدیق داده در Template ها کاربر وارد شده ی فعلی و حق دسترسی های وی، زمانی که از RequestContext (آموزش template پیشرفته را نگاه کنید) استفاده می کنید در template context در دسترس می باشد. نکته : از نظر فنی، این متغیرها تنها در صورتی که شما از RequestContext استفاده کنید و تنظیم TEMPLATE_CONTEXT_PROCESSORS حاوی "django.core.context_processors.auth" باشد (که به طور پیشفرض این طور است) در template context در دسترس می باشند. بار دیگر برای اطلاعات بیشتر می توانید به آموزش template پیشرفته مراجعه کنید. هنگامی که از RequestContext استفاده می کنید، کاربر فعلی (که می تواند هم یک نمونه از User یا یک نمونه ی AnonymousUser باشد) در متغیر template {{ user }} ذخیره می شود {% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %} حق دسترسی های این کاربر در متغیر template {{ perms }} ذخیره شده اند. این یک پروکسی template‑friendly برای تعدادی از متدهای حق دسترسی می باشد که به طور خلاصه توضیح داده شده است. برای استفاده از شیء perms دو روش وجود دارد. می توان در صورتی که کاربر دارای هیچ حق دسترسی برای برخی برنامه های داده شده نداشته باشد، برای بررسی آن از چیزی شبیه به این {% if perms.polls %} استفاده کرد، یا می توان در صورتی که کاربر دارای حق دسترسی خاصی می باشد برای بررسی آن از چیزی شبیه به این {% if perms.polls.can_vote %} استفاده کرد. در نتیجه، می توان حق دسترسی ها را در عبارت template {% if %} بررسی کرد: {% if perms.polls %} <p>You have permission to do something in the polls app.</p> {% if perms.polls.can_vote %} <p>You can vote!</p> {% endif %} {% else %} <p>You don't have permission to do anything in the polls app.</p> {% endif %} حق دسترسی ها، گروه ها و پیام ها چند قسمت دیگر از فریم ورک authentication وجود دارد که تنها به طور روزنامه وار از کنار آن عبور کردیم. در ادامه نگاهی نزدیک تر به آن ها خواهیم داشت. حق دسترسی ها حق دسترسی ها روشی ساده برای "علامت گذاری" کاربران و گروه ها جهت نشان دادن این که کاربر یا گروه علامت گذاری شده دارای توانایی اجرای برخی کارها می باشد. حق دسترسی ها معمولا توسط سایت مدیر جنگو استفاده شده اند، ولی می توان به سادگی در کد خود نیز از آن ها استفاده کرد. سایت مدیر جنگو به صورتی که در زیر بیان شده است از حق دسترسی ها استفاده کرده است: ***دسترسی به view فرم "add"، و اضافه کردن یک شیء که محدود به کاربران با حق دسترسی add برای آن نوع از شیء. ***دسترسی به view لیست تغییر، view فرم "change"، و تغییر یک شیء که به کاربران با حق دسترسی change برای آن نوع شیء محدود شده است. ***دسترسی به حذف یک شیء محدود شده به کاربران با حق دسترسی delete برای آن نوع از شیء. حق دسترسی ها به صورت globally برای هر نوع از آجکت، نه هر نمونه ی خاص از شیء قرار داده شده اند. برای مثال، می توان گفت "Mary قادر است اخبار را تغییر دهد" ولی حق دسترسی ها اجازه نمی دهد که به عنوان مثال بگویید "Mary قادر است اخبار تغییر دهید، ولی تنها آنهایی را که خود او ساخته است" یا "Mary قادر است تنها اخباری را تغییر دهید که دارای یک وضعیت خاصی، انتشار یا ID خاص باشد." این ها سه حق دسترسی اساسی می باشد – اضافه کردن، تغییر دادن، و حذف کردن – که به طور خودکار برای هر مدل جنگو ایجاد شده اند. در پشت صحنه، این حق دسترسی ها هنگامی که شما دستور manage.py sycdb را اجرا می کنید درون جدول پایگاه داده با نام auth_permission اضافه می شوند. این حق دسترسی ها فرمی از "<app>.<action>_<object_name>" می باشند. بدین معنی که اگر دارای یک برنامه ی 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 ذخیره می کند: def create_playlist(request, songs): # Create the playlist with the given songs. # ... request.user.message_set.create( message="Your playlist was added successfully." ) return render_to_response("playlists/create.html", context_instance=RequestContext(request)) هنگامی که شما از RequestContext استفاده می کند، کاربر وارد شده ی فعلی و پیام او در template context به صورت متغیر template {{ message }} در دسترس هستند. در زیر یک مثال از کد template وجود دارد که پیام ها را نمایش می دهد: {% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} دقت داشته باشید که RequestContext در پشت صحنه get_and_delete_messages را فراخوانی می کند، بنابراین هر پیامی حذف شده خواهد بود حتی اگر آن ها را نمایش ندهید. در پایان، دقت داشته باشید که این فریم ورک پیام ها تنها با کاربران در پایگاده داده ی کاربر کار می کنند. برای ارسال پیام ها به کاربران anonymous، از فریم ورک session به طور مستقیم استفاده کنید. خسته نباشید دوستان عزیز . جلسه سیزدهم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . برگرفته از سایت oruji.org ترجمه از سایت www.djangobook.com
  6. این تاپیک پشتیبانی برای آموزش است فریم ورک جنگو ( Django ) چیست ؟ 31/04/96 16:19 31/04/96 16:19 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  7. با عرض سلام خدمت تمام پایتونی های عزیز . امیدوارم هرجا هستید موفق و پیروز باشید . در خدمت شما هستیم با مقاله جنگو چیست ؟ با ما همراه باشید . فریم ورک جنگو ( Django ) چیست ؟ در کل یک توضیح کلی درباره فریم ورک جنگو وجود دارد : جنگو ( Django ) یک چارچوب نرم‌افزاری تحت وب آزاد و متن‌باز است که به زبان پایتون نوشته شده است و از معماری MVC ( Model–view–controller) پیروی می‌کند. هدف اصلی جنگو ساخت آسان سایت‌های پیچیده و وابسته به دیتابیس است و بر پایهٔ قابلیت استفادهٔ مجدد و قابل اتصال بودن اجزای مختلف، توسعهٔ سریع و اصل خودت را تکرار نکن (DRY) طراحی شده است. جنگو سراسر از پایتون استفاده می‌کند، حتی برای تنظیمات، فایل‌ها و مدل‌های اطلاعات . تاثیر پایتون بر این فریم ورک کل این فریم ورک با استفاده از زبان پایتون پیاده سازی شده است . پس بسیاری از ویژگی های خود را از زبان پایتون به ارث برده است . با استفاده از این فریم ورک امکان ایجاد وب سایت هایی حرفه ای و پیچیده در کمترین زمان ممکنه و همچنین با در نظر گرفتن مورادی چون امنیت و سرعت و … فراهم می باشد . در ادامه برخی از ویژگی های قابل تاکید زبان پایتون که تاثیر مهمی بر این فریم ورک دارند مطرح می گردد . *** پایتون زبانی تفسیری بوده و برای اجرا نیازی به کامپایل ندارد . در برنامه نویسی وب با استفاده از این ویژگی بعد از تغییر کد یا ایجاد آن نتایج کار بلافاصله قابل مشاهده می باشد . *** انواع داده در پایتون داینامیک می باشد . پس شما نگرانی از تعریف نوع متغییر ندارید . *** سینتکس زبان پایتون کوتاه و در عین حال واضح و قابل فهم می باشد . این بدین معنی هست که برای انجام کار های مشابه کدی بسیار کمتر لازم می باشد . برای مثال معمولا هر خط پایتون معادل ۱۰ خط در جاوا (Java) می باشد !! *** پایتون روشهایی قدرتمند برای meta-programming در اختیار قرار می دهد . این ویژگی امکان ویرایش با اضافه کردن رفتار ها و توابع را با اشیا در زمان اجرای برنامه فراهم می کند . جدایی از ویژگی های فوق جنگو خود روشهایی برای افزایش سرعت و سهولت برنامه نویسی همزمان با رعایت کامل موارد امنیتی و کارایی را دارا می باشد . که در ادامه با برخی از این ویژگی ها بیشتر آشنا خواهیم شد . چرا شما به فریم ورک نیاز دارید؟ برای اینکه واقعا بفهمیم جنگو چیست و چه کاربردهایی داره باید دقت کنیم به سرورها . اولین چیز و مهمترین کار یک سرور این است که برای شما یک صفحه وب را نمایش بگذارد . شما یک صندوق پستی را تصور کنید که نامه ها را دریافت میکند و میخواند . این وظیفه یک سرور می باشد . بعد از خواندن نامه ها یکی یکی صفحاتی که درخواست شده برای نمایش را به نمایش میزاره . اما وقتی میخواهید چیزی را ارسال کنید، باید محتوایی داشته باشید. و جانگو چیزی است که به شما در ایجاد محتوا کمک می کند. چه اتفاقی می افتد وقتی کسی از یک سرور وب سایت شما درخواست می کند؟ هنگامی که یک درخواست به یک وب سرور می آید، به جنگو منتقل می شود که تلاش می کند تا آنچه را که واقعا درخواست شده است، بفهمد. ابتدا آدرس صفحه وب رو تلاش میکنه تا پیدا کنه . این قسمت توسط urlresolver Django انجام می شه . یک لیست از الگوها را می گیرد و تلاش می کند URL را مطابقت بده . جانگو الگوها را از بالا به پایین بررسی می کنه و اگر چیزی هماهنگ باشه با درخواست کاربر ، جانگو درخواست را به تابع مربوطه می فرسته (که آن تابع نمایش داده بشه ). شما دقیقا یک پستچی رو در نظر بگیرید که داره تو یه خیابون راه میره و طبق آدرس نامه ها داره نامه هارو توی خونه ها میندازه . یکی از کارای جنگو هم همینه. پس متوجه شدید که چگونه urlresolver کار می کنه! به نظرم همین توضیح کوتاه کفایت میکنه راجب جنگو در کل همین قدر بدونید که جنگو فریم ورکی و یا به زبان ساده تر یه کتابخونه ای پر از کد هست که قواعد استفاده خاص خودش رو داره و به ما کمک میکنه برای طراحی سایت با پایتون وقت کمتری رو هدر بدیم . وقتی به آموزش های پروژه محور جنگو در لینک زیر نگاه کنید کم کم متوجه خواهید شد که این فریم ورک چقدر کار مارو راحت تر میکنه برای طراحی سایت آموزش پروژه محور جنگو موفق و پیروز باشید . پایتونی ها
  8. این تاپیک پشتیبانی برای آموزش است آموزش ایجاد پنل مدیریت در جنگو ( جلسه ششم ) - بخش دوم 12/04/96 11:13 12/04/96 11:13 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.
  9. آموزش سایت مدیرت پیشفرض در جنگو ( بخش دوم ) صفحه ی فوق تمام کاربران موجود در پایگاه داده را نمایش می دهد، می توانید این را به صورت پرس وجو پایگاه داده یک نسخه ی زیبا شده از عبارت SELECT * FROM auth_user تصور کنید. در صورتیکه همراه با مثال های کتاب حرکت می کنید، تنها یک کاربر را مشاهده خواهید کرد، ولی هنگامی که کاربران بیشتری وجود داشته باشد، امکانات مفیدی از قبیل فیلتر کردن، چیدن و جستجوی بین آیتم ها را مشاهده خواهید کرد. امکان فیلتر کردن در سمت راست صفحه موجود می باشد، چیدمان از طریق کلیک کردن روی یک header ستون عملی می شود، و جعبه ی جستجو (search box) در قسمت بالا صفحه می باشد اجازه می دهد بر اساس نام کاربری به جستجو بپردازید. بر روی نام کاربری که ساخته اید کلیک کنید، فرم ویرایش برای کاربر را مشاهده خواهید کرد. صفحه ی فوق این اجازه را می دهد که خصوصیات کاربر را تغییر دهید، مانند نام و نام خانوادگی و حق دسترسی های گوناگون. (توجه داشته باشید که برای تغییر رمز عبور کاربر به جای ویرایش کد hash، می بایست بر روی "change password form" در زیر فیلد رمز عبور کلیک کرد.) نکته ی دیگر قابل توجه اینکه فیلدهای مختلف به شکل های مختلف پیاده سازی شده اند؛ به عنوان مثال، فیلد زمان و تاریخ دارای کنترل تقویم، فیلدهای Boolean دارای checkbox ها و فیلدهای حروف حاوی فیلد ساده ی مربوط به متن می باشند. می توان برای حذف کردن یک رکورد بر روی دکمه ی delete در قسمت پایین سمت چپ از فرم ویرایش کلیک کرد. بسته به شیء هایی که قصد حذف کردن آن را دارید، صفحه ی مربوط به تایید برای حذف رکورد نمایان خواهد شد، به عنوان مثال، در صورتیکه یک ناشر را حذف کنید، هر کتاب مربوط به ناشر نیز حذف خواهد شد! همچنین می توان با کلیک کردن بر روی "Add" درون ستون مناسب از صفحه ی خانگی مدیر یک رکورد را اضافه کرد. بعد از کلیک صفحه ای با فیلدهای خصوصیات خالی نمایان می شود که آماده برای پر شدن است. همچنین متوجه این موضوع خواهید شد که رابط مدیر همچنین معتبر بودن ورودی ها را برای شما کنترل می کند. یکی از فیلدهایی را که باید حتما پر شود را خالی بگذارید و یا یک مقدار نا معتبر درون یکی از فیلدها قرار دهید، سپس شما خطاهایی را هنگام ذخیره مانند شکل 5-6 مشاهده خواهید نمود. هنگامی که یک شیء موجود را ویرایش می کنید، متوجه یک لینک History در قسمت راست بالای صفحه خواهید شد. هر تغییری درون رابط مدیر اتفاق بیافتد در اینجا ثبت خواهد شد، و می توان با کیک کردن بر روی لینک History آن تغییر را بررسی کرد. اضافه کردن مدل ها به سایت مدیر یک بخش بسیار مهمی هنوز انجام نشده است. اجازه دهید مدل های خود را به سایت مدیر اضافه کنیم، بنابراین می توان شیء های درون جداول پایگاه داده ی مورد نظر را با این رابط عالی حذف، اضافه و تغییر داد. در این فصل نیز مثال books را دنبال خواهیم کرد که در آن سه مدل به نام های Publisher، َAuthor و book تعریف شده است. درون دایرکتوری books (mysite/books)، یک فایل با نام admin.py ایجاد کرده و کد زیر درون آن وارد کنید: from django.contrib import admin from mysite.books.models import Publisher, Author, Book admin.site.register(Publisher) admin.site.register(Author) admin.site.register(Book) کد فوق برای هر یک از مدل ها یک رابط به مدیر جنگو اضافه می کند. هنگامی که این کار انجام داده شد، به صفحه ی خانگی مدیر درون مرورگر خود رفته (http://127.0.0.1/admin/)، شما باید قسمت "Books" را با لینک های Authors، Books و Publishers مشاهده کنید. (برای موثر واقع شدن تغییرات می بایست سرور را متوقف کرده و دوباره اجرا runserver کنید) شما هم اکنون برای هر از این سه مدل یک رابط مدیر در حال کار خواهید داشت. بسیار ساده است! می توانید زمانی را برای اضافه کردن و تغییر رکوردها اختصاص دهید، تا داده هایی را درون پایگاه داده اضافه کنید. در صورتیکه مثال های آموزش مدل جنگو (ساختن شیء های Publisher) را دنبال کرده و آن ها را حذف نکرده باشید، آن رکوردهای ساخته شده در آموزش مدل جنگو را در صفحه ی لیست تغییرات publisher مشاهده خواهید کرد. یکی از خصوصیات با ارزشی که می توان در اینجا ذکر کرد، کنترل سایت مدیر برای کلیدهای خارجی و رابطه های چند به چند می باشد، هر دوی این حالت ها در مدل Book وجود دارند، برای یادآوری کد زیر مدل Book می باشد: class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title درون صفحه ی "Add book" مدیر (http://127.0.0.1:8000/admin/books/book/add/)، Publisher (یک ForeignKey) با یک جعبه ی انتخاب (select box) نمایش داده شده است، و فیلد Authors (یک ManyToManyField) با یک جعبه ی چند انتخابی (multiple-select box) نمایش داده شده است. هر دو فیلد فوق دارای یک علامت سبز رنگ جمع در جلوی خود می باشند که برای اضافه کردن رکوردهای مرتبط به آن ها می باشد. برای مثال، در صورتیکه شما بر روی آن علامت سبز رنگ جلوی فیلد "Publisher" کلیک کنید، یک پنجره ی pop-up برای اضافه کردن یک ناشر باز می باشد. بعد از آنکه یک ناشر در پنجره ی باز شده ساخته شود، صفحه ی "Add book" با جدید ترین ناشر ساخته شده به روزرسانی می شود. نحوه ی کار سایت مدیر در پشت صحنه، سایت مدیر چه طور کار می کند؟ نحوه ی کار آن بسیار ساده می باشد. هنگامی که جنگو URLconf را هنگام اجرای سرور در urls.py بارگذاری می کند، عبارت admin.autodiscover() که در درون این برای فعال کردن سایت مدیر قرار داده شده است اجرا می شود. این تابع درون تنظیم INSTALLED_APPS به دنبال فایلی به نام admin.py که درون هر یک از ایتم ها جستجو می کند. در صورتیکه admin.py درون یکی از app های داده شده وجود داشته باشد، کد درون آن فایل اجرا می شود. در فایل admin.py درون app مورد نظر یعنی books، فراخوانی هر admin.site.register() به سادگی مدل داده شده را درون سایت مدیر عضو می کند. سایت مدیر تنها یک رابط ویرایش/تغییر برای مدل های عضو شده را نمایش می دهد. همانطور که مشاهده شد، سایت مدیر به صورت خودکار Users و Groups را نمایش می دهد که درون app خود یعنی django.contrib.auth می باشند که این app شامل فایل admin.py خود و Users و Groups درون آن می باشد. app های دیگر django.contrib، مانند django.contrib.redirects، نیز خودشان را درون سایت مدیر اضافه می کنند. از این گذشته، سایت مدیر جنگو تنها یک application جنگو با مدل های خود، view ها و URLpattern ها می باشد. با استفاده از URLconf آن را به برنامه ی خود اضافه می کنید، تنها درون view ها خود از آن استفاده می کنید. می توان template ها، view ها و URLpattern های آن را با رفتن به مسیر django.contrib/admin مشاهده و بازرسی کنید ولی تحریک به تغییر چیزی به طور مستقیم در آنجا نشوید چرا که امکانات استفاده ی غیر مستقیم زیادی برای شما وجود دارد. ایجاد فیلدهای اختیاری بعد از آنکه کمی با سایت مدیر آشنا شده و از آن استفاده کردید، ممکن است متوجه محدودیت هایی شده باشید، مانند اینکه هر فیلد باید پر شده باشد، در حالیکه در بسیاری از موارد شما می خواهید پر کردن بعضی از فیلدها اختیاری باشد، به عنوان مثال، می خواهیم فیلد email مدل Author اختیاری باشد یعنی بتوان فیلد را خالی گذاشت. در دنیای واقعی، شما ممکن است هیچ آدرس الکترونیکی نداشته باشید. برای تعیین کردن فیلد email اختیاری، مدل Book را ویرایش می کنیم (این مدل درون فایل mysite/books/models.py می باشد.) برای این کار به سادگی blank=True را به مانند مثال زیر به فیلد email اضافه می کنیم: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True) حرکت فوق به جنگو می گوید که برای آدرس پست الکترونیک نویسندگان می توان مقدار خالی را نیز در نظر گرفت. به صورت پیش فرض، تمام فیلدها blank=False می باشند، بدین معنی که مقدار خالی را نمی توان برای آن ها در نظر گرفت. تا حالا، بجز متد __unicode__()، مدل های ما به صورت جداول پایگاه داده ما بکار می رفتند یعنی مدل ها عبارات پایتونی معادل عبارات CREATE TABE در SQL بودند. با اضافه کردن blank=True، ما شروع به گسترش دادن مدل فراتر از تعریف ساده ی مشابه با جدول پایگاه داده کرده ایم. حال، کلاس مدل در حال تبدیل شدن به یک مجموعه ی غنی از دانش درباره ی آنچه که شیء های Author هستند و می توانند انجام دهند، می باشد. نه تنها فیلد email یک ستون VARCHAR در پایگاه داده می باشد، بلکه همچنین می دانیم که یک فیلد اختیاری در سایت مدیر جنگو نیز می باشد. این حالت بدین معنی می باشد که فیلد مورد نظر اختیاری می باشد. حالا می توان نویسندگان را بدون نیاز به تهیه ی آدرس الکترونیک اضافه کرد؛ در صورتیکه فرم مورد نظر را با فیلد خالی آدرس الکترونیک Submit کنید دیگر هیچ پیام "This field is required" با رنگ قرمز مشاهده نخواهید کرد. هنگامی که blank=True اضافه شد، فرم ویرایش "Add author" را دوباره بارگذاری کنید (http://127.0.0.1:8000/admin/books/author/add/)، متوجه خواهید شد که نام فیلد "Email" دیگر به صورت تو پر و پر رنگ نمی باشد. ایجاد فیلد های از نوع تاریخ و عددی به صورت اختیاری نکته ی مهم در رابطه با blank=True این است که انجام این حرکت بر روی فیلدهای از نوع عددی و تاریخ نیازمند مقداری توضیح و پیش زمینه می باشد. SQL برای تعیین مقدار خالی دارای روش خاص خودش می باشد – یک مقدار ویژه به نام NULL. NULL می تواند به معنای ناشناخته، نامعتبر یا ... معنی شود. در SQL یک مقدار NULL با یک مقدار رشته ی خالی متفاوت می باشد، درست مثال شیء None در پایتون که با یک رشته ی خالی پایتون ("") متفاوت می باشد. این بدان معنی است که ممکن است یک فیلد حرفی (مانند یک ستون VARCHAR) به طور همزمان هم حاوی مقدار NULL و هم حاوی یک رشته ی خالی باشد. این موضوع می تواند موجب ابهام و گیجی نا خواسته شود. "چرا این رکورد دارای مقدار NULL ولی آن رکورد دیگر دارای یک رشته ی خالی است؟ ایا تفاوتی وجود دارد، یا فقط داده به صورت نا سازگار وارد شده است؟" و: "چطور می توان تمام رکوردهایی که دارای مقدار خالی می باشند را بدست آورد – ایا می توان هر دو رکوردهای NULL و رشته ی خالی را جستجو کرد، یا یک از آن ها را با مقدار رشته ی خالی انتخاب کرد؟" برای کمک به خلاص شدن از این قبیل ابهامات، جنگو به صورت خودکار عبارت های CREATE TABLE (که در آموزش مدل جنگو توضیح داده شده اند) اضافه و برای هر ستون مقدار NOT NULL را در نظر می گیرد. برای مثال، کد زیر عبارت تولید شده برای مدل Author در آموزش مدل جنگو می باشد: CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL ) ; در اغلب موارد، این حالت پیشفرض برای برنامه مطلوب بوده و شما را از درد سر های تناقض و نا هماهنگی های داده حفظ می کند. و برای بقیه ی قسمت های جنگو به خوبی کار می کند، مانند سایت مدیر جنگو، که در زمان خالی گذاشتن یک فیلد یک رشته ی خالی (نه مقدار NULL) وارد می کند. ولی برای نوع ستون هایی که رشته ی خالی را به عنوان مقدار معتبر قبول نمی کنند – ازقبیل تاریخ ها، زمان ها، و اعداد، یک استثناء وجود دارد. در صورتیکه سعی کنید یک رشته ی خالی برای ستون های ذکر شده وارد کنید، بسته به نوع پایگاه داده ی شما احتمالا خطای پایگاه داده رخ خواهد. در این حالت، NULL تنها راه برای تعیین مقدار خالی می باشد. در مدل های جنگو، می توان مقدار NULL را با اضافه کردن null=True به یک فیلد تعیین کرد. در صورتیکه بخواهید اجازه دهید مقادیر خالی در فیلد تاریخ (مانند DateField، TimeField، DateTimeField) یا فیلد عددی (مانند IntegerField، DecimalField، FloatField) قرار گیرند نیاز است از null=True و blank=True با هم استفاده کنید. اجازه دهید مدل Book را برای اینکه بتوان فیلد publication_date را خالی گذاشت تغییر دهیم: class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True) اضافه کردن null=True پیچیده تر از اضافه کردن blank=True می باشد، زیرا null=True از نظر معنایی پایگاه داده را تغییر می دهد – در این حالت عبارت CREATE TABLE تغییر می کند بدین شکل که NOT NULL از فیلد publication_date حذف می شود. برای کامل کردن این تغییر، نیاز است پایگاه داده به روز رسانی شود. به دلایلی، جنگو تلاشی برای تغییرات خودکار در شمای پایگاه داده نمی کند، بنابراین مسئولیت اجرای یک عبارت ALTER TABLE هر زمان که که تغییری در مدل ایجاد شد، به عهده خودتان می باشد. می توان از manage.py dbshell برای این منظور استفاده کرد. در کد زیر نحوه ی حذف NOT NULL در این مورد خاص را مشاهده می کنید: ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL; (توجه داشته باشید که کد فوق برای پایگاه داده ی PostgreSQL می باشد.) توضیح و بحث عمیق تر در مورد این تغییرات در آموزش مدل پیشرفته بحث شده است. به سایت مدیر بر می گردیم، اکنون فرم ویرایش "Add book" اجازه می دهد تا مقدار خالی را برای publication date قرار دهیم. سفارشی کردن نام فیلدها در فرم های ویرایش سایت مدیر، نام هر فیلد از روی نام مدل فیلد تولید می شود. الگوریتم آن بسیار ساده می باشد: جنگو تنها خط های تیره (_) را با فاصله ها جایگزین کرده و حروف اول را تبدیل به حروف بزرگ می کند، بنابراین، برای مثال، فیلد publication_date مدل Book دارای نام "Publication date" خواهد بود. اگر چه نام فیلدها درون مدل به طور زیبایی درون سایت مدیر نیز تغییر می کنند، ولی در برخی موارد ممکن است بخواهید آن نام ها را خودتان تعیین کنید. با استفاده از verbose_name می توان این کار را انجام داد. برای مثال، در زیر نحوه ی تغییر نام فیلد Author.email به "e-mail" نشان داده شده است: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name='e-mail') تغییر فوق را ایجاد کرده و صفحه را دوباره بارگذاری کنید، شما باید فیلد مورد نظر با نام جدید مشاهده کنید. توجه داشته باشید، در صورتیکه حرف اول نامی که انتخاب می کند حرف بزرگ نباشد جنگو به طور خودکار آن را تبدیل به حروف بزرگ می کند. در پایان، توجه داشته باشید که می توان verbos_name را به صوت یک آرگومان موضعی نیز ارسال کرد، کد زیر برابر با مثال قبلی می باشد: class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField('e-mail', blank=True) حرکت فوق برای فیلدهای ManyToManyField و ForeignKey کار نخواهد کرد، زیرا در این فیلدها باید اولین آرگومان یک کلاس مدل باشد. در این موارد، می بایست از verbose_name به صورت واضح استفاده کرد. کلاس های ModelAdmin مرسوم نغییراتی که تاکنون اعمال شد – blank=True، null=True و verbose_name – تغییراتی در سطح مدل بوده اند، نه تغییرات در سطح مدیر. خیلی پیش می آید که تغییرات مورد نیاز سایت مدیر در یک بخش از مدل انجام پذیرد و استفاده شوند، هیچ چیز تعیین شده ای برای مدیر وجود ندارد. گذشته از این، سایت مدیر جنگو امکانات و اختیارات قدترمندی برای سفارشی کردن نحوه ی کار سایت مدیر برای مدل خاص ارائه می کند. سفارشی کردن لیست های تغییر اجازه دهید از طریق تعیین کردن فیلدها که درون لیست تغییر برای مدل Author قرار دارند وارد مسائل سفارشی سازی مدیر شویم. به طور پیشفرض، لیست تغییر نتیجه ی __unicode__() برای هر شیء را نمایش می دهد. در آموزش مدل جنگو، متد __uncode__() برای شیء های Author جهت نمایش نام و فامیلی با هم تعریف شد. class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name='e-mail') def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) در نتیجه لیست تغییر برای شیء های Author نام و نام خانوادگی را با هم نمایش می دهد، همانطور که در شکل 8-6 مشاهده می کنید. می توان این رفتار پیشفرض را با اضافه کردن تعدادی از فیلدها برای تغییر نمایش لیست بهبود بخشید. برای مثال، برای دیدن آدرس الکترونیک هر نویسنده در این لیست، و همچنین اضافه کردن امکان چیدمان بر اساس نام و نام خانوادگی می توان یک کلاس ModelAdmin برای مدل Author تعریف کرد. این کلاس کلید سفارشی سازی مدیر، و یکی از اولیه ترین چیزهایی است که اجازه می دهد لیست فیلدها را جهت نمایش در صفحه ی لیست تغییر تعیین کنید. فایل admin.py را جهت این تغییرات ویرایش کنید: from django.contrib import admin from mysite.books.models import Publisher, Author, Book class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book) در زیر مثال فوق، توضیح داده شده است: ** کلاس AuthorAdmin ساخته شده است. کلاس AuthorAdmin که از کلاس پدر خود یعنی django.admin.ModelAdmin ارث بری کرده است پیکربندی های تعیین شده برای مدل مدیر را نگه می دارد. تنها یک پیکربندی – list_display، انجام شده است که نام فیلدهایی را که قرار است درون لیست تغییر نمایش داده شوند، درون یک تاپل قرار گرفته اند. البته نام فیلدها باید در مدل وجود داشته باشند. ** فراخوانی admin.site.register با اضافه کردن AuthorAdmin بعد از Author تغییر کرده است که می توان آنرا بدین صورت خواند: "مدل Author با امکانات AuthorAdmin عضو شده است." تابع admin.site.register() کلاس فرزند ModelAdmin را به صورت اختیاری به عنوان دومین آرگومان دریافت می کند. در صورتیکه دومین آرگومان را تعیین نکنید (همانطور در مورد Publisher و Book صدق می کند)، جنگو از اختیار پیشفرض مدیر برای آن مدل استفاده می کند. با تغییرات فوق، لیست تغییر نویسنده را دوباره بارگذاری کنید، شما حالا در این صفحه سه ستون را مشاهده خواهید کرد – نام، نام خانوادگی و آدرس الکترونیک. علاوه بر آن، هر کدام از این ستون ها قابلیت چیدمان بر اساس سرتیتر ستون را با کلیک کردن رو آن خواهند داشت. در قدم بعدی اجازه دهید یک نوار جستجوی ساده اضافه کنیم. مانند زیر search_fields را به AuthorAdmin اضافه کنید: class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') search_fields = ('first_name', 'last_name') صفحه را درون مرورگر دوباره بارگذاری کنید، شما باید یک نوار جستجو را در قسمت بالای صفحه مشاهده کنید (شکل 10-6). در کد فوق تنها به صفحه ی تغییر گفته شده است که یک نوار جستجو برای جستجو بر حسب فیلدهای نام و نام خانوادگی ایجاد کند. همانطور که ممکن است کاربر انتظار داشته باشد، جستجو به حروف کوچک و بزرگ حساس نمی باشد و درون هر دو فیلد صورت می گیرد، بنابراین برای رشته ی "bar" هم نویسنده ای که نام وی Barney بوده را خواهد یافت و هم نویسنده ای که نام خانوادگی وی Hobarson می باشد را خواهد یافت. در قدم بعدی، اجازه دهید تعدادی فیلتر برای صفحه ی لیست تغییر مدل Book اضافه کنیم: from django.contrib import admin from mysite.books.models import Publisher, Author, Book class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') search_fields = ('first_name', 'last_name') class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) در کد فوق با آیتم های زیادی سر و کار داریم، در کد فوق یک کلاس BookAdmin از نوع ModelAdmin به طور جداگانه ساخته شده است. ابتدا، یک list_display تنها برای ایجاد لیست زیباتر تعریف شده است. سپس، از list_filter استفاده شده است، که فیلدهایی برای استفاده را درون تاپل قرار می دهد و در سمت راست صفحه ی تغییر لیست نمایش داده می شود. برای فیلدهای تاریخ، جنگو میانبرهایی را برای فیلتر لیست می کند "Today"، "Past 7 days"، "This month"و "This year". list_filter فقط محدود به DateField نمی باشد بلکه با داده های نوع دیگر نیز کار می کند. (سعی کنید با فیلدهای BooleanField و foreignKey هم کار کنید) فیلترها تا زمانیکه حداقل دو مقدار برای انتخاب فرم وجود داشته باشد نمایش داده می شوند. روش دیگربرای ارائه ی فیلترهای تاریخ استفاده از date_hierarcy مانند کد زیر می باشد: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' با اضافه کرد date_hierarchy صفحه ی لیست تغییر یک نوار تاریخ در بالای لیست ایجاد می شود، که در شکل 12-6 مشاهده می کنید، که با یک لیست از سال های در دسترس شروع می کند سپس می توان برای ماه حتی نمایان شدن روز بر روی آن کلیک کرد. توجه داشته باشید که date_hierarchy یک رشته دریافت می کند، نه یک تاپل، زیرا تنها یک فیلد تاریخ می تواند در آن استفاده شود. در پایان، اجازه دهید ترتیب چیدمان پیشفرض لیست را به طوری تغییر دهیم که کتاب ها در صفحه ی لیست تغییر همیشه بر اساس تاریخ به صورت نزولی قرار بگیرند. به طور پیشفرض، لیست تغییر شیء ها را بر اساس کلاس Meta مربوط به مدل مودر نظر چیده می شوند (که در آموزش مدل جنگو به آن اشاره شده است.) – ولی شما این مقدار مرتب کردن را تعیین نکرده اید، پس ترتیب تعریف نشده است. class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) این اختیار چیدمان مدیر دقیقا به صورت چیدمان در کلاس Meta کار می کند، با این تفاوت که آن تنها از اولین فیلد نام در لیست استفاده می کند. تنها یک لیست یا تاپل از نام فیلدها به آن ارسال کنید و یک علامت (-) در ابتدای فیلد برای چیدمان نزولی آن قرار دهید. صفحه ی لیست تغییر را برای مشاهده ی حرکت فوق دوباره بارگذاری کنید. توجه داشته باشید که تیتر "Publication date" حالا شامل یک فلش کوچک می باشد که به نحوه ی چیدمان اشاره می کند. اختیارات اصلی لیست تغییر در اینجا توضیح داده شد. با استفاده از این اختیارات می توانید با قدرت تر کار کنید، با استفاده از تنها چند خط می توانید از یک رابط آماده و پر قدرت استفاده کنید. سفارشی کردن فرم های ویرایش همانطور که لیست تغییر می تواند سفارشی شده باشد، فرم های ویرایش نیز می توانند با روش های مختلف سفارشی شوند. ابتدا اجازه دهید روش چیده شدن فیلدها را سفارشی کنیم. به طور پیشفرض، ترتیب فیلدها در یک فرم ویرایش مطابق با ترتیبی است که آنها در مدل تعریف شده اند. می توان با استفاده از اختیار fields در کلاس فرزند ModelAdmin آن را تغییر داد: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) fields = ('title', 'authors', 'publisher', 'publication_date') بعد از تغییر فوق، فرم ویرایش برای کتاب ها از ترتیب داده شده برای فیلدها استفاده خواهد کرد. این که نویسنده بعد از عنوان کتاب قرار بگیرد کمی طبیعی تر می باشد. البته، ترتیب فیلد باید بسته به به اطلاعات ورودی جریان کار باشد. هر فرم متفاوت است. نکته ی دیگر مفید در اختیار fields این است که اجازه می دهد بعضی فیلد ها را از قرار داشتن در فرم ویرایش کلا محروم کرد. تنها کافیست، فیلدهایی که نمی خواهید در فرم باشند را درون تاپل قرار ندهید. ممکن است از این امکان هنگامی که کاربران مدیر تنها برای ویرایش بعضی از بخش ها مورد اعتماد هستند استفاده کنید. برای مثال، در پایگاده داده ی کتاب ما، فیلد publication_date از حالت قابل ویرایش بودن خارج و مخفی شده است: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) fields = ('title', 'authors', 'publisher') نتیجتا، فرم ویرایش برای کتاب ها هیچ روشی برای تعیین publication date ارائه نمی کند. این حرکت می تواند در صورتیکه یک ویرایشگر ترجیح می دهد نویسندگانش تاریخ را عقب نزنند مفید باشد. هنگامی که یک کاربر از این فرم برای اضافه کردن یک کتاب جدید استفاده می کند، جنگو به آسانی مقدار publication_date را None در نظر می گیرد – بنابراین اطمینان حاصل کنید که فیلد مورد نظر دارای null=True می باشد. استفاده ی عمومی دیگر فرم ویرایش سفارشی برای کار با فیلدهای چند به چند می باشد. همانطور که در فرم ویرایش برای کتاب ها مشاهده کردید، سایت مدیر هر ManyToManyField را به صورت یک جعبه ی چند انتخابه نمایش می دهد، که منطقی ترین راه برای ورودی HTML برای استفاده می باشد – ولی استفاده از جعبه های چند انتخابه می تواند کمی سخت باشد. در صورتیکه که می خواهید چند ایتم را انتخاب کنید، باید برای انجام چنین کاری کلید control و یا در Mac کلید command را نگه پایین نگه داشته و آن ها را انتخاب کنید. سایت مدیر برای این مشکل یک توضیحی را در زیر این فیلد قرار داده است، ولی هنوز هم دارای مشکلاتی می باشد مخصوصا زمانی که فیلد شما حاوی صدها انتخاب باشد. راه حل سایت مدیر filter_horizontal می باشد. آنرا به BookAdmin اضافه کرده و نتیجه را مشاهده کنید: class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',) فرم ویرایش برای کتاب ها را دوباره بارگذاری کنید، بخش "Authors" حالا از یک رابط جاوا اسکریپت کاراتر استفاده می کند که می توان به راحتی درون انتخاب ها جستجو کردن و هر آنچه را که درون "Available authors" انتخاب می کنید را به درون "Chosen authors" و بر عکس ارسال می کند. اکیدا توصیه می شود که برای فیلدهای ManyToManyField که بیشتر از 10 ایتم دارند از filter_horizontal استفاده کنید. روش فوق بسیار آسانتر از استفاده از جعبه ی چند انتخابه می باشد. توجه داشته باشید که همچنین می توان برای چندین فیلد نیز از filter_horizontal استفاده کرد – تنها کافیست نام هر کدام را درون تاپل قرار دهید. کلاس های ModelAdmin همچنین از filter_vertical که تنها در فیلدهای ManyToManyField و نه ForeignKey کار می کند نیز پشتیبانی می کنند، به طور پیشفرض، سایت مدیر برای فیلدهای ForeignKey به سادگی از <select> استفاده می کند، ولی همانطور که برای ManyToManyField نیز از <select> استفاده می شود، ممکن است شما مایل نباشید برای نمایش تمام شیء ها به صورت منوی باز شونده در پایین بار اضافی ای را متحمل شوید. برای مثال در صورتیکه پایگاه داده ی کتاب حاوی صدها ناشر شده باشد، فرم "Add book" مدتی برای بارگذاری زمان خواهد برد، زیرا باید تمام ناشران را برای نمایش در <select> بارگذاری کند. برای حل این مشکل می بایست از raw_id_fields استفاده کرد. نام فیلد ForeignKey را درون این تاپل قرار دهید، فیلدهای مذکور به جای <select> به صورت یک متن ساده نمایش داده می شوند (<input type="text") class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',) raw_id_fields = ('publisher',) درون جعبه ی ورودی چه چیزی وارد می کنید؟ ID مربوط به پایگاه داده ی ناشر. نمی توان به طور عادی ID هر شخص را به خاطر سپرد، به همین دلیل یک شکل ذره بین نیز در شکل فوق مشاهده می کنید که می توان با کلیک بر روی آن توسط یک پنجره ی pop-up ناشران را که می خواهید اضافه کنید. کاربران، گروه ها و حق دسترسی ها بدلیل اینکه با یک superuser وارد می شویم، می توان برای ساختن، ویرایش و حذف کردن هر شیء اقدام کرد. به طور طبیعی، محیط های مختلف نیازمند حق دسترسی های متفاوتی می باشد – بدین صورت نباید باشد که هر شخصی بتواند superuser باشد. سایت مدیر جنگو یک سیستم حق دسترسی را بکاربرده است که می توان برای دادن دسترسی به کاربران خاص برای بخش هایی ار رابط که مورد نیاز است استفاده کرد. همانطور که در اوایل فصل مشاهده کردیم می توان کاربران و حق دسترسی ها را از طریق رابط مدیر همانند دیگر شیء ها ویرایش کرد. شیء های کاربر دارای فیلد های استاندارد نام کاربری، رمز عبور، آدرس پست الکترونیک و نام واقعی به همراه یک مجموعه از فیلد ها که اختیارات کاربر را در درون رابط مدیر تعیین می کند، می باشد. در ابتدا، سه گزینه ی boolean وجود دارد: ** گزینه ی "active" کنترل می کند که ایا کاربر فعال است یا خیر. در صورتیکه این گزینه تیک نخورده باشد و کاربر سعی کند وارد شود، به وی اجازه ی ورود داده نمی شود حتی اگر رمز عبور وی معتبر باشد. ** گزینه ی "staff" کنترل می کند که ایا کاربر اجازه ی ورود به رابط مدیر را دارد یا خیر (مانند اینکه ایا کاربر مورد نظر به عنوان یکی از کارمندان شرکت در نظر گرفته شده است یا خیر). ** گزینه ی "superuser" به کاربر دسترسی کامل را برای اضافه، ساختن و حذف کردن هر ایتم در رابط مدیر را می دهد. در صورتیکه یک کاربر این گزینه را داشته باشد، در آن صورت تمام قوانین دسترسی برای آن کاربر نقض خواهد شد. کاربران مدیر "Normal"، کاربر superuser نیستند بلکه دارای دسترسی مدیر از طریق دسترسی های اختصاص داده شده به آنها می باشند که می تواند شامل محدودیت های دسترسی باشد. هر شیء قابل ویرایش از طریق رابط مدیر (مانند کتاب ها، نویسندگان، ناشران) دارای سه حق دسترسی می باشد: حق دسترسی ساخت، حق دسترسی ویرایش و حق دسترسی حذف. اختصاص دادن حق دسترسی ها به یک کاربر، محدوده اختیارات آن کاربر را برای ایجاد، حذف و یا اعمال تغییرات تعیین می کند. زمانیکه یک کاربر می سازید، کاربر ساخته شده هیچ حق دسترسی ای ندارد، و برای دادن حق دسترسی های خاص حاضر می باشد. برای مثال، شما می توانید حق دسترسی ای برای تغیییر ناشران به آن بدهید، ولی حق حذف کردن ناشران را به کاربر ندهید. توجه داشته باشید که این حق دسترسی ها برای هر مدل تعریف شده می باشند و نه برای هر شیء، بنابراین شما می توانید اینطور بگویید که "john می تواند روی هر کتاب تغییر ایجاد کند"، ولی نمی توان گفت "john می تواند بر روی هر کتابی که با Apress منتشر شده است تغییرات ایجاد کند." قابلیت دوم، حق دسترسی های هر شیء می باشد که کمی پیچیده تر بوده و خارج از حوصله ی این کتاب می باشد ولی در مستندات جنگو درباره ی آن صحبت شده است. نکته : دسترسی به ویرایش کاربران و حق دسترسی ها نیز از طریق سیستم حق دسترسی کنترل شده است. در صورتیکه به شخصی حق دسترسی برای ویرایش کاربران را بدهید، آن شخص قادر به ویرایش حق دسترسی های خود می باشد که این ممکن است آن چیزی نباشد که شما می خواهید! دادن حق دسترسی کاربر برای ویرایش کاربران اساسا تغییر کاربر به superuser می باشد. همچنین می توان کاربران را به گروه ها اختصاص داد. گروه مجموع ای از حق دسترسی هایی می باشد که برای تمام اعضای آن گروه بکار برده می شود. گروه ها برای دادن حق دسترسی های یکسان به زیرمجموعه ای از کاربران مفید می باشد. چه زمانی و به چه دلیل از رابط مدیر استفاده می شود و چه زمانی استفاده نمی شود بعد از آشنایی و کار در این فصل، شما باید یک ایده ی خوب در مورد نحوه ی استفاده از سایت مدیر جنگو داشته باشید. ولی می خواهیم در اینجا چند موضوع را باز کنید و آن هم این است که چرا و کی باید از سایت مدیر جنگو استفاده کرد و چه زمانی نباید از آن استفاده کرد. سایت مدیر جنگو زمانی بسیار پر ارزش می شود که کاربران غیر فنی به وارد کردن داده نیاز داشته باشند، در روزنامه جایی که جنگو اولین توسعه را در آنجا پیدا کرد یک گزارش ویژه در مورد کیفیت آب شهری که چیزی شبیه به مراحل زیر بود، ارائه شده بود: ** گزارشگر مسئولیت داشت برای پروژه با یکی از توسعه دهندگان ملاقات کند و داده ی در دسترس را توضیح دهد. ** توسعه دهنده مدل های جنگو را برای متناسب بودن با آن داده ها طراحی کرده سپس سایت مدیر را برای گزارشگر باز می کرد. ** گزارشگر سایت مدیر را بررسی کرده و کمبود ها و فیلدهای غیر اصلی را به آن اشاره می کرد، توسعه دهنده مکررا مدل ها را تغییر می داد. ** هنگامی که مدل ها مورد توافق قرار گرفت، گزارشگر شروع به وارد کردن داده با استفاده از سایت مدیر می کرد. هم زمان، برنامه نویس می تواند روی توسعه ی عمومی قسمت های views و template ها کار کند (قسمت جالب کار). ورای وظایف واضح برای وارد کردن داده، سایت مدیر در موارد دیگر که در زیر آمده است نیز مفید می باشد: ** بررسی کردن داده ی مدل ها: هنگامی که تعدادی مدل تعریف می کنید، فراخوانی آن ها در رابط مدیر و وارد کردن برخی داده های ساختگی می تواند کاملا مفید باشد. در برخی موارد، می تواند باعث آشکار شدن اشتباهات data-modeling و یا مشکلات دیگر با مدل های شما شود. ** مدیریت داده ی بدست آمده: برای برنامه هایی که بر روی آمدن داده از منابع بیرونی تکیه دارند، سایت مدیر روشی ساده برای بازرسی یا ویرایش این داده ها به شما می دهد. ممکن است آن را کم قدرت تصور کنید، ولی بسیار مناسب تر از امکان خط فرمان پایگاه داده ی شما می باشد. ** مدیریت داده ی app ها به صورت سریع: می توان برای ساخت مدیریت داده ی بسیار سبک app از سایت مدیر استفاده کرد. در صورتیکه که تنها می خواهید برای نیازهای خود چیزی بسازید، نه برای مصارف عمومی، سایت مدیر می تواند راه مناسبی باشد. یک نکته ی پایانی اینکه می خواهیم واضح باشد: سایت مدیر برای همه وقت و همه چیز نیست. سایت مدیر برای یک رابط عمومی بودن در نظر گرفته نشده است، همچنین برای چیدمان و جستجوی پیچیده ی داده ها نیز در نظر گرفته نشده است. همانطور پیش تر در این فصل گفته شد، سایت مدیر برای مدیران مورد اعتماد سایت می باشد. خسته نباشید دوستان عزیز . جلسه ششم بخش دوم کتاب جنگو به پایان رسید . امیدوارم که موفق باشید . ترجمه از سایت www.djangobook.com
  10. آموزش مدل ها در جنگو در آموزش view و urlconf جنگو، اصول ساختن وب سایت های پویا در جنگو (Django) شرح داده شد به عبارت دیگر اصول راه اندازی view ها و URLconf ها را بررسی کردیم. همانطور که توضیح داده شد، view مسئولیت انجام برخی منطق های اختیاری و برگرداندن یک پاسخ را به عهده دارد. در یکی از مثال ها، منطق اختیاری، محاسبه کردن زمان و تاریخ فعلی بود. در برنامه های وب پیشرفته، منطق اختیاری اغلب درگیر تعامل با یک پایگاه داده می باشد. در پشت صحنه، یک وب سایت پایگاه داده محور، وب سایت به یک سرور پایگاه داده متصل می شود، برخی داده ها را از درون آن بازیابی می کند و آن داده ها را درون یک صفحه ی وب نمایش می دهد. یک سایت همچنین ممکن است روش هایی برای بازدیدکنندگان سایت تهیه کند تا بتوانند محتویات پایگاه داده را برای خودشان افزایش دهند. بسیاری از وب سایت های پیچیده یک سری ترکیب از دو موضوع را تهیه می کنند. به عنوان نمونه Amazon.com، یک مثال عالی برای یک سایت پایگاه داده محور می باشد. هر صفحه ی محصول اساسا یک پرس وجو (Query) قالب بندی شده به صورت HTML، درون پایگاه داده ی محصول آمازون می باشد و هنگامی که شما یک بررسی مشتری را ارسال می کنید، آن بررسی ها درون پایگاه داده درج می شوند. جنگو برای ساختن وب سایت های پایگاه داده محور مناسب می باشد، زیرا جنگو (Django) با استفاده از پایتون (Python) ابزار قدرتمندی را برای اجرای پرس و جوهای پایگاه داده ایجاد کرده است. در این فصل به عملکرد لایه ی پایگاه داده ی جنگو پرداخته می شود. (نکته: بهتر است با تئوری های پایگاه داده ی relational و SQL تاحدودی آشنا باشید. آشنا سازی برای این مفاهیم خارج از حوصله ی این کتاب می باشد، ولی حتی اگر شما یک کاربر جدید پایگاه داده می باشید باز هم این کتاب را بخوانید. ممکن است بتوانید مفاهیم و اصول اولیه پایگاه داده را درک کنید.) روش اولیه ی اجرای پرس و جوهای پایگاه داده در view ها همانطور که در آموزش view و urlconf جنگو توضیح داده شد، روش اولیه ی برای تولید خروجی درون یک view (از طریق نوشتن کد به طور مستقیم درون view) برای بازیابی داده از یک پایگاه داده درون یک view می باشد. این روش ساده می باشد زیرا فقط نیاز به استفاده از کتابخانه ی پایتون برای اجرای یک پرس و جوی SQL و کار با نتایج بدست آمده می باشد. در مثال view زیر، از کتابخانه ی MySQLdb (قابل دسترس از طریق آدرس http://wwww.djangoproject.com/r/python-mysql/ استفاده شده است. برای اتصال به پایگاه داده MySQL، بازیابی برخی رکوردها و پر کردن آن برای یک template جهت نمایش به صورت یک صفحه ی وب: from django.shortcuts import render_to_response import MySQLdb def book_list(request): db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost') cursor = db.cursor() cursor.execute('SELECT name FROM books ORDER BY name') names = [row[0] for row in cursor.fetchall()] db.close() return render_to_response('book_list.html', {'names': names}) روش فوق می تواند جوابگوی نیاز ما باشد، اما معایبی دارد که در ادامه به آنها اشاره شده است: ** پارامترهای connection پایگاه داده به صورت مستقیم درون کد قرار گرفته اند. ایده آل آن است که، این پارامترها درون پیکربندی جنگو ذخیره شوند. ** تعداد زیادی کد تکراری مانند ساختن connection، ساختن cursor، اجرا کردن جملات و بستن connection در مثال فوق وجود دارد. برای بهره وری مناسب تر ایده آل آن است که تنها نیازمند تعیین نتایج مورد نیاز برنامه باشیم. ** روش فوق برنامه نویس را به MySQL گره می زند. در صورتیکه برنامه نویس بخواهد پایگاه داده ی خود را تغییر دهد، به عنوان مثال، اگر بخواهد بجای MySQL از PostgreSQL استفاده کند، می بایست از یک adapter پایگاه داده ی متفاوت (psycopg به جای MySQLdb)، پارامتر های connection دیگر و همچنین بسته به ذات پایگاه داده، از جملات SQL متفاوت دیگر استفاده کرد. ولی همانطور که قبلا هم اشاره کردیم ایده آل آن است که، در صورت تغییر پایگاه داده، برنامه نویس دیگر نگران تغییر جملات و ... نباشد. همانطور که انتظار داریم، لایه ی پایگاه داده ی جنگو راه حلی برای حل این مشکلات ارائه کرده است. مثال زیر کد بازنویسی شده ی قبلی را با استفاده از API پایگاه داده ی جنگو نشان می دهد: from django.shortcuts import render_to_response from mysite.books.models import Book def book_list(request): books = Book.objects.order_by('name') return render_to_response('book_list.html', {'books': books}) کد فوق کمی بعد در همین فصل شرح داده خواهد شد. تنها برای حالا کافی است دید کلی نسبت به این API پیدا کنید. الگوی توسعه ی MTV (یا MVC) تاکنون در این کتاب، همواره به درون کد تمرکز شده است، اجازه دهید برای لحظاتی به بررسی یک برنامه ی وب پایگاه داده محور جنگو بپردازیم. همانطور که در فصل گذشته بیان شد، جنگو به نحوی طراحی شده است که برنامه نویس را تشویق کند تا اصول loose coupling و جداسازی بین قسمت های یک برنامه را رعایت کند. در صورتیکه این فلسفه را دنبال کنید، ایجاد تغییرات در یک قسمت مشخص از برنامه هیچ تاثیری بر روی قسمت های دیگر برنامه نخواهد داشت. برای نمونه در توابع view، اهمیت جداسازی منطق برنامه از قسمت ظاهر برنامه با استفاده از template توضیح داده شد. در لایه ی پایگاه داده، همین فلسفه را برای دسترسی به داده بکار خواهیم برد. این سه قسمت با یکدیگر (دسترسی به داده، منطق برنامه و ظاهر برنامه) یک مفهومی را در بر دارد که الگوی Model-View-Controller (MVC) از معماری نرم افزار نامیده می شود. در این الگو، Model به لایه ی دسترسی داده اشاره می کند، View به بخشی از سیستم که به انتخاب آنچه را که باید نمایش دهد و اینکه چطور باید آن را نمایش دهد اشاره می کند و Controller به بخشی از سیستم که بسته به ورودی کاربر تصمیم می گیرد کدام view باید استفاده شود اشاره دارد. چرا از شکل مخفف استفاده می شود؟ هدف از تعریف کردن الگوها بن شکل مخفف مانند MVC، غالبا برای ساده تر کردن صحبت و مکاتبه بین توسعه دهندگان می باشد. بجای آنکه به همکارتان بگویید؛ "بیا یک دسترسی داده بسازیم، سپس بیا جدا کنیم لایه ها را برای کنترل نمایش آن ها، و بعد از آن بیا یک لایه در میان آن قرار دهیم که آن را تنظیم و مرتب کند." می توانید از این خصوصیت استفاده کرده و بگویید؛ "بیا در اینجا از الگوی MVC استفاده کنیم." جنگو الگوی MVC را به اندازه ی کافی از نزدیک دنبال می کند به طوری که می توان آن را یک فریم ورک MVC نامید. در زیر نحوه ی تقسیم بندی M، V و C به درون جنگو را مشاهده می کنید: M، بخش دسترسی به داده می باشد که توسط لایه پایگاه داده جنگو کنترل می شود و در این فصل توضیح داده خواهد شد. V، انتخاب داده برای نمایش و نحوه ی نمایش آن، که با view ها و template ها کنترل شده است. C، بخشی که توسط خود فریم ورک کنترل می شود، بدینصورت که با توجه به URLconf مشخص شده تابع پایتون مناسب برای URL داده شده را فراخوانی می کند. به دلیل آنکه C ("Controler") توسط خود فریم فرک کنترل می شود و بیشترین تحرکات در models ،template و view ها اتفاق می افتد، جنگو به صورت یک فریم ورک MTV نیز شناخته می شود. در الگوی توسعه ی MTV: M مخفف "Model"، لایه ی دسترسی داده می باشد. این لایه حاوی همه اطلاعات درباره ی داده یعنی نحوه ی دسترسی به داده، نحوه ی معتبر ساختن داده، رفتارهایی که داده دارد و ارتباط بین داده می باشد. T مخفف "Template"، لایه ی نمایش می باشد. این لایه حاوی مسائلی درباره ی نحوه ی نمایش می باشد: این که اطلاعات باید به چه نحوی روی یک صفحه ی وب و یا سند دیگر نمایش داده شود. V مخفف "view"، لایه ی منطق برنامه می باشد. این لایه حاوی منطق برنامه برای دسترسی به model و ارائه ی به template مناسب می باشد. می توانید این لایه را به صورت پلی بین model ها و template ها تصور کنید. در صورتیکه با فریم ورک های دیگر توسعه ی وب مانند Ruby on Rails کار کرده باشید، ممکن است تصور کنید view های جنگو نقش "Controller" را بازی می کنند و template ها هم در حکم "views" می باشند. همچین تفسیری درباره ی MVC مایه ی تاسف می باشد. تفسیر جنگو نسبت به MVC، این است که "view" ارائه دادن داده به کاربر را شرح می دهد، این لزوما تنها ظاهر داده نمی باشد، بلکه منظور داده ی ارائه شده می باشد. در مقابل، Ruby on Rails و فریم ورک های مانند آن پیشنهاد می کنند که تصمیم گیری درباره ی اینکه کدام داده باید به کاربر ارائه داده شود از جمله وظایف controller می باشد، در حالیکه، ظاهر داده به طور موکد بر عهده ی view می باشد. پیکربندی پایگاه داده با در نظر گرفتن تمام فلسفه های گفته شد، اجازه دهید کار با لایه ی پایگاه داده ی جنگو را شروع کنیم. در ابتدا، نیاز به تعدادی پیکر بندی اولیه می باشد، در واقع بایستی به جنگو گفته شود که از کدام سرور پایگاه داده استفاده خواهد کرد و به چه صورت به آن متصل خواهد شد. فرض بر این است که سرور پایگاه داده نصب و فعال شده است، و همچنین یک پایگاه داده درون آن ساخته شده است (به عنوان مثال، با استفاده از دستور CREATE DATABASE STATEMENT). در صورتیکه از SQLite استفاده می کنید، به هیچگونه نصب نیاز نخواهد بود، زیر SQLite از فایل های مستقلی روی سیستم برای ذخیره ی داده های خود استفاده می کند. درست مانند TEMPLATE_DIRS که در فصل گذشته استفاده شد، پیکربندی مربوط به پایگاه داده نیز درون فایل settings می باشد: DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = '' توجه داشته باشید برای استفاده از هر کدام نیاز به دانلود و نصب adapter مناسب خود می باشد. هر کدام از پایگاه های داده ی فوق به صورت آزاد درون وب در دسترس می باشند؛ تنها کافیست به سایت مورد نظر که درون جدول 1-5 آمده است مراجعه کرده آن را دانلود کنید. در صورتیکه با سیستم عامل لینوکس کار می کنید، package-management سیستم شما ممکن است package های مناسبی را ارئه دهد. (برای مثال به دنبال package هایی با نام python-postgresql یا python‑psycopg بگردید.) مثال: DATABASE_ENGINE = 'postgresql_psycopg2' DATABASE_NAME نام پایگاه داده ی شما را به جنگو اطلاع می دهد: DATABASE_NAME = 'mydb' در صورتیکه از SQLite استفاده می کنید، آدرس کامل پایگاه داده ی خود را درون این تنظیم قرار دهید: DATABASE_NAME = '/home/django/mydata.db' DATABASE_USER نام کاربری که برای اتصال به پایگاه داده لازم است را به جنگو اطلاع می دهد. DATABASE_PASSWORD رمز عبوری که برای اتصال به پایگاه داده لازم است را به جنگو اطلاع می دهد. در صورتیکه از SQLite استفاده می کنید یا اینکه می خواهید پسوردی در نظر نگیرید، می توانید آن را خالی بگذارید. DATABASE_HOST host مورد نظری که برای اتصال به پایگاه داده لازم است را به جنگو اطلاع می دهد. در صورتیکه پایگاه داده ی شما همانند نصب جنگو در یک رایانه می باشد (مانند localhost) آن را خالی بگذارید. همچنین اگر از SQLite استفاده می کنید نیز می توانید آن را خالی بگذارید. MySQL در اینجا یک مورد خاص می باشد. در صورتیکه مقدار این تنظیم (DATABASE_HOST) با علامت (/) شروع شود و شما از MySQL استفاده کنید، MySQL از طریق یک Unix socket برای socket تعیین شده متصل خواهد شد: DATABASE_HOST = '/var/run/mysql' هنگامی که تنظیمات فوق را انجام داده و فایل settings.py ذخیره کردید، ایده ی خوب است که پیکربندی انجام شده را مورد آزمون قرار دهید. برای انجام این کار، مانند فصل گذشته درون دایرکتوری پروژه ی mysite دستور python manage.py shell را اجرا کنید. (همانطور که در فصل گذشته اشاره شد دستور manage.py shell روشی برای اجرای interpreter پایتون، با تنظیمات صحیح فعال شده ی جنگو می باشد. در این مورد این ضروری است، زیرا نیاز است جنگو تنظیمات فایل را به منظور به دست آوردن اطلاعات connection پایگاه داده برای استفاده بداند.) درون خط فرمان، دستورات زیر را به منظور آزمودن پیکربندی پایگاه داده تایپ کنید: >>> from django.db import connection >>> cursor = connection.cursor() ایجاد اولین App اکنون که مشخص شد اتصال به پایگاه داده با موفقیت انجام شده است، زمان آن رسیده است که یک app برای جنگو ایجاد کنیم، app یک بسته از کد جنگو می باشد که model ها و view ها در آن قرار می گیرد، که همگی با هم درون یک پکیج پایتون قرار گرفته اند و یک برنامه (application) جنگو را نمایش می دهند. در شروع کار جنگو نحوه ی ساختن یک پروژه ی جنگو توضیح داده شد، اکنون فرق بین یک پروژه با یک app(اپلیکیشن)در چیست؟ تفاوت آن در پیکربندی در مقابل کد است: یک پروژه یک نمونه از یک مجموعه ی خاص از app های جنگو به همراه پیکربندی برای آن ها می باشد. از نظر فنی، تنها فرق اساسی که در پروژه وجود دارد ایجاد یک فایل settings می باشد، که اطلاعات connection پایگاه داده، لیست app های نصب شده، تنظیم TEMPLATE_DIRS و غیره را تعریف می کند. app یک دسته ی قابل حمل از عمکرد جنگو می باشد، که معمولا شامل model ها و view ها می باشد که با هم در یک پکیج پایتون قرار دارند. برای مثال، جنگو به همراه تعدادی از app ها، از قبیل سیستم کامنت و رابط خودکار مدیر، ارائه شده است، نکته ی قابل توجه درباره ی این app ها این است که، آن ها قابل حمل می باشند بدین معنی که می تواند آن ها را در چندین پروژه دیگر نیز دوباره استفاد کرد. مقدار بسیار کمی قوانین سخت و محکم نسبت به وفق دادن کد جنگو با این طرح وجود دارد. در صورتیکه بخواهید یک وب سایت ساده بسازید، ممکن است تنها از یک app استفاده کنید. و اگر می خواهید یک وب سایت بزرگ و پیچیده با چندین قسمت مرتبط به هم مانند یک سیستم تجارت الکترونیک و یک تابلوی پیام (message board) بسازید، احتمالا این قسمت ها را درون app ها جدا قرار خواهید داد، به طوری که قادر باشید در صورت نیاز آن ها را به صورت منحصر به فرد در اینده استفاده کنید. در واقع، لزوما همیشه نیاز به ساختن app ها در پروژه نیست، به عنوان نمونه توابع view که تاکنون ساخته شده است حاکی این موضوع می باشد. در آن مواردبه سادگی یک فایل views.py ساخته شده و با توابع view مورد نیاز درون آن پر می شد، و URLconf نیز به آن توابع اشاره می کرد. نیازی به app ها نبود. با این وجود، در یک صورت باید از app ها استفاده شود: در صورتیکه شما از لایه ی پایگاه داده ی (models) جنگو استفاده می کنید، باید یک app جنگو بسازید. model ها باید درون app ها ایجاد شوند. در نتیجه، به منظور ساختن model ها، نیزا به ساختن یک app جدید می باشد. درون دایرکتوری پروژه ی mysite، دستور زیر را برای ساختن app مورد نظر با نام books اجرا کنید: python manage.py startapp books دستور فوق هیچ خروجی ای را تولید نمی کند، در عوض دایرکتوری books را درون دایرکتوری mysite می سازد. در زیر محتویات این دایرکتوری را مشاهده می کند: books/ __init__.py models.py tests.py views.py این فایل ها حاوی model ها و view ها برای این app می باشد. درون ویرایشگر متن مورد علاقه ی خود نگاهی به فایل های models.py و views.py بیاندازید. جفت فایل ها خالی هستند، به غیر از کامنت ها و یک import که درون فایل models.py قرار دارد. تعریف Model ها در پایتون همانطور که پیش تر در این فصل توضیح داده شد، "M" در "MTV" مخفف "Model" می باشد. model جنگو توصیف و شرح داده در پایگاه داده ی شما می باشد، که به صورت کد پایتون نمایش داده می شود. model لایه ی داده ی شما می باشد معادل عبارات SQL مانند CREATE TABLE با این تفاوت که این عبارات بجای SQL درون پایتون می باشند و همچنین آن ها شامل مواردی بیشتر از تعریف ستون های پایگاه داده می باشند. جنگو برای اجرای کد های SQL در پشت صحنه و برگرداندن ساختارهای داده ی پایتون مناسب از model ها استفاده می کند که از طریق آن ها ردیف های جداول پایگاه داده را نمایش می دهد. جنگو همچنین از model ها برای نمایش مفاهیم سطح بالا که SQL لزوما قادر به کنترل آن ها نمی باشد نیز استفاده می شود. در صورتیکه با پایگاه های داده آشنا می باشید، این تفکر برای شما ایجاد می شود که "آیا تعریف کردن data model ها بجای SQL درون پایتون کار زائد و اضافه ای نیست؟" جنگو به چندین دلیل به این روش عمل می کند: ** درون گرایی (استفاده از کد های SQL) مستلزم بار اضافی بوده و همچنین ناقص می باشد. به منظور تهیه ی API ها برای دسترسی به داده، جنگو به طریقی نیاز به شناختن لایه ی پایگاه داده دارد، و دو روش برای انجام آن وجود دارد. روش اول به طور واضح توضیح دادن داده در پایتون می باشد، و روش دوم تعیین data model ها درون پایگاه داده در هنگام اجرا می باشد. روش دوم تمیز تر به نظر می رسد، زیرا داده های جداول شما در یک جا می باشند، ولی این روش دارای مشکلاتی می باشد. اول اینکه تعیین داده های درون پایگاه داده در زمان اجرا بدیهی است که نیازمند یک بار اضافه خواهد بود. در صورتیکه فریم ورک در هر زمان بخواهد درون پایگاه داده اعمالی را انجام دهد خود به خود یک درخواست را پردازش کرده است، و یا حتی، در زمان اولیه وب سرور ممکن است یک بار اضافی در سطح غیر قابل قبول را متحمل شود. دوم آنکه برخی از پایگاه های داده، به ویژه نسخه های قدیمی MySQL، metadata مناسبی را به طور دقیق ذخیره نمی کنند. ** کد نویسی در پایتون لذت بخش می باشد، و نگه داشتن همه چیز در محدوده ی کد پایتون باعث می شود تعداد باری که شما را مجبور کند محیط کد نویسی را تغییر دهد کم می کند. این موضوع یعنی نگه داشتن برنامه نویس در یک محیط کد نویسی بهره وری و راندمان را تا حد امکان افزایش می دهد. نوشتن کد SQL و سپس پایتون و دوباره SQL کاری ذهنیت برنامه نویس را مختل خواهد کرد. ** داشتن data model های ذخیره شده به صورت کد بجای استفاده مستقیم از پایگاه داده، کنترل model هار بسیار آسان تر خواهد کرد. با این روش می توان به سادگی رد تغییرات در لایه های داده را دنبال کرد. ** SQL تنها اجازه ی استفاده از داده های قطعی موجود می دهد، اغلب سیستم های پایگاه داده، به عنوان مثال؛ داده های تخصصی برای نمایش دادن آدرس های email یا URL را تهیه نمی کنند. در صورتیکه model های جنگو این کار را انجام می دهند. مزیت داده های نوع سطح بالا بهره وری بیشتر آن ها و قابلیت دوباره استفاده شدن آن ها می باشد. ** SQL در پایگاه های داده ی مختلف یکجور نبوده و ناسازگار می باشد. بدین معنی که جملات و عبارت SQL برای مثال join ها و یا ... در پایگاه های داده ی مختلف مانند MySQL، PostgreSQL و یا SQLite ممکن است متفاوت از یکدیگر پیاده سازی شوند و هرکدام به روش خود آن را پیاده سازی کنند. تنها ایراد در این روش (استفاده از data model پایتون) این است که کدهای پایتون حالت همزمانی و موازات را به طور واقعی با پایگاه داده ندارند. در صورتیکه در model جنگو تغییر ایجاد کنید، نیاز می باشد که همان تغییرات را نیز درون پایگاه داده انجام دهید تا پایگاه داده با model شما سازگاری خود را حفظ کند. در ادامه ی این فصل استراتژی هایی توضیح داده خواهد شد که از طریق آن ها خواهید توانست این مشکلات را کنترل کنید. اولین Model شما برای داشتن مثالی که هم در این فصل و فصل بعدی بتوان از آن استفاده کرد و آن را پیشرفت داد، بر روی لایه ی داده ی book/author/publisher تمرکز خواهد شد. از این مثال به دلیل اینکه دارای رابطه های شناخته شده ای بین books، authors و publishers می باشد، و داده های استفاده شده داده های مقدماتی استفاده شده در SQL می باشد. داده های استفاده شده با خصوصیات زیر فرض شده اند: * یک نویسنده دارای یک نام، یک فامیلی و یک نشانی پست الکترونیک می باشد. * یک ناشر دارای یک نام، یک نشانی کوچه، یک شهر، یک استان، یک کشور و یک وب سایت می باشد. * یک کتاب دارای یک عنوان و یک تاریخ انتشار می باشد. همچنین هر کتاب دارای یک یا بیشتر نویسنده (رابطه ی چند به چند با authors) و یک ناشر (رابطه ی یک به چند به publishers) می باشد. اولین قدم برای استفاده کردن لایه ی پایگاه داده با جنگو، بیان آن به صورت کد پایتون می باشد. درون فایل models.py که با استفاده از دستور startapp ساخته شده است، کد زیر را وارد کنید: from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() اجازه دهید کد فوق را به طور اجمالی مورد بررسی قرار دهیم. اولین نکته ی قابل توجه این است که هر model به صورت یک کلاس پایتون نمایش داده شده است و هر model کلاس فرزند از کلاس پدر django.db.models.Model می باشد. کلاس پدر Model، حاوی تمام ابزار آلات ضروری برای ساختن شیء هایی می باشد که توانایی تعامل با یک پایگاه داده را داشته باشند. مشاهده می کنید که تعریف فیلدها به چه اندازه مختصر و ساده می باشند. باور کنید یا نه، این تمام کدی می باشد که برای داشتن دسترسی داده ی اولیه با جنگو لازم است. هر model عموما با یک جدول پایگاه داده و هر attribute در یک model با یک ستون جدول در پایگاه داده مطابق می باشد، نام attribute مطابق با نام ستون، و نوع فیلد (مانند CharField) مطابق با نوع ستون (مانند varchar) می باشد. برای مثال مدل Publisher برابر با جدول زیر می باشد (با فرض عبارت CREATE TABLE درون پایگاه داده ی PostgreSQL): CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(30) NOT NULL, "address" varchar(50) NOT NULL, "city" varchar(60) NOT NULL, "state_province" varchar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL ); در واقع، جنگو می تواند عبارت CREATE TABLE را به صورت خودکار تولید کند که در کد فوق مشاهده کردید. در این قانون که برای هر جدول پایگاه داده، یک کلاس در نظر گرفته می شود، در مورد رابطه های چند به چند یک استثنا وجود دارد. در مثال مدل های فوق، Book دارای یک فیلد چند به چند با نام authors می باشد. این فیلد مشخص می کند که هر کتاب دارای یک یا چند نویسنده می باشد، در صورتیکه جدول پایگاه داده Book دارای ستونی به نام authors نمی باشد. در عوض، جنگو یک جدول اضافه می سازد (یک "join table" چند به چند) که رابطه بین کتاب ها و نویسندگان را کنترل می کند. در پایان، توجه داشته باشید که به طور صریح برای هر یک از مدل ها یک کلید اصلی (primary key) تعریف نشده است. جنگو به صورت خودکار برای هر مدل یک فیلد auto_incremen integer primary key ایجاد می کند که id نام دارد. هر مدل جنگو لازم است یک ستون primary key داشته باشد. نصب کردن Model کد مربوط به مدل ها نوشته شد، حالا اجازه دهید جدول ها را درون پایگاه داده نیز ایجاد کنیم. به منظور انجام این کار؛ قدم اول این است که مدل های ساخته شده درون پروژه جنگو را فعال کنیم. برای انجام این کار باید app مورد نظر یعنی books را به لیست "installed apps" درون فایل settings اضافه کنیم. فایل settings.py را دوباره باز کرده، و تنظیم INSTALLED_APPS را پیدا کنید. INSTALLED_APPS به جنگو می گوید که کدام app ها برای پروژه مورد نظر فعال هستند. به طور پیشفرض، این تنظیم به این شکل خواهد بود: INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', ) به طور موفت با گذاشتن علامت (#) در ابتدای هر کدام از رشته ها آن ها را کامنت کنید. (این رشته ها موارد کامنت شده ای برای راحتی کار می باشند که در فصل های بعدی آن ها را فعال کرده و در مورد آن ها بحث خواهیم کرد.) همچنین تنظیمات مربوط به MIDDLEWARE_CLASSES را نیز کامنت کنید؛ مقادیر پیشفرض در MIDDLEWARE_CLASSES به برخی از app هایی که ما آن ها را کامنت کردیم وابسته می باشند. سپس 'mysite.books'را به لیست INSTALLED_APPS اضافه کنید، در پایان تنظیم انجام داده شده چیزی شبیه به مثال زیر خواهد بود: MIDDLEWARE_CLASSES = ( # 'django.middleware.common.CommonMiddleware', # 'django.contrib.sessions.middleware.SessionMiddleware', # 'django.contrib.auth.middleware.AuthenticationMiddleware', ) INSTALLED_APPS = ( # 'django.contrib.auth', # 'django.contrib.contenttypes', # 'django.contrib.sessions', # 'django.contrib.sites', 'mysite.books', ) (همانطور که در فصل گذشته، هنگام تنظیم TEMPLATE_DIRS گفته شد، لازم است حتما یک علمت کاما (,) در انتهای mysite.books قرار دهید، زیرا mysite.books در اینجا یک تک المان تاپل پایتون می باشد، اتفاقا، نویسندگان این کتاب ترجیح می دهند بعد از هر المان تاپل، بدون در نظر گرفتن این که یک تک المان است یا خیر، یک کاما در انتهای آن قرار میدهند. این کار باعث می شود از فراموش کردن قرار دادن کاما در انتهای المان های تک تاپل جلوگیری شود، و گذاشتن کامای اضافه هیچ مشکلی به وجود نخواهد آورد.) 'mysite.books' به app ای که با کار می کنیم اشاره می کند. هر app ای که در تنظیم INSTALLED_APPS قرار دارد با آدرس کامل پایتون آن نمایش داده می شود (مسیر پکیج ها با نقطه جدا شده و به پکیج app هدایت می شوند.) اکنون که app مورد نظر در فایل settings فعال شد، می توانیم جداول پایگاه داده را درون پایگاه داده ایجاد کنیم. ابتدا، اجازه دهید با دستور زیر از معتبر بودن کد های برنامه اطمینان حاصل کنیم: python manage.py validate دستور validate بررسی می کند که ایا کدها و منطق مدل های نوشته صحیح می باشد یا خیر. در صورتیکه همه چیز درست باشد، شما پیام message 0 errors found را مشاهده خواهید کرد. در غیر اینصورت، اطمینان حاصل کنید کدهای مدل شما صحیح می باشد. خطای خروجی، اطلاعات مفیدی درباره ی ایراد موجود در کد، در اختیار شما می گذارد. هر زمان که تصور کردید مشکلاتی درون مدل ها وجود دارد، دستور python manage validate را اجرا کنید. دستور فوق به شما کمک می کند تا تمام مشکلات موجود در مدل را بر طرف کنید. در صورتیکه کد مربوط به مدل شما معتبر می باشد، دستور زیر را برای تولید عبارت های CREATE TABE مربوط به مدل ها در app مورد نظر (books) وارد کنید: python manage.py sqlall books در دستور فوق، books نام app می باشد. این نام همان چیزی است که شما با اجرای دستور manage.py startapp ایجاد کرده اید. هنگامی که شما دستور فوق manage.py sqlall books را اجرا می کنید چیزی شبیه به این را در خروجی مشاهده خواهید کرد: BEGIN; CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(30) NOT NULL, "address" varchar(50) NOT NULL, "city" varchar(60) NOT NULL, "state_province" varchar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL ) ; CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL ) ; CREATE TABLE "books_book" ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED, "publication_date" date NOT NULL ) ; CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY, "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED, "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("book_id", "author_id") ) ; CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id"); COMMIT; به نکات زیر توجه کنید: ** نام جدول ها به طور خودکار با ترکیب نام app (books) و نام مدل (publisher، book و author) به صورت حروف کوچک تولید می شود. ** همانطور که پیش تر توضیح داده شد، جنگو برای هر جدول به طور خودکار یک کلید اصلی (با نام id) اضافه می کند. ** رابطه ی کلید خارجی با عبارت REFERENCES، صریح و روشن ساخته شده است. ** عبارت CREATE TABLE برای پایگاه داده ای که شما استفاده می کنید مناسب خواهد بود، بنابراین تعیین نوع فیلد مانند auto_increment (MySQL)، serial (PostgreSQL)، یا integer primary key (SQLite) به طور خودکار توسط جنگو انجام خواهد شد. همچنین در مورد کتیشن برای نام ستون ها (مانند استفاده از دابل کتیشن یا تک کتیشن) نیز این موضوع صدق می کند. مثال فوق خروجی برای PostgreSQL می باشد. دستور sqlall، در حقیقت جداول پایگاه داده را تولید نمی کند و به عبارت دیگر پایگاه داده نیز لمس نمی کند، تنها خروجی تولید شده توسط جنگو را به زبان SQL مورد نظر نشان می دهد بنابراین شما می توانید آنچه را که جنگو درون پایگاه داده می خواهد اجرا کند را مشاهده کنید. در صورت تمایل، می توانید این خروجی SQL را درون پایگاه داده ی کلاینت کپی کنید، یا با استفاده از علامت (|) یونیکس آن را به صورت مستقیم ارسال کنید (مانند python manage.py sqlall books | psql mydb). با این وجود، جنگو روشی ساده را برای ارسال خروجی تولید شده به پایگاه داده ارائه کرده است: دستور syncdb: python manage.py syncdb بعد از اجرای دستور فوق، خروجی شبیه به مثال زیر مشاهده خواهید کرد: Creating table books_publisher Creating table books_author Creating table books_book Installing index for books.Book model دستور syncdb یک هماهنگی ساده بین مدل ها و پایگاه داده می باشد. دستور فوق تمام مدل های موجود در هر app را درون تنظیم INSTALLED_APPS بررسی می کند، و در صورتیکه جدول مناسب برای مدل های مورد نظر وجود نداشته باشد آن ها را ایجاد می کند. توجه داشته باشید که syncdb تغییرات بوجود آمده در مدل ها و یا حذف مدل ها را با پایگاه داده هماهنگ نمی کند؛ در صورتیکه شما یک تغییر را در مدل ایجاد کرده و یا مدلی را حذف کنید، هنگامی که بخواهید پایگاه داده را با این تغییرات به روز رسانی کنید، دستور syncdb نمی تواند این کار را انجام دهد. (در انتهای همین فصل این موضوع بحث خواهد شد.) در صورتیکه که دستور python manage.py syncdb را دوباره اجرا کنید، اتفاقی رخ نخواهد داد، زیرا هیچ مدل به app مورد نظر (books) اضافه نشده است و یا هیچ app ای به تنظیم INSTALLED_APPS اضافه نشده است. بنابراین اجرای دستور python manage.py syncdb همواره مشکلی بوجود نخواهد آورد. در صورتیکه کنجکاو هستید، می توانید برای لحظاتی به درون خط فرمان پایگاه داده رفته جداول ایجاد شده درون پایگاه داده ی خود را مشاهده کنید. می توانید به صورت دستی دستوراتی را که می خوهید درون خط فرمان کلاینت اجرا کنید (مانند psql برای PostrgreSQL) یا اینکه می توانید دستور python manage.py dbshell، را اجرا کنید، و بسته به تنظیم DATABASE_SERVER، خواهید فهمید کدام خط فرمان اجرا می شود. شیوه ی دوم تقریبا همیشه مناسب تر می باشد. اصول اولیه ی دسترسی به داده هنگامی که شما یک مدل ساخته می شود، جنگو به طور خودکار یک API سطح بالا پایتون را برای کار با آن مدل ها ایجاد می کند. دستور python manage.py shell را اجرا کرده و کدهای زیر را امتحان کنید: >>> from books.models import Publisher >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p1.save() >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.', ... city='Cambridge', state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> p2.save() >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Publisher object>, <Publisher: Publisher object>] چند خط کد فوق کار زیادی را انجام می دهند. نکات برجسته در کد فوق: ** ابتدا، مدل Publisher به import شده است. با این کار می توان با جدول پایگاه داده که حاوی اطلاعات ناشران می باشد در ارتباط بود. ** یک شیء Publisher با مقدار دهی مقدار دهی اولیه ی آن با مقادیری برای هر فیلد (name، address و غیره ...) ساخته شده است. ** برای ذخیره ی شیء درون پایگاه داده، متد save() آن فراخوانی شده است. در پشت صحنه، جنگو عبارت INSERT برای SQL را در اینجا اجرا می کند. ** برای بازیابی اطلاعات ناشران از پایگاه داده، از attribute مورد نظر Publisher.objects برای بدست آوردن مجموعه تمام ناشران استفاده شده است. لیست تمام شیء های Publisher با استفاده از عبارت Publisher.objects.all() واکشی شده است. در پشت صحنه جنگو یک عبارت SELECT را به صورت SQL در اینجا اجرا می کند. نکته ای با اهمیتی که در این مورد واضح و یا روشن به نظر نمی رسد اینکه، هنگامی که شما با استفاده از API مدل جنگو در حال ساختن شیء ها می باشید، جنگو تا وقتی که متد save() را فراخوانی نکنید شیء ها را درون پایگاه داده ذخیره نمی کند. p1 = Publisher(...) # At this point, p1 is not saved to the database yet! p1.save() # Now it is. در صورتیکه می خواهید یک شیء ساخته و آن را درون پایگاه داده با یک حرکت ذخیره کنید، می توانید از متد objects.create() استفاده کنید. مثال زیر با مثال قبلی برابر است: >>> p1 = Publisher.objects.create(name='Apress', ... address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p2 = Publisher.objects.create(name="O'Reilly", ... address='10 Fawcett St.', city='Cambridge', ... state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> publisher_list = Publisher.objects.all() >>> publisher_list به طور طبیعی می توانید کارهای بسیار زیادی را به استفاده از API پایگاه داده ی جنگو انجام دهید، اما در ابتدا با کارهای کوچک بسنده می کنیم. ‫اضافه كردن نمايش رشته اي براي مدل‬ هنگامی که لیستی از ناشران را چاپ می کنیم، خروجی مفیدی نمایش داده نمی شود و بخش های شیء به هیچ وجه قابل تشخیص نیستند: [<Publisher: Publisher object>, <Publisher: Publisher object>] می توان این مشکل را به راحتی با اضافه کردن متد __unicode__() به کلاس Publisher حل کرد. متد __unicode__() به پایتون می گوید که یک شیء را به چه شکل در خروجی نمایش دهد. می توانید نحوه ی استفاده از آن را در عمل با اضافه کردن یک متد __unicode__() درون سه مدل فوق مشاهده کنید: from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): return self.name class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title همانطور که مشاهده می کنید، متد __unicode__() می تواند هر آنچه را که برای برای نمایش یک شیء نیاز است انجام دهید. در مثال فوق متدهای __unicode__() برای Publisher و Book به سادگی نام و عنوان شیء را به ترتیب برمی گردانند، ولی متد __unicode__() برای Author کمی پیچیده تر از بقیه می باشد، بدین صورت که فیلد های first_name و last_name را به یک فاصله به هم وصل کرده و بر می گرداند. تنها الزام برای متد __unicode__() این است که یک شیء یونیکد را بر می گرداند. در صورتیکه متد __unicode__() یک شیء یونیکد را بر نگرداند به عنوان مثال یک integer بر گرداند، در اینصورت پایتون خطای TypeError را با پیامی مانند "coercing to Unicode: need string or buffer, int found"ایجاد خواهد کرد. ‫آبجكت هاي يونيكد‬ شیء یونیکد چیست؟ می توانید بدین شکل تصور کنید که شیء یونیکد یک رشته ی پایتون است که می تواند با بیشتر از یک میلیون نوع محتلف حروف را کار کرده و آن ها را کنترل کند، از نسخه های حروف لهجه دار لاتین، حروف غیر لاتین گرفته تا نمادها و علامت های مبهم و نا مفهوم. رشته های معمولی پایتون رمزی شده (encoded) می باشند، بدین معنی که آن ها به صورت رمز شده مانند ASCII، ISO-8859-1 یا UTF-8 استفاده می شوند. در صورتیکه بخواهید حروف تجملی (هرچیزی خارج از 128 حرف ASCII مانند 0-9 و A-Z) را درون یک رشته ی معمولی پایتون ذخیره کنید، این حروف هنگام چاپ و یا نمایش به صورت به هم ریخته در می ایند. مشکلات زمانی رخ می دهند که شما داده ای را درون یک encoding ذخیره کرده و سعی می کنید آن را با یک encoding متفاوت دیگر ترکیب کنید و یا سعی می کنید آن را درون یک برنامه که دارای یک encoding مشخص می باشد نمایش دهید. همه ی ما صفحات وب و پست الکترونیکی را مشاهده کرده ایم که با حالت "??? ??????" و یا دیگر حروف خراب شده اند؛ این مشکلات عموما مشکلات encoding تلقی می شوند. شیء های یونیکد، اگر چه encoding ندارند؛ ولی آن ها از یک مجموعه از حروف جهانی و سازگار که "Unicode" نامیده می شوند استفاده می کنند. هنگامی که شما با شیء های یونیکد در پایتون سر و کار دارید، می توانید بدون هیچگونه نگرانی نسبت به مسائل encoding آن ها را با یکدیگر ترکیب و استفاده کنید. جنگو در سرتاسر فریم ورک از شیء های یونیکد استفاده می کند. شیء های مدل به صورت شیء های یونیکد بازیابی شده اند، view ها با داده ی یونیکد ارتباط برقرار می کنند و template ها به صورت یونیکد ارائه می شوند. عموما، توسعه دهندگان درون فریم ورک جنگو هیچگونه نگرانی نسبت به درست بودن encoding درون برنامه های نوشته شده توسط جنگو نخواهند داشت. توجه داشته باشید که سطح بالا بودن مطالب در اینجا باعث می شود که اطلاعات شما در مورد شیء های یونیکد کمی گمگ و مبهم باشد، در صورتیکه می خواهید می توانید در نشانی زیر مورد آن اطلاعات بیشتری کسب کنید. http://www.joelonsoftware.com/articles/Unicode.html برای اینکه تغییرات ایجاد شده درون مدل ها (اضافه کردن متد __unicode__()) اعمال شود، از shell پایتون خارج شده و با دستور python manage.py shell دوباره وارد آن شوید. (این ساده ترین راه برای اعمال تغییرات ایجاد شده می باشد) حالا لیست شیء های Publisher قابل فهم تر شده اند: >>> from books.models import Publisher >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Apress>, <Publisher: O'Reilly>] اطمینان حاصل کنید که تمام مدل های تعریف شده دارای متد __unicode__() می باشند، نه فقط برای راحتی خودتان هنگامی که از interactive interpreter استفاده می کنید، بلکه جنگو نیز در مکان های زیادی از خروجی __unicode__() برای نمایش شیء ها استفاده می کند. در پایان، توجه داشته باشید که __unicode__() یک مثال خوب برای اضافه کردن یک رفتار برای مدل ها می باشد. یک مدل جنگو بیشتر از جدول لایه پایگاه داده برای یک شیء توضیح می دهد؛ همچنین مدل جنگو هر عملکردی را که شیء نحوه انجام آن را می داند را توضیح دهد. __unicode__() یک مثال برای عملکرد می باشد بدین معنی که یک مدل نحوه ی نمایش خود را می داند. ‫ درج و به روز رساني داده‬ طریقه ی درج کردن داده درون پایگاه داده را مشاهده کردید: ابتدا یک نمونه از یک مدل را با استفاده از ارگومان های آن مانند زیر ساخته: >>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/') همانطور که در کد فوق ملاحظه می کنید، حرکت فوق تعریف اولیه ی یک کلاس مدل می باشد و پایگاه داده به هیچ وجه لمس نشده است. رکورد مورد نظر تا زمانی که متد save() فراخوانی نشود درون پایگاه داده ذخیره نخواهد شد: >>> p.save() عملیات بالا را تقریبا می تواند به شکل زیر به زبان SQL ترجمه کرد: INSERT INTO books_publisher (name, address, city, state_province, country, website) VALUES ('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA', 'U.S.A.', 'http://www.apress.com/'); به دلیل آنکه مدل Publisher از یک کلید اصلی به نام id که با خاصیت افزایش خودکار تعریف شده است استفاده می کند، نخستین فراخوانی save() یک کار بیشتر انجام می دهد: ارزش کلید اصلی را برای رکورد محاسبه کرده و آن را درون attribute مورد نظر یعنی id که درون نمونه (instance) می باشد، قرار می دهد: >>> p.id 52 # this will differ based on your own data فراخوانی های بعدی save() رکورد را در مکانی ذخیره می کند، بدون ساختن رکورد جدید (مانند عملکرد عبارت Upadate به جای INSERT در SQL) >>> p.name = 'Apress Publishing' >>> p.save() عملیات فوق را می توان تقریبا به شکل زیر به SQL تبدیل کرد: UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52; بله، توجه داشته باشید که نه تنها فیلد مورد نظر بلکه تمام فیلدها به روز رسانی خواهند شد. برای پی بردن به نحوه ی اجرای پرس و جوی زیر بخش "به روز رسانی چندین شیء در یک عبارت" را مطالعه کنید: UPDATE books_publisher SET name = 'Apress Publishing' WHERE id=52; واكشي آبجكت ها‬ برای ساختن برنامه های وب دانستن نحوه ی ساخت و به روز رسانی رکورد های پایگاه داده ضروری می باشد، ولی احتمال این وجود دارد که برنامه های وبی که خواهید ساخت نیاز به به اجرای پرس و جوهای بیشتری نیز داشته باشد. روش بازیابی رکوردها را نیز پیش تر مشاهده کردید: >>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>] مدل کد فوق در SQL چیزی شبیه به کد زیر خواهد بود: SELECT id, name, address, city, state_province, country, website FROM books_publisher; نكته‬ توجه داشته باشید که جنگو هنگامی که به دنبال داده ها می باشد از SELECT * استفاده نمی کند، در عوض لیست تمام فیلدها را به صورت واضح بازیابی می کند. در بعضی شرایط SELECT * می تواند کندتر باشد و (خیلی مهم است) لیست کردن فیلدها به طرز نزدیکی از مکتب پایتون (Zen of Python) پیروی کند: "صراحت بهتر از ابهام است". اجازه دهید از نزدیک بخش های Publisher.objects.all() را مورد بررسی قرار دهیم: ** ابتدا، یک مدل داریم که Publisher تعریف شده است. چیز خاصی در اینجا وجود ندارد: هنگامی که شما می خواهید داده ای را جستجو کنید از یک مدل برای آن داده استفاده می کنید. ** و اما در مورد attribute متد objects. این attribute یک manager نامیده می شود. manager ها به تفصیل در آموزش مدل پیشرفته توضیح داده شده اند. چیزی که برای الان باید بدانید این است که manager ها از تمامی عملکردهای سطح جدول در داده مراقبت می کنند که برای جستجوی داده ها بسیار پر اهمیت می باشند. تمام مدل ها به طور خود کار یک manager، objects دریافت می کنند؛ شما در هر زمان که بخواهید نمونه های مدل را جستجو کنید از استفاده خواهید کرد. ** در نهایت، all() یک متد برای manager، objects می باشد که تمام ردیف های پایگاه داده را بر می گرداند. اگرچه این شیء شبیه به لیست می باشد، اما در واقع یک QuerySet می باشد (یک شیء که مجموعه مشخصی از ردیف های پایگاه داده می باشد.) در ادامه فصل تنها درباره آن ها همانند لیست ها بحث خواهیم کرد. فیلتر کردن داده به طور طبیعی، کم پیش می اید که بخواهیم همه چیز را از پایگاه داده یکباره واکشی کنیم؛ در اغلب موارد، می خواهید با قسمتی از داده ها سر و کار داشته باشید. در API جنگو، شما می توانید داده ی خود را با استفاده از متد filter() فیلتر کنید: >>> Publisher.objects.filter(name='Apress') [<Publisher: Apress>] filter() یک آرگومان به صورت کیورد در یافت می کند که معادل آن در SQL عبارت WHERE می باشد. معادل کد SQL مثال قبلی چیزی مانند کد زیر بود: SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress'; جهت محدود کردن بیشتر خروجی می توانید چندین آرگومان را به درون filter() ارسال کنید: >>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress>] می توان ارسال چندین آرگومان به متد filter() درون SQL مانند عبارت AND داست. در نتیجه، مثال فوق می تواند به شکل زیر یه کد SQL تبدیل شود: SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A.' AND state_province = 'CA'; توجه داشته باشید که به طور پیشفرض جستجو درون SQL با علامت = دقیقا یکسان بودن را بررسی می کند. نوع دیگر جستجو نیز در دسترس می باشد: >>> Publisher.objects.filter(name__contains="press") [<Publisher: Apress>] همانطور که مشاهده می کنید دو علامت (_) بین name و contains قرار دارد. مانند خود پایتون، جنگو از این خصوصیت برای اشاره به این که چیزی به صورت "magic" در حال اتفاق افتادن می باشد استفاده می کند (در اینجا قسمت __contains به عبارت LIKE در SQL تبدیل می شود.) SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%'; انواع دیگر جستجو نیز در دسترس می باشد، از جمله icontains (عبارت LIKE که به حروف کوچک و بزرگ حساس می باشد)، starstwith، endswith و range (پرس و جوی BETWEEN در SQL). بازیابی شیء های تک مثال های filter() فوق همگی یک QuerySET را بر می گردانند، که می توان با آن ها مانند لیست ها کار کرد. گاهی اوقات بسیار مناسب می باشد که تنها یک تک شیء را از پایگاه داده واکشی کنیم. برای این منظور متد get() در دسترس می باشد: >>> Publisher.objects.get(name="Apress") <Publisher: Apress> بجای یک لیست (QuerySet) یک تک شیء برگشت داده شده است. به دلیل آنکه در صورتیکه نتیجه پرس و جو (query) چند شیء باشد موجب بروز خطای MultipleObjectsReturned خواهد شد: >>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {'country': 'U.S.A.'} همچنین در صورتیکه هیچ شیءی برگدانده نشود موجب بروز خطای DoesNotExist خواهد شد >>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist. خطای DoesNotExist یک attribute از کلاس مدل می باشد (Publisher.DoesNotExist). در برنامه های خود می توانید مانند زیر خطاها را کنترل کنید: try: p = Publisher.objects.get(name='Apress') except Publisher.DoesNotExist: print "Apress isn't in the database yet." else: print "Apress is in the database." مرتب سازی داده ها همانطور که در مثال های قبلی مشاهده کردید، ممکن است متوجه این موضوع شده باشید که ترتیب قرار گیری شیء هایی که برگردانده می شوند به صورت تصادفی است. تا تکنون در مورد ترتیب چیدمان داده هایی که از پایگاده داده استخراج می شوند صحبت نشده است، ولی می توان خیلی ساده داده ها را با ترتیب چیدمان دلخواه بازیابی کرد. در برنامه های جنگو، ممکن است بخواهید نتیجه استخراج شده از پایگاه داده را بر اساس یکی از معیارها مرتب کنید (برای مثال بر اساس حروف الفبا). برای انجام این کار از متد order_by() استفاده می شود: >>> Publisher.objects.order_by("name") [<Publisher: Apress>, <Publisher: O'Reilly>] مثال فوق فرق چندانی با متد قبلی یعنی all() ندارد، تنها فرق در این است که حالا SQL ترتیب تعیین شده را نیز لحاظ می کند: SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name; شما می توانید بر اساس هر فیلدی که مایلید خروجی را مرتب کنید: >>> Publisher.objects.order_by("address") [<Publisher: O'Reilly>, <Publisher: Apress>] >>> Publisher.objects.order_by("state_province") [<Publisher: Apress>, <Publisher: O'Reilly>] برای مرتب کردن بر اساس چندین فیلد (در مواردی استفاده می شود که آرگومان اول درون هر دو فیلد یکی باشد، در آن زمان برای رفع ابهام از آرگومان دوم استفاده می شود.) مانند زیر: >>> Publisher.objects.order_by("state_province", "address") [<Publisher: Apress>, <Publisher: O'Reilly>] همچنین می توان ترتیب چیدمان را با استفاده از یک علامت (-) به صورت بر عکس انجام داد: >>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>] در اغلب موارد شما همواره می خواهید در حالت پیشفرض خروجی داده های شما بر اساس یکی از فیلد های چیده شده باشد، در این صورت جنگو این امکان را فراهم می کند که به صورت پیشفرض درون مدل خود این کار انجام دهید، این امکان زمانی مفید واقع می شود که بخواهید به صورت مکرر از order_by() استفاده کنید، در این صورت منطقی این است که روش زیر را انتخاب کنید: class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): return self.name class Meta: ordering = ['name'] در مثال فوق، یک مفهوم جدید معرفی شده است: class Meta، کلاسی می باشد که درون کلاس Publisher تعریف شده است (این کلاس درون class Publisher به صورت داخل رفته indented قرار گرفته است). می توان از کلاس Meta روی هر مدل برای تعیین اختیارات مختلفی استفاده کرد. که در اینجا ما از اختیار مرتب کردن آن استفاده کرده ایم. این اختیار به جنگو می گوید که اگر به صورت واضح برای چیدمان داده از order_by() استفاده نشده است، تمام شیء های Publisher هر زمان که با API پایگاه داده بازیابی شدند، باید بر اساس فیلد name مرتب شوند. جستجوی زنجیری نحوه ی فیلتر کردن داده را مشاهده کردید، همچنین نحوه ی مرتب کردن آن ها را نیز مشاهده نمودید. اغلب، نیز می باشد هر دو مورد فوق را انجام دهید. در این موارد می توان به سادگی با استفاده از قابلیت جستجوی زنجیری هر دوی موارد فوق در یک خط انجام داد: >>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>] همانطور که انتظار می رود، معادل کد فوق در SQL استفاده از یک WHERE و یک ORDER BY می باشد: SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A' ORDER BY name DESC; تقسیم کردن داده یکی دیگر از امکانات مورد نیاز، جستجوی تنها تعدادی از داده های مشخص از ردیف می باشد. تصور کنید تعداد صد ناشر درون پایگاه داده وجود دارد، در صورتیکه شما می خواهید تنها اولین ناشر را نمایش دهید. می توان برای انجام چنین کاری از خصوصیت تقسیم استاندار لیست پایتون استفاده کنید: >>> Publisher.objects.order_by('name')[0] <Publisher: Apress> کد فوق به شکل زیر تبدیل می شود: SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1; به همین صورت، می توان زیر مجموعه ای از داده را نیز بازیابی کرد: >>> Publisher.objects.order_by('name')[0:2] مثال فوق دو شیء را بر می گرداند، و درون SQL به شکل زیر تبدیل خواهد شد: SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2; توجه داشته باشید که قسیم کردن به صورت منفی در اینجا پشتیبانی نمی شود: >>> Publisher.objects.order_by('name')[-1] Traceback (most recent call last): ... AssertionError: Negative indexing is not supported. با وجود این می توانید به راحتی محدودیت فوق را دور بزنید. تنها کافیست عبارت order_by() را به صورت زیر تغییر دهید: >>> Publisher.objects.order_by('-name')[0] به روز رسانی چند شیء در یک عبارت در بخش "درج و به روز رسانی داده" اشاره شد که متد save() تمام ستون های یک ردیف را به روز رسانی می کند. بسته به برنامه ی شما، ممکن است بخواهید تنها یک قسمت از ستون ها را به روز رسانی کنید. به عنوان مثال، تصور کنید می خواهیم Apress را درون Publisher از 'Apress' به 'Apress Publishing' تغییر دهیم. با استفاده از save() به این صورت عمل می توان کرد: >>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save() که درون SQL نیز به این شکل تبدیل خواهد شد: SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress'; UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52; (توجه داشته باشید که مثال فوق فرض را بر این گرفته است که Apress دارای id = 52 می باشد.) می توان در مثال فوق مشاهده نمود که متد save() جنگو نه تنها ستون name را بلکه ارزش تمام ستون ها را به روز رسانی می کند. منطقی تر و بهتر آن است که تنها ستون مورد نظر تغییر کند. برای انجام چنین کاری می توان از متد update() موجود در شیء های QuerySet استفاده مانند زیر استفاده کرد: >>> Publisher.objects.filter(id=52).update(name='Apress Publishing') کد فوق درون SQL به این شکل تبدیل می شود: UPDATE books_publisher SET name = 'Apress Publishing' WHERE id = 52; متد update() بر روی هر QuerySet ای کار می کند، بدین معنی که می توان چندین رکورد را نیز ویرایش کرد. در مثال زیر نحوه تغییر country درون هر رکورد Publisher از 'U.S.A' به USA نشان داده شده است. >>> Publisher.objects.all().update(country='USA') 2 متد update() یک integer بر می گرداند که تعداد رکوردهای تغییر داده شده می باشد، در مثال فوق 2 رکورد تغییر داده شده اند. حذف کردن شیء ها برای حذف کردن یک شیء از پایگاه داده، به سادگی متد delete() را می توان فراخوانی کرد: >>> p = Publisher.objects.get(name="O'Reilly") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>] همچنین می توان چندین شیء را نیز با فراخوانی متد delete() از هر QuerySet حذف کرد. این حالت مانند متد update() که در بخش گذشته نشان داده شد می باشد: >>> Publisher.objects.filter(country='USA').delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() [] هنگام حذف کردن داده ها مراقب باشید! برای احتیاط از حذف شدن تمام داده های موجود در یک جدول مشخص، در صورتیکه می خواهید هر آنچه که درون جدول می باشد را حذف کنید، می توان از متد all() استفاده کرد. به عنوان مثال کد زیر کار نخواهد کرد: >>> Publisher.objects.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'delete' ولی اگر متد all() را اضافه کنید کار خواهد کرد: >>> Publisher.objects.all().delete() در صورتیکه می خواهید تنها بخشی از داده را حذف کنید، نیازی به متد all() نمی باشد: >>> Publisher.objects.filter(country='USA').delete() خسته نباشید دوستان عزیز . جلسه پنجم کتاب جنگو به پایان رسید . کار ترجمه این کتاب زیاد عالی نیست . ولی برای شروع بد نیست . امیدوارم که موفق باشید . ترجمه از سایت www.djangobook.com
  11. این تاپیک پشتیبانی برای آموزش است آموزش مدل ها ( models ) در جنگو ( قسمت پنجم ) 11/04/96 17:57 11/04/96 17:57 لطفا اگرهر سوال یا بازخوردی دارید اینجا ارسال کنید.