django分頁和消息隊列


起源於一個神奇的項目,內容就是爬取搜索引擎的搜索重新建立一個搜索入口.然後就碰到了一些問題:
1. 一次性爬取所有頁面的話爬取速度是一個問題,能不能爬一頁的內容先展示出來,後臺繼續爬取第二頁及以後的頁面,在繼續展示?
2. 爬取的結果展示出來是需要進行分頁的,如何分頁?

這裏首先講解一下如何分頁

Paginator

在django中分頁採用的是Paginator,舉個例子

from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
from hello.models import Topic


def index(request):
    limit = 3  # 每頁顯示的記錄數
    topics = Topic.objects.all()
    paginator = Paginator(topics, limit)  # 實例化一個分頁對象

    page = request.GET.get('page')  # 獲取頁碼
    try:
        topics = paginator.page(page)  # 獲取某頁對應的記錄
    except PageNotAnInteger:  # 如果頁碼不是個整數
        topics = paginator.page(1)  # 取第一頁的記錄
    except EmptyPage:  # 如果頁碼太大,沒有相應的記錄
        topics = paginator.page(paginator.num_pages)  # 取最後一頁的記錄

    return render_to_response('index.html', {'topics': topics})

然後在模板裏面的稍作修改即可

{% for topic in topics.object_list %}
  <p>{{ topic.title }}</p>
{% endfor %}

<!-- 第一種分頁顯示方式 -->
<p>
  {# topics.paginator.page_range 這個函數返回包含一個所有頁碼數的 range 對象 #}
  {# 即 range(1, topics.paginator.num_pages + 1) #}
  {% for page_number in topics.paginator.page_range %}
    {% ifequal page_number  topics.number %}
      {{ page_number }}
    {% else %}
      <a href="?page={{ page_number }}">{{ page_number }}</a>
    {% endifequal %}
  {% endfor %}
</p>


<!-- 另一種分頁顯示方式 -->
<p>
{% if topics.has_previous %}
  <a href="?page={{ topics.previous_page_number }}">Previous</a>
  {% endif %}
  {# topics.paginator.number_pages 返回總頁數 #}
  Page {{ topics.number }} of {{ topics.paginator.num_pages }}.
{% if topics.has_next %}
  <a href="?page={{ topics.next_page_number }}">Next</a>
{% endif %}
</p>

但是在這裏的解決方案不太適用,當前的結果並不是存在數據庫裏面的,而是實時查詢爬取解析得來的,那麼,這種分頁方案似乎是不太適用,沒辦法,最後採取的方式是爬取特定或全部的頁面,存入數據庫,然後進一步的分頁展示.這裏就到了第一個問題:能否後臺運行爬取任務,翻頁時減少等待.
這裏就需要用到celery,注意的一點是以前的版本需要一個單獨的django-celery保障django和celery的聯合使用,現在不需要了,一個celery庫即可.

Celery

按照官方教程配置,建議不懂celery的,先看一下這個,如果出現server問題,解決方案一般是需要安裝對應的server,如redis等. 另外重要的一點爲了能夠使用後臺運行的結果,按照教程中需要安裝django-celery-results,同時在後臺task函數裏面,將結果保存在django_celery_results.models.TaskResult裏面,

from __future__ import absolute_import, unicode_literals
from django-celery-results import models
from celery import shared_task
@shared_task
def func(x, y):
    result = somefunction(x+y)
    id = anotherfunction(x+y)
    django_result = models.TaskResult(task_id = id,result=result)
    django_result.save()
    return id

在後臺運行的結果就可以task_id重新查詢到,當然在這裏如果考慮到實時和數據庫容量的問題,則需要定時或者在sseion結束時刪除掉對應task_id的運行結果,這裏同樣需要用celery制定一個定時任務
Tips:
1. 在運行celery -A proj worker –loglevel=info出現問題”server channel error 406, message: PRECONDITION_FAILED”的一個原因可能是你在另外一個地方運行了同樣的celery 任務,這裏需要的就是關掉其他運行的celery任務,就沒有問題了.
2. 查看文檔比查看博客來的準確,查github作爲具體項目的借鑑.

參考文獻

  1. https://mozillazg.github.io/2013/01/django-pagination-by-use-paginator.html
  2. 文中鏈接引用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章