From c77e62f29ee10beb73df589c822476aebf39de71 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 18 Dec 2025 13:55:46 +0100 Subject: [PATCH 1/5] Update documentation and readme --- MANIFEST.in | 4 ---- README.md | 24 ++++++++++++++++++++++++ README.rst | 31 ------------------------------- SECURITY.md | 7 ------- docs/conf.py | 1 + docs/index.rst | 11 ++++++++++- images/logo-dark.svg | 13 +++++++++++++ images/logo-light.svg | 13 +++++++++++++ linter-requirements.txt | 5 ----- pyproject.toml | 3 ++- setup.cfg | 4 ---- 11 files changed, 63 insertions(+), 53 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 README.md delete mode 100644 README.rst delete mode 100644 SECURITY.md create mode 100644 images/logo-dark.svg create mode 100644 images/logo-light.svg delete mode 100644 linter-requirements.txt delete mode 100644 setup.cfg diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 371ff035..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include django_select2/static/django_select2/django_select2.js -include django_select2/static/django_select2/django_select2.css -prune .github -exclude .* diff --git a/README.md b/README.md new file mode 100644 index 00000000..2b899f6e --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +

+ + + + Django Select2: Custom autocomplete fields for Django + +
+ Documentation +

+ +# Django-Select2 + +[![version](https://img.shields.io/pypi/v/Django-Select2.svg)](https://pypi.python.org/pypi/Django-Select2/) +[![coverage](https://codecov.io/gh/codingjoe/django-select2/branch/main/graph/badge.svg)](https://codecov.io/gh/codingjoe/django-select2) +[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/codingjoe/django-select2/main/LICENSE.txt) + +Custom autocomplete fields for [Django](https://www.djangoproject.com/). + +## Documentation + +Documentation available at . + +> [!NOTE] +> Django\'s admin comes with builtin support for Select2 via the [autocomplete_fields](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields) feature. diff --git a/README.rst b/README.rst deleted file mode 100644 index 93d87344..00000000 --- a/README.rst +++ /dev/null @@ -1,31 +0,0 @@ -|header| - -============== -Django-Select2 -============== - -|version| |coverage| |license| - -Custom autocomplete fields for `Django`_. - -Documentation -------------- - -Documentation available at https://django-select2.readthedocs.io/. - -.. note:: - Django's admin comes with builtin support for Select2 - via the `autocomplete_fields`_ feature. - - -.. _Django: https://www.djangoproject.com/ -.. _Select2: https://select2.org/ -.. _autocomplete_fields: https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields - -.. |header| image:: https://repository-images.githubusercontent.com/266545281/c6db7d26-9f60-454b-845e-395d45c43fa7 -.. |version| image:: https://img.shields.io/pypi/v/Django-Select2.svg - :target: https://pypi.python.org/pypi/Django-Select2/ -.. |coverage| image:: https://codecov.io/gh/codingjoe/django-select2/branch/master/graph/badge.svg - :target: https://codecov.io/gh/codingjoe/django-select2 -.. |license| image:: https://img.shields.io/badge/license-APL2-blue.svg - :target: https://raw.githubusercontent.com/codingjoe/django-select2/master/LICENSE.txt diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index b0208c5f..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,7 +0,0 @@ -# Security - -## Security contact information - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. diff --git a/docs/conf.py b/docs/conf.py index 1dc16c21..9d783b4d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,6 +24,7 @@ version = ".".join(release.split(".")[:2]) master_doc = "index" # default in Sphinx v2 +html_theme = "furo" extensions = [ diff --git a/docs/index.rst b/docs/index.rst index 0b2772ea..c073f1aa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,13 @@ -.. include:: ../README.rst +Django Select2 +============== + +.. image:: https://github.com/codingjoe/django-select2/raw/main/images/logo-light.svg + :align: center + :class: only-light + +.. image:: https://github.com/codingjoe/django-select2/raw/main/images/logo-dark.svg + :align: center + :class: only-dark Installation ------------ diff --git a/images/logo-dark.svg b/images/logo-dark.svg new file mode 100644 index 00000000..cdf92f87 --- /dev/null +++ b/images/logo-dark.svg @@ -0,0 +1,13 @@ + + + + + Django + + + Select2 + + + Custom autocomplete fields for Django + + diff --git a/images/logo-light.svg b/images/logo-light.svg new file mode 100644 index 00000000..deba3eed --- /dev/null +++ b/images/logo-light.svg @@ -0,0 +1,13 @@ + + + + + Django + + + Select2 + + + Custom autocomplete fields for Django + + diff --git a/linter-requirements.txt b/linter-requirements.txt deleted file mode 100644 index 68aafbfd..00000000 --- a/linter-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -bandit==1.9.2 -black==25.12.0 -flake8==7.3.0 -isort==7.0.0 -pydocstyle[toml]==6.3.0 diff --git a/pyproject.toml b/pyproject.toml index efd8eb04..f3cddbe6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "django-select2" authors = [ { name = "Johannes Maron", email = "johannes@maron.family" }, ] -readme = "README.rst" +readme = "README.md" license = { file = "LICENSE" } keywords = ["Django", "select2", "autocomplete", "typeahead"] dynamic = ["version", "description"] @@ -48,6 +48,7 @@ test = [ ] docs = [ "sphinx", + "furo", ] [project.urls] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 6f60592c..00000000 --- a/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length=88 -select = C,E,F,W,B,B950 -ignore = E203, E501, W503, E731 From 36c13f6635ea7d2a2b54886ad51247db9bec50fb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 12:56:28 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b899f6e..6161da6c 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,4 @@ Custom autocomplete fields for [Django](https://www.djangoproject.com/). Documentation available at . > [!NOTE] -> Django\'s admin comes with builtin support for Select2 via the [autocomplete_fields](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields) feature. +> Django's admin comes with builtin support for Select2 via the [autocomplete_fields](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields) feature. From 835d598b66781bde363601b66d2bc677f77bfcf1 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 18 Dec 2025 13:55:46 +0100 Subject: [PATCH 3/5] Update documentation and readme --- .pre-commit-config.yaml | 4 + CONTRIBUTING.rst | 44 +++--- docs/django_select2.rst | 190 +++++++++++++----------- docs/extra.rst | 85 ++++++----- docs/index.rst | 316 +++++++++++++++++++++------------------- 5 files changed, 344 insertions(+), 295 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 01f6a5e7..b8a48da4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,6 +39,10 @@ repos: rev: v0.20.0 hooks: - id: yamlfmt + - repo: https://github.com/dzhu/rstfmt + rev: v0.0.14 + hooks: + - id: rstfmt ci: autoupdate_schedule: weekly skip: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 67782599..5273a7b3 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,26 +1,34 @@ -Contributing -============ +############## + Contributing +############## -Before you start editing the python code, you will need to make sure -you have binary dependencies installed:: +Before you start editing the python code, you will need to make sure you +have binary dependencies installed: - # Debian - sudo apt install -y gettext graphviz google-chrome-stable - # macOS - brew install -y gettext graphviz google-chrome-stable +.. code:: -You may run the tests via:: + # Debian + sudo apt install -y gettext graphviz google-chrome-stable + # macOS + brew install -y gettext graphviz google-chrome-stable - uv run pytest +You may run the tests via: -Documentation pull requests welcome. The Sphinx documentation can be compiled via:: +.. code:: - uv run sphinx-build -W -b doctest -b html docs docs/_build/html + uv run pytest -Bug reports welcome, even more so if they include a correct patch. Much -more so if you start your patch by adding a failing unit test, and correct -the code until zero unit tests fail. +Documentation pull requests welcome. The Sphinx documentation can be +compiled via: -The list of supported Django and Python version can be found in the CI suite setup. -Please make sure to verify that none of the linters or tests failed, before you submit -a patch for review. +.. code:: + + uv run sphinx-build -W -b doctest -b html docs docs/_build/html + +Bug reports welcome, even more so if they include a correct patch. Much +more so if you start your patch by adding a failing unit test, and +correct the code until zero unit tests fail. + +The list of supported Django and Python version can be found in the CI +suite setup. Please make sure to verify that none of the linters or +tests failed, before you submit a patch for review. diff --git a/docs/django_select2.rst b/docs/django_select2.rst index 512739b1..1cb43e39 100644 --- a/docs/django_select2.rst +++ b/docs/django_select2.rst @@ -1,124 +1,140 @@ -API Documentation -================= +################### + API Documentation +################### -Configuration -------------- +*************** + Configuration +*************** .. automodule:: django_select2.conf - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -Widgets -------- +********* + Widgets +********* .. automodule:: django_select2.forms - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -URLs ----- +****** + URLs +****** .. automodule:: django_select2.urls - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -Views ------ +******* + Views +******* .. automodule:: django_select2.views - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: -Cache ------ +******* + Cache +******* .. automodule:: django_select2.cache - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: +************ + JavaScript +************ -JavaScript ----------- +DjangoSelect2 handles the initialization of select2 fields +automatically. Just include ``{{ form.media.js }}`` in your template +before the closing ``body`` tag. That's it! -DjangoSelect2 handles the initialization of select2 fields automatically. Just include -``{{ form.media.js }}`` in your template before the closing ``body`` tag. That's it! +If you insert forms after page load or if you want to handle the +initialization yourself, DjangoSelect2 provides a jQuery plugin, +replacing and enhancing the Select2 plugin. It will handle both normal +and heavy fields. Simply call ``djangoSelect2(options)`` on your select +fields.: -If you insert forms after page load or if you want to handle the initialization -yourself, DjangoSelect2 provides a jQuery plugin, replacing and enhancing the Select2 -plugin. It will handle both normal and heavy fields. Simply call -``djangoSelect2(options)`` on your select fields.:: +.. code:: - $('.django-select2').djangoSelect2(); + $('.django-select2').djangoSelect2(); Please replace all your ``.select2`` invocations with the here provided ``.djangoSelect2``. +********************* + Configuring Select2 +********************* -Configuring Select2 -------------------- - -Select2 options can be configured either directly from Javascript or from within Django -using widget attributes. `(List of options in the Select2 docs) `_. +Select2 options can be configured either directly from Javascript or +from within Django using widget attributes. `(List of options in the +Select2 docs) `_. To pass options in javascript -.. code-block:: javascript - - $('.django-select2').djangoSelect2({ - minimumInputLength: 0, - placeholder: 'Select an option', - }); - -From Django, you can use ``data-`` attributes using the same names in camel-case and -passing them to your widget. Select2 will then pick these up. For example when -initialising a widget in a form, you could do: - -.. code-block:: python - - class MyForm(forms.Form): - my_field = forms.ModelMultipleChoiceField( - widget=ModelSelect2MultipleWidget( - model=MyModel - search_fields=['another_field'] - attrs={ - "data-minimum-input-length": 0, - "data-placeholder": "Select an option", - "data-close-on-select": "false", - } - ) - ) - -(If you do not want to initialize the widget, you could add the attributes by overriding -a widget method and adding them in a super call, e.g. `get_context() `_) - - -Security & Authentication -------------------------- +.. code:: javascript + + $('.django-select2').djangoSelect2({ + minimumInputLength: 0, + placeholder: 'Select an option', + }); + +From Django, you can use ``data-`` attributes using the same names in +camel-case and passing them to your widget. Select2 will then pick these +up. For example when initialising a widget in a form, you could do: + +.. code:: python + + class MyForm(forms.Form): + my_field = forms.ModelMultipleChoiceField( + widget=ModelSelect2MultipleWidget( + model=MyModel + search_fields=['another_field'] + attrs={ + "data-minimum-input-length": 0, + "data-placeholder": "Select an option", + "data-close-on-select": "false", + } + ) + ) + +(If you do not want to initialize the widget, you could add the +attributes by overriding a widget method and adding them in a super +call, e.g. `get_context() +`_) + +*************************** + Security & Authentication +*************************** Security is important. Therefore make sure to read and understand what the security measures in place and their limitations. -Set up a separate cache. If you have a public form that uses a model widget -make sure to setup a separate cache database for Select2. An attacker -could constantly reload your site and fill up the select2 cache. -Having a separate cache allows you to limit the effect to select2 only. +Set up a separate cache. If you have a public form that uses a model +widget make sure to setup a separate cache database for Select2. An +attacker could constantly reload your site and fill up the select2 +cache. Having a separate cache allows you to limit the effect to select2 +only. You might want to add a secure select2 JSON endpoint for data you don't -want to be accessible to the general public. Doing so is easy:: +want to be accessible to the general public. Doing so is easy: + +.. code:: - class UserSelect2View(LoginRequiredMixin, AutoResponseView): - pass + class UserSelect2View(LoginRequiredMixin, AutoResponseView): + pass - class UserSelect2WidgetMixin(object): - def __init__(self, *args, **kwargs): - kwargs['data_view'] = 'user-select2-view' - super(UserSelect2WidgetMixin, self).__init__(*args, **kwargs) + class UserSelect2WidgetMixin(object): + def __init__(self, *args, **kwargs): + kwargs['data_view'] = 'user-select2-view' + super(UserSelect2WidgetMixin, self).__init__(*args, **kwargs) - class MySecretWidget(UserSelect2WidgetMixin, ModelSelect2Widget): - model = MySecretModel - search_fields = ['title__icontains'] + class MySecretWidget(UserSelect2WidgetMixin, ModelSelect2Widget): + model = MySecretModel + search_fields = ['title__icontains'] diff --git a/docs/extra.rst b/docs/extra.rst index 6555189c..1bc6fa4d 100644 --- a/docs/extra.rst +++ b/docs/extra.rst @@ -1,40 +1,44 @@ -Extra -===== +####### + Extra +####### -Chained select2 ---------------- +***************** + Chained select2 +***************** -Suppose you have an address form where a user should choose a Country and a City. -When the user selects a country we want to show only cities belonging to that country. -So the one selector depends on another one. +Suppose you have an address form where a user should choose a Country +and a City. When the user selects a country we want to show only cities +belonging to that country. So the one selector depends on another one. .. note:: - Does not work with the 'light' version (django_select2.forms.Select2Widget). + + Does not work with the 'light' version + (django_select2.forms.Select2Widget). Models -`````` +====== Here are our two models: -.. code-block:: python - - class Country(models.Model): - name = models.CharField(max_length=255) +.. code:: python + class Country(models.Model): + name = models.CharField(max_length=255) - class City(models.Model): - name = models.CharField(max_length=255) - country = models.ForeignKey('Country', related_name="cities") + class City(models.Model): + name = models.CharField(max_length=255) + country = models.ForeignKey("Country", related_name="cities") Customizing a Form -`````````````````` +================== -Lets link two widgets via a *dependent_fields* dictionary. The key represents the name of -the field in the form. The value represents the name of the field in the model (used in `queryset`). +Lets link two widgets via a *dependent_fields* dictionary. The key +represents the name of the field in the form. The value represents the +name of the field in the model (used in `queryset`). -.. code-block:: python - :emphasize-lines: 17 +.. code:: python + :emphasize-lines: 17 class AddressForm(forms.Form): country = forms.ModelChoiceField( @@ -57,17 +61,18 @@ the field in the form. The value represents the name of the field in the model ( ) ) +************************ + Interdependent select2 +************************ -Interdependent select2 ----------------------- - -Also you may want not to restrict the user to which field should be selected first. -Instead you want to suggest to the user options for any select2 depending of his selection in another one. +Also you may want not to restrict the user to which field should be +selected first. Instead you want to suggest to the user options for any +select2 depending of his selection in another one. Customize the form in a manner: -.. code-block:: python - :emphasize-lines: 7 +.. code:: python + :emphasize-lines: 7 class AddressForm(forms.Form): country = forms.ModelChoiceField( @@ -89,22 +94,26 @@ Customize the form in a manner: ) ) -Take attention to country's dependent_fields. The value of 'city' is 'cities' because of -related name used in a filter condition `cities` which differs from widget field name `city`. +Take attention to country's dependent_fields. The value of 'city' is +'cities' because of related name used in a filter condition `cities` +which differs from widget field name `city`. .. caution:: - Be aware of using interdependent select2 in parent-child relation. - When a child is selected, you are restricted to change parent (only one value is available). - Probably you should let the user reset the child first to release parent select2. + Be aware of using interdependent select2 in parent-child relation. + When a child is selected, you are restricted to change parent (only + one value is available). Probably you should let the user reset the + child first to release parent select2. -Multi-dependent select2 ------------------------ +************************* + Multi-dependent select2 +************************* -Furthermore you may want to filter options on two or more select2 selections (some code is dropped for clarity): +Furthermore you may want to filter options on two or more select2 +selections (some code is dropped for clarity): -.. code-block:: python - :emphasize-lines: 14 +.. code:: python + :emphasize-lines: 14 class SomeForm(forms.Form): field1 = forms.ModelChoiceField( diff --git a/docs/index.rst b/docs/index.rst index c073f1aa..a6ef3d7f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,228 +1,239 @@ -Django Select2 -============== +################ + Django Select2 +################ .. image:: https://github.com/codingjoe/django-select2/raw/main/images/logo-light.svg :align: center + :alt: Django Select2: Custom autocomplete fields for Django :class: only-light .. image:: https://github.com/codingjoe/django-select2/raw/main/images/logo-dark.svg :align: center + :alt: Django Select2: Custom autocomplete fields for Django :class: only-dark -Installation ------------- +************** + Installation +************** -Install ``django-select2``:: +Install ``django-select2``: - python3 -m pip install django-select2 +.. code:: -Add ``django_select2`` to your ``INSTALLED_APPS`` in your project settings. -Since version 8, please ensure that Django's admin app is enabled too: + python3 -m pip install django-select2 -.. code-block:: python +Add ``django_select2`` to your ``INSTALLED_APPS`` in your project +settings. Since version 8, please ensure that Django's admin app is +enabled too: - INSTALLED_APPS = [ - # other django apps… - 'django.contrib.admin', - # other 3rd party apps… - 'django_select2', - ] +.. code:: python -Add ``django_select`` to your URL root configuration: + INSTALLED_APPS = [ + # other django apps… + "django.contrib.admin", + # other 3rd party apps… + "django_select2", + ] -.. code-block:: python +Add ``django_select`` to your URL root configuration: - from django.urls import include, path +.. code:: python - urlpatterns = [ - # other patterns… - path("select2/", include("django_select2.urls")), - # other patterns… - ] + from django.urls import include, path + urlpatterns = [ + # other patterns… + path("select2/", include("django_select2.urls")), + # other patterns… + ] The :ref:`Model` -widgets require a **persistent** cache backend across -all application servers. This is because the widget needs to store -meta data to be able to fetch the results based on the user input. +all application servers. This is because the widget needs to store meta +data to be able to fetch the results based on the user input. **This means that the** :class:`.DummyCache` **backend will not work!** The default cache backend is :class:`.LocMemCache`, which is persistent -across a single node. For projects with a single application server -this will work fine, however you will run into issues when -you scale up into multiple servers. +across a single node. For projects with a single application server this +will work fine, however you will run into issues when you scale up into +multiple servers. + +Below is an example setup using Redis, which is a solution that works +for multi-server setups: -Below is an example setup using Redis, which is a solution that -works for multi-server setups: +Make sure you have a Redis server up and running: -Make sure you have a Redis server up and running:: +.. code:: - # Debian - sudo apt-get install redis-server + # Debian + sudo apt-get install redis-server - # macOS - brew install redis + # macOS + brew install redis - # install Redis python client - python3 -m pip install django-redis + # install Redis python client + python3 -m pip install django-redis Next, add the cache configuration to your ``settings.py`` as follows: -.. code-block:: python +.. code:: python - CACHES = { - # … default cache config and others - "select2": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": "redis://127.0.0.1:6379/2", - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - } - } - } + CACHES = { + # … default cache config and others + "select2": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://127.0.0.1:6379/2", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } + } - # Tell select2 which cache configuration to use: - SELECT2_CACHE_BACKEND = "select2" + # Tell select2 which cache configuration to use: + SELECT2_CACHE_BACKEND = "select2" .. note:: - A custom timeout for your cache backend, will serve as an indirect session limit. - Auto select fields will stop working after, once the cache has expired. - It's recommended to use a dedicated cache database with an adequate - cache replacement policy such as LRU, FILO, etc. + A custom timeout for your cache backend, will serve as an indirect + session limit. Auto select fields will stop working after, once the + cache has expired. It's recommended to use a dedicated cache database + with an adequate cache replacement policy such as LRU, FILO, etc. -External Dependencies ---------------------- +*********************** + External Dependencies +*********************** -- jQuery is not included in the package since it is - expected that in most scenarios this would already be available. +- jQuery is not included in the package since it is expected that in + most scenarios this would already be available. - -Quick Start ------------ +************* + Quick Start +************* Here is a quick example to get you started: -First make sure you followed the installation instructions above. -Once everything is setup, let's start with a simple example. +First make sure you followed the installation instructions above. Once +everything is setup, let's start with a simple example. We have the following model: -.. code-block:: python - - # models.py - from django.conf import settings - from django.db import models +.. code:: python + # models.py + from django.conf import settings + from django.db import models - class Book(models.Model): - author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - co_authors = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='co_authored_by') + class Book(models.Model): + author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + co_authors = models.ManyToManyField( + settings.AUTH_USER_MODEL, related_name="co_authored_by" + ) Next, we create a model form with custom Select2 widgets. -.. code-block:: python +.. code:: python - # forms.py - from django import forms - from django_select2 import forms as s2forms + # forms.py + from django import forms + from django_select2 import forms as s2forms - from . import models + from . import models - class AuthorWidget(s2forms.ModelSelect2Widget): - search_fields = [ - "username__icontains", - "email__icontains", - ] + class AuthorWidget(s2forms.ModelSelect2Widget): + search_fields = [ + "username__icontains", + "email__icontains", + ] - class CoAuthorsWidget(s2forms.ModelSelect2MultipleWidget): - search_fields = [ - "username__icontains", - "email__icontains", - ] + class CoAuthorsWidget(s2forms.ModelSelect2MultipleWidget): + search_fields = [ + "username__icontains", + "email__icontains", + ] - class BookForm(forms.ModelForm): - class Meta: - model = models.Book - fields = "__all__" - widgets = { - "author": AuthorWidget, - "co_authors": CoAuthorsWidget, - } + class BookForm(forms.ModelForm): + class Meta: + model = models.Book + fields = "__all__" + widgets = { + "author": AuthorWidget, + "co_authors": CoAuthorsWidget, + } A simple class based view will do, to render your form: -.. code-block:: python +.. code:: python - # views.py - from django.views import generic + # views.py + from django.views import generic - from . import forms, models + from . import forms, models - class BookCreateView(generic.CreateView): - model = models.Book - form_class = forms.BookForm - success_url = "/" + class BookCreateView(generic.CreateView): + model = models.Book + form_class = forms.BookForm + success_url = "/" Make sure to add the view to your ``urls.py``: -.. code-block:: python - - # urls.py - from django.urls import include, path - - from . import views - - urlpatterns = [ - # … other patterns - path("select2/", include("django_select2.urls")), - # … other patterns - path("book/create", views.BookCreateView.as_view(), name="book-create"), - ] - - -Finally, we need a little template, ``myapp/templates/myapp/book_form.html`` - -.. code-block:: HTML - - - - - Create Book - {{ form.media.css }} - - - -

