基于Django EL在Wagtail中实现AJAX无限分页的技术咨询
Hey there! Let's break down how to implement AJAX infinite scrolling in Wagtail—you don't have to feel stuck with messy code anymore. The short answer is: both approaches work, and which one you pick depends on your project's structure and needs. Let's walk through each method with concrete examples.
Option 1: Use Wagtail Page's serve Method (Simpler for Page-Tied Content)
This is great if your paginated content is directly tied to a Wagtail page (like a blog index page). You don't need extra URL routes—just extend your existing page logic to handle AJAX requests.
Step-by-Step Implementation
Update your Page Model
Override theservemethod to detect AJAX requests and return only the content fragment instead of the full page:from wagtail.models import Page from django.core.paginator import Paginator from django.shortcuts import render class BlogIndexPage(Page): def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) # Fetch your paginated content posts = self.get_children().live().public().order_by('-first_published_at') paginator = Paginator(posts, 10) # Show 10 posts per page current_page = request.GET.get('page', 1) context['posts'] = paginator.get_page(current_page) # Flag to check if this is an AJAX request context['is_ajax'] = request.headers.get('X-Requested-With') == 'XMLHttpRequest' return context def serve(self, request, *args, **kwargs): context = self.get_context(request, *args, **kwargs) if context['is_ajax']: # Return only the post items fragment (no full page layout) return render(request, 'blog/post_fragment.html', context) # Return the full page for non-AJAX requests return render(request, self.template, context)Create a Fragment Template
Makeblog/post_fragment.htmlto render just the list items (no header/footer):{% for post in posts %} <div class="post-item"> <h3><a href="{{ post.url }}">{{ post.title }}</a></h3> <p>{{ post.specific.intro }}</p> </div> {% endfor %}Add Frontend JS
Listen for scroll events, send AJAX requests, and append the new content:document.addEventListener('DOMContentLoaded', () => { const postContainer = document.querySelector('.post-container'); let currentPage = 1; let isLoading = false; const loadMorePosts = () => { if (isLoading) return; isLoading = true; currentPage++; fetch(`?page=${currentPage}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => { if (!res.ok) throw new Error('Failed to load posts'); return res.text(); }) .then(html => { if (!html.trim()) { // No more posts—stop listening for scroll window.removeEventListener('scroll', handleScroll); postContainer.insertAdjacentHTML('beforeend', '<p>No more posts to show!</p>'); return; } postContainer.insertAdjacentHTML('beforeend', html); isLoading = false; }) .catch(err => { console.error('Error:', err); isLoading = false; }); }; const handleScroll = () => { // Trigger load when 200px from the bottom const scrollPosition = window.innerHeight + window.scrollY; const pageHeight = document.body.offsetHeight; if (scrollPosition >= pageHeight - 200) loadMorePosts(); }; window.addEventListener('scroll', handleScroll); });
Option 2: Create a Standalone Django View (More Flexible)
Use this if you need reusable pagination logic, or want to return JSON instead of HTML. It requires setting up a separate URL route.
Step-by-Step Implementation
Create an AJAX View
In your app'sviews.py, make a view to handle pagination requests:from django.shortcuts import render from django.http import HttpResponseBadRequest from wagtail.models import Page from django.core.paginator import Paginator def ajax_posts(request): if not request.headers.get('X-Requested-With') == 'XMLHttpRequest': return HttpResponseBadRequest('Invalid request') page_num = request.GET.get('page', 1) posts = Page.objects.type(BlogPage).live().public().order_by('-first_published_at') paginator = Paginator(posts, 10) page = paginator.get_page(page_num) return render(request, 'blog/post_fragment.html', {'posts': page})Add a URL Route
In your project'surls.py, map the view to a URL:from django.urls import path from .views import ajax_posts urlpatterns = [ # ... other routes path('ajax/blog-posts/', ajax_posts, name='ajax_blog_posts'), ]Adjust the Frontend JS
Just update the fetch URL to point to your new route:// ... inside loadMorePosts() fetch(`/ajax/blog-posts/?page=${currentPage}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) // ... rest of the code stays the same
Key Tips to Avoid Headaches
- Handle Loading States: Add a spinner or "Loading..." text while fetching content to prevent duplicate requests.
- Edge Cases: Check if the returned HTML is empty (no more content) and stop listening for scroll events.
- Test Responsively: Make sure the scroll trigger works on mobile devices (adjust the offset if needed).
- Reuse Templates: Keep your fragment template separate from the full page to avoid code duplication.
内容的提问来源于stack exchange,提问作者Henry Lynx

