{% set warning_icon = '' %}
ما تقریباً تمام مراحل مختلف ضروری برای ساختن وبسایتمان را کامل کردهایم: میدانیم که چطور مدل، URL، ویو و تمپلیت بنویسیم. علاوه بر این میدانیم که چطور وبسایتمان را زیباتر کنیم.
زمان تمرین است!
اولین چیزی که در وبلاگمان لازم داریم صفحهای است که یک پست را نشان دهد، درست است؟
ما یک مدل Post
داریم پس لازم نیست چیزی به فایل models.py
اضافه کنیم.
ما با اضافه کردن یک لینک به فایل blog/templates/blog/post_list.html
آغاز میکنیم. این فایل را در ویرایشگر کد باز کنید و تا اینجا باید شبیه به این باشد: {% filename %}blog/templates/blog/post_list.html{% endfilename %}
{% extends 'blog/base.html' %}
{% block content %}
{% for post in posts %}
<article class="post">
<time class="date">
{{ post.published_date }}
</time>
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.text|linebreaksbr }}</p>
</article>
{% endfor %}
{% endblock %}
{% raw %} ما میخواهیم در لیست پست ها، یک لینک از عنوان پست وبلاگی به جزییات پست داشته باشیم. عبارت <h2><a href="">{{ post.title }}</a></h2>
را تغییر دهید تا به صفحه جزییات پست لینک شود: {% endraw %}
{% filename %}{{ warning_icon }} blog/templates/blog/post_list.html{% endfilename %}
<h2><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h2>
{% raw %} زمان آن است که عبارت رازآلود {% url 'post_detail' pk=post.pk %}
را توضیح دهیم. همانطور که ممکن است حدس زده باشید این علامت {% %}
به معنی آن است که ما از سیستم template tag جنگو استفاده میکنیم. این بار ما از یک tag استفاده خواهیم کرد که برایمان یک URL بسازد!{% endraw %}
بخش post_detail
به معنی آن است که جنگو انتظار دارد یک آدرس به نام post_detail، در فایل blog/urls.py
وجود داشته باشد
حالا معنی pk=post.pk
چیست؟ pk
مخفف primary key (کلید اصلی) است که یک نام منحصر به فرد برای هر ردیف از دادهها در پایگاه داده میباشد. هر مدل جنگو یک فیلد به عنوان کلید اصلی دارد، و سوای اینکه چه نام دیگری داشته باشد، همواره به عنوان pk قابل ارجاع است. چون ما در مدل Post
کلید اصلی نساخته بودیم، جنگو یکی برای ما ساخته است (به طور پیشفرض فیلدی که نام آن "id" است، عددی را در خود ذخیره میکند که با هر دیتای جدید یکی به آن اضافه میشود مثلاً 1، 2، 3 ) و برای هر پست جدید در وبلاگ یکی به آن اضافه میکند. ما با نوشتن post.pk
به کلید اصلی دسترسی پیدا میکنیم، همانطور که به فیلدهای دیگر (مانند title
و author
) در آبژکت Post
، دسترسی داشتهایم.
حالا وقتی به آدرس https://127.0.0.1:8000/ میرویم با خظا مواجه میشویم (همانطور که انتظار داشتیم، چرا که ما هنوز برای post_detail
، یک آدرس یا یک view نساخته ایم). چیزی شبیه به این:
بیایید یک URL برای ویو post_detail
در فایل urls.py
بسازیم!
ما میخواهیم جزییات اولین پست ما در این URL دیده شود: https://127.0.0.1:8000/post/1/
بیایید یک آدرس در فایل blog/urls.py
بسازیم تا جنگو را به view با نام post_detail
هدایت کند که جزییات کامل یک پست وبلاگ را نشان میدهد. فایل blog/urls.py
را در ویرایشگر کد باز کنید و خط path('post/<int:pk>/', views.post_detail, name='post_detail'),
را اضافه کنید، چیزی شبیه به این میشود:
{% filename %}{{ warning_icon }} blog/urls.py{% endfilename %}
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
]
بخش post/<int:pk>/
یک الگوی URL را معرفی میکند که برای شما توضیح خواهیم داد:
post/
نشان میدهد که آدرس باید با کلمه post شروع شده باشد و در ادامه آن یک / وجود داشته باشد. تا اینجا همه چیز خوب است.<int:pk>
این بخش ممکن است کمی گیج کننده باشد، این عبارت به این معنی است که جنگو انتظار دارد که یک عدد صحیح دریافت کند و مقدار آن را به یک متغیر به نامpk
نسبت خواهد داد./
- درنهایت ما به یک / در انتهای آدرس نیاز داریم.
یعنی اگر شما آدرس https://127.0.0.1:8000/post/5/
را در مرورگر خود وارد کنید جنگو متوجه خواهد شد که شما به دنبال یک view به نام post_detail
هستید و مقدار متغیر pk
را برابر 5
قرار میدهد و این اطلاعات را به view مورد نظر ارسال میکند.
بسیار عالی، ما یک الگوی آدرس جدید به فایل blog/urls.py
اضافه کردیم! حالا صفحه https://127.0.0.1:8000/ را دوباره بارگزاری کنید، بووم! سرور دوباره متوقف شده است. به کنسول نگاهی بیندازید، یک پیغام خطای دیگر دیده میشود!
یادتان هست که مرحله بعدی چیست؟ اضافه کردن ویو!
این بار view ما یک پارامتر اضافه دریافت کرده است، pk
. ویو ما باید بتواند آن را دریافت کند، درست است؟ پس ما تابع خودمان را به این صورت تعریف میکنیم def post_detail(request, pk):
. باید توجه داشته باشید که این پارامتر باید دقیقاً همان نامی را داشته باشد که ما در urls
تعریف کردهایم (pk
). همچنین توجه کنید که حذف این متغیر، غلط است و باعث ایجاد خطا خواهد شد.
حالا ما میخواهیم فقط و فقط یک پست وبلاگی را داشته باشیم. برای اینکار از کوئری ست به شکل زیر استفاده میکنیم:
{% filename %}{{ warning_icon }} blog/views.py{% endfilename %}
Post.objects.get(pk=pk)
اما این کد یک مشکل دارد. اگر هیچ Post
با کلید اصلی
(pk
) وجود نداشته باشد، یک خطای زشت دریافت خواهیم کرد!
چنین چیزی را نمیخواهیم! اما خوشبختانه جنگو چیزی دارد که میتواند این مشکل را حل کند: get_object_or_404
. در صورتی که هیچ Post
با pk
داده شده وجود نداشته باشد، یک صفحه خطای زیباتر یعنی صفحه Page Not Found 404
را نشان خواهد داد.
خبر خوب اینکه شما میتوانید صفحه Page not found
اختصاصی برای خودتان را با سلیقه خودتان بسازید. اما این الان موضوع خیلی مهمی نیست بنابراین ما از آن رد خواهیم شد.
وقت آن است که یک ویو به فایل views.py
اضافه کنیم!
در فایل blog/urls.py
ما یک الگوی آدرس به نام post_detail
ساختیم که به یک ویو با نام views.post_detail
ارجاع داشت. این به این معنی است که جنگو انتظار دارد که یک تابع با نام post_detail
در فایل blog/views.py
وجود داشته باشد.
زمان آن است که فایل blog/views.py
را در ویرایشگر کد باز کنید و خطوط زیر را در ردیف from
ها اضافه کنید:
{% filename %}blog/views.py{% endfilename %}
from django.shortcuts import render, get_object_or_404
و در انتهای فایل، ویو خودمان را اضافه خواهیم کرد:
{% filename %}blog/views.py{% endfilename %}
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
بله، حالا وقت بارگذاری مجدد صفحه است: https://127.0.0.1:8000/
کار میکند! ولی وقتی روی لینک مربوط به عنوان یک پست کلیک کنید چه اتفاقی میافتد؟
وای نه! یک خطای دیگر! اما حالا ما میدانیم که چطور با این خطا برخورد کنیم، درست است؟ ما باید یک تمپلیت اضافه کنیم!
در پوشه blog/templates/blog
یک فایل به نام post_detail.html
بسازید و آن را در ویرایشگر کد باز کنید.
حالا کد زیر را تایپ کنید:
{% filename %}blog/templates/blog/post_detail.html{% endfilename %}
{% extends 'blog/base.html' %}
{% block content %}
<article class="post">
{% if post.published_date %}
<time class="date">
{{ post.published_date }}
</time>
{% endif %}
<h2>{{ post.title }}</h2>
<p>{{ post.text|linebreaksbr }}</p>
</article>
{% endblock %}
یک بار دیگر ما فایل base.html
را اضافه کردهایم. ما میخواهیم در بلوک content
، جزییات یک پست منتشر شده (اگر چنین پستی وجود داشت) شامل عنوان و متن را نشان دهیم. اما باید در مورد چند موضوع مهم صحبت کنیم، خب؟
{% raw %}{% if ... %} ... {% endif %}
یک تمپلیت تگ است که وقتی میخواهیم چیزی را چک کنیم از آن استفاده میکنیم. (عبارت if ... else ...
را از بخش آشنایی با پایتون به یاد دارید؟) در این سناریو ما میخواهیم بررسی کنیم آیا بخش published_date
مربوط به یک پست خالی نیست.{% endraw %}
بسیار خوب، ما میتوانیم صفحه وبلاگمان را دوباره بارگذاری کنیم و ببینیم آیا خطا TemplateDoesNotExist
از بین رفته است یا نه.
واو! بالاخره کار کرد!
خوب خواهد بود اگر ببینیم وبسایت شما هنوز روی PythonAnywhere کار میکند یا نه؟پس بیایید یک بار دیگر آن را منتشر کنیم.
{% filename %}خط فرمان{% endfilename %}
$ git status
$ git add .
$ git status
$ git commit -m "Added view and template for detailed blog post as well as CSS for the site."
$ git push
سپس در کنسول PythonAnywhere Bash console تایپ کنید:
{% filename %}PythonAnywhere command-line{% endfilename %}
$ cd ~/<your-pythonanywhere-domain>.pythonanywhere.com
$ git pull
[...]
(یادتان باشد که <your-pythonanywhere-domain>
را با زیر دامنه اصلی خود در PythonAnywhere عوض کنید البته بدون آکولادها.)
سرورهایی مانند PythonAnywhere دوست دارند که با فایلهای ایستا (مانند فایلهای CSS) به گونهای متفاوت از فایلهای پایتون برخورد کنند، چرا که میتوانند آن ها را بهینه کنند و سرعت لود شدن آنها را افزایش دهند. در نتیجه هرگاه که ما تغییری در فایلهای CSS میدهیم ، باید دستوری اضافه بر روی آن اجرا کنیم تا به سرور بگوییم که این فایلها تغییر کرده اند. این دستور collectstatic
نام دارد.
ابتدا با فعال کردن محیط مجازی شروع کنیم اگر از قبل هنوز فعال نیست (PythonAnywhere از دستوری به نام workon
برای این کار استفاده میکند این دستور دقیقاً شبیه source myenv/bin/activate
است که شما بر روی کامپیوتر خودتان اجرا میکنید):
{% filename %}PythonAnywhere command-line{% endfilename %}
$ workon <your-pythonanywhere-domain>.pythonanywhere.com
(ola.pythonanywhere.com)$ python manage.py collectstatic
[...]
دستور manage.py collectstatic
تقریباً شبیه دستور manage.py migrate
است. ما تغییراتی در کدها میدهیم و به جنگو میگوییم این تغییرات را apply (اعمال) کند، چه در فایلهای ایستا در سرور باشد چه در پایگاه داده.
به هرحال، الان ما آماده هستیم، به سراغ "Web" page بروید (از طریق دکمه menu در گوشه بالا و سمت راست کنسول) و دکمه Reload را بزنید و سپس به صفحه https://subdomain.pythonanywhere.com نگاه کنید تا نتیجه را ببینید.
به نتیجه رسید! تبریک! :)