آموزش تولید محتوای غیر html ای در جنگو (Django)
معمولا هنگامی که درباره ی deploy کردن وب سایت ها صحبت می شود، موضوع صحبت درباره ی تولید HTML می باشد. البته که موارد بسیار دیگری نیز وجود دارد؛ ما از وب برای توزیع داده در تمام قابل بندی ها استفاده می کنیم: RSS، PDF، عکس ها و غیره ....
تاکنون، تمرکز بر روی تولید HTML بوده است، ولی در این آموزش از کتاب، از این مسیر منحرف شده و به استفاده ی جنگو برای تولید انواع دیگر محتویات به غیر از HTML خواهیم پرداخت.
جنگو دارای ابزار داخلی مناسبی می باشد که می توان برای تولید برخی محتوای به غیر از HTML از آن استفاده کرد:
- Feed های پیوند RSS/Atom
- Sitemap ها (یک قالب بندی XML توسعه داده شده توسط گوگل که به موتورهای جستجو راهنمایی هایی را می دهد)
هر کدام از ابزار فوق در این فصل بررسی خواهند شد، ولی ابتدا قواعد اولیه را پوشش خواهیم داد.
اصول اولیه: view ها و MIME-type ها
آموزش view و urlconf جنگو را بخاطر بیاورید که یک تابع view به سادگی یک تابپ پایتون می باشد که یک درخواست وب را دریافت کرده و یک پاسخ وب را بر می گرداند. این پاسخ می تواند محتویات HTML از صفحه ی وب یا یک تغییر مسیر، یا یک خطای 404، یا یک سند XML، یا حتی یک تصویر ... و یا هر چیز دیگری باشد.
به طور رسمی تر، یک تابع view جنگو باید
- قبول یک نمونه ی HttpRequest به صورت اولین آرگومان
- برگرداندن یک نمونه ی HttpResponse
نکته ی کلیدی جهت برگرداندن محتوای غیر HTML ای از یک view درون کلاس HttpResponse قرار دارد، به ویژه آرگومان mimetype. با قرار دادن mimetype ، می توان به مرورگر نشان داد که یک پاسخ از قالب بندی متفاوت را بر مر گردانیم.
برای مثال، اجازه دهید به view زیر که یک تصویر با قالب بندی PNG را بر می گرداند را بررسی کنیم، تنها فایل را از حافظه خوانده ایم:
from django.http import HttpResponse
def my_image(request):
image_data = open("/path/to/my/image.png", "rb").read()
return HttpResponse(image_data, mimetype="image/png")
همین! در صورتی که شما مسیر تصویر را در فراخوانی open() با یک مسیر برای یک تصویر واقعی جا به جا کنید، می توانید از این view به سادگی برای نمایش یک تصویر استفاده کنید، و مرورگر به درستی آن را نمایش خواهد داد.
نکته ی مهم دیگر که باید در نظر داشت این است که، شیء های HttpResponse با API استاندارد پایتون یعنی "file like object" کار می کنند. این بدین معناست که می توان، از یک نمونه ی HttpResponse را در هر جای پایتون (یا یک کتابخانه ی third-party) که انتظار یک فایل را دارد استفاده کنید.
برای مثالی از نحوه ی کارکرد آن، اجازه دهید نگاهی به تولید CSV با جنگو بیاندازیم.
تولید CSV
CSV یک قالب بندی ساده می باشد که معمولا توسط نرم افزار spreadsheet استفاده می شود. اساسا یک سری از ردیف های جدول با هر سلول در ردیف جدا شده توسط یک علامت کاما می باشد (CVS مخفف comma‑seperated values می باشد). برای مثال، در زیر برخی داده های "unruly" مسافران هوایی در قالب بندی CSV می باشد.
Year,Unruly Airline Passengers
1995,146
1996,184
1997,235
1998,200
1999,226
2000,251
2001,299
2002,273
2003,281
2004,304
2005,203
2006,134
2007,147
نکته
لیست قبلی حاوی اعداد واقعی می باشد! این لیست از مدیریت هواپیمایی فدرال آمریکا آمده است.
هر چند CSV ساده به نظر می رسد، قالب بندی جزئیات آن دارای توافق جهانی نمی باشد. قسمت های مختلف نرم افزار CVS های مختلفی را تولید و استفاده می کند. خوشبختانه، پایتون دارای یک کتابخانه ی استاندار CSV با نام cvs می باشد.
به دلیل آنکه ماژول csv به صورت file-like objects کار می کند، استفاده از آن ساده تر از HttpResponse می باشد:
import csv
from django.http import HttpResponse
# Number of unruly passengers each year 1995 - 2005. In a real application
# this would likely come from a database or some other back-end data store.
UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203]
def unruly_passengers_csv(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=unruly.csv'
# Create the CSV writer using the HttpResponse as the "file."
writer = csv.writer(response)
writer.writerow(['Year', 'Unruly Airline Passengers'])
for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS):
writer.writerow([year, num])
return response
کد فوق باید بسیار واضح باشد، ولی یک نکاتی ویژه برای ذکر کردن وجود دارد:
- Response به جای mimetype پیشفرض یعنی text/html با mimetype مورد نظر یعنی text/csv معین شده است.
- متغیر response یک Content-Disposition header اضافه دریافت می کند که حاوی نام فایل CSV می باشد. این header (بخش "attachment") مرورگر را برای یک جا جهت ذخیره کردن فایل به جای نمایش آن بر می انگیزد. نام این فایل دلخواه می باشد؛ آن را هر چه که می خواهید نام گذاری کنید. این توسط مرورگر در دیالوگ "Save As" استفاده خواهد شد.
جهت اختصاص دادن یک header در یک HttpResponse ، تنها کافیست به صورت یک دیکشنری و مجموعه ای از کلید و ارزش ها با آن رفتار کنید.
- در دست ترجمه/تالیف ... به داخل API، CSV_generation ساده می باشد: تنها response را به صورت اولین آرگومان به csv.writer ارسال کنید.
- برای هر ردیف در فایل CSV، فراخوانی writer.writerow مانند شیءی به صورت یک لیست یا تاپل آن را ارسال می کند.
- ماژول CSV برای گذاشتن کتیشن برای ما محتاط می باشد، بنابراین نگرانی ای درباره ی رد کردن رشته های با کتیشن یا کاما در آن ها نخواهید داشت. تنها اطلاعات را برای writerow() ارسال می کند.
این الگوی کلی ای می باشد که شما در هر زمان که نیاز به برگرداندن محتوای غیر HTML ای دارید از آن استفاده خواهید کرد: ساختن یک شیء HttpResponse (با یک mimetype ویژه)، ارسال کردن آن به چیزی که انتظار یک فایل را دارد، و سپس برگرداند یک response.
اجازه دهید به نگاهی تعداد بیشتری از مثال بیاندازیم.
ساختن PDF
Portable Document Format (PDF) یک قالب بندی توسعه یافته توسط Adobe می باشد که برای نمایش اسناد قابل چاپ استفاده می شود، قالب بندی pixel-perfect کامل، فونت های جاسازی شده، و گرافیک دو بعدی. می توان یک PDF را معادل دیجیتال یک سند چاپ شده دانست؛ در واقع، PDF ها اغلب در توزیع اسناد به قصد چاپ آن ها استفاده می شود.
می توان به سادگی PDF ها را با پایتون و جنگو، با تشکر از کتابخانه ی عالی منبع باز ReportLab تولید کرد (htt://www.reportlab.org/rl_toolkit.html). مزیت تولید فایل های PDF به طور پویا این است که، می توانید PDF های سفارشی را برای اهداف مختلف ایجاد کنید.
در دست ترجمه/تالیف ...
نصب ReportLab
قبل از تولید PDF نیاز می باشد ReportLab را نصب کنید که معمولا خیلی ساده می باشد: تنها کافیست کتابخانه ی آن را از http://www.reportlab.org/downloads.html دانلود و نصب کنید.
نکته
در صورتیکه توزیع مدرن لینوک استفاده می کنید، ممکن است package management خود را قبل از نصب ReportLab بررسی کنید. اغلب repository های پکیج دارای ReportLab اضافه شده می باشند.
برای مثال، در صورتی که از ubuntu استفاده می کنید، به سادگی دستور apt-get install python-reportlab این کار را انجام خواهد داد.
راهنمای کاربر (به طور طبیعی تنها به صورت یک فایل PDF در دسترس می باشد) در http://www.reportlab.org/rsrc/userguide.pdf دارای دستور العمل اضافه می باشد.
می توان نصب بودن ReportLab را با import کردن آن در interactive interpreter پایتون آزمایش کرد:
>>> import reportlab
در صورتیکه دستور فوق هیچ خطایی را ایجاد نکند، نصب درست انجام شده است.
نوشتن view
همانند CSV، تولید PDF ها به صورت پویا با جنگو ساده می باشد، چرا که API مخصوص ReportLab به صورت file‑like object عمل می کند.
در زیر مثال "Hello World" را مشاهده می کنید:
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def hello_pdf(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'
# Create the PDF object, using the response object as its "file."
p = canvas.Canvas(response)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly, and we're done.
p.showPage()
p.save()
return response
نکاتی در مورد کد فوق:
- در کد فوق از mimetype مخصوص PDF یعنی application/pdf استفاده شده است. این حالت به مرورگرها می گوید که سند به جای یک فایل HTML، یک فایل PDF می باشد. در صورتی که این اطلاعات را جا بیاندازید، مرورگرها پاسخ را به صورت HTML تفسیر می کنند، نتیجه ی به هم ریخته و نا مفهومی در پنجره ی مرورگر ایجاد خواهد کرد.
- در دست ترجمه/تالیف ...: تنها کافیست response را به صورت آرگومان اول برای canvas.Canvas ارسال کنید. کلاس Canvas انتظار یک file-like object را دارد، و شیء های HttpResponse نیز همینطور.
- تمام متدهای در دست ترجمه/تالیف ...
- در پایان، فراخوانی showPage() و save() در فایل PDF با اهمیت می باشد – در غیر این صورت در فایل PDF خراب خواهد شد.
PDF های پیچیده
در صورتی که یک سند پیچیده ی PDF (یا هر قطعه داده ی بزرگ) را ایجاد می کنید، استفاده کردن از کتابخانه ی cStringIO به صورت یک محل نگهداری موقت برای فایل PDF خودتان را بررسی کنید. کتابخانه ی cStringIO یک رابط file-like object را تهیه می کند که برای حداکثر بهره وری در C نوشته شده است.
در زیر مثال "Hello World" قبلی با استفاده از cStringIO باز نویسی شده است:
from cStringIO import StringIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def hello_pdf(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'
temp = StringIO()
# Create the PDF object, using the StringIO object as its "file."
p = canvas.Canvas(temp)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly.
p.showPage()
p.save()
# Get the value of the StringIO buffer and write it to the response.
response.write(temp.getvalue())
return response
امکانات دیگر
مجموعه ی کاملی از انواع دیگر محتویاتی که می توان در پایتون تولید کرد وجود دارد. در دست ترجمه/تالیف ...:
- فایل های ZIP: کتابخانه ی استاندارد پایتون حاوی ماژول zipfile می باشد، که می تواند فایل های ZIP را هم بنویسد و هم بخواهند. می توان برای تولید بایگانی کردن تعدادی از فایل های مورد نیاز یا فشرده ساختن اسناد بزرگ از آن استفاده کرد. همچنین می توان فایل های TAR را با استفاده از ماژول tarfile در کتابخانه ی استاندارد پایتون تولید نمود.
- تصاویر پویا: کتابخانه ی تصویر پایتون (PIL؛ http://www.pythonware.com/products/pil/) یک جعبه ابزار خارق العاده برای تولید تصاویر (PNG، JPEG، GIF و مقدار بیشتری) می باشد. شما می توانید برای کاهش اندازه ی تصاویر به عکس های ریز به طور خودکار، تصاویر چندگانه ی مرکب در یک فریم و یا حتی برای انجام پردازش تصویر تحت وب از آن استفاده کنید.
- طرح ها و نمودارها: تعدادی کتابخانه ی قدرتمند برای طرح ها و نمودارها در پایتون وجود دارد که می توان برای تولید نقشه های مورد نیاز، نمودارها، طرح ها و گرافیک ها از آن استفاده می کرد. مسلما نمی توان تمام آن ها را در اینجا لیست کرد، بنابراین تعداد از موارد برجسته در زیر نام برده شده اند:
- matplotlib (http://matplotlib.sourceforge.net/) می تواند برای تولید نوعی از طرح های با کیفیت بالا که معمولا توسط MatLab یا Matematica تولید شده اند استفاده شود.
- pygraphviz (http://networkx.lanl.gov/pygraphviz/)، یک رابط برای لایه ی ابزار گرافیک Graphviz (http://graphviz.org/) می باشد، که می تواند برای تولید نمودارهای ساخت یافته از گرافیک ها و شبکه ها استفاده شود.
به طور کلی، هر کتابخانه ی پایتون قابل ایجاد درون یک فایل می تواند درون جنگو استفاده شود. امکانات عظیم می باشند.
اکنون که با قواعد اولیهه ی تولید محتوای غیر HTML ای، آشنا شدیم، اجازه دهید تصورمان را یک درجه افزایش دهیم. جنگو تعداد ابزار بسیار جذاب برای تولید انواع رایج محتوای غیر HTML ای ارائه می دهد.
فریم ورک یا چارچوب Syndication Feed
جنگو یک فریم ورک یا چارچوب سطح بالا syndication-feed-generating را ارائه می کند که ایجاد RSS و Atom را ساده می کند.
RSS چیست؟ Atom جیست؟
RSS و Atom هر دو XML-based می باشند، که شما می توانید برای تهیه ی به روز رسانی به طور خودکار "feed" های محتویات سایت از آن استفاده کنید. برای اطلاعات بیشتر در مورد RSS می توانید به http://www.whatsrss.com/ مراجعه کنید، و در http://www.atomenabled.org/ نیر می توانید اطلاعاتی راجع به Atom بدست آوردید.
برای ایجاد هر syndication feed، همه باید یک کلاس کوچک پایتون بنویسند. می توانید هر مقدار "feed" که می خواهید بسازید.
فریم ورک یا چارچوب سطح بالای feed-generating یک view می باشد که به طور قرارداد به /feeds/ وصل شده است. جنگو از باقی مانده ی URL (هر چیزی بعد از /feeds/) برای تعیین feed برای برگرداندن استفاده می کند.
برای ایجاد یک feed، شما یک Feed Class خواهید نوشت و در URLconf خود به آن اشاره می کنید.
مقدار دهی اولیه
برای فعال کردن syndication feed ها روی سایت جنگوی خود، URLconf زیر را اضافه کنید:
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}
),
این خط به جنگو می گوید؛ از فریم ورک RSS جهت کنترل تمام URL هایی که با "feeds/" شروع می شوند استفاده کند. (می توانید پیشوند "feeds/" را برای متناسب ساختن با نیازهای خودتان تغییر دهید.)
این خط URLconf دارای یک آرگومان اضافی می باشد: {'feed_dict': feeds}. از این آرگومان اضافه جهت ارسال feed هایی که باید تحت URL منتشر شده باشند استفاده کنید.
به طور خاص، feed_dict باید یک دیکشنری باشد که نام URL را به کلاس Feed مرتبط می کند. می توانید feed_dict را درون خود URLconf تعریف کنید. در زیر مثال کامل URLconf را ملاحظه می کنید:
from django.conf.urls.defaults import *
from mysite.feeds import LatestEntries, LatestEntriesByCategory
feeds = {
'latest': LatestEntries,
'categories': LatestEntriesByCategory,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
مثال قبلی دو feed را در نظر گرفته است:
- feed اول که با LatestEntries نشان داده شده است و در feeds/latest/ قرار خواهد گرفت.
- دومین feed که با latestEntriesByCategory نمایش داده شده و در feeds/categories قرار خواهد گرفت.
زمان راه اندازی، نیاز خواهید داشت خود کلاس های Feed را تعریف کنید.
یک کلاس Feed یک کلاس ساده ی پایتون می باشد که یک syndication feed را نشان می دهد. یک feed می تواند ساده باشد (مانند یک feed "سایت اخبار"، یا یک feed اولیه که آخرین ورودی های یک بلاگ را نمایش می دهد) یا بسیار پیچیده (مانند یک feed که تمام ورودی های بلاگ در یک طبقه بندی خاص جایی که طبقه بندی متغیر می باشد نمایش می دهد).
کلاس های Feed باید از کلاس django.contrib.syndication.feeds.Feed مشتق شوند. آن ها می توانند در هر جایی از درخت کد شما قرار بگیرند.
یک Feed ساده
مثال ساده ی زیر یک Feed از پنج ورودی آخر برای بلاگ داده شده را توضیح می دهد:
from django.contrib.syndication.feeds import Feed
from mysite.blog.models import Entry
class LatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
نکات قابل توجه در کد فوق از این قرار می باشند:
- کلاس فوق از کلاس django.contrib.syndication.feeds.Feed مشتق شده است.
- title، link و description به ترتیب با المان های <title>، <link> و <description> برابر می باشند.
- items() یک متد می باشد که به سادگی یک لیست از شیء هایی که باید به صورت المان های <item> شامل شده در feed باشند بر می گرداند. اگر چه مثال فوق شیء های Entry را که از API پایگاه داده ی جنگو استفاده می کنند بر می گرداند، item() نباید نمونه های مدل را بر گرداند.
تنها یک گام بیشتر وجود دارد. در یک RSS feed، هر <item> دارای یک <title>، <link> و <description> می باشد. نیاز است داده ای که قرار است برای درون آن المنت ها قرار گیرد، به فریم ورک گفته شود.
- برای تعیین محتویات <title> و <description>، template های جنگو را با نام های feeds/latest_title.html و feeds/latest_description.html ایجاد کنید، جایی که latest، slug تعیین شده در URLconf برای feed داده شده می باشد. توجه داشته باشید که پسوند .html الزامی می باشد.
سیستم RSS آن template را برای هر آیتم render می کند، ارسال آن دو متغیر template:
- obj: شیء فعلی (هر کدام از شیء هایی که در items() بر گردانده شده است).
- site: نمایش یک شیء django.models.core.sites.Site سایت فعلی. این برای {{ site.domain }} یا {{ site.name }} مفید می باشد.
در صورتیکه یک template برای title یا description ایجاد نکنید، فریم ورک به صورت پیشفرض از template، {{ obj }} استفاده می کند – این template نمایش رشته ی معمولی شیء می باشد. (برای شیء های مدل، این متد __unicode__() خواهد بود.)
همچنین می توان نام این دو template را از طریق تعیین title_template و description_template به صورت attribute های کلاس Feed تغییر داد.
- برای تعیین محتویات <link>، دو option وجود دارد. برای هر آیتم در items()، جنگو ابتدا سعی می کند یک متد get_absolute_url() روی آن شیء اجرا کند. در صورتی که متد وجود نداشته باشد، سعی می کند یک متد item_link() در کلاس Feed فراخوانی کند، ارسال آن یک پارامتر تنها، item، که خود شیء می باشد.
هر دوی get_absolute_url() و item_link() باید URL ایتم را به صورت یک رشته ی معمولی پایتون بر گردانند.
- برای مثال LatestEntries قبلی، می توانیم template های بسیار ساده ی feed را داشته باشیم. latest_title.html حاوی:
{{ obj.title }}
و latest_description.html حاوی:
{{ obj.description }}
این اغلب بسیار ساده می باشد ...
یک Feed پیچیده تر
فریم ورک همچنین feed های پیچیده تر را نیز از طریق پارامترها پشتیبانی می کند.
برای مثال، تصور کنید بلاگ شما یک RSS feed برای هر تگ مجزایی که شما برای طبقه بندی ورودی های خودتان استفاده کرده اید ارائه می کند. ساختن یک کلاس Feed جدا برای هر تگ احمقانه می باشد؛ نقض قانون "Don't Repeat Yourself" (DRY) می باشد.
در عوض، چارچوب syndication به شما اجازه می دهد feed های جنریکی ایجاد کنید که آیتم های متکی بر اطلاعات در آدرس feed ها را گردانند.
feed های تعیین تگ شما می تواند مانند زیر URL ها را استفاده کنند:
- http://example.com/feeds/tags/python/: ورودی های فعلی علامت زده شده با "python" را بر می گرداند
- http://example.com/feeds/tags/cats/: ورودی های فعلی علامت زده شده با "cats" را بر می گرداند
slug در اینجا "tags" می باشد. فریم ورک syndication، bit های URL اضافه ی بعد از slug را می بیند – 'python' و 'cats' – و به شما جهت گفتن معنی آن bit های URL و نحوه ی تاثیر آن ها در دست ترجمه/تالیف ...
یک مثال این موضوع را واضح تر می کند. در کد زیر feed های تگ تعیین را ملاحظه می کنید:
from django.core.exceptions import ObjectDoesNotExist
from mysite.blog.models import Entry, Tag
class TagFeed(Feed):
def get_object(self, bits):
# In case of "/feeds/tags/cats/dogs/mice/", or other such
# clutter, check that bits has only one member.
if len(bits) != 1:
raise ObjectDoesNotExist
return Tag.objects.get(tag=bits[0])
def title(self, obj):
return "My Blog: Entries tagged with %s" % obj.tag
def link(self, obj):
return obj.get_absolute_url()
def description(self, obj):
return "Entries tagged with %s" % obj.tag
def items(self, obj):
entries = Entry.objects.filter(tags__id__exact=obj.id)
return entries.order_by('-pub_date')[:30]
در کد فوق اصل اولیه ی الگوریتم فریم ورک RSS وجود دارد، به توجه به این کلاس و یک درخواست برای URL مورد نظر یعنی /feeds/tags/python/:
- فریم ورک آدرس /feeds/tags/python/ را دریافت می کند و ملاحظه می کند که یک تکه ی اضافی از URL بعد از slug وجود دارد. فریم ورک رشته ی باقی مانده را از توسط حرف ("/") جدا کرده و متد کلاس Feed یعنی get_object را فراخوانی کرده و bit ها را به آن ارسال می کند.
در این مورد، bits مورد نظر ['python'] می باشد. برای یک درخواست به /feeds/tags/python/django/، bit ها ['python', 'django'] می باشند.
- get_object() مسئول بازیابی شیء Tag داده شده از bit های داده شده می باشد.
در این مورد، get_object() برای بازیابی Tag از API پایگاده داده ی جنگو استفاده می کند. توجه داشته باشید که get_object() در صورتیکه پارامتر های غیر معتبر داده شود، باید خطای django.core.exceptions.ObjectDoesNotExist ایجاد کند. هیچ try/except ای در اطراف فراخوانی Tag.DoesNotExist وجود ندارد، زیرا این کار ضروری نمی باشد. آن تابع زمان شکست Tag.DoesNotExist ایجاد می کند، و Tag.DoesNotexist از OjbectDoesNotexist مشتق شده است. بروز objectDoesNotexist در get_object()تولید یک خطای 404 برای آن درخواست را به جنگو می گوید.
- برای تولید <title>، <link> و <description> مربوط به feed، جنگو متدهای title()، link() و Description() را مورد استفاده قرار می دهد. در مثال قبلی، آن ها attribute های ساده ی کلاس رشته بودند، ولی این مثال نشان می دهد که، آن ها می تواند هم رشته و هم متد باشند. برای هر title، link و description، جنگو الگوریتم زیر را دنبال می کند:
- 1. سعی می کند یک متد را فراخوانی کند، ارسال آرگومان obj، جایی که obj شیءی می باشد که توسط get_object() بر گردانده شده است.
- در صورت شکست، جنگو تلاش می کند یک متد با هیچ آرگومانی را فراخوانی کند.
- در صورت شکست، جنگو attribute کلاس را استفاده می کند.
- در پایان، توجه داشته باشید که items() در این مثال همچنین آرگومان obj را نیز دریافت می کند. الگوریتم برای items همانند الگوریتم توضیح داده شده در گام قبلی می باشد – ابتدا، سعی می کند items(obj)، سپس items() و در پایان یک attribute کلاس items (که باید یک لیست باشد).
مستندات کامل از تمام متدها و attribute ها از کلاس های Feed همواره از اسناد رسمی جنگو قابل دسترسی می باشد (http://docs.djangoproject.com/en/dev/ref/contrib/syndication)
تعیین نوع Feed
به طور پیشفرض، فریم ورک syndication، RSS 2.0 را تهیه می کند. برای تغییر آن، یک attribute، feed_type برای کلاس Feed خودتان اضافه کنید:
from django.utils.feedgenerator import Atom1Feed
class MyFeed(Feed):
feed_type = Atom1Feed
توجه داشته باشید که feed_type را برای یک شیء کلاس در نظر گرفته اید، نه یک نمونه، در حال حاضر انواع feed های در دسترس در جدول 1-11 نشان داده شده اند.
جدول ۱-۱۳
کلاس Feed | Format |
---|---|
Django.utils.feedgenerator.Rss201rev2Feed | RSS 2.01 (default) |
Django.utils.feedgenerator.RssUserland091Feed | RSS 0.91 |
Django.utils.feedgenerator.Atom1Feed | Atom 1.0 |
Enclosures
برای تعیین enclosure ها (مانند منابع media مرتبط با یک آیتم feed مانند feed های MP3 podcast)، item_enclosure_url، item_enclosure_length و item_enclosure_mime_type را استفاده کنید، به عنوان مثال:
from myproject.models import Song
class MyFeedWithEnclosures(Feed):
title = "Example feed with enclosures"
link = "/feeds/example-with-enclosures/"
def items(self):
return Song.objects.all()[:30]
def item_enclosure_url(self, item):
return item.song_url
def item_enclosure_length(self, item):
return item.song_length
item_enclosure_mime_type = "audio/mpeg"
البته که شما یک شیء Song با فیلدهای song_url و song_length (مانند اندازه ی بایت ها) ساخته اید.
Language
Feed های ساخته شده توسط فریم ورک syndication به طور خودکار شامل تگ مناسب <language> (RSS 2.0) یا attribute، xml:lang (Atom) می باشند. این به طور مستقیم از تنظیم LANGUAGE_CODE آمده است.
URLs
لینک متد/attribute می تواند هم یک URL مستقل (مانند "/blog/") یا یک URL با آدرس کامل دامنه و پروتکل (مانند "http://www.example.com/blog/") باشد. در صورتی که link دامنه را بر نگرداند، فریم ورک syndication دامنه ی سایت فعلی را به همراه تنظیم SITE_ID درج خواهد کرد. (برای اطلاعات بیشتر در مورد SITE_ID و چارچوب سایت ها به پکیج django.contrib مراجعه کنید.)
feed های Atom نیازمند یک <link rel="self"> می باشند که مکان فعلی feed را تعریف کند. فریم ورک syndication به طور خودکار این را قرار می دهد.
انتشار feed های Atom و RSS پشت سر هم
برخی از توسعه دهندگان تمایل دارند، فیلد های هر دو نسخه ی Atom و RSS در دسترس قرار دهند. انجام این کار با جنگو بسیار آسان می باشد: تنها کافیست یک کلاس فرزند از کلاس feed خود ایجاد کرده و feed_type را برای چیزی متفاوت قرار دهید. سپس URLconf خود را جهت اضافه کردن نسخه های اضافه به روز رسانی کنید. در زیر یک مثال کامل را ملاحظه می کنید:
from django.contrib.syndication.feeds import Feed
from django.utils.feedgenerator import Atom1Feed
from mysite.blog.models import Entry
class RssLatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
class AtomLatestEntries(RssLatestEntries):
feed_type = Atom1Feed
و در زیر URL همراه:
from django.conf.urls.defaults import *
from myproject.feeds import RssLatestEntries, AtomLatestEntries
feeds = {
'rss': RssLatestEntries,
'atom': AtomLatestEntries,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
فریم ورک یا چارچوب نقشه ی سایت
نقشه ی سایت یک فایل XML در وب سایت شما می باشد که به indexer های موتور جستجو نحوه ی تغییر مکرر و نحوه ی ارتباط برخی صفحات مهم با دیگر صفحات سایت شما را می گوید. این اطلاعات به موتورهای جستجوی فهرست سایت شما کمک می کند.
به عنوان مثال، در زیر یک تکه از نقشه ی سایت برای وب سایت جنگو وجود دارد (http://www.djangoproject.com/sitemap.xml):
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.djangoproject.com/documentation/</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://www.djangoproject.com/documentation/0_90/</loc>
<changefreq>never</changefreq>
<priority>0.1</priority>
</url>
...
</urlset>
برای مشاهده ی نقشه ی سایت های بیشتر به http://www.sitemaps.org/ مراجعه کنید.
فریم ورک نقشه ی سایت جنگو، ساختن این فایل XML را با اجازه دادن به شما جهت بیان این اطلاعات در کد پایتون خودکار می کند. جهت ساختن یک نقشه ی سایت، تنها نیاز به نوشتن یک کلاس Sitemap و اشاره ی به آن درون URLconf می باشد.
نصب
جهت نصب برنامه ی نقشه ی سایت، مراحل زیر را دنبال کنید:
- 'django.contrib.sitemaps' را به تنظیم INSTALLED_APPS اضافه کنید.
- 'django.template.loaders.app_directories.load_template_source'باید در تنظیم TEMPLATE_LOADERS وجود داشته باشد. این حالت به طور پیشفرض وجود دارد، بنابراین در صورتی که این تنظیم را تغییر داده اید، تنها کافیست آن را به حالت اول خود بر گردانید.
- اطمینان حاصل کنید که سایت های چارچوب را نصب کرده اید (به پکیج django.contrib مراجعه کنید).
نکته
برنامه ی نقشه ی سایت در هر جدول دیتابیسی نصب نمی شود. تنها دلیلی که برای رفتن به داخل INSTALLED_APPS نیاز دارد این است که template loader مورد نظر یعنی load_template_source بتواند template های پیشفرض را پیدا کند.
مقدار دهی اولیه
جهت فعال کردن تولید نقشه ی سایت در سایت جنگو، خط زیر را درون URLconf اضافه کنید:
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
خط فوق برای ساختن یک نقشه ی سایت هنگامی که یک کلاینت به /sitemap.xml دسترسی پیدا می کند در نظر گرفته شده است. توجه داشته باشید که حرف نقطه در sitemap.xml با یک علامت ("\") میسر شده است، زیرا نقطه ها معنی خاصی در regular expression ها دارند.
نام فایل نقشه ی سایت اهمیتی ندارد، ولی مکان آن مهم می باشد. موتورهای جستجو تنها لینک ها را در نقشه ی سایت شما برای سطح URL فعلی و پایین فهرست می کنند. برای مثال، در صورتی که sitemap.xml در دایرکتوری ریشه ی شما می باشد، ممکن است به هر URL ای در سایت شما رجوع کند. در صورتی که نقشه ی سایت در /content/sitemap.xml باشد، ممکن است تنها به URL هایی که با /content/ شروع می شوند رجوع کند.
view نقشه ی سایت یک آرگومان اضافه ی الزامی دریافت می کند: {'sitemaps': sitemaps}. نقشه های سایت باید یک دیکشنری باشند که بخش کوتاه لیبلی (مانند blog یا news) را به کلاس Sitemap آن (مانند BlogSitemap یا NewsSitemap) مرتبط می سازد. همچنین ممکن است به یک instance از یک کلاس Sitemap (مانند BlogSitemap(some_var)) مرتبط سازد.
کلاس های Sitemap
یک کلاس Sitemap یک کلاس ساده ی پایتون می باشد که یک بخش از ورودی های نقشه ی سایت شما را نشان می دهد. برای مثال، یک کلاس Sitemap هنگامی که دیگری می تواند تمام رخدادها در در رخدادهای تقویم نشان دهد تمام ورودی های وبلاگ شما را نشان دهد.
در ساده ترین مورد، تمام این بخش ها با یکدیگر داخل یک sitemap.xml بدست می آیند، ولی استفاده از فریم ورک برای تولید یک فهرست نقشه ی سایت که به فایل های نقشه ی سایت منحصر به فرد نیز ممکن است، یکی برا هر بخش.
کلاس های Sitemap باید از کلاس django.contrib.sitemaps.Sitemap مشتق شوند. آن ها می توانند هر جایی در درخت کد پایتون شما قرار بگیرند.
برای مثال، فرض می کنیم شما دارای یک سیستم بلاگ می باشید، با یک مدل Entry، و می خواهید نقشه ی سایت تمام لینک ها به ورودی منحصر به فرد بلاگ را شامل شود. در زیر کلاس Sitemap مورد نظر وجود دارد:
from django.contrib.sitemaps import Sitemap
from mysite.blog.models import Entry
class BlogSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Entry.objects.filter(is_draft=False)
def lastmod(self, obj):
return obj.pub_date
declare کردن نقشه ی سایت باید بسیار شبیه به یک Feed باشد. در دست ترجمه/تالیف ...
همانند کلاس های Feed، اعضای Sitemap می توانند متد یا attribute ها باشند. به بخش کمی قبل در این آموزش از کتاب، "یک مثال پیچیده" برای نحوه ی انجام این اعمال مراجعه کنید.
یک کلاس نقشه ی سایت می تواند متد/attribute های زیر را تعریف کند:
- items(required): لیستی از شیء ها تهیه می کند. فریم ورک نوع شیء هایی که هستند را زیر نظر ندارد؛ تمام موضوع این است که این شیء ها به متدهای location()، lastmod()، changefreq() و priority() ارسال شده باشند.
- location(optional): URL مستقل برای شیء داده شده را می دهد. در اینجا، "URL مستقل" به معنی یک URL می باشد که شامل پروتکل یا دامنه نباشد. تعدادی مثال را در زیر مشاهده می کنید:
- خوب: '/foo/bar/'
- بد: 'example.com/foo/bar/'
- بد: 'http://example.com/foo/bar/'
در صورتیکه location تهیه نشده باشد، فریم ورک متد get_absolute_url() را در هر شیء به صورت برگشت داده شده توسط items() فراخوانی خواهد کرد.
- lastmod(optional): شیء های "آخرین اصلاح" زمان و تاریخ، به صورت یک شیء datetime پایتون.
- changefreq(optional): هر چند وقت یک بار شیء را تغییر می دهد. مقادیر ممکن (به صورت داده شده توسط تعیین نقشه های سایت) به قرار زیر می باشند:
- 'always'
- 'hourly'
- 'daily'
- 'weekly'
- 'monthly'
- 'yearly'
- 'never'
- priority(optional): یک پیشنهاد اولویت فهرست سازی بین 0.0 و 1.0. اولویت پیشفرض یک صفحه 0.5 می باشد؛ برای اطلاعات بیشتر درباره ی عمکرد اولویت به http://sitemaps.org/ مراجعه کنید.
میانبرها
فریم ورک نقشه ی سایت کلاس های مناسبی برای موارد مشترک تهیه کرده است. این کلاس ها در بخش های زیر توضیح داده شده اند.
FlatPageSitemap
کلاس django.contrib.sitemaps.FlatPageSitemap به تمام صفحات مسطح تعریف شده برای سایت فعلی نگاه می کند و یک ورودی در نقشه ی سایت ایجاد می کند. این ورودی ها تنها شامل attribute مورد نظر یعنی location می شوند – نه lastmod، changefreq یا priority.
برای اطلاعات بیشتر در مورد صفحات مسطخ به پکیج django.contrib مراجعه کنید.
GenericSitemap
کلاس GenericSitemap با هر view جنریکی کار می کند (به view های generic مراجعه کنید) که شما آن ها را فرا گرفته اید.
برای استفاده از آن، یک نمونه بسازید، ارسال در info_dict یکسان که شما به view های جنریک ارسال می کنید. تنها نیازمندی این است که دیکشنری یک ورودی queryset داشته باشد. ممکن است همچنین یک ورودی date_field داشته باشد که یک فیلد تاریخ برای شیء های بازیابی شده از queryset را تعیین می کند. این برای attribute، lastmode در نقشه ی سایت تولید شده مورد استفاده قرار خواهد گرفت. شما ممکن است آرگومان های کیورد priority و changefreq را برای سازنده ی GenericSitemap جهت تعیین این attribute ها برای همه ی URL ها ارسال کنید.
در زیر مثالی از یک URLconf که از هر دوی FlatPageSitemap و GenericSiteMap (با شیء فرضی Entry از پیش) استفاده می کند وجود دارد:
from django.conf.urls.defaults import *
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from mysite.blog.models import Entry
info_dict = {
'queryset': Entry.objects.all(),
'date_field': 'pub_date',
}
sitemaps = {
'flatpages': FlatPageSitemap,
'blog': GenericSitemap(info_dict, priority=0.6),
}
urlpatterns = patterns('',
# some generic view using info_dict
# ...
# the sitemap
(r'^sitemap\.xml$',
'django.contrib.sitemaps.views.sitemap',
{'sitemaps': sitemaps})
)
ساخت فهرست نقشه ی سایت
فریم ورک نقشه ی سایت همچنین دارای توانایی برای ساختن یک فهرست نقشه ی سایت می باشد که به فایل های منحصر به فرد نقشه ی سایت رجوع می کند، یکی برای هر بخش تعریف شده در دیکشنری sitemaps. تنها تفاوت ها در کاربرد هستند:
- شما از دو view در URLconf خود استفاده می کنید: django.contrib.views.index و django.conrib.sitemaps.views.sitemap.
- django.contrib.sitemaps.views.sitemap باید یک آرگومان کیورد section دریافت کند.
در زیر خطوط URLconf مربوط برای مثال قبلی وجود دارد:
(r'^sitemap.xml$',
'django.contrib.sitemaps.views.index',
{'sitemaps': sitemaps}),
(r'^sitemap-(?P<section>. ).xml$',
'django.contrib.sitemaps.views.sitemap',
{'sitemaps': sitemaps})
کد فوق به طور اتوماتیک یک فایل sitemap.xml تولید می کند که به هر دوی sitemap-flatpages.cml و sitemap-blog.xml رجوع می کند. کلاس های Sitemap و دیکشنری sitemaps هیچگاه تغییر نمی کنند.
پینگ کردن گوگل
ممکن است بخواهید گوگل را هنگامی که نقشه ی سایت شما تغییر می کند پینگ کنید، تا به آن اجازه دهید دوباره فهرست گذاری سایت شما را بداند. چارچوب یک تابع تنها برای فقط برای این کار تهیه کرده است: django.contrib.sitemaps.ping_google().
ping_google() یک آرگومان اختیاری دریافت می کند، sitemap_url، که باید URL مستقل از نقشه ی سایت سایت شما باشد (مانند '/sitemap.xml'). در صورتی که آرگومان تهیه نشده باشد، ping_google() تلاش می کند نقشه ی سایت شما را توسط انجام یک جستجوی بر عکس در URLconf بسنجد.
ping_google() در صورتی که نتواند URL نقشه ی سایت را تعیین کند، خطای django.contrib.sitemaps.SitemapNotFound را ایجاد خواهد کرد.
روش مفید برای فراخوانی ping_google به شکل یک متد save() مدل می باشد:
from django.contrib.sitemaps import ping_google
class Entry(models.Model):
# ...
def save(self, *args, **kwargs):
super(Entry, self).save(*args, **kwargs)
try:
ping_google()
except Exception:
# Bare 'except' because we could get a variety
# of HTTP-related exceptions.
pass
راهکار موثر تر، فراخوانی ping_google() از یک اسکریپت cron یا برخی وظایف برنامه ریزی شده ی دیگر می باشد. تابع یک HTTP request برای سرور های گوگل ایجاد می کند، بنابراین شما ممکن نیست بخواهید برای معرفی در هر بار فراخوانی save() بار اضافه ی شبکه را داشته باشید.
در پایان، در صورتی که 'django.contrib.sitemaps' درون تنظیم INSTALLED_APPS می باشد، manage.py شما شامل یک دستور جدید، ping_google خواهد بود. این برای دسترسی به پینگ کردن درون خط فرمان مفید می باشد. برای مثال:
python manage.py ping_google /sitemap.xml