آخرین تنظیمات
سلام
شاید از قبل متوجه این موضوع شده باشید که وقتی فردی پاسخی را برای یک پست ارسال می کند فیلد last_update
بروز رسانی نمی شود. حالا می خواهیم این مورد را درست کنیم.
boards/views.py
@login_required def reply_topic(request, pk, topic_pk): topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk) if request.method == 'POST': form = PostForm(request.POST) if form.is_valid(): post = form.save(commit=False) post.topic = topic post.created_by = request.user post.save() topic.last_updated = timezone.now() # <- here topic.save() # <- and here return redirect('topic_posts', pk=pk, topic_pk=topic_pk) else: form = PostForm() return render(request, 'reply_topic.html', {'topic': topic, 'form': form})
کار بعدی که قصد انجام آن را داریم کنترل روی سیستم شمارندۀ بازدیدهاست. ما نمی خواهیم بازدید چندبارۀ یک کاربر از یک صفحه را به عنوان چند بازدید در نظر بگیریم. چرا که در تحلیل ها با ترافیک جعلی و نتیجه گیری غلط مواجه خواهیم شد. برای این کار می توانیم از session یا نشست استفاده کنیم.
boards/views.py
class PostListView(ListView): model = Post context_object_name = 'posts' template_name = 'topic_posts.html' paginate_by = 20 def get_context_data(self, **kwargs): session_key = 'viewed_topic_{}'.format(self.topic.pk) # <-- here if not self.request.session.get(session_key, False): self.topic.views += 1 self.topic.save() self.request.session[session_key] = True # <-- until here kwargs['topic'] = self.topic return super().get_context_data(**kwargs) def get_queryset(self): self.topic = get_object_or_404(Topic, board__pk=self.kwargs.get('pk'), pk=self.kwargs.get('topic_pk')) queryset = self.topic.posts.order_by('created_at') return queryset
کار بعدی بهبود وضعیت لیست تاپیک هاست.
در حال حاضر تنها راه ممکن برای کاربر کلیک روی عنوان تاپیک و انتقال وی به اولین صفحه است. به همین منظور می توانیم به شکل زیر عمل کنیم.
boards/models.py
import math from django.db import models class Topic(models.Model): # ... def __str__(self): return self.subject def get_page_count(self): count = self.posts.count() pages = count / 20 return math.ceil(pages) def has_many_pages(self, count=None): if count is None: count = self.get_page_count() return count > 6 def get_page_range(self): count = self.get_page_count() if self.has_many_pages(count): return range(1, 5) return range(1, count + 1)
در قالب topics.html هم مشابه زیر پیاده سازی خود را انجام می دهیم.
templates/topics.html
<table class="table table-striped mb-4"> <thead class="thead-inverse"> <tr> <th>Topic</th> <th>Starter</th> <th>Replies</th> <th>Views</th> <th>Last Update</th> </tr> </thead> <tbody> {% for topic in topics %} {% url 'topic_posts' board.pk topic.pk as topic_url %} <tr> <td> <p class="mb-0"> <a href="{{ topic_url }}">{{ topic.subject }}</a> </p> <small class="text-muted"> Pages: {% for i in topic.get_page_range %} <a href="{{ topic_url }}?page={{ i }}">{{ i }}</a> {% endfor %} {% if topic.has_many_pages %} ... <a href="{{ topic_url }}?page={{ topic.get_page_count }}">Last Page</a> {% endif %} </small> </td> <td class="align-middle">{{ topic.starter.username }}</td> <td class="align-middle">{{ topic.replies }}</td> <td class="align-middle">{{ topic.views }}</td> <td class="align-middle">{{ topic.last_updated|naturaltime }}</td> </tr> {% endfor %} </tbody> </table>
مشابه صفحه بندی برای هر تاپیک، به صورت جداگانه می خواهیم در جدول لیست تاپیک ها نیز برای تاپیک های مختلف صفحات را شماره گذاری کرده و نمایش دهیم.
در صفحۀ پاسخ ها در حال حاضر تمام پاسخ های یک تاپیک را لیست کرده ایم. حالا می خواهیم نمایش آن را به ۱۰ پست آخر محدود کنیم.
boards/models.py
class Topic(models.Model): # ... def get_last_ten_posts(self): return self.posts.order_by('-created_at')[:10]
templates/reply_topic.html
{% block content %} <form method="post" class="mb-4" novalidate> {% csrf_token %} {% include 'includes/form.html' %} <button type="submit" class="btn btn-success">Post a reply</button> </form> {% for post in topic.get_last_ten_posts %} <!-- here! --> <div class="card mb-2"> <!-- code suppressed --> </div> {% endfor %} {% endblock %}
کار بعدی این است که وقتی کاربری به یک پست پاسخ می دهد؛ پس از ارسال پاسخ به اولین صفحه ریدایرکت شده و انتقال یابد. می خواهیم این موضوع را با انتقال کاربر به «آخرین صفحه» بهبود دهیم.
می توانیم به هر پست یک id وشناسه نسبت دهیم.
templates/topic_posts.html
{% block content %} <div class="mb-4"> <a href="{% url 'reply_topic' topic.board.pk topic.pk %}" class="btn btn-primary" role="button">Reply</a> </div> {% for post in posts %} <div id="{{ post.pk }}" class="card {% if forloop.last %}mb-4{% else %}mb-2{% endif %} {% if forloop.first %}border-dark{% endif %}"> <!-- code suppressed --> </div> {% endfor %} {% include 'includes/pagination.html' %} {% endblock %}
قسمت مهم این کد مربوط به عبارت زیر است:
<div id="{{ post.pk }}" ...>
سپس در قسمت view به این شکل از ان استفاده می کنیم.
boards/views.py
@login_required def reply_topic(request, pk, topic_pk): topic = get_object_or_404(Topic, board__pk=pk, pk=topic_pk) if request.method == 'POST': form = PostForm(request.POST) if form.is_valid(): post = form.save(commit=False) post.topic = topic post.created_by = request.user post.save() topic.last_updated = timezone.now() topic.save() topic_url = reverse('topic_posts', kwargs={'pk': pk, 'topic_pk': topic_pk}) topic_post_url = '{url}?page={page}#{id}'.format( url=topic_url, id=post.pk, page=topic.get_page_count() ) return redirect(topic_post_url) else: form = PostForm() return render(request, 'reply_topic.html', {'topic': topic, 'form': form})
در topic_post_url با استفاده از آخرین صفحه یک url ساخته ایم و به آن anchorای را اضافه کرده ایم که برابر با ID آخرین پست باشد.
با این کار لازم است که فایل تست خود را هم بروز رسانی کنیم.
boards/tests/test_view_reply_topic.py
class SuccessfulReplyTopicTests(ReplyTopicTestCase): # ... def test_redirection(self): ''' A valid form submission should redirect the user ''' url = reverse('topic_posts', kwargs={'pk': self.board.pk, 'topic_pk': self.topic.pk}) topic_posts_url = '{url}?page=1#2'.format(url=url) self.assertRedirects(self.response, topic_posts_url)
همانطور که در عکس قبلی مشاهده می شود؛ مشکل بعدی مربوط به شماره گذاری صفحات در زمانی است که تعداد صفحات خیلی بالا باشد. بهترین کار این است که قالب pagination.html را ویرایش کنیم.
templates/includes/pagination.html
{% if is_paginated %} <nav aria-label="Topics pagination" class="mb-4"> <ul class="pagination"> {% if page_obj.number > 1 %} <li class="page-item"> <a class="page-link" href="?page=1">First</a> </li> {% else %} <li class="page-item disabled"> <span class="page-link">First</span> </li> {% endif %} {% if page_obj.has_previous %} <li class="page-item"> <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a> </li> {% else %} <li class="page-item disabled"> <span class="page-link">Previous</span> </li> {% endif %} {% for page_num in paginator.page_range %} {% if page_obj.number == page_num %} <li class="page-item active"> <span class="page-link"> {{ page_num }} <span class="sr-only">(current)</span> </span> </li> {% elif page_num > page_obj.number|add:'-3' and page_num < page_obj.number|add:'3' %} <li class="page-item"> <a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a> </li> {% endif %} {% endfor %} {% if page_obj.has_next %} <li class="page-item"> <a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a> </li> {% else %} <li class="page-item disabled"> <span class="page-link">Next</span> </li> {% endif %} {% if page_obj.number != paginator.num_pages %} <li class="page-item"> <a class="page-link" href="?page={{ paginator.num_pages }}">Last</a> </li> {% else %} <li class="page-item disabled"> <span class="page-link">Last</span> </li> {% endif %} </ul> </nav> {% endif %}
خیلی خوب در اینجا آموزش های مربوط به پیاده سازی بورد جنگو به پایان می رسد. البته موارد زیاد دیگری مانده که ممکن است در آینده پیرامون آن آموزش هایی را تهیه کنم. مثل بهبود واسط کاربری، آپلود فایل، ساخت بخش مدیریت و…
قسمت بعدی این سری از آموش ها(فصل هشتم) مربوط به توسعه و راه اندازی پروژه به عنوان یک محصول نهایی خواهد بود.
شما می توانید کد های مربوط به این فصل را از آدرس زیر دانلود کنید :)
http://tamadon.net/python/code/550-django-beginners-guide-part7.zip
ترجمۀ اختصاصی توسط تمدن
مطلب بعدی: فصل بعدی: ورژن کنترل | Version Control
مطلب قبلی: تصویر پروفایل یا آواتار
دیدگاه خود را ثبت کنید
تمایل دارید در گفتگو شرکت کنید؟نظری بدهید!