Create a new Book

-
- {% csrf_token %} - {{ form.as_p }} - -
- - {{ form.media.js }} - - +.. code:: python + + # urls.py + from django.urls import include, path + + from . import views + + urlpatterns = [ + # … other patterns + path("select2/", include("django_select2.urls")), + # … other patterns + path("book/create", views.BookCreateView.as_view(), name="book-create"), + ] + +Finally, we need a little template, +``myapp/templates/myapp/book_form.html`` + +.. code:: HTML + + + + + Create Book + {{ form.media.css }} + + + +

Create a new Book

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ + {{ form.media.js }} + + Done - enjoy the wonders of Select2! - -Changelog ---------- +*********** + Changelog +*********** See `Github releases`_. -.. _Github releases: https://github.com/codingjoe/django-select2/releases +.. _github releases: https://github.com/codingjoe/django-select2/releases -All Contents -============ +############## + All Contents +############## Contents: @@ -234,9 +245,10 @@ Contents: extra CONTRIBUTING -Indices and tables -================== +#################### + Indices and tables +#################### -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +- :ref:`genindex` +- :ref:`modindex` +- :ref:`search` From 04b8e248bd69b7448341aaeabb0c4b17faa9d22a Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 18 Dec 2025 14:11:55 +0100 Subject: [PATCH 4/5] Fix docs --- .pre-commit-config.yaml | 6 +++--- docs/extra.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8a48da4..a5660596 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,10 +39,10 @@ repos: rev: v0.20.0 hooks: - id: yamlfmt - - repo: https://github.com/dzhu/rstfmt - rev: v0.0.14 + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v1.0.2 hooks: - - id: rstfmt + - id: sphinx-lint ci: autoupdate_schedule: weekly skip: diff --git a/docs/extra.rst b/docs/extra.rst index 1bc6fa4d..b7853714 100644 --- a/docs/extra.rst +++ b/docs/extra.rst @@ -38,7 +38,7 @@ represents the name of the field in the form. The value represents the name of the field in the model (used in `queryset`). .. code:: python - :emphasize-lines: 17 + :emphasize-lines: 17 class AddressForm(forms.Form): country = forms.ModelChoiceField( @@ -72,7 +72,7 @@ select2 depending of his selection in another one. Customize the form in a manner: .. code:: python - :emphasize-lines: 7 + :emphasize-lines: 7 class AddressForm(forms.Form): country = forms.ModelChoiceField( @@ -113,7 +113,7 @@ Furthermore you may want to filter options on two or more select2 selections (some code is dropped for clarity): .. code:: python - :emphasize-lines: 14 + :emphasize-lines: 14 class SomeForm(forms.Form): field1 = forms.ModelChoiceField( From 0622125fee447be8f46f6b470f77bc768194ed1d Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 18 Dec 2025 14:14:08 +0100 Subject: [PATCH 5/5] use code block --- docs/extra.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/extra.rst b/docs/extra.rst index b7853714..aab12d8b 100644 --- a/docs/extra.rst +++ b/docs/extra.rst @@ -37,7 +37,7 @@ Lets link two widgets via a *dependent_fields* dictionary. The key represents the name of the field in the form. The value represents the name of the field in the model (used in `queryset`). -.. code:: python +.. code-block:: python :emphasize-lines: 17 class AddressForm(forms.Form): @@ -71,7 +71,7 @@ select2 depending of his selection in another one. Customize the form in a manner: -.. code:: python +.. code-block:: python :emphasize-lines: 7 class AddressForm(forms.Form): @@ -112,7 +112,7 @@ which differs from widget field name `city`. Furthermore you may want to filter options on two or more select2 selections (some code is dropped for clarity): -.. code:: python +.. code-block:: python :emphasize-lines: 14 class SomeForm(forms.Form):