Paginate Django formset

前端 未结 5 901
失恋的感觉
失恋的感觉 2020-12-29 14:42

I have a model formset that I want to display 10 forms at a time using Django\'s Paginator, but it can\'t be done like paginator = Paginator(formset, 10). What\

相关标签:
5条回答
  • 2020-12-29 15:07

    A more elegant solution is to set ordered=True on the Page object so that it can be passed to a ModelFormSet.

    Here is an example:

    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)
    

    This is more efficient than the accepted answer because avoids the second database query that takes place in the line:

    page_query = query.filter(id__in=[object.id for object in objects])
    
    0 讨论(0)
  • 2020-12-29 15:13

    The problem here is that you're using brands (a Page) in a context that's expecting a QuerySet. So, we need that damn QuerySet. You are in right way, but a lot of code.

    In source code we have:

    class Page(collections.Sequence):
    
        def __init__(self, object_list, number, paginator):
            self.object_list = object_list
            self.number = number
            self.paginator = paginator
            ...
    

    So, our queryset in self.object_list attribute and just use it!

    formset = SomeModelFormSet(queryset=objects.object_list)
    
    0 讨论(0)
  • 2020-12-29 15:21

    More correct way to use this

    ...
    formset = FormSet(queryset=page_query.object_list)
    ...
    
    0 讨论(0)
  • 2020-12-29 15:23

    This is a generic example of the solution I found to my problem:

    In the forms.py file:

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

    In the views.py file:

    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))
    

    You need to create the formset with the objects in the present page, otherwise, when you try to do formset = FormSet(request.POST, request.FILES) in the POST method, Django raises a MultiValueDictKeyError error.

    In the template.html file:

    {% 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 %}
    
    0 讨论(0)
  • 2020-12-29 15:23

    Agree with Elrond Supports Monica. Fake attribute is interesting way to resolve the ordering error (Cannot reorder a query once a slice has been taken.)

    But it can be fixed in one line also queryset = queryset.order_by(Entry._meta.pk.name)

    This fake ordering is need for avoid error in django.form.modelsBaseModelFormSet(BaseFormSet).get_queryset(): line #640
    that make artificial ordering by pk but it impossible after slicing (LIMIT-ations in SQL )

    More detailed example

        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)
    
    0 讨论(0)
提交回复
热议问题