You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在基于类的ListView中使用Django-RQ?示例查询集疑问

给你搞定Django-RQ结合ListView查询集的方案

嘿,我明白你想把Django-RQ和这个产品列表视图的查询集结合起来的需求,但得先理清一个关键点:Django的ListView是同步渲染页面的,没法直接在get_queryset里用异步任务返回结果——因为异步任务是在后台跑的,没法即时把数据传给页面渲染。不过有两种靠谱的方案能实现你的目标,我给你详细拆解:

方案一:异步预取+缓存查询结果(适合耗时但访问频繁的场景)

这个思路是用RQ异步计算查询集并缓存起来,视图优先读缓存,缓存没命中的时候再触发异步任务更新,同时先返回基础的同步查询结果(不让用户空等)。

第一步:写个RQ异步任务

先在你的tasks.py里定义任务,专门处理查询并缓存结果:

from django.core.cache import cache
from .models import Product

def fetch_partner_products(partner_id):
    # 这里可以加优化,比如用select_related/prefetch_related减少数据库查询次数
    queryset = Product.objects.filter(stockrecords__partner__id=partner_id)
    # 把查询集转成ID列表存缓存(比存整个对象更高效)
    product_ids = list(queryset.values_list('id', flat=True))
    # 设置缓存键和过期时间(比如1小时,根据你的数据更新频率调整)
    cache_key = f"partner_products_{partner_id}"
    cache.set(cache_key, product_ids, 3600)
    return product_ids

第二步:修改你的ListView视图

把原来的get_queryset改成优先读缓存,没缓存就触发异步任务:

from django.core.cache import cache
from django_rq import enqueue
from .tasks import fetch_partner_products

class PartnerCatalogueView(generic.ListView):
    """ Browse all products in the catalogue """
    template_name = 'catalogue/partner.html'
    paginate_by = 9
    model = Product
    context_object_name = 'prod'

    def get_queryset(self, **kwargs):
        partner_id = self.kwargs['pk']
        cache_key = f"partner_products_{partner_id}"
        cached_product_ids = cache.get(cache_key)

        if cached_product_ids:
            # 直接从缓存拿ID,快速生成查询集
            queryset = Product.objects.filter(id__in=cached_product_ids)
        else:
            # 缓存没命中时,先同步返回基础查询集(不让用户等)
            queryset = super().get_queryset().filter(stockrecords__partner__id=partner_id)
            # 扔个异步任务到RQ队列,后台更新缓存
            enqueue(fetch_partner_products, partner_id)
        
        return queryset

方案二:前端异步加载(适合查询极慢,不想让用户等页面的场景)

如果你的查询集计算特别耗时,干脆让页面先渲染空框架,然后用AJAX异步拉取数据,后端用RQ处理查询并返回结果。

第一步:写个API视图处理异步请求

新增一个API视图,负责检查缓存或触发RQ任务:

from django.http import JsonResponse
from django.core.cache import cache
from django_rq import enqueue, get_queue
from .models import Product
from .tasks import fetch_partner_products

def partner_products_api(request, pk):
    partner_id = pk
    cache_key = f"partner_products_{partner_id}"
    cached_product_ids = cache.get(cache_key)

    if cached_product_ids:
        # 缓存存在,返回序列化后的产品数据
        products = list(Product.objects.filter(id__in=cached_product_ids).values('id', 'name', 'price'))
        return JsonResponse({'status': 'ready', 'products': products})
    else:
        # 检查任务是否已经在队列里,避免重复触发
        queue = get_queue()
        task_id = f"fetch_partner_products_{partner_id}"
        if task_id not in queue.get_job_ids():
            enqueue(fetch_partner_products, partner_id, job_id=task_id)
        # 返回"加载中"状态,让前端重试
        return JsonResponse({'status': 'pending'})

第二步:修改ListView渲染空页面,加前端AJAX逻辑

视图只需要把partner_id传到模板,不用处理查询集:

class PartnerCatalogueView(generic.ListView):
    template_name = 'catalogue/partner.html'
    model = Product
    context_object_name = 'prod'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 把partner_id传给模板,供AJAX用
        context['partner_id'] = self.kwargs['pk']
        return context

然后在catalogue/partner.html模板里加JS,实现异步加载:

<!-- 先渲染空的产品容器 -->
<div id="products-container" class="product-grid">
    <p>加载中...</p>
</div>

<script>
const partnerId = "{{ partner_id }}";
const productsContainer = document.getElementById('products-container');

function fetchProducts() {
    fetch(`/api/partner/${partnerId}/products/`)
        .then(res => res.json())
        .then(data => {
            if (data.status === 'ready') {
                // 清空加载提示,渲染产品列表
                productsContainer.innerHTML = '';
                data.products.forEach(prod => {
                    productsContainer.innerHTML += `
                        <div class="product-card">
                            <h3>${prod.name}</h3>
                            <p>价格:$${prod.price}</p>
                        </div>
                    `;
                });
            } else {
                // 还在加载,2秒后重试
                setTimeout(fetchProducts, 2000);
            }
        })
        .catch(err => {
            productsContainer.innerHTML = '<p>加载出错了,请刷新页面重试</p>';
        });
}

// 页面加载完就开始请求数据
window.addEventListener('load', fetchProducts);
</script>

额外提醒

  • 确保你已经正确配置了Django-RQ(比如用Redis作为消息队列,在settings里设置好RQ的配置)
  • 缓存的过期时间要根据你的产品数据更新频率来调整,别太长导致用户看到旧数据
  • 如果需要分页,方案一可以缓存分页后的ID列表,方案二可以在API里接收page参数处理分页

内容的提问来源于stack exchange,提问作者takeiteasy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:17:49