分页Django表单集

9

我有一个模型表单集(model formset),想要使用Django的Paginator每次显示10个表单,但是不能像这样做:paginator = Paginator(formset, 10)。如果有办法,正确的做法是什么?


我也花了很多时间试图解决这个问题。结果发现我们的想法完全错误了。为什么要对表单集进行分页?你一次只能编辑一页,对吧?因此,像往常一样对整个查询进行分页,并仅为单个页面创建表单集!搞定。https://code.djangoproject.com/ticket/30632 - Beki
5个回答

17

这是我发现用于解决问题的通用示例:

forms.py 文件中:

class MyForm(ModelForm):
    class Meta:
        model = MyModel
        fields = ('description',)

views.py文件中:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

FormSet = modelformset_factory(MyModel, form=MyForm, extra=0)
if request.method == 'POST':
    formset = FormSet(request.POST, request.FILES)
    # Your validation and rest of the 'POST' code
else:
    query = MyModel.objects.filter(condition)
    paginator = Paginator(query, 10) # Show 10 forms per page
    page = request.GET.get('page')
    try:
        objects = paginator.page(page)
    except PageNotAnInteger:
        objects = paginator.page(1)
    except EmptyPage:
        objects = paginator.page(paginator.num_pages)
    page_query = query.filter(id__in=[object.id for object in objects])
    formset = FormSet(queryset=page_query)
    context = {'objects': objects, 'formset': formset}
    return render_to_response('template.html', context,
                              context_instance=RequestContext(request))

您需要在当前页面中使用对象创建表单集,否则,当您在POST方法中尝试执行formset = FormSet(request.POST, request.FILES)时,Django会引发MultiValueDictKeyError错误。

template.html文件中:

{% if objects %}
    <form action="" method="post">
        {% csrf_token %}
        {{ formset.management_form }}
        {% for form in formset.forms %}
            {{ form.id }}
            <!-- Display each form -->
            {{ form.as_p }}
        {% endfor %}
        <input type="submit" value="Save" />
    </form>

    <div class="pagination">
        <span class="step-links">
            {% if objects.has_previous %}
                <a href="?page={{ objects.previous_page_number }}">Previous</a>
            {% endif %}

            <span class="current">
                Page {{ objects.number }} of {{ objects.paginator.num_pages }}
            </span>

            {% if objects.has_next %}
                <a href="?page={{ objects.next_page_number }}">next</a>
            {% endif %}
        </span>
    </div>
{% else %}
    <p>There are no objects.</p>
{% endif %}

重点在于“您需要使用当前页面中的对象创建表单集”。您可能会遇到除MultiValueDictKeyError之外的其他错误。 - Lord Elrond

3
更优雅的解决方案是在Page对象上设置ordered=True,以便可以将其传递给ModelFormSet
下面是一个示例:
forms_per_page = 10
current_page = 1

ModelFormSet = modelformset_factory(MyModel, form=MyForm)
queryset = MyModel.objects.all()

paginator = Paginator(queryset, forms_per_page)
page_object = paginator.page(current_page)
page_object.ordered = True

form = ModelFormSet(queryset=page_object)

这比已接受的答案更有效,因为它避免了在以下行中进行的第二个数据库查询:
page_query = query.filter(id__in=[object.id for object in objects])

你能提供一个更完整的例子吗?因为你正在将你的答案与被接受的答案进行比较。 - Bobort

2

更正确的使用方法

...
formset = FormSet(queryset=page_query.object_list)
...

你能详细说明一下吗?听起来很有趣。 - Ze'ev G
这是行不通的,因为BaseModelFormSet需要QuerySet对象,而且会在使用list时失败。 - Gill Bates
1
这导致出现以下错误:Cannot filter a query once a slice has been taken. @GillBates: object_list 返回的是一个 QuerySet,而不是一个 list。所以请使用 @Filly 的答案! - Caumons

0
这里的问题是您正在使用页面(Page)在期望QuerySet的上下文中。因此,我们需要那个该死的QuerySet。您正在正确的道路上,但需要大量代码。
源代码中我们有:
class Page(collections.Sequence):

    def __init__(self, object_list, number, paginator):
        self.object_list = object_list
        self.number = number
        self.paginator = paginator
        ...

那么,我们的查询集在 self.object_list 属性中,直接使用它即可!
formset = SomeModelFormSet(queryset=objects.object_list)

0

同意Elrond Supports Monica。虚假属性是解决排序错误(无法在切片后重新排序查询)的有趣方法。

但也可以用一行代码来解决: queryset = queryset.order_by(Entry._meta.pk.name)

这种虚假排序是为了避免在django.form.modelsBaseModelFormSet(BaseFormSet).get_queryset(): line #640中出现错误, 该错误通过pk进行人工排序,但在切片后不可能进行排序(SQL中的限制)。

更详细的例子

    queryset = Entry.objects.all()
    queryset = queryset.order_by(Entry._meta.pk.name)

    paginator = Paginator(object_list=queryset, per_page=10)
    page_obj = paginator.get_page(request.GET.get('page'))

    EntryFormSet = modelformset_factory(Entry, EntryForm, extra=0)
    entryformset = EntryFormSet(queryset=page_obj.object_list)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接