نکتههای جنگویی - کش کردن - قسمت اول
توییتر رو در نظر بگیرید. یک کاربر ممکنه ۵۰ هزار توییت داشته باشه، اگر برای هر بار رفرش کردن Profile که حاوی توییتهای کاربره، یک کوئری برای دریافت اطلاعات به سرور زده شه، بار خیلی خیلی زیادی به سرور وارد میشه.
راهکاری که در این مواقع استفاده میشه، کش کردن اطلاعات هست. مطلب کامل این موضوع در داکیومنت جنگو. برای این مطلب من یک پروژهی آزمایشی را ساختم که برای نکتههای دیگر جنگویی از همین استفاده خواهم کرد.
برای شروع یک دایرکتوری با اسم story ساختم:
➜ mkdir story
وارد دایرکتوری story میشم و یک virtualenv درست میکنم:
➜ cd story
➜ story python3 -m virtualenv -p python3 .venv
بعد از ساختن virtualenv، محیط مجازی رو فعال میکنم:
➜ story source ./.venv/bin/activate
جنگو رو نصب میکنم:
(.venv) ➜ story pip3 instal django
و بعد از نصب جنگو، پروژهی خودم که اسمش A هست رو میسازم:
(.venv) ➜ story django-admin startproject A
یک app به اسم stories درست میکنم:
(.venv) ➜ A ./manage.py startapp stories
حالا این app رو به INSTALLED_APPS اضافه میکنم:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # My Django apps 'stories.apps.StoriesConfig', ]
حالا به stories/models.py میرم و مدل اپ رو میسازم که مدل من همچین چیزی هست:
from django.db import models from django.contrib.auth.models import User from django.urls import reverse class Story(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_stories') title = models.CharField(max_length=100) slug = models.SlugField(max_length=100, unique=True) body = models.TextField() created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) status = models.BooleanField(default=True) class Meta: verbose_name_plural = 'stories' ordering = ('-created', ) def __str__(self): return self.title
حالا مدلی که ساختم رو migrate میکنم:
(.venv) ➜ A ./manage.py makemigrations
(.venv) ➜ A ./manage.py migrate
حالا در stories/views.py دو view که برای نمایش لیست داستانها و نمایش یک داستان براساس slug مینویسم:
def stories_list(request): context = {} stories = Story.objects.filter(status=True) context['stories'] = stories return render(request, 'stories/index.html', context)
def stories_detail(request, slug): story = get_object_or_404(Story, slug=slug, status=True) return render(request, 'stories/detail.html', {'story': story})
بعد از ساخت viewها دو فایل html برای نمایش اطلاعات میسازم.
stories/templates └── stories ├── detail.html └── index.html
کدهای index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>لیست داستانها</title> </head> <body style="direction: rtl; text-align: justify"> <h1>فهرست داستانها</h1> <hr> {% for story in stories %} <h3><a href="{% url 'stories:stories_detail' story.slug %}">{{ story.title }}</a></h3> <small>{{ story.created|timesince }}</small><span>{{ story.user }}</span> {% endfor %} </body> </html>
کدهای detail.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ story.title }}</title> </head> <body style="direction: rtl; text-align: justify"> <h1>مطالعه داستان</h1><a href="{% url 'stories:stories_list' %}">خانه</a> <hr> <h3>{{ story.title }}</h3> <small>{{ story.created|timesince }}</small> | <span>{{ story.user }}</span> <hr> <p>{{ story.body|safe }}</p> </body> </html>
نکته: قاعدتا باید یه base.html بسازم و فایلهای htmlی رو از اون extend کنم ولی در این سری مطالب هدف ما طراحی ظاهر نیست.
حالا میرم سراغ stories/urls.py (که باید ایجاد شه):
from django.urls import path from . import views app_name = 'stories' urlpatterns = [ path('', views.stories_list, name='stories_list'), path('<slug:slug>/', views.stories_detail, name='stories_detail'), ]
برای تنظیم urlهای اپ داستان به سراغ A/urls.py میرم و urlهای اپ خودم رو بهش معرفی میکنم:
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), # My apps urls path('', include('stories.urls')), ]
تا اینجا یه پروژهی جنگویی ساختیم، که یه اپ برای نوشتن داستان هم داره. حالا باید یه کاربر ادمین بسازیم و پروژه رو اجرا کنیم:
(.venv) ➜ A ./manage.py createsuperuser Username: admin Email address: Password: Password (again): The password is too similar to the username. This password is too short. It must contain at least 8 characters. This password is too common. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully.
نام کاربری admin و رمزعبور admin رو انتخاب کردم. و پروژه رو اجرا میکنم:
(.venv) ➜ A ./manage.py runserver Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). August 23, 2020 - 03:07:12 Django version 3.1, using settings 'A.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
در مرورگر به آدرس:
http://localhost:8000/admin/
که بریم و بعد از وارد کردن اطلاعات کاربر ادمین، وارد پنل ادمین میشیم. اما من هنوز اپ stories رو به پنل ادمین اضافه نکردم. که به stories/admin.py میرم و این کار رو انجام میدم:
from django.contrib import admin from .models import Story @admin.register(Story) class StoryAdmin(admin.ModelAdmin): list_display = ('user', 'title', 'slug', 'created', 'status') list_filter = ('user', 'created') search_fields = ('title', 'body') raw_id_fields = ('user', )
اگر حالا دوباره صفحهی پنل ادمین رو رفرش کنم، میبینم که اپ استوری به پنل ادمین اضافه شده.
میخواستم تمام مطالب کش کرن رو داخل یه پست بنویسم، اما تصمیم گرفتم که تبدیل به دو بخشش کنم که بخش اول ساخت یه پروژهی جنگو هست و برای بقیهی مطالب جنگویی از همین پروژه استفاده کنم و لینک به همین صفحه بدم. قسمت دوم کش کردن که موضوع اصلی بحث ما هم هست رو بهزودی مینویسم.
اگر خطا و مشکلی هم در مطلب دیدید زیر پست بهم اطلاع بدید.
پایان قسمت اول./