Evgenii Legotckoi
8 января 2018 г. 13:52

Django - Урок 031. Смена URL без перезагрузки страницы с частичной подгрузкой контента

Чем меньше информации приходится передавать сайту на каждый запрос, тем лучше. Поскольку получаем меньшую нагрузку на сервер и на канал связи. Первым таким улучшением на сайте я сделал подгрузку списка статей при навигации по пагинатору страниц.

Смысл в том, что когда пользователь хочет перейти на следующую страницу со списком статей и кликает на ссылку в пагинаторе , то производится перехват события клика, событие отменяется, но высылается AJAX запрос на сервер с номером запрашиваемой страницы. Когда сервер получает такой запрос, он производит рендеринг только списка статей и высылает его обратно.


Замена URL в браузере без перезагрузки страницы

Для главной страницы сайта я создал JavaScript файл index.js, в котором будет логика AJAX запроса, а также привязка обработчика клика по ссылкам в пагинаторе. Важным моментом является то, что я использую для пагинатора django-bootstrap3 . Соответственно, чтобы подключить обработчик клика, я использую селектор '.paginator > li > a' .

Содержимое файла будет следующим

  1. class Index {
  2.  
  3. static initPaginator() {
  4. document.body.querySelectorAll('.pagination > li > a')
  5. .forEach( link => link.addEventListener('click', Index.pagination_link_clickHandler) );
  6. }
  7.  
  8. static pagination_link_clickHandler(event){
  9. event.preventDefault(); // запрещаем событие
  10.  
  11. let path = event.target.href; // забираем путь
  12. let page = Global.getURLParameter(path, 'page');
  13.  
  14. if (typeof page !== 'undefined') {
  15. jQuery.ajax({
  16. url: jQuery(this).attr('action'),
  17. type: 'POST',
  18. data: {'page': getURLParameter(path, 'page')}, // забираем номер страницы, которую нужно отобразить
  19.  
  20. success : function (json) {
  21.   // Если запрос прошёл успешно и сайт вернул результат
  22. if (json.result)
  23. {
  24. window.history.pushState({route: path}, "EVILEG", path); // устанавливаем URL в строку браузера
  25. jQuery("#articles-list").replaceWith(articles); // Заменяем div со списком статей на новый
  26. Index.initPaginator(); // Переинициализируем пагинатор
  27. jQuery(window).scrollTop(0); // Скроллим страницу в начало
  28. }
  29. }
  30. });
  31. }
  32. }
  33. }
  34.  
  35. Index.initPaginator();

Я стараюсь использовать стандарт ecmascript 6. Поэтому для страницы index используется класс Index для обобщения применяемых методов.

Для получения номера страницы в URL используем функцию getURLParameter .

Структура шаблонов

Таким образом у нас должна быть специальная структура шаблонов, а именно, должен быть шаблон для рендеринга списка статей, в моём случае шаблон с превью одной статьи, а также шаблон главной страницы сайта.

То есть будет:

  • article_preview.html - этот шаблон рассматривать не будем, поскольку он не интересен для нас в данном случае
  • article_previews_list.html
  • index.html

index.html

index.html наследован от базового шаблона base.html в котором имеется блок для подключения javascript файлов.

  1. {% extends 'base.html' %}
  2. {% block page %}
  3. {% include 'article_previews_list.html' %}
  4. {% endblock %}
  5. {% block javascript_footer %}
  6. <script src="/static/js/index.js"></script>
  7. {% endblock %}

article_previews_list.html

Список статей представляет собой div , который будет заменяться при каждом запросе.

  1. <div id="articles-list">
  2. {% load bootstrap_pagination from bootstrap3 %}
  3. {% for article in object_list %}
  4. {% include 'knowledge/article_preview.html' %}
  5. {% endfor %}
  6. {% bootstrap_pagination object_list pages_to_show="10" %}
  7. </div>

urls.py

Маршрут для главной страницы будет выглядеть следующим образом

  1. urlpatterns = [
  2. url(r'^$', views.IndexView.as_view(), name='index')
  3. ]

views.py

А теперь рассмотрим представление, которое будет возвращать список статей или сразу всю страницу

  1. class IndexView(View):
  2. template_name = 'index.html'
  3.  
  4. def get(self, request):
  5. return render(request=request, template_name=self.template_name, context={'object_list': get_paginated_page(request, Article.objects.all())})
  6.  
  7. def post(self, request):
  8. if request.is_ajax():
  9. return JsonResponse({
  10. "result": True,
  11. "articles": render_to_string(
  12. request=request,
  13. template_name='article_previews_list.html',
  14. context={'object_list': get_paginated_page(request, Article.objects.all())}
  15. )
  16. })
  17. else:
  18. raise Http404()

Важным моментом является то, что сайт должен корректно обрабатывать оба вида запросов, как обычный, так и AJAX запрос. Поскольку пользователь может зайти как напрямую на одну из страниц, так и с помощью пагинации на сайте.

Возможно, вас в данном коде заинтересует метод get_paginated_page, который с помощью класса Paginator генерирует нужную страницу для отображения.

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
  4.  
  5.  
  6. def get_paginated_page(request, objects, number=10):
  7. current_page = Paginator(objects, number)
  8. page = request.GET.get('page') if request.method == 'GET' else request.POST.get('page')
  9. try:
  10. return current_page.page(page)
  11. except PageNotAnInteger:
  12. return current_page.page(1)
  13. except EmptyPage:
  14. return current_page.page(current_page.num_pages)

Для Django рекомендую VDS-сервера хостера Timeweb .

Вам это нравится? Поделитесь в социальных сетях!

B
  • 24 февраля 2020 г. 11:37

Евгений Здравствуйте! Не могу понять вот эту часть кода: url: jQuery(this).attr('action')
наверное здесь должен быть путь к url, тогда 'action' на какой url указывает?

Evgenii Legotckoi
  • 24 февраля 2020 г. 14:54

Добрый день. Там будет url, на который указывает ссылка тега a в пагинаторе, если правильно помню )) Написал этот код и забыл.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь