Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacement for the default ManyToMany Widget of Forms

Tags:

django

I know who to replace the ManyToMany Widget in the django admin interface:

class SomeModelAdmin(admin.ModelAdmin):
    filter_horizontal = ('users',)

But how to replace the default widget in all views?

I guess this could be implemented without changing one line in my code or in django.

It would be great if I could the something like the Author part (two boxes for a ManyToMany input) in this picture with a widget:

enter image description here

I can use ctrl + click, but my users can't. They want an easier way.

Is there a way to replace all select-multiple widgets without many changes in my code?

like image 695
guettli Avatar asked Apr 08 '16 09:04

guettli


2 Answers

You can use a base class which ovride formfield_for_manytomany

from django.contrib.admin import widgets

class ManyToManyAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        kwargs['widget']= widgets.FilteredSelectMultiple(
            db_field.verbose_name,
            db_field.name in self.filter_vertical
        )

        return super(admin.ModelAdmin, self).formfield_for_manytomany(
            db_field, request=request, **kwargs)

class SomeModelAdmin(ManyToManyAdmin):
    pass
like image 185
ahmed Avatar answered Oct 26 '22 12:10

ahmed


If I do not understand your question, please clarify in the comments.

The Django Form Widgets Documentation offers several pre-fab widgets that you can use to specify how a field in your form is rendered.

Without modifying the code in my views, I can simply add a widget keyword arg to the appropriate form fields.

forms.py (v1)

class TestForm(forms.Form):
    CHOICES = (
    (1, "choice1"),
    (2, "choice2"),
    (3, "choice3"),
    )
    field = forms.ChoiceField(choices=CHOICES)

This renders as a dropdown select:

<select id="id_field" name="field">
  <option value="1">choice1</option>
  <option value="2">choice2</option>
  <option value="3">choice3</option>
</select>

Now adding the widget keyword arg to my field:

forms.py (v2)

field = forms.ChoiceField(choices=CHOICES, widget=forms.CheckboxSelectMultiple)

This renders as a list of checkboxes:

<ul id="id_field">
<li>
  <label for="id_field_0"><input id="id_field_0" name="field" type="checkbox" value="1" /> choice1</label>
</li>
<li>
  <label for="id_field_1"><input id="id_field_1" name="field" type="checkbox" value="2" /> choice2</label>
</li>
<li>
  <label for="id_field_2"><input id="id_field_2" name="field" type="checkbox" value="3" /> choice3</label>
</li>
</ul>

Edit

It is also possible to add widgets that are used by Django Admin. See Django multi-select widget? for more information about this. Simply import the appropriate widget:

from django.contrib.admin.widgets import FilteredSelectMultiple

and use that instead. Note that in this particular case you will also need to include the form's media to get the desired effect.

like image 33
brianpck Avatar answered Oct 26 '22 13:10

brianpck