16

I need help with form which looks like a Gmail inbox and have multiple actions. There is a list of items and I want to wrap it with form, on the way that every item have checkbox in the front of the line. So when user select few items he is able to click on two buttons with different actions for example delete and mark read.

<form action="">
    {% for item in object_list %}
    <input type="checkbox" id="item.id">
    {{ item.name }}
    {% endfor %}
    <button type="submit" name="delete">Delete</button>
    <button type="submit" name="mark_read">Mark read</button>
</form>

I can find which submit button is user click on if use if 'delete' in request.POST but I cant refer to any form because Django form cant be defined with unknown number of fields as I think. So how can I process selected items in view?

if request.method == 'POST':
    form = UnknownForm(request.POST):
    if 'delete' in request.POST:
        'delete selected items'
    if 'mark_read' in erquest.POST:
        'mark selected items as read'
    return HttpResponseRedirect('')

4 Answers 4

36

Multiple checkboxes with the same name are all the same field.

<input type="checkbox" value="{{item.id}}" name="choices">
<input type="checkbox" value="{{item.id}}" name="choices">
<input type="checkbox" value="{{item.id}}" name="choices">

you can collect and aggregate them with a single django form field.

class UnknownForm(forms.Form):
    choices = forms.MultipleChoiceField(
        choices = LIST_OF_VALID_CHOICES, # this is optional
        widget  = forms.CheckboxSelectMultiple,
    )

Specifically, you can use a ModelMultipleChoiceField.

class UnknownForm(forms.Form):
    choices = forms.ModelMultipleChoiceField(
        queryset = queryset_of_valid_choices, # not optional, use .all() if unsure
        widget  = forms.CheckboxSelectMultiple,
    )

if request.method == 'POST':
    form = UnknownForm(request.POST):
    if 'delete' in request.POST:
        for item in form.cleaned_data['choices']:
            item.delete()
    if 'mark_read' in request.POST:
        for item in form.cleaned_data['choices']:
            item.read = True; item.save()
Sign up to request clarification or add additional context in comments.

Comments

7

I was having this same issue using a class base view and iterating through a list of unknown size in the Django HTML template.

This solution uses 'post' and works for me. I'm putting this here because the above solutions were helpful but didn't solve the looping issue for me.

HTML Template:

<form action="" method="post">
    {% for item in object_list %}
        <input type="checkbox" value="{{item.id}}" name="my_object">
        {{ item.name }}
    {% endfor %}
    <button type="submit" name="delete">Delete</button>
    <button type="submit" name="mark_read">Mark read</button>
</form>

Form:

class MyForm(forms.Form):
    my_object = forms.MultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
    )

In the class-based view, access the POST data using a post function. This will allow access to a list of your checked items, your context, and other form data.

View:

Class MyView(FormView):
    template_name = "myawesometemplate.html"
    form_class = MyForm
...
# add your code here 

    def post(self, request, *args, **kwargs):
        ...
        context = self.get_context_data()
        ...
        if 'delete' in request.POST:
            for item in form.POST.getlist('my_object'):
                # Delete
        if 'mark_read' in request.POST:
            for item in form.POST.getlist('my_object'):
                # Mark as read

6 Comments

Forgive my newbieness, but how does it know what Model to get the data from for the checkboxes?
@dbinott are you talking about when it displays the checkbox information or after the form is submitted?
When it displays the checkbox info. I am trying to output a list of songs grouped by album and need to be able check each song.
create a function in your class called get_context_data(self, **kwargs). Inside you can grab the context data and set the data to be used by your checkboxes. eg. context[checkbox_list] = self.list_of_checkbox_items then you can iterate through or access that data in your template using the variable 'checkbox_list' Here is a good explanation in the django docs: class based views with mixin
@dbinott also to set the model, you do this at the top of your class with the other variable initializations: class AuthorDetail(FormMixin, DetailView): model = Author form_class = AuthorInterestForm def get_context_data() ...
|
6

I can't comment Thomas solution so I do it here.

For ModelMultipleChoiceField the argument name is not choices but queryset.

So to take the last example:

class UnknownForm(forms.Form):
choices = forms.ModelMultipleChoiceField(
    choices = queryset_of_valid_choices, # not optional, use .all() if unsure
    widget  = forms.CheckboxSelectMultiple,
)

Comments

-1

I found this to be very useful when adding groups or permissions to a user.

Make sure that you have the default django groups and permissions included in your view for the page.

from django.contrib.auth.models import Permission, Group

If you are pulling the choices from a database table you can use a django form object with a widget to dynamically load all of the available choices. You also need to make sure that your form names are the same as the model name.

    groups = forms.ModelMultipleChoiceField(label='Groups', required=False, queryset=Group.objects.all(), widget=forms.CheckboxSelectMultiple)
user_permissions = forms.ModelMultipleChoiceField(label='Permissions', required=False, queryset=Permission.objects.all(), widget=forms.CheckboxSelectMultiple)

Then in the post section of the view method for that page you can get the selected choices returned as a list of the choice objects, and add them to the user object with a for loop.

u.save()  # required to save twice, so this saves the other form fields.
        user.groups.clear()
        u.user_permissions.clear()
        # print('Group name:', form.cleaned_data['groups'])
        for group in form.cleaned_data['groups']:
            print(group)  # Prints to your console for debugging
            user.groups.add(group)
        for permission in form.cleaned_data['user_permissions']:
            print(permission)  # Prints to your console for debugging
            user.user_permissions.add(permission)
        u.save()  #This saves the groups and permissions

This may still need some logic if no groups are selected, but should be enough to get started.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.