ISO-27001-Risk-Management/.venv/lib/python3.11/site-packages/django_multiselectfield-1.0.1.dist-info/METADATA
2025-09-24 18:13:40 +02:00

686 lines
25 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Metadata-Version: 2.4
Name: django-multiselectfield
Version: 1.0.1
Summary: Django multiple select field
Home-page: https://github.com/goinnn/django-multiselectfield
Author: Pablo Martin
Author-email: goinnn@gmail.com
License: LGPL 3
Keywords: django,multiple,select,field,choices
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
License-File: COPYING.LGPLv3
Requires-Dist: django>=3.2
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: summary
=======================
django-multiselectfield
=======================
.. image:: https://github.com/goinnn/django-multiselectfield/actions/workflows/tests.yaml/badge.svg
:target: https://github.com/goinnn/django-multiselectfield/actions/workflows/tests.yaml
.. image:: https://coveralls.io/repos/goinnn/django-multiselectfield/badge.png?branch=master
:target: https://coveralls.io/r/goinnn/django-multiselectfield
.. image:: https://badge.fury.io/py/django-multiselectfield.png
:target: https://badge.fury.io/py/django-multiselectfield
-------------------
Support this package by donating here! ➡️
=========================================
If you find this package useful, consider supporting it:
.. image:: https://raw.githubusercontent.com/goinnn/django-multiselectfield/master/assets/arial-yellow.png
:target: https://www.buymeacoffee.com/goinnn
:alt: Buy Me a Coffee
:height: 40px
.. image:: https://img.shields.io/badge/PayPal-badge?style=plastic&logo=paypal&color=white
:target: https://www.paypal.com/paypalme/goinnn
:alt: Paypal
:height: 40px
-------------------
django-multiselectfield provides new model and form fields for Django models, allowing multiple selections from a list of choices. The selected values are stored in the database as a CharField containing a comma-separated values.
This package is inspired by this `snippet <https://djangosnippets.org/snippets/1200/>`_.
*Note: This snippet is from 2008, and a lot has changed since then.*
**Supported Python versions**: 3.8+
**Supported Django versions**: 3.2+
1. Installation
================
1.1 Install with pip
---------------------
.. code-block:: bash
$ pip install django-multiselectfield
1.2 Configure your models.py
----------------------------
.. code-block:: python
from multiselectfield import MultiSelectField
# ...
MY_CHOICES = (('item_key1', 'Item title 1.1'),
('item_key2', 'Item title 1.2'),
('item_key3', 'Item title 1.3'),
('item_key4', 'Item title 1.4'),
('item_key5', 'Item title 1.5'))
MY_CHOICES2 = (('1', 'Item title 2.1'),
('2', 'Item title 2.2'),
('3', 'Item title 2.3'),
('4', 'Item title 2.4'),
('5', 'Item title 2.5'))
class MyModel(models.Model):
# .....
my_field = MultiSelectField(choices=MY_CHOICES, default=['item_key1', 'item_key5'])
my_field2 = MultiSelectField(choices=MY_CHOICES2, min_choices=2, max_choices=3, max_length=3)
# Do not use integer choices like this:
MY_INTEGER_CHOICES2 = ((1, 'Item title 2.1'),
(2, 'Item title 2.2'),
(3, 'Item title 2.3'),
(4, 'Item title 2.4'),
(5, 'Item title 2.5'))
# Because when MultiSelectField retrieves data from db, it cannot know if the values are integers or strings.
# In other words, MultiSelectField save the same data for MY_CHOICES2 and MY_INTEGER_CHOICES2
# Or in practice it should be the same MY_CHOICES2 and MY_INTEGER_CHOICES2
1.3 In your settings.py
-----------------------
Only required if you want the translation of django-multiselectfield or need its static files.
.. code-block:: python
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
#.....................#
'multiselectfield',
)
1.4 SortMultiSelectField
------------------------
Since version 1.0.0 (2025-06-12), this package also includes a another field type called: SortMultiSelectField.
For this field to work, you need to include `jQuery <https://jquery.com/download/>`_ (already included in the Django admin) and `jQuery UI <https://jqueryui.com/download/>`_.
You can include them by updating the ModelAdmins form or directly in change_form.html (less efficient but faster), as shown in the `example project: change_form.html line 11 <https://github.com/goinnn/django-multiselectfield/blob/b7d113a4a1ad6b35698126729264942e30e30039/example/templates/admin/change_form.html#L11>`_.
1.5 Other recommendations
-------------------------
`As django recommended: <https://docs.djangoproject.com/en/5.2/ref/models/fields/#django.db.models.Field.null>`_ Avoid using null on string-based fields such as CharField and TextField.
MultiSelectField is based on CharField (MultiSelectField inheritances of CharField). So, if the field is not required, use only blank=True (null=False by default):
.. code-block:: python
class MyModel(models.Model):
# .....
my_field = MultiSelectField(choices=MY_CHOICES, blank=True)
2. Custom and integrations
===========================
2.1 Customizing templates
--------------------------
You can customize the HTML of this widget in your form template. To do so, you will need to loop through ``form.{field}.field.choices``. Here is an example that displays the field label underneath/after the checkbox for a ``MultiSelectField`` called ``providers``:
.. code-block:: HTML+Django
{% for value, text in form.providers.field.choices %}
<div class="ui slider checkbox">
<input id="id_providers_{{ forloop.counter0 }}" name="{{ form.providers.name }}" type="checkbox" value="{{ value }}"{% if value in checked_providers %} checked="checked"{% endif %}>
<label>{{ text }}</label>
</div>
{% endfor %}
2.2 Fixing CSS alignment in the Django administration
------------------------------------------------------
This fixes alignment. The labels appear slightly lower than the checkboxes, and the label width is very small.
Include the following CSS file: multiselectfield/css/admin-multiselectfield.css
You can include it by updating the ModelAdmins form or directly in change_form.html (less efficient but faster), as shown in the `example project: change_form.html line 7 <https://github.com/goinnn/django-multiselectfield/blob/b7d113a4a1ad6b35698126729264942e30e30039/example/templates/admin/change_form.html#L7>`_.
2.3 Add a filter to the Django administration
----------------------------------------------
You can see it in `example project: admin.py line 23 <https://github.com/goinnn/django-multiselectfield/blob/4ee111e11e2f3a51aa693c0863ee64d93b4a097d/example/app/admin.py#L23>`_
.. code-block:: python
from django.contrib import admin
def _multiple_choice_filter(field_name, label):
class MultiSelectFilter(admin.SimpleListFilter):
title = label
parameter_name = field_name
def lookups(self, request, model_admin):
return model_admin.model._meta.get_field(field_name).flatchoices
def queryset(self, request, queryset):
value = self.value()
if value:
queryset = queryset.filter(Q(**{
f'{self.parameter_name}__exact': value,
}) | Q(**{
f'{self.parameter_name}__startswith': f'{value},',
}) | Q(**{
f'{self.parameter_name}__endswith': f',{value}'
}) | Q(**{
f'{self.parameter_name}__icontains': f',{value},'
}))
return queryset
return MultiSelectFilter
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'categories', 'tags', 'published_in')
list_filter = (
_multiple_choice_filter('categories', _('categories')),
_multiple_choice_filter('tags', _('tags')),
_multiple_choice_filter('favorite_tags', _('favourite tags')),
_multiple_choice_filter('published_in', _('province or state')),
_multiple_choice_filter('chapters', _('chapters')),
)
2.4 Add a django multiselect field to list_display in Django administration
----------------------------------------------------------------------------
Django doesn't provide built-in support for custom fields.
2.4.1 Option 1. Use get_FOO_display
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Change them individually
.. code-block:: python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'get_categories_display',)
@admin.display(description=_('categories'), ordering='categories')
def get_categories_display(self, obj):
return obj.get_categories_display()
2.4.2 Option 2. Monkey patching Django
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you have many django multiselect fields in list_display, the previous option can be much work.
You can see it in the `example project: apps.py line 34 <https://github.com/goinnn/django-multiselectfield/blob/65376239ae7491414f896adb4d314349ff7c2667/example/app/apps.py#L34>`_.
This code is inspired by django code. It is possible that for other versions of Django you may need to adapt it.
.. code-block:: python
from django.apps import AppConfig
from django import VERSION
from django.contrib.admin import utils
from django.utils.hashable import make_hashable
from multiselectfield.db.fields import MultiSelectField
class AppAppConfig(AppConfig):
name = 'app'
verbose_name = 'app'
def ready(self):
if not hasattr(utils, '_original_display_for_field'):
utils._original_display_for_field = utils.display_for_field
utils.display_for_field = patched_display_for_field
# Monkey patching for use multiselect field in list_display
def patched_display_for_field(value, field, empty_value_display, avoid_link=False):
if isinstance(field, MultiSelectField) and getattr(field, "flatchoices", None):
try:
flatchoices = dict(field.flatchoices)
return ', '.join([str(flatchoices.get(v, empty_value_display)) for v in value]) or empty_value_display
except TypeError:
# Allow list-like choices.
flatchoices = dict(make_hashable(field.flatchoices))
value = make_hashable(value)
return ', '.join([str(flatchoices.get(v, empty_value_display)) for v in value]) or empty_value_display
if VERSION < (5, 2):
return utils._original_display_for_field(value, field, empty_value_display)
return utils._original_display_for_field(value, field, empty_value_display, avoid_link=avoid_link)
2.5 Add support for read-only fields in the Django administration
-----------------------------------------------------------------
Django doesn't provide built-in support for custom fields.
You can see it in the `example project: apps.py line 52 <https://github.com/goinnn/django-multiselectfield/blob/65376239ae7491414f896adb4d314349ff7c2667/example/app/apps.py#L52>`_. Log in to the Django admin in the sample project using the following credentials: user-readonly / DMF-123.
This code is inspired by django code. It is possible that for other versions of Django you may need to adapt it.
.. code-block:: python
from django.apps import AppConfig
from django.contrib.admin.helpers import AdminReadonlyField
from django.contrib.admin.utils import display_for_field, lookup_field
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields.related import (
ForeignObjectRel,
ManyToManyRel,
OneToOneField,
)
from django.template.defaultfilters import linebreaksbr
from django.utils.html import conditional_escape
from django.utils.translation import gettext_lazy as _
from multiselectfield.db.fields import MultiSelectField
class AppAppConfig(AppConfig):
name = 'app'
verbose_name = 'app'
def ready(self):
if not hasattr(AdminReadonlyField, '_original_contents'):
AdminReadonlyField._original_contents = AdminReadonlyField.contents
AdminReadonlyField.contents = patched_contents
def patched_contents(self):
from django.contrib.admin.templatetags.admin_list import _boolean_icon
field, obj, model_admin = (
self.field["field"],
self.form.instance,
self.model_admin,
)
try:
f, attr, value = lookup_field(field, obj, model_admin)
except (AttributeError, ValueError, ObjectDoesNotExist):
result_repr = self.empty_value_display
else:
if field in self.form.fields:
widget = self.form[field].field.widget
# This isn't elegant but suffices for contrib.auth's
# ReadOnlyPasswordHashWidget.
if getattr(widget, "read_only", False):
return widget.render(field, value)
if f is None:
if getattr(attr, "boolean", False):
result_repr = _boolean_icon(value)
else:
if hasattr(value, "__html__"):
result_repr = value
else:
result_repr = linebreaksbr(value)
else:
if isinstance(f.remote_field, ManyToManyRel) and value is not None:
result_repr = ", ".join(map(str, value.all()))
elif (
isinstance(f.remote_field, (ForeignObjectRel, OneToOneField))
and value is not None
):
result_repr = self.get_admin_url(f.remote_field, value)
# Custom: start
elif isinstance(f, MultiSelectField):
if value in f.empty_values:
result_repr = self.empty_value_display
else:
result_repr = getattr(obj, f'get_{f.name}_display')()
# Custom: end
else:
result_repr = display_for_field(value, f, self.empty_value_display)
result_repr = linebreaksbr(result_repr)
return conditional_escape(result_repr)
2.6 Django REST Framework
-------------------------
Django REST Framework comes with a ``MultipleChoiceField`` that works perfectly with this:
.. code-block:: python
from rest_framework import fields, serializers
from myapp.models import MY_CHOICES, MY_CHOICES2
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
# ...
my_field = fields.MultipleChoiceField(choices=MY_CHOICES)
my_field2 = fields.MultipleChoiceField(choices=MY_CHOICES2)
# ...
3. Tests
========
All tests pass on Django 3.2.0, 4.0.0, 4.1.0, 4.2.0, 5.0.0 and 5.1.0
4. Development
==============
You can get the last bleeding edge version of django-multiselectfield by doing a clone of its git repository:
.. code-block:: bash
git clone https://github.com/goinnn/django-multiselectfield
5. Example project
===================
There is a fully configured example project in the `example directory <https://github.com/goinnn/django-multiselectfield/tree/master/example/>`_. You can run it as usual:
.. code-block:: bash
python manage.py migrate
python manage.py loaddata app_data
python manage.py runserver
# And go to http://localhost:8000. You will be automatically authenticated as a superuser.
1.0.1 (2025-06-12)
-------------------
* Badge image worked on GitHub but was broken on PyPI — fixed it.
1.0.0 (2025-06-12)
-------------------
This release introduces multiple changes that are incompatible with previous versions.
The major version number has been incremented following `Semantic Versioning (SemVer) <https://semver.org/>`_, as several components of the package have changed in ways that may require updates in client code.
The internal codebase has been significantly cleaned up and reorganized, making it more maintainable and consistent.
This version contains **40% fewer lines of code** compared to the previous release.
Less code means fewer bugs, easier maintenance, and better long-term sustainability.
* Breaking changes
* Remove MSFList (`01dcad230dc368b88a39bfc36f90ddd145f381a2 <https://github.com/goinnn/django-multiselectfield/commit/01dcad230dc368b88a39bfc36f90ddd145f381a2>`_):
* Removed: (`50d3f785883e0a314f2dc89950e3fe1e88a7ede6 <https://github.com/goinnn/django-multiselectfield/commit/50d3f785883e0a314f2dc89950e3fe1e88a7ede6>`_)
* It was created to support MultiSelectFields in admin.list_display, but it never actually worked. If you add a multiselect field to list_display, Django does not call to __str__ method of MSGList (renamed to MSFList)
* It was created for integer choices too and it is a misconception. This is explained in the README file.
* Remove MSFFlatchoices (`01dcad230dc368b88a39bfc36f90ddd145f381a2 <https://github.com/goinnn/django-multiselectfield/commit/01dcad230dc368b88a39bfc36f90ddd145f381a2>`_):
* Removed: (`5638247c1d70670d4f81adf35143ef17a7d7575e <https://github.com/goinnn/django-multiselectfield/commit/5638247c1d70670d4f81adf35143ef17a7d7575e>`_)
* In list_display, labels for the choices are now shown (comma-separated) instead of the values of the choices (comma-separated).
* In to_python method, value is a list or a string. (`c4579138dda2833cbce26afbf57da5353aa45690 <https://github.com/goinnn/django-multiselectfield/commit/c4579138dda2833cbce26afbf57da5353aa45690>`_)
* Remove set case and dict case
* If this breaks something, please create a test to help understand the use case.
* Removing integer choices:
* It was a mistake. MultiSelectField inherits of CharField, not IntegerField.
* It is impossible knows if original choice is (1, 'Item title 2.1') or ('1', 'Item title 2.1')
* Fix: Form instance generated twice since Django (`#168 <https://github.com/goinnn/django-multiselectfield/pull/168>`_)
* Fix CSS admin:
* (`#173 <https://github.com/goinnn/django-multiselectfield/pull/173>`_)
* (`7711f4aa755c81d00f07ce8a6ff0fd9240061f9c <https://github.com/goinnn/django-multiselectfield/commit/7711f4aa755c81d00f07ce8a6ff0fd9240061f9c>`_)
* Fix Properly Display Categorized Choices in get_FOO_display (`#169 <https://github.com/goinnn/django-multiselectfield/pull/169>`_)
* SortMultiSelectField: (`#172 <https://github.com/goinnn/django-multiselectfield/pull/172>`_)
* Documentation:
* How to add a filter to the Django administration:
* (`e36cbae4c3b39dac4a3fee03fdda9622a101f22d <https://github.com/goinnn/django-multiselectfield/commit/e36cbae4c3b39dac4a3fee03fdda9622a101f22d>`_)
* Inspired by (`#116 <https://github.com/goinnn/django-multiselectfield/issues/116>`_)
* How add a django multiselect field to list_display in Django administration
* (`5638247c1d70670d4f81adf35143ef17a7d7575e <https://github.com/goinnn/django-multiselectfield/commit/5638247c1d70670d4f81adf35143ef17a7d7575e>`_)
* How to add support for read-only fields in the Django administration:
* (`65376239ae7491414f896adb4d314349ff7c2667 <https://github.com/goinnn/django-multiselectfield/commit/65376239ae7491414f896adb4d314349ff7c2667>`_)
* Cleanup: Removed outdated code and updated compatibility:
* Updated syntax for the targeted Python and Django versions. (`#161 <https://github.com/goinnn/django-multiselectfield/pull/161>`_)
* Add pre-commit hooks (`#161 <https://github.com/goinnn/django-multiselectfield/pull/161>`_)
* Fixed GitHub Actions workflow.
* Removed Travis CI configuration.
* Fix Coveralls integrations
* Improved the README: clarified the correct versions of Python and Django.
Thanks to:
* `mikemanger <https://github.com/mikemanger>`_
* `piranhaphish <https://github.com/piranhaphish>`_
* `kunalgrover05 <https://github.com/kunalgrover05>`_
* `royatkup <https://github.com/royatkup>`_
* `qasimgulzar <https://github.com/qasimgulzar>`_
* `austin-schick <https://github.com/austin-schick>`_
* `Pfizer-BradleyBare <https://github.com/Pfizer-BradleyBare>`_
* `SuperSandro2000 <https://github.com/SuperSandro2000>`_
* `jucajuca <https://github.com/jucajuca>`_
* `filipefigcorreia <https://github.com/filipefigcorreia>`_
* `ZippoLag <https://github.com/ZippoLag>`_
* `leifdenby <https://github.com/leifdenby>`_
* `jordanvs <https://github.com/jordanvs>`_
* `hetdev <https://github.com/hetdev>`_
* `blag <https://github.com/blag>`_
Special thanks to:
* `ccalero <https://github.com/ccalero>`_ for fighting and updating django-multiselectfield
* `Joinup Green Intelligence <https://joinup.es>`_ for believing in free (libre) software
0.1.13 (2024-06-30)
-------------------
* Return MSFList instead of a plain list from form fields (`#118 <https://github.com/goinnn/django-multiselectfield/pull/118>`_, `#135 <https://github.com/goinnn/django-multiselectfield/pull/135>`_)
* Fix CI (`#122 <https://github.com/goinnn/django-multiselectfield/pull/122>`_, `#147 <https://github.com/goinnn/django-multiselectfield/pull/147>`_, `#148 <https://github.com/goinnn/django-multiselectfield/pull/148>`_, `#151 <https://github.com/goinnn/django-multiselectfield/pull/151>`_)
* Add ``min_choices`` to defaults when converting to form field (`#123 <https://github.com/goinnn/django-multiselectfield/pull/123>`_)
* Django 5.0 support and remove old compatibility (`#148 <https://github.com/goinnn/django-multiselectfield/pull/148>`_)
Thanks to:
* `tomasgarzon <https://github.com/tomasgarzon>`_
* `aleh-rymasheuski <https://github.com/aleh-rymasheuski>`_
* `nametkin <https://github.com/nametkin>`_
* `karolyi <https://github.com/karolyi>`_
* `olivierdalang <https://github.com/olivierdalang>`_
* `PetrDlouhy <https://github.com/PetrDlouhy>`_
0.1.12 (2020-02-20)
-------------------
* Optimize multiselectfield to_python method
* Thanks to:
* `daimon99 <https://github.com/daimon99>`_
0.1.11 (2019-12-19)
-------------------
* Added support for Django 3
* Added support for Python 3.8
* Thanks to:
* `thijsBoehme <https://github.com/thijsBoehme>`_
0.1.9 (2019-10-02)
------------------
* Added support for Django 2
* Added support for Python 3.6
* Drop support for Python (2.6, 3.3)
* Thanks to:
* `hirokinko <https://github.com/hirokinko>`_
0.1.6 (2017-05-10)
------------------
* Added support for Django 1.11
* Added support for Python 3.6
* Improved rendering in Django admin
* Improved documentation
* Thanks to:
* `atten <https://github.com/atten>`_
* `ixc <https://github.comixc>`_
* `LeilaniAnn <https://github.comLeilaniAnn>`_
0.1.5 (2017-01-02)
------------------
* Added support for Django 1.8-1.10
* Added support for named groups in choices
* Added support for min_choices argument
* Various fixes
* More tests
* Thanks to:
* `danilogbotelho <https://github.comdanilogbotelho>`_
* `dmitry-krasilnikov <https://github.comdmitry-krasilnikov>`_
* `Kamil Dębowski <https://github.comkdebowski>`_
0.1.4 (2016-02-23)
------------------
* Fixed warning about SubfieldBase
* Added support for Django 1.8+
* Added support for named groups
* We now play nice with django-dynamic-fixture
* More tests
0.1.3 (2014-10-13)
------------------
* Support to Django 1.7 (I'm sorry to the delay)
* Adding get_FIELD_list function
* Fix an error when a MultiSelectField was reandonly at the admin site
* Thanks to:
* `Hernil <https://github.com/hernil>`_
* `Vasyl Stanislavchuk <https://github.com/vasyabigi>`_
* `Litchfield <https://github.com/litchfield/>`_
* `Chris-erickson <https://github.com/chris-erickson>`_
0.1.2 (2014-04-04)
------------------
* Include the spanish translations to the pypi egg
* Improvements in the readme file
* Windows OS compatibility
* Thanks to:
* `StillNewb <https://github.com/StillNewb>`_
* `Diego Yungh <https://github.com/DiegoYungh>`_
0.1.1 (2013-12-04)
------------------
* Move the multiselectfield app to parent folder
* Details
0.1.0 (2013-11-30)
------------------
* Test/example project
* Now works if the first composant of the list of tuple is an integer
* Now max_length is not required, the Multiselect field calculate it automatically.
* The max_choices attr can be a attr in the model field
* Refactor the code
* Spanish translations
* Support to python2.6
* Thanks to:
* `Daniele Procida <https://github.com/evildmp>`_
0.0.3 (2013-09-11)
------------------
* Python 3 compatible
* Fix an error, the snippet had another error when the choices were translatables
* Improvements in the README file
0.0.2 (2012-09-28)
------------------
* Fix an error, the snippet had an error.
0.0.1 (2012-09-27)
------------------
* Initial version from the next `snippet <https://djangosnippets.org/snippets/1200/>`_