# What widget for m2m like in django admin?

**URL:** <https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510>\
**Category:** Forms & APIs\
**Created:** [November 18, 2024, 12:01am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510 "2024-11-18T00:01:06Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [November 18, 2024, 12:01am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/1 "2024-11-18T00:01:06Z")

</div>

Which widget can be used in a form to manage data with many-to-many relationships similar to the widgets of the user’s administrative form? The task seems to be standard, it has a solution, but there is no description of it anywhere. Some kind of “conspiracy of silence” may be …

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [November 18, 2024, 2:53am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/2 "2024-11-18T02:53:48Z")

</div>

Welcome @MikeWinny !

It’s not just a widget, there’s also some JavaScript and css involved as well.

You’ll want to find the components associated with the css class `related-widget-wrapper`, this is going to include

- `RelatedObjectLookups.js` in `django.contrib.admin.static.admin.js.admin`,
- `related_widget_wrapper.html` in `django.contrib.admin.templates.admin.widgets`,  
and
- `forms.css`, `responsive.css` and `widgets.css` in `django.contrib.admin.static.admin.css`.

Note: As these are undocumented, there is no assurance that these are going to remain the same across releases. As with any undocumented feature, it can be changed without notice and without going through the normal deprecation process.  
As a result, you might want to copy those components into your project and not just reference the existing versions from the Django installation.

---

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [December 6, 2024, 2:11pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/3 "2024-12-06T14:11:12Z")

</div>

Hi, Ken!  
Thank you for answer and recomendations.  
For a long time I have been trying to figure out how to use the technologies used in the above code, but so far without success. A very sophisticated, but universal code.  
In particular, it is unclear how to set the value of the `admin_site` parameter when using the User model from the Django kernel, and which one for models from the application.  
Unfortunately, I could not find any examples of successful use of this approach,

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [December 6, 2024, 3:17pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/4 "2024-12-06T15:17:03Z")

</div>

First, I think I copied some wrong information in my previous response. (The basic idea was right, but I think I identified the wrong javascript file.)

But beyond that, I think I’m kinda lost here and need more context about what it is you’re trying to do.

If you’re copying these components into your own app, I don’t see where the `admin_site` parameter needs to play any part in this.

The basic steps would be to find the JavaScript, CSS, and Templates that the admin uses, copy them to your app, and use them in a manner similar to how the admin does it. But there’s no need to implement everything in your code the way that the admin does it.  
(I’ve never done this, so I don’t have anything resembling a sample to work from.)

I believe the key point to keep in mind here is that if it’s done correctly, the Django code in the view doesn’t need to change - this is all front-end work with a custom widget. What the HTML form returns should be the same as what a multi-select box returns, making the use of this widget transparent to Django itself.

---

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [December 10, 2024, 11:54am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/5 "2024-12-10T11:54:28Z")

</div>

Hi, Ken!  
I probably defined the order of using the many-to-many functionality from the django admin panel in the application forms.  
However, it still refuses to search for a template in the templates folder of the application, and searches for it only in django core folders.  
Screen error message contains follow:

```auto
Template-loader postmortem
Django tried loading these templates, in this order:

Using engine django:

django.template.loaders.filesystem.Loader: C:\Users\USER1\.virtualenvs\application\lib\site-packages\django\forms\templates\inc\_related_widget_wrapper.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\Users\USER1\.virtualenvs\application\lib\site-packages\django\contrib\admin\templates\inc\_related_widget_wrapper.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\Users\USER1\.virtualenvs\application\lib\site-packages\django\contrib\auth\templates\inc\_related_widget_wrapper.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\Users\USER1\.virtualenvs\application\lib\site-packages\django_bootstrap5\templates\inc\_related_widget_wrapper.html (Source does not exist)

```

Template ‘ **\_related\_widget\_wrapper.html** ’ placed in ‘application/templates/inc’ folder, but in this case django doesn’t even look for it there.  
What could be the problem here?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [December 10, 2024, 1:12pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/6 "2024-12-10T13:12:13Z")

</div>

What is your `TEMPLATES` setting?

What is your `INSTALLED_APPS` setting?

(It doesn’t appear to be looking anywhere in your project, so those are the first two settings I’d check.)

---

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [December 10, 2024, 7:42pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/7 "2024-12-10T19:42:31Z")

</div>

Some standard (and worked without this wdget):

```auto
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

```

```auto
INSTALLED_APPS = [
    'main',
    'acc_req_answers',
    'advanced_user',
    'subject',
    'audited',
    'contractor',
    'department',
    'parameters',
    'project',
    'currency',
    'reports',
    'ml_models',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django_bootstrap5',

]

```

I guess this could be tied to the `admin_site` parameter in the widget call, which I made equal to `admin.site` from `django.contrib import admin`, but I have no idea what to equate it to in a regular application.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [December 10, 2024, 8:19pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/8 "2024-12-10T20:19:22Z")

</div>

What does your view and template look like where you’re trying to use this?

---

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [December 10, 2024, 9:01pm UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/9 "2024-12-10T21:01:43Z")

</div>

I use it in forms. For example, the form for Project atributes edit:

```auto
class ProjectForm(ModelForm):

    def __init__ (self, *args, **kwargs):
        super(). __init__ (*args, **kwargs)
        # personalize choices
        self.fields['access_list'].widget = RelatedFieldWidgetWrapper2(
            self.fields['access_list'].widget,
            self.instance._meta.get_field('access_list').remote_field,
            admin.site,
        )

    access_list = ModelMultipleChoiceField(
        queryset=User.objects.all(),
        widget=FilteredSelectMultiple(
            "Users",
            is_stacked=False
        )
    )

    class Meta:
        model = AuditProject
        fields = ('department', 'audited', 'name', 'manager', 'description', 'access_list')

```

where `RelatedFieldWidgetWrapper2` - copy of admin’s `RelatedFieldWidgetWrapper` with changed `template_name`, and `access_list` - m2m relation with `User` object.  
With form interact by view:

```auto
class ProjectEditView(LoginRequiredMixin, UpdateView):
    model = AuditProject
    template_name = 'project/project_edit.html'
    form_class = ProjectForm
    success_url = reverse_lazy('projects_list_url')

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [December 12, 2024, 3:55am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/10 "2024-12-12T03:55:54Z")

</div>

I’ve finally had a chance to spend some time to look at this in more detail.

In a way, it’s both easier and more difficult than I thought. There’s a lot of styling that needs to be accounted for in your CSS. If you’ve got a lot of css that you’re using, you may find that you’ll need to override some of it. For example, the admin css overrides the styling of the `<h2>` element.

Anyway, looking at the minimal requirements - this is what my PoC looks like.

CSS and JS currently included from the admin:

```auto
<link href="{% static 'admin/css/responsive.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'admin/css/widgets.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'admin/css/forms.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'admin/css/base.css' %}" type="text/css" rel="stylesheet">
<script src="{% url 'admin:jsi18n' %}"></script>
<script src="{% static 'admin/js/core.js' %}"></script>
<script src="{% static 'admin/js/SelectBox.js' %}"></script>
<script src="{% static 'admin/js/SelectFilter2.js' %}"></script>

```

The four JS files all seem to be necessary. Those CSS files aren’t strictly necessary, but you would need to know what selectors in those files are being used by the widget, and style them accordingly.

The template fragment for rendering the form:

```auto
{{ form }}

```

(Yep, the work for converting the select element to this widget is done by the JavaScript.)

The view:

```auto
class TestUpdate(UpdateView):
    model = TestModel
    form_class = TestForm
    template_name = 'test/form_template.html'

```

Strictly speaking, the template\_name attribute isn’t necessary if I name the template according to the defined default. (It would be something like `test/mytest_detail.html`)

The form:

```auto
from django.contrib.admin.widgets import FilteredSelectMultiple
class TestForm(forms.ModelForm):
    class Meta:
        model = TestModel
        fields = ' __all__'
        widgets = {
            'user': FilteredSelectMultiple(
                'Users',
                is_stacked=False,
            )
        }

```

In this sample, my `TestModel` has an m2m with `User` through a field named `user`.

---

<div class="post-metadata">

**Author:** ![MikeWinny](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mikewinny/32/24654_2.png) [@MikeWinny](https://forum.djangoproject.com/u/MikeWinny)\
**Post date:** [December 12, 2024, 9:52am UTC](https://forum.djangoproject.com/t/what-widget-for-m2m-like-in-django-admin/36510/11 "2024-12-12T09:52:00Z")

</div>

Hi, Ken!  
Thank you!  
You found an easier way to solve the problem.  
For more reliability, I moved the widget code, script and style files inside the project.
