Django: Replacement for the default ManyToMany Wid

2020-08-23 11:50发布

问题:

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:

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?

回答1:

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.



回答2:

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


回答3:

You need to replace default widgets in django forms.

This works for me:

    from django import forms
    from django.contrib.admin.widgets import FilteredSelectMultiple
    # replace MultipleChoise in Forms
    forms.MultipleChoiceField.widget = FilteredSelectMultiple("verbose name", is_stacked=False)
    # replace MultipleChoise in ModelForms
    forms.ModelMultipleChoiceField.widget = FilteredSelectMultiple("verbose name", is_stacked=False)
  1. Add code above to file forms_setup.py in your main project
  2. Then in settings.py import file forms_setup.py:

    from .forms_setup import *