├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── dependabot.yml └── workflows │ ├── coverage.yml │ └── tests.yml ├── .gitignore ├── .readthedocs.yaml ├── AUTHORS ├── CHANGES.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── django_filters ├── __init__.py ├── compat.py ├── conf.py ├── constants.py ├── exceptions.py ├── fields.py ├── filters.py ├── filterset.py ├── locale │ ├── ar │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── be │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── bg │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── cs │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── da │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── el │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_AR │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fa │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fi │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ro │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── sk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── uk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── rest_framework │ ├── __init__.py │ ├── backends.py │ ├── filters.py │ └── filterset.py ├── templates │ └── django_filters │ │ ├── rest_framework │ │ ├── crispy_form.html │ │ └── form.html │ │ └── widgets │ │ └── multiwidget.html ├── utils.py ├── views.py └── widgets.py ├── docs ├── Makefile ├── assets │ └── form.png ├── conf.py ├── dev │ └── tests.txt ├── guide │ ├── install.txt │ ├── migration.txt │ ├── rest_framework.txt │ ├── tips.txt │ └── usage.txt ├── index.txt ├── make.bat └── ref │ ├── fields.txt │ ├── filters.txt │ ├── filterset.txt │ ├── settings.txt │ └── widgets.txt ├── pyproject.toml ├── requirements ├── docs.txt ├── maintainer.txt ├── test-ci.txt └── test.txt ├── runshell.py ├── runtests.py ├── setup.cfg ├── tests ├── __init__.py ├── models.py ├── rest_framework │ ├── __init__.py │ ├── apps.py │ ├── models.py │ ├── templates │ │ └── filter_template.html │ ├── test_backends.py │ ├── test_filters.py │ ├── test_filterset.py │ └── test_integration.py ├── settings.py ├── templates │ └── tests │ │ └── book_filter.html ├── test_conf.py ├── test_fields.py ├── test_filtering.py ├── test_filters.py ├── test_filterset.py ├── test_forms.py ├── test_utils.py ├── test_views.py ├── test_widgets.py ├── urls.py └── utils.py └── tox.ini /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Found an issue, here's the place. Got a question? Use Discussions instead. 4 | --- 5 | 6 | 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Keep GitHub Actions up to date with GitHub's Dependabot... 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" # Group all Actions updates into a single larger pull request 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/coverage.yml 2 | name: Post coverage comment 3 | 4 | on: 5 | workflow_run: 6 | workflows: ["Tests"] 7 | types: 8 | - completed 9 | 10 | jobs: 11 | test: 12 | name: Run tests & display coverage 13 | runs-on: ubuntu-latest 14 | if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' 15 | permissions: 16 | # Gives the action the necessary permissions for publishing new 17 | # comments in pull requests. 18 | pull-requests: write 19 | # Gives the action the necessary permissions for editing existing 20 | # comments (to avoid publishing multiple comments in the same PR) 21 | contents: write 22 | # Gives the action the necessary permissions for looking up the 23 | # workflow that launched this workflow, and download the related 24 | # artifact that contains the comment to be published 25 | actions: read 26 | steps: 27 | # DO NOT run actions/checkout here, for security reasons 28 | # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 29 | - name: Post comment 30 | uses: py-cov-action/python-coverage-comment-action@v3 31 | with: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} 34 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tests 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | 10 | jobs: 11 | tests: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Ensure latest setuptools 25 | run: | 26 | python -m pip install --upgrade pip setuptools 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install coverage[toml] tox tox-py unittest-xml-reporting 30 | - name: Run tox 31 | run: | 32 | python -m pip --version 33 | python -m tox --version 34 | python -m tox --py current 35 | - name: Coverage reporting 36 | run: | 37 | coverage combine 38 | coverage report --show-missing 39 | mv .coverage .coverage.${{ matrix.python-version }} 40 | - name: Store coverage file 41 | uses: actions/upload-artifact@v4 42 | with: 43 | include-hidden-files: true 44 | name: coverage-${{ matrix.python-version }} 45 | path: .coverage.${{ matrix.python-version }} 46 | 47 | coverage: 48 | name: Coverage 49 | runs-on: ubuntu-latest 50 | needs: tests 51 | permissions: 52 | # If the author is a maintainer, the permission level is set by the 53 | # values below. 54 | # `pull-requests: write` is needed for publishing new comments in pull 55 | # requests. 56 | # `contents: write` is needed for pushing data to the 57 | # `python-coverage-comment-action` branch, and for editing existing 58 | # comments (to avoid publishing multiple comments in the same PR) 59 | # In case the pull request comes from a forked repository, the maximum 60 | # permission level is read, so the permissions below won't be acted upon 61 | # by GitHub. 62 | # https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token 63 | pull-requests: write 64 | contents: write 65 | steps: 66 | - uses: actions/checkout@v4 67 | 68 | - uses: actions/download-artifact@v4 69 | id: download 70 | with: 71 | pattern: coverage-* 72 | merge-multiple: true 73 | 74 | - name: Coverage comment 75 | id: coverage_comment 76 | uses: py-cov-action/python-coverage-comment-action@v3 77 | with: 78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 79 | MERGE_COVERAGE_FILES: true 80 | 81 | - name: Store Pull Request comment to be posted 82 | uses: actions/upload-artifact@v4 83 | if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' 84 | with: 85 | name: python-coverage-comment-action 86 | path: python-coverage-comment-action.txt 87 | 88 | isort: 89 | runs-on: ubuntu-latest 90 | steps: 91 | - uses: actions/checkout@v4 92 | - name: Set up Python 93 | uses: actions/setup-python@v5 94 | with: 95 | python-version: "3.13" 96 | - name: Ensure latest setuptools 97 | run: | 98 | python -m pip install --upgrade pip setuptools 99 | - name: Install dependencies 100 | run: | 101 | python -m pip install tox 102 | - name: Run tox 103 | run: | 104 | python -m pip --version 105 | python -m tox --version 106 | python -m tox -e isort,lint,docs 107 | 108 | 109 | warnings: 110 | runs-on: ubuntu-latest 111 | steps: 112 | - uses: actions/checkout@v4 113 | - name: Set up Python 114 | uses: actions/setup-python@v5 115 | with: 116 | python-version: "3.13" 117 | - name: Ensure latest setuptools 118 | run: | 119 | python -m pip install --upgrade pip setuptools 120 | - name: Install dependencies 121 | run: | 122 | python -m pip install tox 123 | - name: Run tox 124 | run: | 125 | python -m pip --version 126 | python -m tox --version 127 | python -m tox -e warnings 128 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | build/ 4 | dist/ 5 | docs/_build 6 | .python-version 7 | .tox 8 | .coverage 9 | .coverage.* 10 | .xmlcoverage/ 11 | .venv/ 12 | .idea 13 | .env 14 | .vscode 15 | _docs 16 | .DS_Store -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.readthedocs.io/en/stable/config-file/v2.html 2 | 3 | version: 2 4 | 5 | # Build documentation in the docs/ directory with Sphinx 6 | sphinx: 7 | configuration: docs/conf.py 8 | 9 | # Set the version of Python and other tools you might need 10 | build: 11 | os: ubuntu-22.04 12 | tools: 13 | python: "3.10" 14 | 15 | python: 16 | install: 17 | - requirements: requirements/docs.txt 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | Thanks to the following people for contributing to django-filter. 5 | 6 | Ben Firshman 7 | Alex Gaynor 8 | Jannis Leidel 9 | Martin Mahner 10 | Brian Rosner 11 | Adam Vandenberg 12 | Florian Apolloner 13 | Andrew Ball 14 | Tino de Bruijn 15 | Maximillian Dornseif 16 | Marc Fargas 17 | Vladimir Sidorenko 18 | Tom Christie 19 | Remco Wendt 20 | Axel Haustant 21 | Brad Erickson 22 | Diogo Laginha -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Alex Gaynor and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * The names of its contributors may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include CHANGES.rst 3 | include LICENSE 4 | include README.rst 5 | include runshell.py 6 | include runtests.py 7 | recursive-include docs * 8 | recursive-include requirements * 9 | recursive-include tests * 10 | recursive-include django_filters/locale * 11 | recursive-include django_filters/templates *.html 12 | prune docs/_build 13 | global-exclude __pycache__ 14 | global-exclude *.py[co] 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: deps, test, clean 2 | 3 | deps: 4 | pip install -r ./requirements/test.txt 5 | 6 | test: 7 | ./runtests.py 8 | 9 | clean: 10 | rm -r build dist django_filter.egg-info 11 | 12 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Filter 2 | ============= 3 | 4 | Django-filter is a reusable Django application allowing users to declaratively 5 | add dynamic ``QuerySet`` filtering from URL parameters. 6 | 7 | Full documentation on `read the docs`_. 8 | 9 | .. image:: https://raw.githubusercontent.com/carltongibson/django-filter/python-coverage-comment-action-data/badge.svg 10 | :target: https://github.com/carltongibson/django-filter/tree/python-coverage-comment-action-data 11 | 12 | .. image:: https://badge.fury.io/py/django-filter.svg 13 | :target: http://badge.fury.io/py/django-filter 14 | 15 | 16 | Versioning and stability policy 17 | ------------------------------- 18 | 19 | Django-Filter is a mature and stable package. It uses a two-part CalVer 20 | versioning scheme, such as ``21.1``. The first number is the year. The second 21 | is the release number within that year. 22 | 23 | On an on-going basis, Django-Filter aims to support all current Django 24 | versions, the matching current Python versions, and the latest version of 25 | Django REST Framework. 26 | 27 | Please see: 28 | 29 | * `Status of supported Python versions `_ 30 | * `List of supported Django versions `_ 31 | 32 | Support for Python and Django versions will be dropped when they reach 33 | end-of-life. Support for Python versions will be dropped when they reach 34 | end-of-life, even when still supported by a current version of Django. 35 | 36 | Other breaking changes are rare. Where required, every effort will be made to 37 | apply a "Year plus two" deprecation period. For example, a change initially 38 | introduced in ``23.x`` would offer a fallback where feasible and finally be 39 | removed in ``25.1``. Where fallbacks are not feasible, breaking changes without 40 | deprecation will be called out in the release notes. 41 | 42 | 43 | Installation 44 | ------------ 45 | 46 | Install using pip: 47 | 48 | .. code-block:: sh 49 | 50 | pip install django-filter 51 | 52 | Then add ``'django_filters'`` to your ``INSTALLED_APPS``. 53 | 54 | .. code-block:: python 55 | 56 | INSTALLED_APPS = [ 57 | ... 58 | 'django_filters', 59 | ] 60 | 61 | 62 | Usage 63 | ----- 64 | 65 | Django-filter can be used for generating interfaces similar to the Django 66 | admin's ``list_filter`` interface. It has an API very similar to Django's 67 | ``ModelForms``. For example, if you had a Product model you could have a 68 | filterset for it with the code: 69 | 70 | .. code-block:: python 71 | 72 | import django_filters 73 | 74 | class ProductFilter(django_filters.FilterSet): 75 | class Meta: 76 | model = Product 77 | fields = ['name', 'price', 'manufacturer'] 78 | 79 | 80 | And then in your view you could do: 81 | 82 | .. code-block:: python 83 | 84 | def product_list(request): 85 | filter = ProductFilter(request.GET, queryset=Product.objects.all()) 86 | return render(request, 'my_app/template.html', {'filter': filter}) 87 | 88 | 89 | Usage with Django REST Framework 90 | -------------------------------- 91 | 92 | Django-filter provides a custom ``FilterSet`` and filter backend for use with 93 | Django REST Framework. 94 | 95 | To use this adjust your import to use 96 | ``django_filters.rest_framework.FilterSet``. 97 | 98 | .. code-block:: python 99 | 100 | from django_filters import rest_framework as filters 101 | 102 | class ProductFilter(filters.FilterSet): 103 | class Meta: 104 | model = Product 105 | fields = ('category', 'in_stock') 106 | 107 | 108 | For more details see the `DRF integration docs`_. 109 | 110 | 111 | Support 112 | ------- 113 | 114 | If you need help you can start a `discussion`_. For commercial support, please 115 | `contact Carlton Gibson via his website `_. 116 | 117 | .. _`discussion`: https://github.com/carltongibson/django-filter/discussions 118 | .. _`read the docs`: https://django-filter.readthedocs.io/en/main/ 119 | .. _`DRF integration docs`: https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html 120 | -------------------------------------------------------------------------------- /django_filters/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from importlib import util as importlib_util 3 | 4 | from .filters import * 5 | from .filterset import FilterSet, UnknownFieldBehavior 6 | 7 | # We make the `rest_framework` module available without an additional import. 8 | # If DRF is not installed, no-op. 9 | if importlib_util.find_spec("rest_framework"): 10 | from . import rest_framework 11 | del importlib_util 12 | 13 | __version__ = "25.1" 14 | 15 | 16 | def parse_version(version): 17 | """ 18 | '0.1.2.dev1' -> (0, 1, 2, 'dev1') 19 | '0.1.2' -> (0, 1, 2) 20 | """ 21 | v = version.split(".") 22 | ret = [] 23 | for p in v: 24 | if p.isdigit(): 25 | ret.append(int(p)) 26 | else: 27 | ret.append(p) 28 | return tuple(ret) 29 | 30 | 31 | VERSION = parse_version(__version__) 32 | -------------------------------------------------------------------------------- /django_filters/compat.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | # django-crispy-forms is optional 4 | try: 5 | import crispy_forms 6 | except ImportError: 7 | crispy_forms = None 8 | 9 | 10 | def is_crispy(): 11 | return "crispy_forms" in settings.INSTALLED_APPS and crispy_forms 12 | -------------------------------------------------------------------------------- /django_filters/conf.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings as dj_settings 2 | from django.core.signals import setting_changed 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | from .utils import deprecate 6 | 7 | DEFAULTS = { 8 | "DISABLE_HELP_TEXT": False, 9 | "DEFAULT_LOOKUP_EXPR": "exact", 10 | # empty/null choices 11 | "EMPTY_CHOICE_LABEL": "---------", 12 | "NULL_CHOICE_LABEL": None, 13 | "NULL_CHOICE_VALUE": "null", 14 | "VERBOSE_LOOKUPS": { 15 | # transforms don't need to be verbose, since their expressions are chained 16 | "date": _("date"), 17 | "year": _("year"), 18 | "month": _("month"), 19 | "day": _("day"), 20 | "week_day": _("week day"), 21 | "hour": _("hour"), 22 | "minute": _("minute"), 23 | "second": _("second"), 24 | # standard lookups 25 | "exact": "", 26 | "iexact": "", 27 | "contains": _("contains"), 28 | "icontains": _("contains"), 29 | "in": _("is in"), 30 | "gt": _("is greater than"), 31 | "gte": _("is greater than or equal to"), 32 | "lt": _("is less than"), 33 | "lte": _("is less than or equal to"), 34 | "startswith": _("starts with"), 35 | "istartswith": _("starts with"), 36 | "endswith": _("ends with"), 37 | "iendswith": _("ends with"), 38 | "range": _("is in range"), 39 | "isnull": _("is null"), 40 | "regex": _("matches regex"), 41 | "iregex": _("matches regex"), 42 | "search": _("search"), 43 | # postgres lookups 44 | "contained_by": _("is contained by"), 45 | "overlap": _("overlaps"), 46 | "has_key": _("has key"), 47 | "has_keys": _("has keys"), 48 | "has_any_keys": _("has any keys"), 49 | "trigram_similar": _("search"), 50 | }, 51 | } 52 | 53 | 54 | DEPRECATED_SETTINGS = [] 55 | 56 | 57 | def is_callable(value): 58 | # check for callables, except types 59 | return callable(value) and not isinstance(value, type) 60 | 61 | 62 | class Settings: 63 | def __getattr__(self, name): 64 | if name not in DEFAULTS: 65 | msg = "'%s' object has no attribute '%s'" 66 | raise AttributeError(msg % (self.__class__.__name__, name)) 67 | 68 | value = self.get_setting(name) 69 | 70 | if is_callable(value): 71 | value = value() 72 | 73 | # Cache the result 74 | setattr(self, name, value) 75 | return value 76 | 77 | def get_setting(self, setting): 78 | django_setting = "FILTERS_%s" % setting 79 | 80 | if setting in DEPRECATED_SETTINGS and hasattr(dj_settings, django_setting): 81 | deprecate("The '%s' setting has been deprecated." % django_setting) 82 | 83 | return getattr(dj_settings, django_setting, DEFAULTS[setting]) 84 | 85 | def change_setting(self, setting, value, enter, **kwargs): 86 | if not setting.startswith("FILTERS_"): 87 | return 88 | setting = setting[8:] # strip 'FILTERS_' 89 | 90 | # ensure a valid app setting is being overridden 91 | if setting not in DEFAULTS: 92 | return 93 | 94 | # if exiting, delete value to repopulate 95 | if enter: 96 | setattr(self, setting, value) 97 | else: 98 | delattr(self, setting) 99 | 100 | 101 | settings = Settings() 102 | setting_changed.connect(settings.change_setting) 103 | -------------------------------------------------------------------------------- /django_filters/constants.py: -------------------------------------------------------------------------------- 1 | ALL_FIELDS = "__all__" 2 | 3 | 4 | EMPTY_VALUES = ([], (), {}, "", None) 5 | -------------------------------------------------------------------------------- /django_filters/exceptions.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import FieldError 2 | 3 | 4 | class FieldLookupError(FieldError): 5 | def __init__(self, model_field, lookup_expr): 6 | super().__init__( 7 | "Unsupported lookup '%s' for field '%s'." % (lookup_expr, model_field) 8 | ) 9 | -------------------------------------------------------------------------------- /django_filters/locale/ar/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/ar/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/ar/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # FULL NAME , 2020. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 12 | "PO-Revision-Date: 2024-06-16 14:09+0000\n" 13 | "Last-Translator: Ahmed Nehad \n" 14 | "Language-Team: Arabic \n" 16 | "Language: ar\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 21 | "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" 22 | "X-Generator: Weblate 5.6-dev\n" 23 | 24 | #: conf.py:16 25 | msgid "date" 26 | msgstr "تاريخ" 27 | 28 | #: conf.py:17 29 | msgid "year" 30 | msgstr "سنة" 31 | 32 | #: conf.py:18 33 | msgid "month" 34 | msgstr "شهر" 35 | 36 | #: conf.py:19 37 | msgid "day" 38 | msgstr "يوم" 39 | 40 | #: conf.py:20 41 | msgid "week day" 42 | msgstr "يوم الأسبوع" 43 | 44 | #: conf.py:21 45 | msgid "hour" 46 | msgstr "ساعة" 47 | 48 | #: conf.py:22 49 | msgid "minute" 50 | msgstr "دقيقة" 51 | 52 | #: conf.py:23 53 | msgid "second" 54 | msgstr "ثانية" 55 | 56 | #: conf.py:27 conf.py:28 57 | msgid "contains" 58 | msgstr "يحتوي على" 59 | 60 | #: conf.py:29 61 | msgid "is in" 62 | msgstr "في داخل" 63 | 64 | #: conf.py:30 65 | msgid "is greater than" 66 | msgstr "أكبر من" 67 | 68 | #: conf.py:31 69 | msgid "is greater than or equal to" 70 | msgstr "أكبر من أو يساوي" 71 | 72 | #: conf.py:32 73 | msgid "is less than" 74 | msgstr "أصغر من" 75 | 76 | #: conf.py:33 77 | msgid "is less than or equal to" 78 | msgstr "أصغر من أو يساوي" 79 | 80 | #: conf.py:34 conf.py:35 81 | msgid "starts with" 82 | msgstr "يبدأ ب" 83 | 84 | #: conf.py:36 conf.py:37 85 | msgid "ends with" 86 | msgstr "ينتهي ب" 87 | 88 | #: conf.py:38 89 | msgid "is in range" 90 | msgstr "في النطاق" 91 | 92 | #: conf.py:39 93 | msgid "is null" 94 | msgstr "ليس موجود" 95 | 96 | #: conf.py:40 conf.py:41 97 | msgid "matches regex" 98 | msgstr "يطابق التعبير العادي" 99 | 100 | #: conf.py:42 conf.py:49 101 | msgid "search" 102 | msgstr "بحث" 103 | 104 | #: conf.py:44 105 | msgid "is contained by" 106 | msgstr "موجود في" 107 | 108 | #: conf.py:45 109 | msgid "overlaps" 110 | msgstr "يتداخل" 111 | 112 | #: conf.py:46 113 | msgid "has key" 114 | msgstr "لديه مفتاح" 115 | 116 | #: conf.py:47 117 | msgid "has keys" 118 | msgstr "لديه مفاتيح" 119 | 120 | #: conf.py:48 121 | msgid "has any keys" 122 | msgstr "لديه أي مفاتيح" 123 | 124 | #: fields.py:94 125 | msgid "Select a lookup." 126 | msgstr "حدد بحث" 127 | 128 | #: fields.py:198 129 | msgid "Range query expects two values." 130 | msgstr "إستعلام النطاق يتوقع قيمتين" 131 | 132 | #: filters.py:437 133 | msgid "Today" 134 | msgstr "اليوم" 135 | 136 | #: filters.py:438 137 | msgid "Yesterday" 138 | msgstr "أمس" 139 | 140 | #: filters.py:439 141 | msgid "Past 7 days" 142 | msgstr "الأيام السبعة الماضية" 143 | 144 | #: filters.py:440 145 | msgid "This month" 146 | msgstr "هذا الشهر" 147 | 148 | #: filters.py:441 149 | msgid "This year" 150 | msgstr "هذه السنة" 151 | 152 | #: filters.py:543 153 | msgid "Multiple values may be separated by commas." 154 | msgstr "يمكن فصل القيم المتعددة بفواصل." 155 | 156 | #: filters.py:721 157 | #, python-format 158 | msgid "%s (descending)" 159 | msgstr "%s (تنازلي)" 160 | 161 | #: filters.py:737 162 | msgid "Ordering" 163 | msgstr "الترتيب" 164 | 165 | #: rest_framework/filterset.py:33 166 | #: templates/django_filters/rest_framework/form.html:5 167 | msgid "Submit" 168 | msgstr "إرسال" 169 | 170 | #: templates/django_filters/rest_framework/crispy_form.html:4 171 | #: templates/django_filters/rest_framework/form.html:2 172 | msgid "Field filters" 173 | msgstr "مرشحات الحقل" 174 | 175 | #: utils.py:308 176 | msgid "exclude" 177 | msgstr "استبعاد" 178 | 179 | #: widgets.py:58 180 | msgid "All" 181 | msgstr "كل" 182 | 183 | #: widgets.py:162 184 | msgid "Unknown" 185 | msgstr "مجهول" 186 | 187 | #: widgets.py:162 188 | msgid "Yes" 189 | msgstr "نعم" 190 | 191 | #: widgets.py:162 192 | msgid "No" 193 | msgstr "لا" 194 | -------------------------------------------------------------------------------- /django_filters/locale/be/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/be/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/be/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-filter\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 7 | "PO-Revision-Date: 2016-09-29 11:47+0300\n" 8 | "Last-Translator: Eugena Mikhaylikova \n" 9 | "Language-Team: TextTempearture\n" 10 | "Language: be\n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 15 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" 16 | "%100>=11 && n%100<=14)? 2 : 3);\n" 17 | "X-Generator: Poedit 1.8.9\n" 18 | 19 | #: conf.py:16 20 | msgid "date" 21 | msgstr "дата" 22 | 23 | #: conf.py:17 24 | msgid "year" 25 | msgstr "год" 26 | 27 | #: conf.py:18 28 | msgid "month" 29 | msgstr "месяц" 30 | 31 | #: conf.py:19 32 | msgid "day" 33 | msgstr "дзень" 34 | 35 | #: conf.py:20 36 | msgid "week day" 37 | msgstr "дзень тыдня" 38 | 39 | #: conf.py:21 40 | msgid "hour" 41 | msgstr "гадзіну" 42 | 43 | #: conf.py:22 44 | msgid "minute" 45 | msgstr "хвіліна" 46 | 47 | #: conf.py:23 48 | msgid "second" 49 | msgstr "секунда" 50 | 51 | #: conf.py:27 conf.py:28 52 | msgid "contains" 53 | msgstr "змяшчае" 54 | 55 | #: conf.py:29 56 | msgid "is in" 57 | msgstr "у" 58 | 59 | #: conf.py:30 60 | msgid "is greater than" 61 | msgstr "больш чым" 62 | 63 | #: conf.py:31 64 | msgid "is greater than or equal to" 65 | msgstr "больш або роўна" 66 | 67 | #: conf.py:32 68 | msgid "is less than" 69 | msgstr "менш чым" 70 | 71 | #: conf.py:33 72 | msgid "is less than or equal to" 73 | msgstr "менш або роўна" 74 | 75 | #: conf.py:34 conf.py:35 76 | msgid "starts with" 77 | msgstr "пачынаецца" 78 | 79 | #: conf.py:36 conf.py:37 80 | msgid "ends with" 81 | msgstr "заканчваецца" 82 | 83 | #: conf.py:38 84 | msgid "is in range" 85 | msgstr "у дыяпазоне" 86 | 87 | #: conf.py:39 88 | msgid "is null" 89 | msgstr "" 90 | 91 | #: conf.py:40 conf.py:41 92 | msgid "matches regex" 93 | msgstr "адпавядае рэгулярнаму выразу" 94 | 95 | #: conf.py:42 conf.py:49 96 | msgid "search" 97 | msgstr "пошук" 98 | 99 | #: conf.py:44 100 | msgid "is contained by" 101 | msgstr "змяшчаецца ў" 102 | 103 | #: conf.py:45 104 | msgid "overlaps" 105 | msgstr "перакрываецца" 106 | 107 | #: conf.py:46 108 | msgid "has key" 109 | msgstr "мае ключ" 110 | 111 | #: conf.py:47 112 | msgid "has keys" 113 | msgstr "мае ключы" 114 | 115 | #: conf.py:48 116 | msgid "has any keys" 117 | msgstr "мае любыя ключы" 118 | 119 | #: fields.py:94 120 | msgid "Select a lookup." 121 | msgstr "" 122 | 123 | #: fields.py:198 124 | msgid "Range query expects two values." 125 | msgstr "Запыт дыяпазону чакае два значэння." 126 | 127 | #: filters.py:437 128 | msgid "Today" 129 | msgstr "Сёння" 130 | 131 | #: filters.py:438 132 | msgid "Yesterday" 133 | msgstr "Учора" 134 | 135 | #: filters.py:439 136 | msgid "Past 7 days" 137 | msgstr "Мінулыя 7 дзён" 138 | 139 | #: filters.py:440 140 | msgid "This month" 141 | msgstr "За гэты месяц" 142 | 143 | #: filters.py:441 144 | msgid "This year" 145 | msgstr "У гэтым годзе" 146 | 147 | #: filters.py:543 148 | msgid "Multiple values may be separated by commas." 149 | msgstr "Некалькі значэнняў могуць быць падзеленыя коскамі." 150 | 151 | #: filters.py:721 152 | #, python-format 153 | msgid "%s (descending)" 154 | msgstr "%s (па змяншэнні)" 155 | 156 | #: filters.py:737 157 | msgid "Ordering" 158 | msgstr "Парадак" 159 | 160 | #: rest_framework/filterset.py:33 161 | #: templates/django_filters/rest_framework/form.html:5 162 | msgid "Submit" 163 | msgstr "Адправіць" 164 | 165 | #: templates/django_filters/rest_framework/crispy_form.html:4 166 | #: templates/django_filters/rest_framework/form.html:2 167 | msgid "Field filters" 168 | msgstr "Фільтры па палях" 169 | 170 | #: utils.py:308 171 | msgid "exclude" 172 | msgstr "выключаючы" 173 | 174 | #: widgets.py:58 175 | msgid "All" 176 | msgstr "Усе" 177 | 178 | #: widgets.py:162 179 | msgid "Unknown" 180 | msgstr "Не было прапанавана" 181 | 182 | #: widgets.py:162 183 | msgid "Yes" 184 | msgstr "Ды" 185 | 186 | #: widgets.py:162 187 | msgid "No" 188 | msgstr "Няма" 189 | 190 | #~ msgid "Any date" 191 | #~ msgstr "Любая дата" 192 | -------------------------------------------------------------------------------- /django_filters/locale/bg/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/bg/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/bg/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Hristo Gatsinski , 2019. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-filter\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2019-12-21 19:36+0200\n" 12 | "Last-Translator: Hristo Gatsinski \n" 13 | "Language-Team: \n" 14 | "Language: bg\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 1.8.9\n" 20 | 21 | #: conf.py:16 22 | msgid "date" 23 | msgstr "дата" 24 | 25 | #: conf.py:17 26 | msgid "year" 27 | msgstr "година" 28 | 29 | #: conf.py:18 30 | msgid "month" 31 | msgstr "месец" 32 | 33 | #: conf.py:19 34 | msgid "day" 35 | msgstr "ден" 36 | 37 | #: conf.py:20 38 | msgid "week day" 39 | msgstr "ден от седмицата" 40 | 41 | #: conf.py:21 42 | msgid "hour" 43 | msgstr "час" 44 | 45 | #: conf.py:22 46 | msgid "minute" 47 | msgstr "минута" 48 | 49 | #: conf.py:23 50 | msgid "second" 51 | msgstr "секунда" 52 | 53 | #: conf.py:27 conf.py:28 54 | msgid "contains" 55 | msgstr "съдържа" 56 | 57 | #: conf.py:29 58 | msgid "is in" 59 | msgstr "в" 60 | 61 | #: conf.py:30 62 | msgid "is greater than" 63 | msgstr "е по-голям от" 64 | 65 | #: conf.py:31 66 | msgid "is greater than or equal to" 67 | msgstr "е по-голям или равен на" 68 | 69 | #: conf.py:32 70 | msgid "is less than" 71 | msgstr "е по-малък от" 72 | 73 | #: conf.py:33 74 | msgid "is less than or equal to" 75 | msgstr "е по-малък или равен на" 76 | 77 | #: conf.py:34 conf.py:35 78 | msgid "starts with" 79 | msgstr "започва с" 80 | 81 | #: conf.py:36 conf.py:37 82 | msgid "ends with" 83 | msgstr "завършва с" 84 | 85 | #: conf.py:38 86 | msgid "is in range" 87 | msgstr "е в диапазона" 88 | 89 | #: conf.py:39 90 | msgid "is null" 91 | msgstr "" 92 | 93 | #: conf.py:40 conf.py:41 94 | msgid "matches regex" 95 | msgstr "съвпада с регуларен израз" 96 | 97 | #: conf.py:42 conf.py:49 98 | msgid "search" 99 | msgstr "търсене" 100 | 101 | #: conf.py:44 102 | msgid "is contained by" 103 | msgstr "се съдържа от" 104 | 105 | #: conf.py:45 106 | msgid "overlaps" 107 | msgstr "припокрива" 108 | 109 | #: conf.py:46 110 | msgid "has key" 111 | msgstr "има ключ" 112 | 113 | #: conf.py:47 114 | msgid "has keys" 115 | msgstr "има ключове" 116 | 117 | #: conf.py:48 118 | msgid "has any keys" 119 | msgstr "има който и да е ключ" 120 | 121 | #: fields.py:94 122 | msgid "Select a lookup." 123 | msgstr "Изберете справка" 124 | 125 | #: fields.py:198 126 | msgid "Range query expects two values." 127 | msgstr "Търсенето по диапазон изисква две стойности" 128 | 129 | #: filters.py:437 130 | msgid "Today" 131 | msgstr "Днес" 132 | 133 | #: filters.py:438 134 | msgid "Yesterday" 135 | msgstr "Вчера" 136 | 137 | #: filters.py:439 138 | msgid "Past 7 days" 139 | msgstr "Последните 7 дни" 140 | 141 | #: filters.py:440 142 | msgid "This month" 143 | msgstr "Този месец" 144 | 145 | #: filters.py:441 146 | msgid "This year" 147 | msgstr "Тази година" 148 | 149 | #: filters.py:543 150 | msgid "Multiple values may be separated by commas." 151 | msgstr "Множество стойности може да се разделят със запетая" 152 | 153 | #: filters.py:721 154 | #, python-format 155 | msgid "%s (descending)" 156 | msgstr "%s (намалавящ)" 157 | 158 | #: filters.py:737 159 | msgid "Ordering" 160 | msgstr "Подредба" 161 | 162 | #: rest_framework/filterset.py:33 163 | #: templates/django_filters/rest_framework/form.html:5 164 | msgid "Submit" 165 | msgstr "Изпращане" 166 | 167 | #: templates/django_filters/rest_framework/crispy_form.html:4 168 | #: templates/django_filters/rest_framework/form.html:2 169 | msgid "Field filters" 170 | msgstr "Филтри на полетата" 171 | 172 | #: utils.py:308 173 | msgid "exclude" 174 | msgstr "изключва" 175 | 176 | #: widgets.py:58 177 | msgid "All" 178 | msgstr "Всичко" 179 | 180 | #: widgets.py:162 181 | msgid "Unknown" 182 | msgstr "Неизвестен" 183 | 184 | #: widgets.py:162 185 | msgid "Yes" 186 | msgstr "Да" 187 | 188 | #: widgets.py:162 189 | msgid "No" 190 | msgstr "Не" 191 | -------------------------------------------------------------------------------- /django_filters/locale/cs/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/cs/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/cs/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-filter\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 7 | "PO-Revision-Date: 2024-09-13 20:26+0000\n" 8 | "Last-Translator: Jakub Boukal \n" 9 | "Language-Team: Czech \n" 11 | "Language: cs\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " 16 | "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" 17 | "X-Generator: Weblate 5.8-dev\n" 18 | 19 | #: conf.py:16 20 | msgid "date" 21 | msgstr "datum" 22 | 23 | #: conf.py:17 24 | msgid "year" 25 | msgstr "rok" 26 | 27 | #: conf.py:18 28 | msgid "month" 29 | msgstr "měsíc" 30 | 31 | #: conf.py:19 32 | msgid "day" 33 | msgstr "den" 34 | 35 | #: conf.py:20 36 | msgid "week day" 37 | msgstr "den v týdnu" 38 | 39 | #: conf.py:21 40 | msgid "hour" 41 | msgstr "hodinu" 42 | 43 | #: conf.py:22 44 | msgid "minute" 45 | msgstr "minutu" 46 | 47 | #: conf.py:23 48 | msgid "second" 49 | msgstr "vteřina" 50 | 51 | #: conf.py:27 conf.py:28 52 | msgid "contains" 53 | msgstr "obsahuje" 54 | 55 | #: conf.py:29 56 | msgid "is in" 57 | msgstr "v" 58 | 59 | #: conf.py:30 60 | msgid "is greater than" 61 | msgstr "více než" 62 | 63 | #: conf.py:31 64 | msgid "is greater than or equal to" 65 | msgstr "větší nebo roven" 66 | 67 | #: conf.py:32 68 | msgid "is less than" 69 | msgstr "méně než" 70 | 71 | #: conf.py:33 72 | msgid "is less than or equal to" 73 | msgstr "menší nebo rovné" 74 | 75 | #: conf.py:34 conf.py:35 76 | msgid "starts with" 77 | msgstr "začíná" 78 | 79 | #: conf.py:36 conf.py:37 80 | msgid "ends with" 81 | msgstr "končí" 82 | 83 | #: conf.py:38 84 | msgid "is in range" 85 | msgstr "v rozsahu" 86 | 87 | #: conf.py:39 88 | msgid "is null" 89 | msgstr "je null" 90 | 91 | #: conf.py:40 conf.py:41 92 | msgid "matches regex" 93 | msgstr "odpovídá normálnímu výrazu" 94 | 95 | #: conf.py:42 conf.py:49 96 | msgid "search" 97 | msgstr "vyhledávání" 98 | 99 | #: conf.py:44 100 | msgid "is contained by" 101 | msgstr "je obsažen v" 102 | 103 | #: conf.py:45 104 | msgid "overlaps" 105 | msgstr "překrývají" 106 | 107 | #: conf.py:46 108 | msgid "has key" 109 | msgstr "má klíč" 110 | 111 | #: conf.py:47 112 | msgid "has keys" 113 | msgstr "má klíče" 114 | 115 | #: conf.py:48 116 | msgid "has any keys" 117 | msgstr "má nějaké klíče" 118 | 119 | #: fields.py:94 120 | msgid "Select a lookup." 121 | msgstr "Vyberte lookup." 122 | 123 | #: fields.py:198 124 | msgid "Range query expects two values." 125 | msgstr "Rozsah dotazu očekává dvě hodnoty." 126 | 127 | #: filters.py:437 128 | msgid "Today" 129 | msgstr "Dnes" 130 | 131 | #: filters.py:438 132 | msgid "Yesterday" 133 | msgstr "Včera" 134 | 135 | #: filters.py:439 136 | msgid "Past 7 days" 137 | msgstr "Posledních 7 dní" 138 | 139 | #: filters.py:440 140 | msgid "This month" 141 | msgstr "Tento měsíc" 142 | 143 | #: filters.py:441 144 | msgid "This year" 145 | msgstr "Tento rok" 146 | 147 | #: filters.py:543 148 | msgid "Multiple values may be separated by commas." 149 | msgstr "Více hodnot lze oddělit čárkami." 150 | 151 | #: filters.py:721 152 | #, python-format 153 | msgid "%s (descending)" 154 | msgstr "%s (sestupně)" 155 | 156 | #: filters.py:737 157 | msgid "Ordering" 158 | msgstr "Řád z" 159 | 160 | #: rest_framework/filterset.py:33 161 | #: templates/django_filters/rest_framework/form.html:5 162 | msgid "Submit" 163 | msgstr "Odeslat" 164 | 165 | #: templates/django_filters/rest_framework/crispy_form.html:4 166 | #: templates/django_filters/rest_framework/form.html:2 167 | msgid "Field filters" 168 | msgstr "Filtry na polích" 169 | 170 | #: utils.py:308 171 | msgid "exclude" 172 | msgstr "s výjimkou" 173 | 174 | #: widgets.py:58 175 | msgid "All" 176 | msgstr "Všechno" 177 | 178 | #: widgets.py:162 179 | msgid "Unknown" 180 | msgstr "Není nastaveno" 181 | 182 | #: widgets.py:162 183 | msgid "Yes" 184 | msgstr "Ano" 185 | 186 | #: widgets.py:162 187 | msgid "No" 188 | msgstr "Ne" 189 | 190 | #~ msgid "Any date" 191 | #~ msgstr "Jakékoliv datum" 192 | -------------------------------------------------------------------------------- /django_filters/locale/da/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/da/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/da/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: django-filter\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 6 | "PO-Revision-Date: 2017-10-28\n" 7 | "Last-Translator: Danni Randeris \n" 8 | "Language-Team: Danni Randeris \n" 9 | "Language: da\n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "X-Generator: Poedit 2.0.1\n" 14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 15 | 16 | #: conf.py:16 17 | msgid "date" 18 | msgstr "dato" 19 | 20 | #: conf.py:17 21 | msgid "year" 22 | msgstr "år" 23 | 24 | #: conf.py:18 25 | msgid "month" 26 | msgstr "måned" 27 | 28 | #: conf.py:19 29 | msgid "day" 30 | msgstr "dag" 31 | 32 | #: conf.py:20 33 | msgid "week day" 34 | msgstr "ugedag" 35 | 36 | #: conf.py:21 37 | msgid "hour" 38 | msgstr "time" 39 | 40 | #: conf.py:22 41 | msgid "minute" 42 | msgstr "minut" 43 | 44 | #: conf.py:23 45 | msgid "second" 46 | msgstr "sekund" 47 | 48 | #: conf.py:27 conf.py:28 49 | msgid "contains" 50 | msgstr "indeholder" 51 | 52 | #: conf.py:29 53 | msgid "is in" 54 | msgstr "er i" 55 | 56 | #: conf.py:30 57 | msgid "is greater than" 58 | msgstr "er større end" 59 | 60 | #: conf.py:31 61 | msgid "is greater than or equal to" 62 | msgstr "er større end eller lig med" 63 | 64 | #: conf.py:32 65 | msgid "is less than" 66 | msgstr "er mindre end" 67 | 68 | #: conf.py:33 69 | msgid "is less than or equal to" 70 | msgstr "er mindre end eller lig med" 71 | 72 | #: conf.py:34 conf.py:35 73 | msgid "starts with" 74 | msgstr "starter med" 75 | 76 | #: conf.py:36 conf.py:37 77 | msgid "ends with" 78 | msgstr "slutter med" 79 | 80 | #: conf.py:38 81 | msgid "is in range" 82 | msgstr "er i intervallet" 83 | 84 | #: conf.py:39 85 | msgid "is null" 86 | msgstr "" 87 | 88 | #: conf.py:40 conf.py:41 89 | msgid "matches regex" 90 | msgstr "matcher regex" 91 | 92 | #: conf.py:42 conf.py:49 93 | msgid "search" 94 | msgstr "søg" 95 | 96 | #: conf.py:44 97 | msgid "is contained by" 98 | msgstr "er indeholdt af" 99 | 100 | #: conf.py:45 101 | msgid "overlaps" 102 | msgstr "overlapper" 103 | 104 | #: conf.py:46 105 | msgid "has key" 106 | msgstr "har string" 107 | 108 | #: conf.py:47 109 | msgid "has keys" 110 | msgstr "har stringe" 111 | 112 | #: conf.py:48 113 | msgid "has any keys" 114 | msgstr "har hvilken som helst string" 115 | 116 | #: fields.py:94 117 | msgid "Select a lookup." 118 | msgstr "" 119 | 120 | #: fields.py:198 121 | msgid "Range query expects two values." 122 | msgstr "Interval forespørgslen forventer to værdier." 123 | 124 | #: filters.py:437 125 | msgid "Today" 126 | msgstr "I dag" 127 | 128 | #: filters.py:438 129 | msgid "Yesterday" 130 | msgstr "I går" 131 | 132 | #: filters.py:439 133 | msgid "Past 7 days" 134 | msgstr "Sidste 7 dage" 135 | 136 | #: filters.py:440 137 | msgid "This month" 138 | msgstr "Denne måned" 139 | 140 | #: filters.py:441 141 | msgid "This year" 142 | msgstr "Dette år" 143 | 144 | #: filters.py:543 145 | msgid "Multiple values may be separated by commas." 146 | msgstr "Flere værdier kan adskilles via komma." 147 | 148 | #: filters.py:721 149 | #, python-format 150 | msgid "%s (descending)" 151 | msgstr "%s (aftagende)" 152 | 153 | #: filters.py:737 154 | msgid "Ordering" 155 | msgstr "Sortering" 156 | 157 | #: rest_framework/filterset.py:33 158 | #: templates/django_filters/rest_framework/form.html:5 159 | #, fuzzy 160 | msgid "Submit" 161 | msgstr "Indsend" 162 | 163 | #: templates/django_filters/rest_framework/crispy_form.html:4 164 | #: templates/django_filters/rest_framework/form.html:2 165 | #, fuzzy 166 | msgid "Field filters" 167 | msgstr "Felt filtre" 168 | 169 | #: utils.py:308 170 | msgid "exclude" 171 | msgstr "udelad" 172 | 173 | #: widgets.py:58 174 | msgid "All" 175 | msgstr "Alle" 176 | 177 | #: widgets.py:162 178 | msgid "Unknown" 179 | msgstr "Ukendt" 180 | 181 | #: widgets.py:162 182 | msgid "Yes" 183 | msgstr "Ja" 184 | 185 | #: widgets.py:162 186 | msgid "No" 187 | msgstr "Nej" 188 | 189 | #~ msgid "Any date" 190 | #~ msgstr "Hvilken som helst dag" 191 | -------------------------------------------------------------------------------- /django_filters/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-filter\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2013-08-10 12:29+0100\n" 12 | "Last-Translator: Florian Apolloner \n" 13 | "Language-Team: \n" 14 | "Language: de\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 1.5.4\n" 20 | 21 | #: conf.py:16 22 | msgid "date" 23 | msgstr "Datum" 24 | 25 | #: conf.py:17 26 | msgid "year" 27 | msgstr "Jahr" 28 | 29 | #: conf.py:18 30 | msgid "month" 31 | msgstr "Monat" 32 | 33 | #: conf.py:19 34 | msgid "day" 35 | msgstr "Tag" 36 | 37 | #: conf.py:20 38 | msgid "week day" 39 | msgstr "Wochentag" 40 | 41 | #: conf.py:21 42 | msgid "hour" 43 | msgstr "Stunde" 44 | 45 | #: conf.py:22 46 | msgid "minute" 47 | msgstr "Minute" 48 | 49 | #: conf.py:23 50 | msgid "second" 51 | msgstr "Sekunde" 52 | 53 | #: conf.py:27 conf.py:28 54 | msgid "contains" 55 | msgstr "enthält" 56 | 57 | #: conf.py:29 58 | msgid "is in" 59 | msgstr "ist in" 60 | 61 | #: conf.py:30 62 | msgid "is greater than" 63 | msgstr "ist größer als" 64 | 65 | #: conf.py:31 66 | msgid "is greater than or equal to" 67 | msgstr "ist größer oder gleich" 68 | 69 | #: conf.py:32 70 | msgid "is less than" 71 | msgstr "ist kleiner als" 72 | 73 | #: conf.py:33 74 | msgid "is less than or equal to" 75 | msgstr "ist kleiner oder gleich" 76 | 77 | #: conf.py:34 conf.py:35 78 | msgid "starts with" 79 | msgstr "beginnt mit" 80 | 81 | #: conf.py:36 conf.py:37 82 | msgid "ends with" 83 | msgstr "endet mit" 84 | 85 | #: conf.py:38 86 | msgid "is in range" 87 | msgstr "ist im Bereich" 88 | 89 | #: conf.py:39 90 | msgid "is null" 91 | msgstr "" 92 | 93 | #: conf.py:40 conf.py:41 94 | msgid "matches regex" 95 | msgstr "passt auf Regex" 96 | 97 | #: conf.py:42 conf.py:49 98 | msgid "search" 99 | msgstr "Suche" 100 | 101 | #: conf.py:44 102 | msgid "is contained by" 103 | msgstr "ist enthalten in" 104 | 105 | #: conf.py:45 106 | msgid "overlaps" 107 | msgstr "überlappen" 108 | 109 | #: conf.py:46 110 | msgid "has key" 111 | msgstr "hat Schlüssel" 112 | 113 | #: conf.py:47 114 | msgid "has keys" 115 | msgstr "hat Schlüssel" 116 | 117 | #: conf.py:48 118 | msgid "has any keys" 119 | msgstr "hat beliebige Schlüssel" 120 | 121 | #: fields.py:94 122 | msgid "Select a lookup." 123 | msgstr "" 124 | 125 | #: fields.py:198 126 | msgid "Range query expects two values." 127 | msgstr "Die Bereichsabfrage erwartet zwei Werte." 128 | 129 | #: filters.py:437 130 | msgid "Today" 131 | msgstr "Heute" 132 | 133 | #: filters.py:438 134 | msgid "Yesterday" 135 | msgstr "Gestern" 136 | 137 | #: filters.py:439 138 | msgid "Past 7 days" 139 | msgstr "Letzte 7 Tage" 140 | 141 | #: filters.py:440 142 | msgid "This month" 143 | msgstr "Diesen Monat" 144 | 145 | #: filters.py:441 146 | msgid "This year" 147 | msgstr "Dieses Jahr" 148 | 149 | #: filters.py:543 150 | msgid "Multiple values may be separated by commas." 151 | msgstr "Mehrere Werte können durch Kommas getrennt sein." 152 | 153 | #: filters.py:721 154 | #, python-format 155 | msgid "%s (descending)" 156 | msgstr "%s (absteigend)" 157 | 158 | #: filters.py:737 159 | msgid "Ordering" 160 | msgstr "Sortierung" 161 | 162 | #: rest_framework/filterset.py:33 163 | #: templates/django_filters/rest_framework/form.html:5 164 | msgid "Submit" 165 | msgstr "Absenden" 166 | 167 | #: templates/django_filters/rest_framework/crispy_form.html:4 168 | #: templates/django_filters/rest_framework/form.html:2 169 | msgid "Field filters" 170 | msgstr "Feldfilter" 171 | 172 | #: utils.py:308 173 | msgid "exclude" 174 | msgstr "ausschließen" 175 | 176 | #: widgets.py:58 177 | msgid "All" 178 | msgstr "Alle" 179 | 180 | #: widgets.py:162 181 | msgid "Unknown" 182 | msgstr "Unbekannte" 183 | 184 | #: widgets.py:162 185 | msgid "Yes" 186 | msgstr "Ja" 187 | 188 | #: widgets.py:162 189 | msgid "No" 190 | msgstr "Nein" 191 | 192 | #~ msgid "Any date" 193 | #~ msgstr "Alle Daten" 194 | -------------------------------------------------------------------------------- /django_filters/locale/el/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/el/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/el/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Serafeim Papastefanos , 2017. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-filter\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2017-11-16 10:04+0200\n" 12 | "Last-Translator: Serafeim Papastefanos \n" 13 | "Language-Team: \n" 14 | "Language: de\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 1.6.5\n" 20 | 21 | #: conf.py:16 22 | msgid "date" 23 | msgstr "ημερομηνία" 24 | 25 | #: conf.py:17 26 | msgid "year" 27 | msgstr "έτος" 28 | 29 | #: conf.py:18 30 | msgid "month" 31 | msgstr "μήνας" 32 | 33 | #: conf.py:19 34 | msgid "day" 35 | msgstr "ημέρα" 36 | 37 | #: conf.py:20 38 | msgid "week day" 39 | msgstr "ημέρα της εβδομάδας" 40 | 41 | #: conf.py:21 42 | msgid "hour" 43 | msgstr "ώρα" 44 | 45 | #: conf.py:22 46 | msgid "minute" 47 | msgstr "λεπτό" 48 | 49 | #: conf.py:23 50 | msgid "second" 51 | msgstr "δευτερόλεπτο" 52 | 53 | #: conf.py:27 conf.py:28 54 | msgid "contains" 55 | msgstr "περιέχει" 56 | 57 | #: conf.py:29 58 | msgid "is in" 59 | msgstr "είναι εντός των" 60 | 61 | #: conf.py:30 62 | msgid "is greater than" 63 | msgstr "είναι μεγαλύτερο από" 64 | 65 | #: conf.py:31 66 | msgid "is greater than or equal to" 67 | msgstr "είναι μεγαλύτερο ή ίσο του" 68 | 69 | #: conf.py:32 70 | msgid "is less than" 71 | msgstr "είναι μικρότερο από" 72 | 73 | #: conf.py:33 74 | msgid "is less than or equal to" 75 | msgstr "είναι μικρότερο ή ίσο του" 76 | 77 | #: conf.py:34 conf.py:35 78 | msgid "starts with" 79 | msgstr "ξεκινά με" 80 | 81 | #: conf.py:36 conf.py:37 82 | msgid "ends with" 83 | msgstr "τελειώνει με" 84 | 85 | #: conf.py:38 86 | msgid "is in range" 87 | msgstr "είναι εντος του εύρους" 88 | 89 | #: conf.py:39 90 | msgid "is null" 91 | msgstr "" 92 | 93 | #: conf.py:40 conf.py:41 94 | msgid "matches regex" 95 | msgstr "περιέχει regex" 96 | 97 | #: conf.py:42 conf.py:49 98 | msgid "search" 99 | msgstr "αναζήτηση" 100 | 101 | #: conf.py:44 102 | msgid "is contained by" 103 | msgstr "περιέχεται σε" 104 | 105 | #: conf.py:45 106 | msgid "overlaps" 107 | msgstr "επικαλύπτεται" 108 | 109 | #: conf.py:46 110 | msgid "has key" 111 | msgstr "έχει το κλειδί" 112 | 113 | #: conf.py:47 114 | msgid "has keys" 115 | msgstr "έχει τα κλειδιά" 116 | 117 | #: conf.py:48 118 | msgid "has any keys" 119 | msgstr "έχει οποιαδήποτε κλειδιά" 120 | 121 | #: fields.py:94 122 | msgid "Select a lookup." 123 | msgstr "" 124 | 125 | #: fields.py:198 126 | msgid "Range query expects two values." 127 | msgstr "Το ερώτημα εύρους απαιτεί δύο τιμές," 128 | 129 | #: filters.py:437 130 | msgid "Today" 131 | msgstr "Σήμερα" 132 | 133 | #: filters.py:438 134 | msgid "Yesterday" 135 | msgstr "Χτες" 136 | 137 | #: filters.py:439 138 | msgid "Past 7 days" 139 | msgstr "Τις προηγούμενες 7 ημέρες" 140 | 141 | #: filters.py:440 142 | msgid "This month" 143 | msgstr "Αυτό το μήνα" 144 | 145 | #: filters.py:441 146 | msgid "This year" 147 | msgstr "Αυτό το έτος" 148 | 149 | #: filters.py:543 150 | msgid "Multiple values may be separated by commas." 151 | msgstr "Οι πολλαπλές τιμές πρέπει να διαχωρίζονται με κόμμα." 152 | 153 | #: filters.py:721 154 | #, python-format 155 | msgid "%s (descending)" 156 | msgstr "%s (φθίνουσα" 157 | 158 | #: filters.py:737 159 | msgid "Ordering" 160 | msgstr "Ταξινόμηση" 161 | 162 | #: rest_framework/filterset.py:33 163 | #: templates/django_filters/rest_framework/form.html:5 164 | msgid "Submit" 165 | msgstr "Υποβολή" 166 | 167 | #: templates/django_filters/rest_framework/crispy_form.html:4 168 | #: templates/django_filters/rest_framework/form.html:2 169 | msgid "Field filters" 170 | msgstr "Φίλτρα πεδίων" 171 | 172 | #: utils.py:308 173 | msgid "exclude" 174 | msgstr "απέκλεισε" 175 | 176 | #: widgets.py:58 177 | msgid "All" 178 | msgstr "Όλα" 179 | 180 | #: widgets.py:162 181 | msgid "Unknown" 182 | msgstr "Άγνωστο" 183 | 184 | #: widgets.py:162 185 | msgid "Yes" 186 | msgstr "Ναι" 187 | 188 | #: widgets.py:162 189 | msgid "No" 190 | msgstr "Όχι" 191 | 192 | #~ msgid "Any date" 193 | #~ msgstr "Οποιαδήποτε ημερομηνία" 194 | -------------------------------------------------------------------------------- /django_filters/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/es/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the django_filter package. 4 | # Carlos Goce, 2017. 5 | # Nicolás Stuardo, 2020 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: \n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 12 | "PO-Revision-Date: 2023-02-12 14:36+0000\n" 13 | "Last-Translator: gallegonovato \n" 14 | "Language-Team: Spanish \n" 16 | "Language: es\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 21 | "X-Generator: Weblate 4.16-dev\n" 22 | 23 | #: conf.py:16 24 | msgid "date" 25 | msgstr "fecha" 26 | 27 | #: conf.py:17 28 | msgid "year" 29 | msgstr "año" 30 | 31 | #: conf.py:18 32 | msgid "month" 33 | msgstr "mes" 34 | 35 | #: conf.py:19 36 | msgid "day" 37 | msgstr "día" 38 | 39 | #: conf.py:20 40 | msgid "week day" 41 | msgstr "día de la semana" 42 | 43 | #: conf.py:21 44 | msgid "hour" 45 | msgstr "hora" 46 | 47 | #: conf.py:22 48 | msgid "minute" 49 | msgstr "minuto" 50 | 51 | #: conf.py:23 52 | msgid "second" 53 | msgstr "segundo" 54 | 55 | #: conf.py:27 conf.py:28 56 | msgid "contains" 57 | msgstr "contiene" 58 | 59 | #: conf.py:29 60 | msgid "is in" 61 | msgstr "presente en" 62 | 63 | #: conf.py:30 64 | msgid "is greater than" 65 | msgstr "mayor que" 66 | 67 | #: conf.py:31 68 | msgid "is greater than or equal to" 69 | msgstr "mayor o igual que" 70 | 71 | #: conf.py:32 72 | msgid "is less than" 73 | msgstr "menor que" 74 | 75 | #: conf.py:33 76 | msgid "is less than or equal to" 77 | msgstr "menor o igual que" 78 | 79 | #: conf.py:34 conf.py:35 80 | msgid "starts with" 81 | msgstr "comienza por" 82 | 83 | #: conf.py:36 conf.py:37 84 | msgid "ends with" 85 | msgstr "termina por" 86 | 87 | #: conf.py:38 88 | msgid "is in range" 89 | msgstr "en el rango" 90 | 91 | #: conf.py:39 92 | msgid "is null" 93 | msgstr "es nulo" 94 | 95 | #: conf.py:40 conf.py:41 96 | msgid "matches regex" 97 | msgstr "coincide con la expresión regular" 98 | 99 | #: conf.py:42 conf.py:49 100 | msgid "search" 101 | msgstr "buscar" 102 | 103 | #: conf.py:44 104 | msgid "is contained by" 105 | msgstr "contenido en" 106 | 107 | #: conf.py:45 108 | msgid "overlaps" 109 | msgstr "solapado" 110 | 111 | #: conf.py:46 112 | msgid "has key" 113 | msgstr "contiene la clave" 114 | 115 | #: conf.py:47 116 | msgid "has keys" 117 | msgstr "contiene las claves" 118 | 119 | #: conf.py:48 120 | msgid "has any keys" 121 | msgstr "contiene alguna de las claves" 122 | 123 | #: fields.py:94 124 | msgid "Select a lookup." 125 | msgstr "Seleccione un operador de consulta." 126 | 127 | #: fields.py:198 128 | msgid "Range query expects two values." 129 | msgstr "Consultar un rango requiere dos valores." 130 | 131 | #: filters.py:437 132 | msgid "Today" 133 | msgstr "Hoy" 134 | 135 | #: filters.py:438 136 | msgid "Yesterday" 137 | msgstr "Ayer" 138 | 139 | #: filters.py:439 140 | msgid "Past 7 days" 141 | msgstr "Últimos 7 días" 142 | 143 | #: filters.py:440 144 | msgid "This month" 145 | msgstr "Este mes" 146 | 147 | #: filters.py:441 148 | msgid "This year" 149 | msgstr "Este año" 150 | 151 | #: filters.py:543 152 | msgid "Multiple values may be separated by commas." 153 | msgstr "Múltiples valores separados por comas." 154 | 155 | #: filters.py:721 156 | #, python-format 157 | msgid "%s (descending)" 158 | msgstr "%s (descendente)" 159 | 160 | #: filters.py:737 161 | msgid "Ordering" 162 | msgstr "Ordenado" 163 | 164 | #: rest_framework/filterset.py:33 165 | #: templates/django_filters/rest_framework/form.html:5 166 | msgid "Submit" 167 | msgstr "Enviar" 168 | 169 | #: templates/django_filters/rest_framework/crispy_form.html:4 170 | #: templates/django_filters/rest_framework/form.html:2 171 | msgid "Field filters" 172 | msgstr "Filtros de campo" 173 | 174 | #: utils.py:308 175 | msgid "exclude" 176 | msgstr "excluye" 177 | 178 | #: widgets.py:58 179 | msgid "All" 180 | msgstr "Todo" 181 | 182 | #: widgets.py:162 183 | msgid "Unknown" 184 | msgstr "Desconocido" 185 | 186 | #: widgets.py:162 187 | msgid "Yes" 188 | msgstr "Sí" 189 | 190 | #: widgets.py:162 191 | msgid "No" 192 | msgstr "No" 193 | 194 | #~ msgid "Any date" 195 | #~ msgstr "Cualquier fecha" 196 | -------------------------------------------------------------------------------- /django_filters/locale/es_AR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/es_AR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/es_AR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the django_filter package. 4 | # Gonzalo Bustos, 2015. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2015-10-11 20:53-0300\n" 12 | "Last-Translator: Gonzalo Bustos\n" 13 | "Language-Team: Spanish (Argentina)\n" 14 | "Language: es_AR\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 1.6.10\n" 20 | 21 | #: conf.py:16 22 | #, fuzzy 23 | #| msgid "Any date" 24 | msgid "date" 25 | msgstr "Cualquier fecha" 26 | 27 | #: conf.py:17 28 | #, fuzzy 29 | #| msgid "This year" 30 | msgid "year" 31 | msgstr "Este año" 32 | 33 | #: conf.py:18 34 | #, fuzzy 35 | #| msgid "This month" 36 | msgid "month" 37 | msgstr "Este mes" 38 | 39 | #: conf.py:19 40 | #, fuzzy 41 | #| msgid "Today" 42 | msgid "day" 43 | msgstr "Hoy" 44 | 45 | #: conf.py:20 46 | msgid "week day" 47 | msgstr "" 48 | 49 | #: conf.py:21 50 | msgid "hour" 51 | msgstr "" 52 | 53 | #: conf.py:22 54 | msgid "minute" 55 | msgstr "" 56 | 57 | #: conf.py:23 58 | msgid "second" 59 | msgstr "" 60 | 61 | #: conf.py:27 conf.py:28 62 | msgid "contains" 63 | msgstr "" 64 | 65 | #: conf.py:29 66 | msgid "is in" 67 | msgstr "" 68 | 69 | #: conf.py:30 70 | msgid "is greater than" 71 | msgstr "" 72 | 73 | #: conf.py:31 74 | msgid "is greater than or equal to" 75 | msgstr "" 76 | 77 | #: conf.py:32 78 | msgid "is less than" 79 | msgstr "" 80 | 81 | #: conf.py:33 82 | msgid "is less than or equal to" 83 | msgstr "" 84 | 85 | #: conf.py:34 conf.py:35 86 | msgid "starts with" 87 | msgstr "" 88 | 89 | #: conf.py:36 conf.py:37 90 | msgid "ends with" 91 | msgstr "" 92 | 93 | #: conf.py:38 94 | msgid "is in range" 95 | msgstr "" 96 | 97 | #: conf.py:39 98 | msgid "is null" 99 | msgstr "" 100 | 101 | #: conf.py:40 conf.py:41 102 | msgid "matches regex" 103 | msgstr "" 104 | 105 | #: conf.py:42 conf.py:49 106 | msgid "search" 107 | msgstr "" 108 | 109 | #: conf.py:44 110 | msgid "is contained by" 111 | msgstr "" 112 | 113 | #: conf.py:45 114 | msgid "overlaps" 115 | msgstr "" 116 | 117 | #: conf.py:46 118 | msgid "has key" 119 | msgstr "" 120 | 121 | #: conf.py:47 122 | msgid "has keys" 123 | msgstr "" 124 | 125 | #: conf.py:48 126 | msgid "has any keys" 127 | msgstr "" 128 | 129 | #: fields.py:94 130 | msgid "Select a lookup." 131 | msgstr "" 132 | 133 | #: fields.py:198 134 | msgid "Range query expects two values." 135 | msgstr "" 136 | 137 | #: filters.py:437 138 | msgid "Today" 139 | msgstr "Hoy" 140 | 141 | #: filters.py:438 142 | msgid "Yesterday" 143 | msgstr "" 144 | 145 | #: filters.py:439 146 | msgid "Past 7 days" 147 | msgstr "Últimos 7 días" 148 | 149 | #: filters.py:440 150 | msgid "This month" 151 | msgstr "Este mes" 152 | 153 | #: filters.py:441 154 | msgid "This year" 155 | msgstr "Este año" 156 | 157 | #: filters.py:543 158 | msgid "Multiple values may be separated by commas." 159 | msgstr "" 160 | 161 | #: filters.py:721 162 | #, python-format 163 | msgid "%s (descending)" 164 | msgstr "" 165 | 166 | #: filters.py:737 167 | msgid "Ordering" 168 | msgstr "" 169 | 170 | #: rest_framework/filterset.py:33 171 | #: templates/django_filters/rest_framework/form.html:5 172 | msgid "Submit" 173 | msgstr "" 174 | 175 | #: templates/django_filters/rest_framework/crispy_form.html:4 176 | #: templates/django_filters/rest_framework/form.html:2 177 | msgid "Field filters" 178 | msgstr "" 179 | 180 | #: utils.py:308 181 | msgid "exclude" 182 | msgstr "" 183 | 184 | #: widgets.py:58 185 | msgid "All" 186 | msgstr "Todos" 187 | 188 | #: widgets.py:162 189 | msgid "Unknown" 190 | msgstr "" 191 | 192 | #: widgets.py:162 193 | msgid "Yes" 194 | msgstr "" 195 | 196 | #: widgets.py:162 197 | msgid "No" 198 | msgstr "" 199 | 200 | #~ msgid "This is an exclusion filter" 201 | #~ msgstr "Este es un filtro de exclusión" 202 | -------------------------------------------------------------------------------- /django_filters/locale/fa/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/fa/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/fa/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: conf.py:16 22 | msgid "date" 23 | msgstr "تاریخ" 24 | 25 | #: conf.py:17 26 | msgid "year" 27 | msgstr "سال" 28 | 29 | #: conf.py:18 30 | msgid "month" 31 | msgstr "ماه" 32 | 33 | #: conf.py:19 34 | msgid "day" 35 | msgstr "روز" 36 | 37 | #: conf.py:20 38 | msgid "week day" 39 | msgstr "روز هفته" 40 | 41 | #: conf.py:21 42 | msgid "hour" 43 | msgstr "ساعت" 44 | 45 | #: conf.py:22 46 | msgid "minute" 47 | msgstr "دقیقه" 48 | 49 | #: conf.py:23 50 | msgid "second" 51 | msgstr "ثانیه" 52 | 53 | #: conf.py:27 conf.py:28 54 | msgid "contains" 55 | msgstr "شامل" 56 | 57 | #: conf.py:29 58 | msgid "is in" 59 | msgstr "هست در" 60 | 61 | #: conf.py:30 62 | msgid "is greater than" 63 | msgstr "بزرگتر است از" 64 | 65 | #: conf.py:31 66 | msgid "is greater than or equal to" 67 | msgstr "بزرگتر یا مساوی است" 68 | 69 | #: conf.py:32 70 | msgid "is less than" 71 | msgstr "کوچکتر است از" 72 | 73 | #: conf.py:33 74 | msgid "is less than or equal to" 75 | msgstr "کوچکتر یا مساوی است" 76 | 77 | #: conf.py:34 conf.py:35 78 | msgid "starts with" 79 | msgstr "شروع می شود با" 80 | 81 | #: conf.py:36 conf.py:37 82 | msgid "ends with" 83 | msgstr "به پایان می رسد با" 84 | 85 | #: conf.py:38 86 | msgid "is in range" 87 | msgstr "در محدوده" 88 | 89 | #: conf.py:39 90 | msgid "is null" 91 | msgstr "خالی است" 92 | 93 | #: conf.py:40 conf.py:41 94 | msgid "matches regex" 95 | msgstr "با ریجکس منطبق است" 96 | 97 | #: conf.py:42 conf.py:49 98 | msgid "search" 99 | msgstr "جستجو" 100 | 101 | #: conf.py:44 102 | msgid "is contained by" 103 | msgstr "وجود دارد در" 104 | 105 | #: conf.py:45 106 | msgid "overlaps" 107 | msgstr "تداخل دارد" 108 | 109 | #: conf.py:46 110 | msgid "has key" 111 | msgstr "حاوی کلید است" 112 | 113 | #: conf.py:47 114 | msgid "has keys" 115 | msgstr "حاوی کلیدها است" 116 | 117 | #: conf.py:48 118 | msgid "has any keys" 119 | msgstr "حاوی هر کلیدی است" 120 | 121 | #: fields.py:94 122 | msgid "Select a lookup." 123 | msgstr "یک لوک آپ را انتخاب کنید." 124 | 125 | #: fields.py:198 126 | msgid "Range query expects two values." 127 | msgstr "محدوده کوئری دو مقدار را انتظار دارد." 128 | 129 | #: filters.py:437 130 | msgid "Today" 131 | msgstr "امروز" 132 | 133 | #: filters.py:438 134 | msgid "Yesterday" 135 | msgstr "دیروز" 136 | 137 | #: filters.py:439 138 | msgid "Past 7 days" 139 | msgstr "۷ روز گذشته" 140 | 141 | #: filters.py:440 142 | msgid "This month" 143 | msgstr "این ماه" 144 | 145 | #: filters.py:441 146 | msgid "This year" 147 | msgstr "امسال" 148 | 149 | #: filters.py:543 150 | msgid "Multiple values may be separated by commas." 151 | msgstr "ممکن است چندین مقدار با کاما از هم جدا شوند." 152 | 153 | #: filters.py:721 154 | #, python-format 155 | msgid "%s (descending)" 156 | msgstr "%s (نزولی)" 157 | 158 | #: filters.py:737 159 | msgid "Ordering" 160 | msgstr "مرتب سازی" 161 | 162 | #: rest_framework/filterset.py:33 163 | #: templates/django_filters/rest_framework/form.html:5 164 | msgid "Submit" 165 | msgstr "ارسال" 166 | 167 | #: templates/django_filters/rest_framework/crispy_form.html:4 168 | #: templates/django_filters/rest_framework/form.html:2 169 | msgid "Field filters" 170 | msgstr "فیلترهای فیلد" 171 | 172 | #: utils.py:308 173 | msgid "exclude" 174 | msgstr "به غیر از" 175 | 176 | #: widgets.py:58 177 | msgid "All" 178 | msgstr "همه" 179 | 180 | #: widgets.py:162 181 | msgid "Unknown" 182 | msgstr "ناشناس" 183 | 184 | #: widgets.py:162 185 | msgid "Yes" 186 | msgstr "بله" 187 | 188 | #: widgets.py:162 189 | msgid "No" 190 | msgstr "خیر" 191 | -------------------------------------------------------------------------------- /django_filters/locale/fi/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the django_filter package. 4 | # Carlos Goce, 2017. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 17:45+0200\n" 11 | "PO-Revision-Date: 2023-02-12 14:36+0000\n" 12 | "Last-Translator: Janne Tervo \n" 13 | "Language-Team: Finnish \n" 15 | "Language: fi\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 4.16-dev\n" 21 | 22 | #: conf.py:16 23 | msgid "date" 24 | msgstr "päivämäärä" 25 | 26 | #: conf.py:17 27 | msgid "year" 28 | msgstr "vuosi" 29 | 30 | #: conf.py:18 31 | msgid "month" 32 | msgstr "kuukausi" 33 | 34 | #: conf.py:19 35 | msgid "day" 36 | msgstr "päivä" 37 | 38 | #: conf.py:20 39 | msgid "week day" 40 | msgstr "viikonpäivä" 41 | 42 | #: conf.py:21 43 | msgid "hour" 44 | msgstr "tunti" 45 | 46 | #: conf.py:22 47 | msgid "minute" 48 | msgstr "minuutti" 49 | 50 | #: conf.py:23 51 | msgid "second" 52 | msgstr "sekunti" 53 | 54 | #: conf.py:27 conf.py:28 55 | msgid "contains" 56 | msgstr "sisältää" 57 | 58 | #: conf.py:29 59 | msgid "is in" 60 | msgstr "löytyy" 61 | 62 | #: conf.py:30 63 | msgid "is greater than" 64 | msgstr "suurempi kuin" 65 | 66 | #: conf.py:31 67 | msgid "is greater than or equal to" 68 | msgstr "suurempi tai yhtäsuuri kuin" 69 | 70 | #: conf.py:32 71 | msgid "is less than" 72 | msgstr "pienempi kuin" 73 | 74 | #: conf.py:33 75 | msgid "is less than or equal to" 76 | msgstr "pienempi tai yhtäsuuri kuin" 77 | 78 | #: conf.py:34 conf.py:35 79 | msgid "starts with" 80 | msgstr "alkaa" 81 | 82 | #: conf.py:36 conf.py:37 83 | msgid "ends with" 84 | msgstr "päättyy" 85 | 86 | #: conf.py:38 87 | msgid "is in range" 88 | msgstr "on välillä" 89 | 90 | #: conf.py:39 91 | msgid "is null" 92 | msgstr "on null" 93 | 94 | #: conf.py:40 conf.py:41 95 | msgid "matches regex" 96 | msgstr "täsmää säännölliseen lausekkeeseen" 97 | 98 | #: conf.py:42 conf.py:49 99 | msgid "search" 100 | msgstr "hae" 101 | 102 | #: conf.py:44 103 | msgid "is contained by" 104 | msgstr "sisältyy kokonaan" 105 | 106 | #: conf.py:45 107 | msgid "overlaps" 108 | msgstr "on päällekkäinen" 109 | 110 | #: conf.py:46 111 | msgid "has key" 112 | msgstr "sisältää avaimen" 113 | 114 | #: conf.py:47 115 | msgid "has keys" 116 | msgstr "sisältää avaimet" 117 | 118 | #: conf.py:48 119 | msgid "has any keys" 120 | msgstr "sisältää minkä tahansa avaimista" 121 | 122 | #: fields.py:94 123 | msgid "Select a lookup." 124 | msgstr "Hakuehto vaaditaan." 125 | 126 | #: fields.py:198 127 | msgid "Range query expects two values." 128 | msgstr "Välin hakuun tarvitaan kaksi arvoa." 129 | 130 | #: filters.py:437 131 | msgid "Today" 132 | msgstr "Tänään" 133 | 134 | #: filters.py:438 135 | msgid "Yesterday" 136 | msgstr "Eilen" 137 | 138 | #: filters.py:439 139 | msgid "Past 7 days" 140 | msgstr "Edelliset 7 päivää" 141 | 142 | #: filters.py:440 143 | msgid "This month" 144 | msgstr "Tässä kuussa" 145 | 146 | #: filters.py:441 147 | msgid "This year" 148 | msgstr "Tänä vuonna" 149 | 150 | #: filters.py:543 151 | msgid "Multiple values may be separated by commas." 152 | msgstr "Voit syöttää useita arvoja pilkulla erotettuna." 153 | 154 | #: filters.py:721 155 | #, python-format 156 | msgid "%s (descending)" 157 | msgstr "%s (laskeva)" 158 | 159 | #: filters.py:737 160 | msgid "Ordering" 161 | msgstr "Järjestä" 162 | 163 | #: rest_framework/filterset.py:33 164 | #: templates/django_filters/rest_framework/form.html:5 165 | msgid "Submit" 166 | msgstr "Lähetä" 167 | 168 | #: templates/django_filters/rest_framework/crispy_form.html:4 169 | #: templates/django_filters/rest_framework/form.html:2 170 | msgid "Field filters" 171 | msgstr "Kenttävalinnat" 172 | 173 | #: utils.py:312 174 | msgid "exclude" 175 | msgstr "poissulje" 176 | 177 | #: widgets.py:58 178 | msgid "All" 179 | msgstr "Kaikki" 180 | 181 | #: widgets.py:162 182 | msgid "Unknown" 183 | msgstr "Tuntematon" 184 | 185 | #: widgets.py:162 186 | msgid "Yes" 187 | msgstr "Kyllä" 188 | 189 | #: widgets.py:162 190 | msgid "No" 191 | msgstr "Ei" 192 | -------------------------------------------------------------------------------- /django_filters/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the django_filter package. 4 | # Axel Haustant , 2013. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2024-01-18 14:00+0000\n" 12 | "Last-Translator: Nils Van Zuijlen \n" 13 | "Language-Team: French \n" 15 | "Language: fr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 20 | "X-Generator: Weblate 5.4-dev\n" 21 | 22 | #: conf.py:16 23 | msgid "date" 24 | msgstr "date" 25 | 26 | #: conf.py:17 27 | msgid "year" 28 | msgstr "année" 29 | 30 | #: conf.py:18 31 | msgid "month" 32 | msgstr "mois" 33 | 34 | #: conf.py:19 35 | msgid "day" 36 | msgstr "jour" 37 | 38 | #: conf.py:20 39 | msgid "week day" 40 | msgstr "jour de la semaine" 41 | 42 | #: conf.py:21 43 | msgid "hour" 44 | msgstr "heure" 45 | 46 | #: conf.py:22 47 | msgid "minute" 48 | msgstr "minute" 49 | 50 | #: conf.py:23 51 | msgid "second" 52 | msgstr "seconde" 53 | 54 | #: conf.py:27 conf.py:28 55 | msgid "contains" 56 | msgstr "contient" 57 | 58 | #: conf.py:29 59 | msgid "is in" 60 | msgstr "est inclus dans" 61 | 62 | #: conf.py:30 63 | msgid "is greater than" 64 | msgstr "supérieur à" 65 | 66 | #: conf.py:31 67 | msgid "is greater than or equal to" 68 | msgstr "supérieur ou égal à" 69 | 70 | #: conf.py:32 71 | msgid "is less than" 72 | msgstr "inférieur à" 73 | 74 | #: conf.py:33 75 | msgid "is less than or equal to" 76 | msgstr "inférieur ou égale à" 77 | 78 | #: conf.py:34 conf.py:35 79 | msgid "starts with" 80 | msgstr "commence par" 81 | 82 | #: conf.py:36 conf.py:37 83 | msgid "ends with" 84 | msgstr "se termine par" 85 | 86 | #: conf.py:38 87 | msgid "is in range" 88 | msgstr "entre" 89 | 90 | #: conf.py:39 91 | msgid "is null" 92 | msgstr "est nul" 93 | 94 | #: conf.py:40 conf.py:41 95 | msgid "matches regex" 96 | msgstr "correspond à l'expression régulière" 97 | 98 | #: conf.py:42 conf.py:49 99 | msgid "search" 100 | msgstr "recherche" 101 | 102 | #: conf.py:44 103 | msgid "is contained by" 104 | msgstr "est contenu dans" 105 | 106 | #: conf.py:45 107 | msgid "overlaps" 108 | msgstr "chevauche" 109 | 110 | #: conf.py:46 111 | msgid "has key" 112 | msgstr "contient la clé" 113 | 114 | #: conf.py:47 115 | msgid "has keys" 116 | msgstr "contient les clés" 117 | 118 | #: conf.py:48 119 | msgid "has any keys" 120 | msgstr "a une des clés" 121 | 122 | #: fields.py:94 123 | msgid "Select a lookup." 124 | msgstr "Sélectionner un opérateur." 125 | 126 | #: fields.py:198 127 | msgid "Range query expects two values." 128 | msgstr "La fourchette doit avoir 2 valeurs." 129 | 130 | #: filters.py:437 131 | msgid "Today" 132 | msgstr "Aujourd'hui" 133 | 134 | #: filters.py:438 135 | msgid "Yesterday" 136 | msgstr "Hier" 137 | 138 | #: filters.py:439 139 | msgid "Past 7 days" 140 | msgstr "7 derniers jours" 141 | 142 | #: filters.py:440 143 | msgid "This month" 144 | msgstr "Ce mois-ci" 145 | 146 | #: filters.py:441 147 | msgid "This year" 148 | msgstr "Cette année" 149 | 150 | #: filters.py:543 151 | msgid "Multiple values may be separated by commas." 152 | msgstr "Les valeurs multiples doivent être séparées par des virgules." 153 | 154 | #: filters.py:721 155 | #, python-format 156 | msgid "%s (descending)" 157 | msgstr "%s (décroissant)" 158 | 159 | #: filters.py:737 160 | msgid "Ordering" 161 | msgstr "Tri" 162 | 163 | #: rest_framework/filterset.py:33 164 | #: templates/django_filters/rest_framework/form.html:5 165 | msgid "Submit" 166 | msgstr "Envoyer" 167 | 168 | #: templates/django_filters/rest_framework/crispy_form.html:4 169 | #: templates/django_filters/rest_framework/form.html:2 170 | msgid "Field filters" 171 | msgstr "Filtres de champ" 172 | 173 | #: utils.py:308 174 | msgid "exclude" 175 | msgstr "Exclut" 176 | 177 | #: widgets.py:58 178 | msgid "All" 179 | msgstr "Tous" 180 | 181 | #: widgets.py:162 182 | msgid "Unknown" 183 | msgstr "Inconnu" 184 | 185 | #: widgets.py:162 186 | msgid "Yes" 187 | msgstr "Oui" 188 | 189 | #: widgets.py:162 190 | msgid "No" 191 | msgstr "Non" 192 | 193 | #~ msgid "This is an exclusion filter" 194 | #~ msgstr "Ceci est un filtre d'exclusion" 195 | -------------------------------------------------------------------------------- /django_filters/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the django_filter package. 4 | # Carlos Goce, 2017. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2023-06-11 16:51+0000\n" 12 | "Last-Translator: Daniele Tricoli \n" 13 | "Language-Team: Italian \n" 15 | "Language: it\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 4.18-dev\n" 21 | 22 | #: conf.py:16 23 | msgid "date" 24 | msgstr "data" 25 | 26 | #: conf.py:17 27 | msgid "year" 28 | msgstr "anno" 29 | 30 | #: conf.py:18 31 | msgid "month" 32 | msgstr "mese" 33 | 34 | #: conf.py:19 35 | msgid "day" 36 | msgstr "giorno" 37 | 38 | #: conf.py:20 39 | msgid "week day" 40 | msgstr "giorno della settimana" 41 | 42 | #: conf.py:21 43 | msgid "hour" 44 | msgstr "ora" 45 | 46 | #: conf.py:22 47 | msgid "minute" 48 | msgstr "minuto" 49 | 50 | #: conf.py:23 51 | msgid "second" 52 | msgstr "secondo" 53 | 54 | #: conf.py:27 conf.py:28 55 | msgid "contains" 56 | msgstr "contiene" 57 | 58 | #: conf.py:29 59 | msgid "is in" 60 | msgstr "presente in" 61 | 62 | #: conf.py:30 63 | msgid "is greater than" 64 | msgstr "maggiore di" 65 | 66 | #: conf.py:31 67 | msgid "is greater than or equal to" 68 | msgstr "maggiore o uguale di" 69 | 70 | #: conf.py:32 71 | msgid "is less than" 72 | msgstr "minore di" 73 | 74 | #: conf.py:33 75 | msgid "is less than or equal to" 76 | msgstr "minore o uguale di" 77 | 78 | #: conf.py:34 conf.py:35 79 | msgid "starts with" 80 | msgstr "comincia per" 81 | 82 | #: conf.py:36 conf.py:37 83 | msgid "ends with" 84 | msgstr "termina per" 85 | 86 | #: conf.py:38 87 | msgid "is in range" 88 | msgstr "nell'intervallo" 89 | 90 | #: conf.py:39 91 | msgid "is null" 92 | msgstr "nullo" 93 | 94 | #: conf.py:40 conf.py:41 95 | msgid "matches regex" 96 | msgstr "coincide con la espressione regolare" 97 | 98 | #: conf.py:42 conf.py:49 99 | msgid "search" 100 | msgstr "cerca" 101 | 102 | #: conf.py:44 103 | msgid "is contained by" 104 | msgstr "contenuto in" 105 | 106 | #: conf.py:45 107 | msgid "overlaps" 108 | msgstr "sovrapposto" 109 | 110 | #: conf.py:46 111 | msgid "has key" 112 | msgstr "contiene la chiave" 113 | 114 | #: conf.py:47 115 | msgid "has keys" 116 | msgstr "contiene le chiavi" 117 | 118 | #: conf.py:48 119 | msgid "has any keys" 120 | msgstr "contiene qualsiasi chiave" 121 | 122 | #: fields.py:94 123 | msgid "Select a lookup." 124 | msgstr "" 125 | 126 | #: fields.py:198 127 | msgid "Range query expects two values." 128 | msgstr "La query di intervallo richiede due valori." 129 | 130 | #: filters.py:437 131 | msgid "Today" 132 | msgstr "Oggi" 133 | 134 | #: filters.py:438 135 | msgid "Yesterday" 136 | msgstr "Ieri" 137 | 138 | #: filters.py:439 139 | msgid "Past 7 days" 140 | msgstr "Ultimi 7 giorni" 141 | 142 | #: filters.py:440 143 | msgid "This month" 144 | msgstr "Questo mese" 145 | 146 | #: filters.py:441 147 | msgid "This year" 148 | msgstr "Questo anno" 149 | 150 | #: filters.py:543 151 | msgid "Multiple values may be separated by commas." 152 | msgstr "Più valori separati da virgole." 153 | 154 | #: filters.py:721 155 | #, python-format 156 | msgid "%s (descending)" 157 | msgstr "%s (decrescente)" 158 | 159 | #: filters.py:737 160 | msgid "Ordering" 161 | msgstr "Ordinamento" 162 | 163 | #: rest_framework/filterset.py:33 164 | #: templates/django_filters/rest_framework/form.html:5 165 | msgid "Submit" 166 | msgstr "Invia" 167 | 168 | #: templates/django_filters/rest_framework/crispy_form.html:4 169 | #: templates/django_filters/rest_framework/form.html:2 170 | msgid "Field filters" 171 | msgstr "Filtri del campo" 172 | 173 | #: utils.py:308 174 | msgid "exclude" 175 | msgstr "escludi" 176 | 177 | #: widgets.py:58 178 | msgid "All" 179 | msgstr "Tutti" 180 | 181 | #: widgets.py:162 182 | msgid "Unknown" 183 | msgstr "Sconosciuto" 184 | 185 | #: widgets.py:162 186 | msgid "Yes" 187 | msgstr "Sì" 188 | 189 | #: widgets.py:162 190 | msgid "No" 191 | msgstr "No" 192 | 193 | #~ msgid "Any date" 194 | #~ msgstr "Qualsiasi data" 195 | -------------------------------------------------------------------------------- /django_filters/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-08-21 12:25+0000\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Storm Heg \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | #: conf.py:16 21 | msgid "date" 22 | msgstr "datum" 23 | 24 | #: conf.py:17 25 | msgid "year" 26 | msgstr "jaar" 27 | 28 | #: conf.py:18 29 | msgid "month" 30 | msgstr "maand" 31 | 32 | #: conf.py:19 33 | msgid "day" 34 | msgstr "dag" 35 | 36 | #: conf.py:20 37 | msgid "week day" 38 | msgstr "weekdag" 39 | 40 | #: conf.py:21 41 | msgid "hour" 42 | msgstr "uur" 43 | 44 | #: conf.py:22 45 | msgid "minute" 46 | msgstr "minuur" 47 | 48 | #: conf.py:23 49 | msgid "second" 50 | msgstr "seconde" 51 | 52 | #: conf.py:27 conf.py:28 53 | msgid "contains" 54 | msgstr "bevat" 55 | 56 | #: conf.py:29 57 | msgid "is in" 58 | msgstr "zit in" 59 | 60 | #: conf.py:30 61 | msgid "is greater than" 62 | msgstr "is groter dan" 63 | 64 | #: conf.py:31 65 | msgid "is greater than or equal to" 66 | msgstr "is groter dan of gelijk aan" 67 | 68 | #: conf.py:32 69 | msgid "is less than" 70 | msgstr "is minder dan" 71 | 72 | #: conf.py:33 73 | msgid "is less than or equal to" 74 | msgstr "is minder dan of gelijk aan" 75 | 76 | #: conf.py:34 conf.py:35 77 | msgid "starts with" 78 | msgstr "begint met" 79 | 80 | #: conf.py:36 conf.py:37 81 | msgid "ends with" 82 | msgstr "eindigt met" 83 | 84 | #: conf.py:38 85 | msgid "is in range" 86 | msgstr "zit in bereik" 87 | 88 | #: conf.py:39 89 | msgid "is null" 90 | msgstr "is null" 91 | 92 | #: conf.py:40 conf.py:41 93 | msgid "matches regex" 94 | msgstr "matcht regex" 95 | 96 | #: conf.py:42 conf.py:49 97 | msgid "search" 98 | msgstr "zoek" 99 | 100 | #: conf.py:44 101 | msgid "is contained by" 102 | msgstr "wordt bevat door" 103 | 104 | #: conf.py:45 105 | msgid "overlaps" 106 | msgstr "overlapt" 107 | 108 | #: conf.py:46 109 | msgid "has key" 110 | msgstr "heeft key" 111 | 112 | #: conf.py:47 113 | msgid "has keys" 114 | msgstr "heeft keys" 115 | 116 | #: conf.py:48 117 | msgid "has any keys" 118 | msgstr "heeft keys" 119 | 120 | #: fields.py:94 121 | msgid "Select a lookup." 122 | msgstr "Selecteer een lookup." 123 | 124 | #: fields.py:198 125 | msgid "Range query expects two values." 126 | msgstr "Bereik query verwacht twee waarden." 127 | 128 | #: filters.py:437 129 | msgid "Today" 130 | msgstr "Vandaag" 131 | 132 | #: filters.py:438 133 | msgid "Yesterday" 134 | msgstr "Gisteren" 135 | 136 | #: filters.py:439 137 | msgid "Past 7 days" 138 | msgstr "Afgelopen 7 dagen" 139 | 140 | #: filters.py:440 141 | msgid "This month" 142 | msgstr "Deze maand" 143 | 144 | #: filters.py:441 145 | msgid "This year" 146 | msgstr "Dit jaar" 147 | 148 | #: filters.py:543 149 | msgid "Multiple values may be separated by commas." 150 | msgstr "Meerdere waarden kunnen gescheiden worden door komma's." 151 | 152 | #: filters.py:721 tests/test_filters.py:1670 153 | #, python-format 154 | msgid "%s (descending)" 155 | msgstr "%s (aflopend)" 156 | 157 | #: filters.py:737 158 | msgid "Ordering" 159 | msgstr "Volgorde" 160 | 161 | #: rest_framework/filterset.py:33 162 | #: templates/rest_framework/form.html:5 163 | msgid "Submit" 164 | msgstr "Indienen" 165 | 166 | #: templates/django_filters/rest_framework/crispy_form.html:4 167 | #: templates/django_filters/rest_framework/form.html:2 168 | msgid "Field filters" 169 | msgstr "Veld filters" 170 | 171 | #: utils.py:323 172 | msgid "exclude" 173 | msgstr "uitsluiten" 174 | 175 | #: widgets.py:58 176 | msgid "All" 177 | msgstr "Alles" 178 | 179 | #: widgets.py:162 180 | msgid "Unknown" 181 | msgstr "Onbekend" 182 | 183 | #: widgets.py:162 184 | msgid "Yes" 185 | msgstr "Ja" 186 | 187 | #: widgets.py:162 188 | msgid "No" 189 | msgstr "Nee" 190 | -------------------------------------------------------------------------------- /django_filters/locale/pl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/pl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/pl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django_filters 0.0.1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2023-04-10 20:47+0000\n" 12 | "Last-Translator: Quadric \n" 13 | "Language-Team: Polish \n" 15 | "Language: pl\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n" 20 | "%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n" 21 | "%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" 22 | "X-Generator: Weblate 4.17-dev\n" 23 | 24 | #: conf.py:16 25 | msgid "date" 26 | msgstr "data" 27 | 28 | #: conf.py:17 29 | msgid "year" 30 | msgstr "rok" 31 | 32 | #: conf.py:18 33 | msgid "month" 34 | msgstr "miesiąc" 35 | 36 | #: conf.py:19 37 | msgid "day" 38 | msgstr "dzień" 39 | 40 | #: conf.py:20 41 | msgid "week day" 42 | msgstr "dzień tygodnia" 43 | 44 | #: conf.py:21 45 | msgid "hour" 46 | msgstr "godzina" 47 | 48 | #: conf.py:22 49 | msgid "minute" 50 | msgstr "minuta" 51 | 52 | #: conf.py:23 53 | msgid "second" 54 | msgstr "sekunda" 55 | 56 | #: conf.py:27 conf.py:28 57 | msgid "contains" 58 | msgstr "zawiera" 59 | 60 | #: conf.py:29 61 | msgid "is in" 62 | msgstr "zawiera się w" 63 | 64 | #: conf.py:30 65 | msgid "is greater than" 66 | msgstr "powyżej" 67 | 68 | #: conf.py:31 69 | msgid "is greater than or equal to" 70 | msgstr "powyżej lub równe" 71 | 72 | #: conf.py:32 73 | msgid "is less than" 74 | msgstr "poniżej" 75 | 76 | #: conf.py:33 77 | msgid "is less than or equal to" 78 | msgstr "poniżej lub równe" 79 | 80 | #: conf.py:34 conf.py:35 81 | msgid "starts with" 82 | msgstr "zaczyna się od" 83 | 84 | #: conf.py:36 conf.py:37 85 | msgid "ends with" 86 | msgstr "kończy się na" 87 | 88 | #: conf.py:38 89 | msgid "is in range" 90 | msgstr "zawiera się w zakresie" 91 | 92 | #: conf.py:39 93 | msgid "is null" 94 | msgstr "jest wartością null" 95 | 96 | #: conf.py:40 conf.py:41 97 | msgid "matches regex" 98 | msgstr "pasuje do wyrażenia regularnego" 99 | 100 | #: conf.py:42 conf.py:49 101 | msgid "search" 102 | msgstr "szukaj" 103 | 104 | #: conf.py:44 105 | msgid "is contained by" 106 | msgstr "zawiera się w" 107 | 108 | #: conf.py:45 109 | msgid "overlaps" 110 | msgstr "nakłada się" 111 | 112 | #: conf.py:46 113 | msgid "has key" 114 | msgstr "posiada klucz" 115 | 116 | #: conf.py:47 117 | msgid "has keys" 118 | msgstr "posiada klucze" 119 | 120 | #: conf.py:48 121 | msgid "has any keys" 122 | msgstr "posiada jakiekolwiek klucze" 123 | 124 | #: fields.py:94 125 | msgid "Select a lookup." 126 | msgstr "Wybierz wyszukiwanie." 127 | 128 | #: fields.py:198 129 | msgid "Range query expects two values." 130 | msgstr "Zapytanie o zakres oczekuje dwóch wartości." 131 | 132 | #: filters.py:437 133 | msgid "Today" 134 | msgstr "Dziś" 135 | 136 | #: filters.py:438 137 | msgid "Yesterday" 138 | msgstr "Wczoraj" 139 | 140 | #: filters.py:439 141 | msgid "Past 7 days" 142 | msgstr "Ostatnie 7 dni" 143 | 144 | #: filters.py:440 145 | msgid "This month" 146 | msgstr "Ten miesiąc" 147 | 148 | #: filters.py:441 149 | msgid "This year" 150 | msgstr "Ten rok" 151 | 152 | #: filters.py:543 153 | msgid "Multiple values may be separated by commas." 154 | msgstr "Wiele wartości można rozdzielić przecinkami." 155 | 156 | #: filters.py:721 157 | #, python-format 158 | msgid "%s (descending)" 159 | msgstr "%s (malejąco)" 160 | 161 | #: filters.py:737 162 | msgid "Ordering" 163 | msgstr "Sortowanie" 164 | 165 | #: rest_framework/filterset.py:33 166 | #: templates/django_filters/rest_framework/form.html:5 167 | msgid "Submit" 168 | msgstr "Wyślij" 169 | 170 | #: templates/django_filters/rest_framework/crispy_form.html:4 171 | #: templates/django_filters/rest_framework/form.html:2 172 | msgid "Field filters" 173 | msgstr "Filtry pola" 174 | 175 | #: utils.py:308 176 | msgid "exclude" 177 | msgstr "wyklucz" 178 | 179 | #: widgets.py:58 180 | msgid "All" 181 | msgstr "Wszystko" 182 | 183 | #: widgets.py:162 184 | msgid "Unknown" 185 | msgstr "Nieznane" 186 | 187 | #: widgets.py:162 188 | msgid "Yes" 189 | msgstr "Tak" 190 | 191 | #: widgets.py:162 192 | msgid "No" 193 | msgstr "Nie" 194 | 195 | #~ msgid "Any date" 196 | #~ msgstr "Dowolna data" 197 | 198 | #~ msgid "This is an exclusion filter" 199 | #~ msgstr "Jest to filtr wykluczający" 200 | -------------------------------------------------------------------------------- /django_filters/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Django Filter translation. 2 | # Copyright (C) 2017 3 | # This file is distributed under the same license as the django_filter package. 4 | # Anderson Scouto da Silva, 2017. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2023-06-30 13:51+0000\n" 12 | "Last-Translator: Diogo Silva \n" 13 | "Language-Team: Portuguese (Brazil) \n" 15 | "Language: pt_BR\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 20 | "X-Generator: Weblate 5.0-dev\n" 21 | 22 | #: conf.py:16 23 | msgid "date" 24 | msgstr "data" 25 | 26 | #: conf.py:17 27 | msgid "year" 28 | msgstr "ano" 29 | 30 | #: conf.py:18 31 | msgid "month" 32 | msgstr "mês" 33 | 34 | #: conf.py:19 35 | msgid "day" 36 | msgstr "dia" 37 | 38 | #: conf.py:20 39 | msgid "week day" 40 | msgstr "dia da semana" 41 | 42 | #: conf.py:21 43 | msgid "hour" 44 | msgstr "hora" 45 | 46 | #: conf.py:22 47 | msgid "minute" 48 | msgstr "minuto" 49 | 50 | #: conf.py:23 51 | msgid "second" 52 | msgstr "segundo" 53 | 54 | #: conf.py:27 conf.py:28 55 | msgid "contains" 56 | msgstr "contém" 57 | 58 | #: conf.py:29 59 | msgid "is in" 60 | msgstr "presente em" 61 | 62 | #: conf.py:30 63 | msgid "is greater than" 64 | msgstr "é maior que" 65 | 66 | #: conf.py:31 67 | msgid "is greater than or equal to" 68 | msgstr "é maior ou igual que" 69 | 70 | #: conf.py:32 71 | msgid "is less than" 72 | msgstr "é menor que" 73 | 74 | #: conf.py:33 75 | msgid "is less than or equal to" 76 | msgstr "é menor ou igual que" 77 | 78 | #: conf.py:34 conf.py:35 79 | msgid "starts with" 80 | msgstr "começa com" 81 | 82 | #: conf.py:36 conf.py:37 83 | msgid "ends with" 84 | msgstr "termina com" 85 | 86 | #: conf.py:38 87 | msgid "is in range" 88 | msgstr "está no range" 89 | 90 | #: conf.py:39 91 | msgid "is null" 92 | msgstr "é nulo" 93 | 94 | #: conf.py:40 conf.py:41 95 | msgid "matches regex" 96 | msgstr "coincide com a expressão regular" 97 | 98 | #: conf.py:42 conf.py:49 99 | msgid "search" 100 | msgstr "buscar" 101 | 102 | #: conf.py:44 103 | msgid "is contained by" 104 | msgstr "está contido por" 105 | 106 | #: conf.py:45 107 | msgid "overlaps" 108 | msgstr "sobrepõe" 109 | 110 | #: conf.py:46 111 | msgid "has key" 112 | msgstr "contém a chave" 113 | 114 | #: conf.py:47 115 | msgid "has keys" 116 | msgstr "contém as chaves" 117 | 118 | #: conf.py:48 119 | msgid "has any keys" 120 | msgstr "contém uma das chaves" 121 | 122 | #: fields.py:94 123 | msgid "Select a lookup." 124 | msgstr "Selecione uma pesquisa." 125 | 126 | #: fields.py:198 127 | msgid "Range query expects two values." 128 | msgstr "Consulta por range requer dois valores." 129 | 130 | #: filters.py:437 131 | msgid "Today" 132 | msgstr "Hoje" 133 | 134 | #: filters.py:438 135 | msgid "Yesterday" 136 | msgstr "Ontem" 137 | 138 | #: filters.py:439 139 | msgid "Past 7 days" 140 | msgstr "Últimos 7 dias" 141 | 142 | #: filters.py:440 143 | msgid "This month" 144 | msgstr "Este mês" 145 | 146 | #: filters.py:441 147 | msgid "This year" 148 | msgstr "Este ano" 149 | 150 | #: filters.py:543 151 | msgid "Multiple values may be separated by commas." 152 | msgstr "Valores múltiplos podem ser separados por vírgulas." 153 | 154 | #: filters.py:721 155 | #, python-format 156 | msgid "%s (descending)" 157 | msgstr "%s (decrescente)" 158 | 159 | #: filters.py:737 160 | msgid "Ordering" 161 | msgstr "Ordenado" 162 | 163 | #: rest_framework/filterset.py:33 164 | #: templates/django_filters/rest_framework/form.html:5 165 | msgid "Submit" 166 | msgstr "Enviar" 167 | 168 | #: templates/django_filters/rest_framework/crispy_form.html:4 169 | #: templates/django_filters/rest_framework/form.html:2 170 | msgid "Field filters" 171 | msgstr "Filtros de campo" 172 | 173 | #: utils.py:308 174 | msgid "exclude" 175 | msgstr "excluir" 176 | 177 | #: widgets.py:58 178 | msgid "All" 179 | msgstr "Tudo" 180 | 181 | #: widgets.py:162 182 | msgid "Unknown" 183 | msgstr "Desconhecido" 184 | 185 | #: widgets.py:162 186 | msgid "Yes" 187 | msgstr "Sim" 188 | 189 | #: widgets.py:162 190 | msgid "No" 191 | msgstr "Não" 192 | 193 | #~ msgid "Any date" 194 | #~ msgstr "Qualquer data" 195 | -------------------------------------------------------------------------------- /django_filters/locale/ro/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 14:47+0000\n" 11 | "PO-Revision-Date: 2023-02-10 16:28+0000\n" 12 | "Last-Translator: Dan Braghis \n" 13 | "Language-Team: Romanian \n" 15 | "Language: ro\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " 20 | "20)) ? 1 : 2;\n" 21 | "X-Generator: Weblate 4.16-dev\n" 22 | 23 | #: conf.py:16 24 | msgid "date" 25 | msgstr "dată" 26 | 27 | #: conf.py:17 28 | msgid "year" 29 | msgstr "an" 30 | 31 | #: conf.py:18 32 | msgid "month" 33 | msgstr "lună" 34 | 35 | #: conf.py:19 36 | msgid "day" 37 | msgstr "zi" 38 | 39 | #: conf.py:20 40 | msgid "week day" 41 | msgstr "zi a săptămânii" 42 | 43 | #: conf.py:21 44 | msgid "hour" 45 | msgstr "oră" 46 | 47 | #: conf.py:22 48 | msgid "minute" 49 | msgstr "minută" 50 | 51 | #: conf.py:23 52 | msgid "second" 53 | msgstr "secundă" 54 | 55 | #: conf.py:27 conf.py:28 56 | msgid "contains" 57 | msgstr "conține" 58 | 59 | #: conf.py:29 60 | msgid "is in" 61 | msgstr "este în" 62 | 63 | #: conf.py:30 64 | msgid "is greater than" 65 | msgstr "este mai mare decât" 66 | 67 | #: conf.py:31 68 | msgid "is greater than or equal to" 69 | msgstr "este mai mare sau egal cu" 70 | 71 | #: conf.py:32 72 | msgid "is less than" 73 | msgstr "este mai mic decât" 74 | 75 | #: conf.py:33 76 | msgid "is less than or equal to" 77 | msgstr "este mai mic sau egal cu" 78 | 79 | #: conf.py:34 conf.py:35 80 | msgid "starts with" 81 | msgstr "începe cu" 82 | 83 | #: conf.py:36 conf.py:37 84 | msgid "ends with" 85 | msgstr "se termină cu" 86 | 87 | #: conf.py:38 88 | msgid "is in range" 89 | msgstr "este în intervalul" 90 | 91 | #: conf.py:39 92 | msgid "is null" 93 | msgstr "este nul" 94 | 95 | #: conf.py:40 conf.py:41 96 | msgid "matches regex" 97 | msgstr "se potrivește cu expresia regex" 98 | 99 | #: conf.py:42 conf.py:49 100 | msgid "search" 101 | msgstr "căutare" 102 | 103 | #: conf.py:44 104 | msgid "is contained by" 105 | msgstr "cuprins de" 106 | 107 | #: conf.py:45 108 | msgid "overlaps" 109 | msgstr "se suprapune" 110 | 111 | #: conf.py:46 112 | msgid "has key" 113 | msgstr "are cheia" 114 | 115 | #: conf.py:47 116 | msgid "has keys" 117 | msgstr "are cheile" 118 | 119 | #: conf.py:48 120 | msgid "has any keys" 121 | msgstr "are orice cheie" 122 | 123 | #: fields.py:94 124 | msgid "Select a lookup." 125 | msgstr "Selectați o căutare." 126 | 127 | #: fields.py:198 128 | msgid "Range query expects two values." 129 | msgstr "Interogarea de interval așteaptă două valori." 130 | 131 | #: filters.py:437 132 | msgid "Today" 133 | msgstr "Astăzi" 134 | 135 | #: filters.py:438 136 | msgid "Yesterday" 137 | msgstr "Ieri" 138 | 139 | #: filters.py:439 140 | msgid "Past 7 days" 141 | msgstr "Ultimele 7 zile" 142 | 143 | #: filters.py:440 144 | msgid "This month" 145 | msgstr "Luna aceasta" 146 | 147 | #: filters.py:441 148 | msgid "This year" 149 | msgstr "Anul acesta" 150 | 151 | #: filters.py:543 152 | msgid "Multiple values may be separated by commas." 153 | msgstr "Valorile multiple pot fi separate prin virgule." 154 | 155 | #: filters.py:721 156 | #, python-format 157 | msgid "%s (descending)" 158 | msgstr "%s (descescător)" 159 | 160 | #: filters.py:737 161 | msgid "Ordering" 162 | msgstr "Rânduire" 163 | 164 | #: rest_framework/filterset.py:33 165 | #: templates/django_filters/rest_framework/form.html:5 166 | msgid "Submit" 167 | msgstr "Trimite" 168 | 169 | #: templates/django_filters/rest_framework/crispy_form.html:4 170 | #: templates/django_filters/rest_framework/form.html:2 171 | msgid "Field filters" 172 | msgstr "Filtre de câmp" 173 | 174 | #: utils.py:312 175 | msgid "exclude" 176 | msgstr "exclude" 177 | 178 | #: widgets.py:58 179 | msgid "All" 180 | msgstr "Toate" 181 | 182 | #: widgets.py:162 183 | msgid "Unknown" 184 | msgstr "Necunoscut" 185 | 186 | #: widgets.py:162 187 | msgid "Yes" 188 | msgstr "Da" 189 | 190 | #: widgets.py:162 191 | msgid "No" 192 | msgstr "Nu" 193 | -------------------------------------------------------------------------------- /django_filters/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-filter\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2016-09-29 11:47+0300\n" 12 | "Last-Translator: Mikhail Mitrofanov \n" 13 | "Language-Team: \n" 14 | "Language: ru\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 19 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" 20 | "%100>=11 && n%100<=14)? 2 : 3);\n" 21 | "X-Generator: Poedit 1.8.9\n" 22 | 23 | #: conf.py:16 24 | msgid "date" 25 | msgstr "дата" 26 | 27 | #: conf.py:17 28 | msgid "year" 29 | msgstr "год" 30 | 31 | #: conf.py:18 32 | msgid "month" 33 | msgstr "месяц" 34 | 35 | #: conf.py:19 36 | msgid "day" 37 | msgstr "день" 38 | 39 | #: conf.py:20 40 | msgid "week day" 41 | msgstr "день недели" 42 | 43 | #: conf.py:21 44 | msgid "hour" 45 | msgstr "час" 46 | 47 | #: conf.py:22 48 | msgid "minute" 49 | msgstr "минута" 50 | 51 | #: conf.py:23 52 | msgid "second" 53 | msgstr "секунда" 54 | 55 | #: conf.py:27 conf.py:28 56 | msgid "contains" 57 | msgstr "содержит" 58 | 59 | #: conf.py:29 60 | msgid "is in" 61 | msgstr "в" 62 | 63 | #: conf.py:30 64 | msgid "is greater than" 65 | msgstr "больше чем" 66 | 67 | #: conf.py:31 68 | msgid "is greater than or equal to" 69 | msgstr "больше или равно" 70 | 71 | #: conf.py:32 72 | msgid "is less than" 73 | msgstr "меньше чем" 74 | 75 | #: conf.py:33 76 | msgid "is less than or equal to" 77 | msgstr "меньше или равно" 78 | 79 | #: conf.py:34 conf.py:35 80 | msgid "starts with" 81 | msgstr "начинается" 82 | 83 | #: conf.py:36 conf.py:37 84 | msgid "ends with" 85 | msgstr "заканчивается" 86 | 87 | #: conf.py:38 88 | msgid "is in range" 89 | msgstr "в диапазоне" 90 | 91 | #: conf.py:39 92 | msgid "is null" 93 | msgstr "" 94 | 95 | #: conf.py:40 conf.py:41 96 | msgid "matches regex" 97 | msgstr "соответствует регулярному выражению" 98 | 99 | #: conf.py:42 conf.py:49 100 | msgid "search" 101 | msgstr "поиск" 102 | 103 | #: conf.py:44 104 | msgid "is contained by" 105 | msgstr "содержится в" 106 | 107 | #: conf.py:45 108 | msgid "overlaps" 109 | msgstr "перекрывается" 110 | 111 | #: conf.py:46 112 | msgid "has key" 113 | msgstr "имеет ключ" 114 | 115 | #: conf.py:47 116 | msgid "has keys" 117 | msgstr "имеет ключи" 118 | 119 | #: conf.py:48 120 | msgid "has any keys" 121 | msgstr "имеет любые ключи" 122 | 123 | #: fields.py:94 124 | msgid "Select a lookup." 125 | msgstr "" 126 | 127 | #: fields.py:198 128 | msgid "Range query expects two values." 129 | msgstr "Запрос диапазона ожидает два значения." 130 | 131 | #: filters.py:437 132 | msgid "Today" 133 | msgstr "Сегодня" 134 | 135 | #: filters.py:438 136 | msgid "Yesterday" 137 | msgstr "Вчера" 138 | 139 | #: filters.py:439 140 | msgid "Past 7 days" 141 | msgstr "Прошедшие 7 дней" 142 | 143 | #: filters.py:440 144 | msgid "This month" 145 | msgstr "За этот месяц" 146 | 147 | #: filters.py:441 148 | msgid "This year" 149 | msgstr "В этом году" 150 | 151 | #: filters.py:543 152 | msgid "Multiple values may be separated by commas." 153 | msgstr "Несколько значений могут быть разделены запятыми." 154 | 155 | #: filters.py:721 156 | #, python-format 157 | msgid "%s (descending)" 158 | msgstr "%s (по убыванию)" 159 | 160 | #: filters.py:737 161 | msgid "Ordering" 162 | msgstr "Порядок" 163 | 164 | #: rest_framework/filterset.py:33 165 | #: templates/django_filters/rest_framework/form.html:5 166 | msgid "Submit" 167 | msgstr "Отправить" 168 | 169 | #: templates/django_filters/rest_framework/crispy_form.html:4 170 | #: templates/django_filters/rest_framework/form.html:2 171 | msgid "Field filters" 172 | msgstr "Фильтры по полям" 173 | 174 | #: utils.py:308 175 | msgid "exclude" 176 | msgstr "исключая" 177 | 178 | #: widgets.py:58 179 | msgid "All" 180 | msgstr "Все" 181 | 182 | #: widgets.py:162 183 | msgid "Unknown" 184 | msgstr "Не задано" 185 | 186 | #: widgets.py:162 187 | msgid "Yes" 188 | msgstr "Да" 189 | 190 | #: widgets.py:162 191 | msgid "No" 192 | msgstr "Нет" 193 | 194 | #~ msgid "Any date" 195 | #~ msgstr "Любая дата" 196 | -------------------------------------------------------------------------------- /django_filters/locale/sk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/sk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/sk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2023-07-21 19:07+0000\n" 12 | "Last-Translator: Milan Šalka \n" 13 | "Language-Team: Slovak \n" 15 | "Language: sk\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " 20 | ">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" 21 | "X-Generator: Weblate 5.0-dev\n" 22 | "X-Translated-Using: django-rosetta 0.8.1\n" 23 | 24 | #: conf.py:16 25 | msgid "date" 26 | msgstr "dátum" 27 | 28 | #: conf.py:17 29 | msgid "year" 30 | msgstr "rok" 31 | 32 | #: conf.py:18 33 | msgid "month" 34 | msgstr "mesiac" 35 | 36 | #: conf.py:19 37 | msgid "day" 38 | msgstr "deň" 39 | 40 | #: conf.py:20 41 | msgid "week day" 42 | msgstr "deň týždňa" 43 | 44 | #: conf.py:21 45 | msgid "hour" 46 | msgstr "hodina" 47 | 48 | #: conf.py:22 49 | msgid "minute" 50 | msgstr "minúta" 51 | 52 | #: conf.py:23 53 | msgid "second" 54 | msgstr "sekunda" 55 | 56 | #: conf.py:27 conf.py:28 57 | msgid "contains" 58 | msgstr "obsahuje" 59 | 60 | #: conf.py:29 61 | msgid "is in" 62 | msgstr "je v" 63 | 64 | #: conf.py:30 65 | msgid "is greater than" 66 | msgstr "je vačší než" 67 | 68 | #: conf.py:31 69 | msgid "is greater than or equal to" 70 | msgstr "je vačší alebo rovný ako" 71 | 72 | #: conf.py:32 73 | msgid "is less than" 74 | msgstr "je menší než" 75 | 76 | #: conf.py:33 77 | msgid "is less than or equal to" 78 | msgstr "je menší alebo rovný ako" 79 | 80 | #: conf.py:34 conf.py:35 81 | msgid "starts with" 82 | msgstr "začína s" 83 | 84 | #: conf.py:36 conf.py:37 85 | msgid "ends with" 86 | msgstr "končí s" 87 | 88 | #: conf.py:38 89 | msgid "is in range" 90 | msgstr "je v rozsahu" 91 | 92 | #: conf.py:39 93 | msgid "is null" 94 | msgstr "je nulová" 95 | 96 | #: conf.py:40 conf.py:41 97 | msgid "matches regex" 98 | msgstr "spĺňa regex" 99 | 100 | #: conf.py:42 conf.py:49 101 | msgid "search" 102 | msgstr "hľadať" 103 | 104 | #: conf.py:44 105 | msgid "is contained by" 106 | msgstr "je obsiahnutý" 107 | 108 | #: conf.py:45 109 | msgid "overlaps" 110 | msgstr "presahuje" 111 | 112 | #: conf.py:46 113 | msgid "has key" 114 | msgstr "má kľúč" 115 | 116 | #: conf.py:47 117 | msgid "has keys" 118 | msgstr "má kľúče" 119 | 120 | #: conf.py:48 121 | msgid "has any keys" 122 | msgstr "má akékoľvek kľúče" 123 | 124 | #: fields.py:94 125 | msgid "Select a lookup." 126 | msgstr "Vyberte vyhľadávanie." 127 | 128 | #: fields.py:198 129 | msgid "Range query expects two values." 130 | msgstr "Rozsah očakáva dve hodnoty." 131 | 132 | #: filters.py:437 133 | msgid "Today" 134 | msgstr "Dnes" 135 | 136 | #: filters.py:438 137 | msgid "Yesterday" 138 | msgstr "Včera" 139 | 140 | #: filters.py:439 141 | msgid "Past 7 days" 142 | msgstr "Posledných 7 dní" 143 | 144 | #: filters.py:440 145 | msgid "This month" 146 | msgstr "Tento mesiac" 147 | 148 | #: filters.py:441 149 | msgid "This year" 150 | msgstr "Tento rok" 151 | 152 | #: filters.py:543 153 | msgid "Multiple values may be separated by commas." 154 | msgstr "Viacero hodnôt môže byť oddelených čiarkami." 155 | 156 | #: filters.py:721 157 | #, python-format 158 | msgid "%s (descending)" 159 | msgstr "%s (klesajúco)" 160 | 161 | #: filters.py:737 162 | msgid "Ordering" 163 | msgstr "Zoradenie" 164 | 165 | #: rest_framework/filterset.py:33 166 | #: templates/django_filters/rest_framework/form.html:5 167 | msgid "Submit" 168 | msgstr "Potvrdiť" 169 | 170 | #: templates/django_filters/rest_framework/crispy_form.html:4 171 | #: templates/django_filters/rest_framework/form.html:2 172 | msgid "Field filters" 173 | msgstr "Filtre poľa" 174 | 175 | #: utils.py:308 176 | msgid "exclude" 177 | msgstr "neobsahuje" 178 | 179 | #: widgets.py:58 180 | msgid "All" 181 | msgstr "Všetky" 182 | 183 | #: widgets.py:162 184 | msgid "Unknown" 185 | msgstr "Neznáme" 186 | 187 | #: widgets.py:162 188 | msgid "Yes" 189 | msgstr "Áno" 190 | 191 | #: widgets.py:162 192 | msgid "No" 193 | msgstr "Nie" 194 | 195 | #~ msgid "Any date" 196 | #~ msgstr "Akýkoľvek dátum" 197 | -------------------------------------------------------------------------------- /django_filters/locale/uk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/uk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/uk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-filter\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 7 | "PO-Revision-Date: 2024-01-01 15:10+0000\n" 8 | "Last-Translator: Сергій \n" 9 | "Language-Team: Ukrainian \n" 11 | "Language: uk\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 " 16 | "? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > " 17 | "14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % " 18 | "100 >=11 && n % 100 <=14 )) ? 2: 3);\n" 19 | "X-Generator: Weblate 5.4-dev\n" 20 | 21 | #: conf.py:16 22 | msgid "date" 23 | msgstr "дата" 24 | 25 | #: conf.py:17 26 | msgid "year" 27 | msgstr "рік" 28 | 29 | #: conf.py:18 30 | msgid "month" 31 | msgstr "місяць" 32 | 33 | #: conf.py:19 34 | msgid "day" 35 | msgstr "день" 36 | 37 | #: conf.py:20 38 | msgid "week day" 39 | msgstr "день тижня" 40 | 41 | #: conf.py:21 42 | msgid "hour" 43 | msgstr "година" 44 | 45 | #: conf.py:22 46 | msgid "minute" 47 | msgstr "хвилина" 48 | 49 | #: conf.py:23 50 | msgid "second" 51 | msgstr "секунда" 52 | 53 | #: conf.py:27 conf.py:28 54 | msgid "contains" 55 | msgstr "містить" 56 | 57 | #: conf.py:29 58 | msgid "is in" 59 | msgstr "в" 60 | 61 | #: conf.py:30 62 | msgid "is greater than" 63 | msgstr "більше ніж" 64 | 65 | #: conf.py:31 66 | msgid "is greater than or equal to" 67 | msgstr "більше або дорівнює" 68 | 69 | #: conf.py:32 70 | msgid "is less than" 71 | msgstr "менше ніж" 72 | 73 | #: conf.py:33 74 | msgid "is less than or equal to" 75 | msgstr "менше або дорівнює" 76 | 77 | #: conf.py:34 conf.py:35 78 | msgid "starts with" 79 | msgstr "починається" 80 | 81 | #: conf.py:36 conf.py:37 82 | msgid "ends with" 83 | msgstr "закінчується" 84 | 85 | #: conf.py:38 86 | msgid "is in range" 87 | msgstr "в діапазоні" 88 | 89 | #: conf.py:39 90 | msgid "is null" 91 | msgstr "є порожнім" 92 | 93 | #: conf.py:40 conf.py:41 94 | msgid "matches regex" 95 | msgstr "відповідає регулярному виразу" 96 | 97 | #: conf.py:42 conf.py:49 98 | msgid "search" 99 | msgstr "пошук" 100 | 101 | #: conf.py:44 102 | msgid "is contained by" 103 | msgstr "міститься в" 104 | 105 | #: conf.py:45 106 | msgid "overlaps" 107 | msgstr "перекривається" 108 | 109 | #: conf.py:46 110 | msgid "has key" 111 | msgstr "має ключ" 112 | 113 | #: conf.py:47 114 | msgid "has keys" 115 | msgstr "має ключі" 116 | 117 | #: conf.py:48 118 | msgid "has any keys" 119 | msgstr "має будь-які ключі" 120 | 121 | #: fields.py:94 122 | msgid "Select a lookup." 123 | msgstr "Оберіть оператор запиту." 124 | 125 | #: fields.py:198 126 | msgid "Range query expects two values." 127 | msgstr "Запит діапазону очікує два значення." 128 | 129 | #: filters.py:437 130 | msgid "Today" 131 | msgstr "Сьогодні" 132 | 133 | #: filters.py:438 134 | msgid "Yesterday" 135 | msgstr "Вчора" 136 | 137 | #: filters.py:439 138 | msgid "Past 7 days" 139 | msgstr "Минулі 7 днів" 140 | 141 | #: filters.py:440 142 | msgid "This month" 143 | msgstr "За цей місяць" 144 | 145 | #: filters.py:441 146 | msgid "This year" 147 | msgstr "В цьому році" 148 | 149 | #: filters.py:543 150 | msgid "Multiple values may be separated by commas." 151 | msgstr "Кілька значень можуть бути розділені комами." 152 | 153 | #: filters.py:721 154 | #, python-format 155 | msgid "%s (descending)" 156 | msgstr "%s (по спадаючій)" 157 | 158 | #: filters.py:737 159 | msgid "Ordering" 160 | msgstr "Порядок" 161 | 162 | #: rest_framework/filterset.py:33 163 | #: templates/django_filters/rest_framework/form.html:5 164 | msgid "Submit" 165 | msgstr "Відправити" 166 | 167 | #: templates/django_filters/rest_framework/crispy_form.html:4 168 | #: templates/django_filters/rest_framework/form.html:2 169 | msgid "Field filters" 170 | msgstr "Фільтри по полях" 171 | 172 | #: utils.py:308 173 | msgid "exclude" 174 | msgstr "виключаючи" 175 | 176 | #: widgets.py:58 177 | msgid "All" 178 | msgstr "Усе" 179 | 180 | #: widgets.py:162 181 | msgid "Unknown" 182 | msgstr "Не задано" 183 | 184 | #: widgets.py:162 185 | msgid "Yes" 186 | msgstr "Так" 187 | 188 | #: widgets.py:162 189 | msgid "No" 190 | msgstr "Немає" 191 | 192 | #~ msgid "Any date" 193 | #~ msgstr "Будь-яка дата" 194 | -------------------------------------------------------------------------------- /django_filters/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/django_filters/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_filters/locale/zh_CN/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Kane Blueriver , 2016. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2023-02-10 11:07+0000\n" 11 | "PO-Revision-Date: 2023-05-07 03:57+0000\n" 12 | "Last-Translator: Lattefang <370358679@qq.com>\n" 13 | "Language-Team: Chinese (Simplified) \n" 15 | "Language: zh_CN\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=1; plural=0;\n" 20 | "X-Generator: Weblate 4.18-dev\n" 21 | 22 | #: conf.py:16 23 | msgid "date" 24 | msgstr "日期" 25 | 26 | #: conf.py:17 27 | msgid "year" 28 | msgstr "年" 29 | 30 | #: conf.py:18 31 | msgid "month" 32 | msgstr "月" 33 | 34 | #: conf.py:19 35 | msgid "day" 36 | msgstr "日" 37 | 38 | #: conf.py:20 39 | msgid "week day" 40 | msgstr "工作日" 41 | 42 | #: conf.py:21 43 | msgid "hour" 44 | msgstr "小时" 45 | 46 | #: conf.py:22 47 | msgid "minute" 48 | msgstr "分钟" 49 | 50 | #: conf.py:23 51 | msgid "second" 52 | msgstr "秒" 53 | 54 | #: conf.py:27 conf.py:28 55 | msgid "contains" 56 | msgstr "包含" 57 | 58 | #: conf.py:29 59 | msgid "is in" 60 | msgstr "在" 61 | 62 | #: conf.py:30 63 | msgid "is greater than" 64 | msgstr "大于" 65 | 66 | #: conf.py:31 67 | msgid "is greater than or equal to" 68 | msgstr "大于等于" 69 | 70 | #: conf.py:32 71 | msgid "is less than" 72 | msgstr "小于" 73 | 74 | #: conf.py:33 75 | msgid "is less than or equal to" 76 | msgstr "小于等于" 77 | 78 | #: conf.py:34 conf.py:35 79 | msgid "starts with" 80 | msgstr "以……开始" 81 | 82 | #: conf.py:36 conf.py:37 83 | msgid "ends with" 84 | msgstr "以……结尾" 85 | 86 | #: conf.py:38 87 | msgid "is in range" 88 | msgstr "在范围内" 89 | 90 | #: conf.py:39 91 | msgid "is null" 92 | msgstr "为空" 93 | 94 | #: conf.py:40 conf.py:41 95 | msgid "matches regex" 96 | msgstr "匹配正则表达式" 97 | 98 | #: conf.py:42 conf.py:49 99 | msgid "search" 100 | msgstr "搜索" 101 | 102 | #: conf.py:44 103 | msgid "is contained by" 104 | msgstr "包含在" 105 | 106 | #: conf.py:45 107 | msgid "overlaps" 108 | msgstr "重叠" 109 | 110 | #: conf.py:46 111 | msgid "has key" 112 | msgstr "单值" 113 | 114 | #: conf.py:47 115 | msgid "has keys" 116 | msgstr "多值" 117 | 118 | #: conf.py:48 119 | msgid "has any keys" 120 | msgstr "任何值" 121 | 122 | #: fields.py:94 123 | msgid "Select a lookup." 124 | msgstr "选择查找。" 125 | 126 | #: fields.py:198 127 | msgid "Range query expects two values." 128 | msgstr "范围查询需要两个值。" 129 | 130 | #: filters.py:437 131 | msgid "Today" 132 | msgstr "今日" 133 | 134 | #: filters.py:438 135 | msgid "Yesterday" 136 | msgstr "昨日" 137 | 138 | #: filters.py:439 139 | msgid "Past 7 days" 140 | msgstr "过去 7 日" 141 | 142 | #: filters.py:440 143 | msgid "This month" 144 | msgstr "本月" 145 | 146 | #: filters.py:441 147 | msgid "This year" 148 | msgstr "今年" 149 | 150 | #: filters.py:543 151 | msgid "Multiple values may be separated by commas." 152 | msgstr "多个值可以用逗号分隔。" 153 | 154 | #: filters.py:721 155 | #, python-format 156 | msgid "%s (descending)" 157 | msgstr "%s(降序)" 158 | 159 | #: filters.py:737 160 | msgid "Ordering" 161 | msgstr "排序" 162 | 163 | #: rest_framework/filterset.py:33 164 | #: templates/django_filters/rest_framework/form.html:5 165 | msgid "Submit" 166 | msgstr "提交" 167 | 168 | #: templates/django_filters/rest_framework/crispy_form.html:4 169 | #: templates/django_filters/rest_framework/form.html:2 170 | msgid "Field filters" 171 | msgstr "字段过滤器" 172 | 173 | #: utils.py:308 174 | msgid "exclude" 175 | msgstr "排除" 176 | 177 | #: widgets.py:58 178 | msgid "All" 179 | msgstr "全部" 180 | 181 | #: widgets.py:162 182 | msgid "Unknown" 183 | msgstr "未知" 184 | 185 | #: widgets.py:162 186 | msgid "Yes" 187 | msgstr "是" 188 | 189 | #: widgets.py:162 190 | msgid "No" 191 | msgstr "否" 192 | 193 | #~ msgid "This is an exclusion filter" 194 | #~ msgstr "未启用该过滤器" 195 | -------------------------------------------------------------------------------- /django_filters/rest_framework/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from .backends import DjangoFilterBackend 3 | from .filters import * 4 | from .filterset import FilterSet 5 | -------------------------------------------------------------------------------- /django_filters/rest_framework/backends.py: -------------------------------------------------------------------------------- 1 | from django.template import loader 2 | 3 | from .. import compat, utils 4 | from . import filterset 5 | 6 | 7 | class DjangoFilterBackend: 8 | filterset_base = filterset.FilterSet 9 | raise_exception = True 10 | 11 | @property 12 | def template(self): 13 | if compat.is_crispy(): 14 | return "django_filters/rest_framework/crispy_form.html" 15 | return "django_filters/rest_framework/form.html" 16 | 17 | def get_filterset(self, request, queryset, view): 18 | filterset_class = self.get_filterset_class(view, queryset) 19 | if filterset_class is None: 20 | return None 21 | 22 | kwargs = self.get_filterset_kwargs(request, queryset, view) 23 | return filterset_class(**kwargs) 24 | 25 | def get_filterset_class(self, view, queryset=None): 26 | """ 27 | Return the `FilterSet` class used to filter the queryset. 28 | """ 29 | filterset_class = getattr(view, "filterset_class", None) 30 | filterset_fields = getattr(view, "filterset_fields", None) 31 | 32 | if filterset_class: 33 | filterset_model = filterset_class._meta.model 34 | 35 | # FilterSets do not need to specify a Meta class 36 | if filterset_model and queryset is not None: 37 | assert issubclass( 38 | queryset.model, filterset_model 39 | ), "FilterSet model %s does not match queryset model %s" % ( 40 | filterset_model, 41 | queryset.model, 42 | ) 43 | 44 | return filterset_class 45 | 46 | if filterset_fields and queryset is not None: 47 | MetaBase = getattr(self.filterset_base, "Meta", object) 48 | 49 | class AutoFilterSet(self.filterset_base): 50 | class Meta(MetaBase): 51 | model = queryset.model 52 | fields = filterset_fields 53 | 54 | return AutoFilterSet 55 | 56 | return None 57 | 58 | def get_filterset_kwargs(self, request, queryset, view): 59 | return { 60 | "data": request.query_params, 61 | "queryset": queryset, 62 | "request": request, 63 | } 64 | 65 | def filter_queryset(self, request, queryset, view): 66 | filterset = self.get_filterset(request, queryset, view) 67 | if filterset is None: 68 | return queryset 69 | 70 | if not filterset.is_valid() and self.raise_exception: 71 | raise utils.translate_validation(filterset.errors) 72 | return filterset.qs 73 | 74 | def to_html(self, request, queryset, view): 75 | filterset = self.get_filterset(request, queryset, view) 76 | if filterset is None: 77 | return None 78 | 79 | template = loader.get_template(self.template) 80 | context = {"filter": filterset} 81 | return template.render(context, request) 82 | -------------------------------------------------------------------------------- /django_filters/rest_framework/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import filters 2 | 3 | from ..filters import * # noqa 4 | from ..widgets import BooleanWidget 5 | 6 | __all__ = filters.__all__ 7 | 8 | 9 | class BooleanFilter(filters.BooleanFilter): 10 | def __init__(self, *args, **kwargs): 11 | kwargs.setdefault("widget", BooleanWidget) 12 | 13 | super().__init__(*args, **kwargs) 14 | -------------------------------------------------------------------------------- /django_filters/rest_framework/filterset.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | from django_filters import filterset 7 | 8 | from .. import compat 9 | from .filters import BooleanFilter, IsoDateTimeFilter 10 | 11 | FILTER_FOR_DBFIELD_DEFAULTS = deepcopy(filterset.FILTER_FOR_DBFIELD_DEFAULTS) 12 | FILTER_FOR_DBFIELD_DEFAULTS.update( 13 | { 14 | models.DateTimeField: {"filter_class": IsoDateTimeFilter}, 15 | models.BooleanField: {"filter_class": BooleanFilter}, 16 | models.NullBooleanField: {"filter_class": BooleanFilter}, 17 | } 18 | ) 19 | 20 | 21 | class FilterSet(filterset.FilterSet): 22 | FILTER_DEFAULTS = FILTER_FOR_DBFIELD_DEFAULTS 23 | 24 | @property 25 | def form(self): 26 | form = super().form 27 | 28 | if compat.is_crispy(): 29 | from crispy_forms.helper import FormHelper 30 | from crispy_forms.layout import Layout, Submit 31 | 32 | layout_components = list(form.fields.keys()) + [ 33 | Submit("", _("Submit"), css_class="btn-default"), 34 | ] 35 | helper = FormHelper() 36 | helper.form_method = "GET" 37 | helper.layout = Layout(*layout_components) 38 | 39 | form.helper = helper 40 | 41 | return form 42 | -------------------------------------------------------------------------------- /django_filters/templates/django_filters/rest_framework/crispy_form.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | {% load i18n %} 3 | 4 |

{% trans "Field filters" %}

5 | {% crispy filter.form %} 6 | -------------------------------------------------------------------------------- /django_filters/templates/django_filters/rest_framework/form.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |

{% trans "Field filters" %}

3 |
4 | {{ filter.form.as_p }} 5 | 6 |
7 | -------------------------------------------------------------------------------- /django_filters/templates/django_filters/widgets/multiwidget.html: -------------------------------------------------------------------------------- 1 | {% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %} 2 | -------------------------------------------------------------------------------- /django_filters/views.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ImproperlyConfigured 2 | from django.views.generic import View 3 | from django.views.generic.list import ( 4 | MultipleObjectMixin, 5 | MultipleObjectTemplateResponseMixin, 6 | ) 7 | 8 | from .constants import ALL_FIELDS 9 | from .filterset import filterset_factory 10 | 11 | 12 | class FilterMixin: 13 | """ 14 | A mixin that provides a way to show and handle a FilterSet in a request. 15 | """ 16 | 17 | filterset_class = None 18 | filterset_fields = ALL_FIELDS 19 | strict = True 20 | 21 | def get_filterset_class(self): 22 | """ 23 | Returns the filterset class to use in this view 24 | """ 25 | if self.filterset_class: 26 | return self.filterset_class 27 | elif self.model: 28 | return filterset_factory(model=self.model, fields=self.filterset_fields) 29 | else: 30 | msg = "'%s' must define 'filterset_class' or 'model'" 31 | raise ImproperlyConfigured(msg % self.__class__.__name__) 32 | 33 | def get_filterset(self, filterset_class): 34 | """ 35 | Returns an instance of the filterset to be used in this view. 36 | """ 37 | kwargs = self.get_filterset_kwargs(filterset_class) 38 | return filterset_class(**kwargs) 39 | 40 | def get_filterset_kwargs(self, filterset_class): 41 | """ 42 | Returns the keyword arguments for instantiating the filterset. 43 | """ 44 | kwargs = { 45 | "data": self.request.GET or None, 46 | "request": self.request, 47 | } 48 | try: 49 | kwargs.update( 50 | { 51 | "queryset": self.get_queryset(), 52 | } 53 | ) 54 | except ImproperlyConfigured: 55 | # ignore the error here if the filterset has a model defined 56 | # to acquire a queryset from 57 | if filterset_class._meta.model is None: 58 | msg = ( 59 | "'%s' does not define a 'model' and the view '%s' does " 60 | "not return a valid queryset from 'get_queryset'. You " 61 | "must fix one of them." 62 | ) 63 | args = (filterset_class.__name__, self.__class__.__name__) 64 | raise ImproperlyConfigured(msg % args) 65 | return kwargs 66 | 67 | def get_strict(self): 68 | return self.strict 69 | 70 | 71 | class BaseFilterView(FilterMixin, MultipleObjectMixin, View): 72 | def get(self, request, *args, **kwargs): 73 | filterset_class = self.get_filterset_class() 74 | self.filterset = self.get_filterset(filterset_class) 75 | 76 | if ( 77 | not self.filterset.is_bound 78 | or self.filterset.is_valid() 79 | or not self.get_strict() 80 | ): 81 | self.object_list = self.filterset.qs 82 | else: 83 | self.object_list = self.filterset.queryset.none() 84 | 85 | context = self.get_context_data( 86 | filter=self.filterset, object_list=self.object_list 87 | ) 88 | return self.render_to_response(context) 89 | 90 | 91 | class FilterView(MultipleObjectTemplateResponseMixin, BaseFilterView): 92 | """ 93 | Render some list of objects with filter, set by `self.model` or 94 | `self.queryset`. 95 | `self.queryset` can actually be any iterable of items, not just a queryset. 96 | """ 97 | 98 | template_name_suffix = "_filter" 99 | 100 | 101 | def object_filter( 102 | request, 103 | model=None, 104 | queryset=None, 105 | template_name=None, 106 | extra_context=None, 107 | context_processors=None, 108 | filter_class=None, 109 | ): 110 | class ECFilterView(FilterView): 111 | """Handle the extra_context from the functional object_filter view""" 112 | 113 | def get_context_data(self, **kwargs): 114 | context = super().get_context_data(**kwargs) 115 | extra_context = self.kwargs.get("extra_context") or {} 116 | for k, v in extra_context.items(): 117 | if callable(v): 118 | v = v() 119 | context[k] = v 120 | return context 121 | 122 | kwargs = dict( 123 | model=model, 124 | queryset=queryset, 125 | template_name=template_name, 126 | filterset_class=filter_class, 127 | ) 128 | view = ECFilterView.as_view(**kwargs) 129 | return view(request, extra_context=extra_context) 130 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-filter.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-filter.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-filter" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-filter" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | livehtml: 155 | sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 156 | -------------------------------------------------------------------------------- /docs/assets/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/docs/assets/form.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-filter documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Sep 17 11:25:20 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import os 15 | import sys 16 | 17 | from django_filters import __version__ 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ----------------------------------------------------- 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be extensions 30 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 31 | extensions = [ 32 | "sphinx.ext.intersphinx" 33 | ] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ["_templates"] 37 | 38 | # The suffix of source filenames. 39 | source_suffix = ".txt" 40 | 41 | # The encoding of source files. 42 | # source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = "index" 46 | 47 | # General information about the project. 48 | project = u"django-filter" 49 | copyright = u"2022, Alex Gaynor, Carlton Gibson and others." 50 | 51 | # The version info for the project you're documenting, acts as replacement for 52 | # |version| and |release|, also used in various other places throughout the 53 | # built documents. 54 | # 55 | # The short X.Y version. 56 | version = __version__ 57 | # The full version, including alpha/beta/rc tags. 58 | release = __version__ 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | # language = None 63 | 64 | # There are two options for replacing |today|: either, you set today to some 65 | # non-false value, then it is used: 66 | # today = '' 67 | # Else, today_fmt is used as the format for a strftime call. 68 | # today_fmt = '%B %d, %Y' 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | exclude_patterns = ["_build"] 73 | 74 | # The reST default role (used for this markup: `text`) to use for all documents. 75 | # default_role = None 76 | 77 | # If true, '()' will be appended to :func: etc. cross-reference text. 78 | # add_function_parentheses = True 79 | 80 | # If true, the current module name will be prepended to all description 81 | # unit titles (such as .. function::). 82 | # add_module_names = True 83 | 84 | # If true, sectionauthor and moduleauthor directives will be shown in the 85 | # output. They are ignored by default. 86 | # show_authors = False 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | pygments_style = "sphinx" 90 | 91 | # A list of ignored prefixes for module index sorting. 92 | # modindex_common_prefix = [] 93 | 94 | 95 | # -- Options for HTML output --------------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | html_theme = "furo" 100 | 101 | # Theme options are theme-specific and customize the look and feel of a theme 102 | # further. For a list of options available for each theme, see the 103 | # documentation. 104 | # html_theme_options = {} 105 | 106 | # Add any paths that contain custom themes here, relative to this directory. 107 | # html_theme_path = [] 108 | 109 | # The name for this set of Sphinx documents. If None, it defaults to 110 | # " v documentation". 111 | # html_title = None 112 | 113 | # A shorter title for the navigation bar. Default is the same as html_title. 114 | # html_short_title = None 115 | 116 | # The name of an image file (relative to this directory) to place at the top 117 | # of the sidebar. 118 | # html_logo = None 119 | 120 | # The name of an image file (within the static path) to use as favicon of the 121 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 122 | # pixels large. 123 | # html_favicon = None 124 | 125 | # Add any paths that contain custom static files (such as style sheets) here, 126 | # relative to this directory. They are copied after the builtin static files, 127 | # so a file named "default.css" will overwrite the builtin "default.css". 128 | # html_static_path = ['_static'] 129 | 130 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 131 | # using the given strftime format. 132 | # html_last_updated_fmt = '%b %d, %Y' 133 | 134 | # If true, SmartyPants will be used to convert quotes and dashes to 135 | # typographically correct entities. 136 | # html_use_smartypants = True 137 | 138 | # Custom sidebar templates, maps document names to template names. 139 | # html_sidebars = {} 140 | 141 | # Additional templates that should be rendered to pages, maps page names to 142 | # template names. 143 | # html_additional_pages = {} 144 | 145 | # If false, no module index is generated. 146 | # html_domain_indices = True 147 | 148 | # If false, no index is generated. 149 | # html_use_index = True 150 | 151 | # If true, the index is split into individual pages for each letter. 152 | # html_split_index = False 153 | 154 | # If true, links to the reST sources are added to the pages. 155 | # html_show_sourcelink = True 156 | 157 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 158 | # html_show_sphinx = True 159 | 160 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 161 | # html_show_copyright = True 162 | 163 | # If true, an OpenSearch description file will be output, and all pages will 164 | # contain a tag referring to it. The value of this option must be the 165 | # base URL from which the finished HTML is served. 166 | # html_use_opensearch = '' 167 | 168 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 169 | # html_file_suffix = None 170 | 171 | # Output file base name for HTML help builder. 172 | htmlhelp_basename = "django-filterdoc" 173 | 174 | 175 | # -- Options for LaTeX output -------------------------------------------------- 176 | 177 | latex_elements = { 178 | # The paper size ('letterpaper' or 'a4paper'). 179 | #'papersize': 'letterpaper', 180 | # The font size ('10pt', '11pt' or '12pt'). 181 | #'pointsize': '10pt', 182 | # Additional stuff for the LaTeX preamble. 183 | #'preamble': '', 184 | } 185 | 186 | # Grouping the document tree into LaTeX files. List of tuples 187 | # (source start file, target name, title, author, documentclass [howto/manual]). 188 | latex_documents = [ 189 | ( 190 | "index", 191 | "django-filter.tex", 192 | u"django-filter Documentation", 193 | u"Alex Gaynor and others.", 194 | "manual", 195 | ), 196 | ] 197 | 198 | # The name of an image file (relative to this directory) to place at the top of 199 | # the title page. 200 | # latex_logo = None 201 | 202 | # For "manual" documents, if this is true, then toplevel headings are parts, 203 | # not chapters. 204 | # latex_use_parts = False 205 | 206 | # If true, show page references after internal links. 207 | # latex_show_pagerefs = False 208 | 209 | # If true, show URL addresses after external links. 210 | # latex_show_urls = False 211 | 212 | # Documents to append as an appendix to all manuals. 213 | # latex_appendices = [] 214 | 215 | # If false, no module index is generated. 216 | # latex_domain_indices = True 217 | 218 | 219 | # -- Options for manual page output -------------------------------------------- 220 | 221 | # One entry per manual page. List of tuples 222 | # (source start file, name, description, authors, manual section). 223 | man_pages = [ 224 | ( 225 | "index", 226 | "django-filter", 227 | u"django-filter Documentation", 228 | [u"Alex Gaynor and others."], 229 | 1, 230 | ) 231 | ] 232 | 233 | # If true, show URL addresses after external links. 234 | # man_show_urls = False 235 | 236 | 237 | # -- Options for Texinfo output ------------------------------------------------ 238 | 239 | # Grouping the document tree into Texinfo files. List of tuples 240 | # (source start file, target name, title, author, 241 | # dir menu entry, description, category) 242 | texinfo_documents = [ 243 | ( 244 | "index", 245 | "django-filter", 246 | u"django-filter Documentation", 247 | u"Alex Gaynor and others.", 248 | "django-filter", 249 | "One line description of project.", 250 | "Miscellaneous", 251 | ), 252 | ] 253 | 254 | # Documents to append as an appendix to all manuals. 255 | # texinfo_appendices = [] 256 | 257 | # If false, no module index is generated. 258 | # texinfo_domain_indices = True 259 | 260 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 261 | # texinfo_show_urls = 'footnote' 262 | 263 | 264 | intersphinx_mapping = { 265 | "django": ( 266 | "https://docs.djangoproject.com/en/stable", 267 | "https://docs.djangoproject.com/en/stable/_objects/", 268 | ), 269 | "python": ('https://docs.python.org/3', None) 270 | } 271 | 272 | # elide module names in class headings 273 | add_module_names = False 274 | -------------------------------------------------------------------------------- /docs/dev/tests.txt: -------------------------------------------------------------------------------- 1 | ====================== 2 | Running the Test Suite 3 | ====================== 4 | 5 | The easiest way to run the django-filter tests is to check out the source 6 | code and create a virtualenv where you can install the test dependencies. 7 | Django-filter uses a custom test runner to configure the environment, so a 8 | wrapper script is available to set up and run the test suite. 9 | 10 | .. note:: 11 | 12 | The following assumes you have `virtualenv`__ and `git`__ installed. 13 | 14 | __ https://virtualenv.pypa.io/en/stable/ 15 | __ https://git-scm.com 16 | 17 | Clone the repository 18 | -------------------- 19 | 20 | Get the source code using the following command: 21 | 22 | .. code-block:: bash 23 | 24 | $ git clone https://github.com/carltongibson/django-filter.git 25 | 26 | Switch to the django-filter directory: 27 | 28 | .. code-block:: bash 29 | 30 | $ cd django-filter 31 | 32 | Set up the virtualenv 33 | --------------------- 34 | 35 | Create a new virtualenv to run the test suite in: 36 | 37 | .. code-block:: bash 38 | 39 | $ virtualenv venv 40 | 41 | Then activate the virtualenv and install the test requirements: 42 | 43 | .. code-block:: bash 44 | 45 | $ source venv/bin/activate 46 | $ pip install -r requirements/test.txt 47 | 48 | Execute the test runner 49 | ----------------------- 50 | 51 | Run the tests with the runner script: 52 | 53 | .. code-block:: bash 54 | 55 | $ python runtests.py 56 | 57 | 58 | Test all supported versions 59 | --------------------------- 60 | 61 | You can also use the excellent tox testing tool to run the tests against all 62 | supported versions of Python and Django. Install tox, and then simply run: 63 | 64 | .. code-block:: bash 65 | 66 | $ pip install tox 67 | $ tox 68 | 69 | 70 | Housekeeping 71 | ------------ 72 | 73 | The ``isort`` utility is used to maintain module imports. You can either test 74 | the module imports with the appropriate `tox` env, or with `isort` directly. 75 | 76 | .. code-block:: bash 77 | 78 | $ pip install tox 79 | $ tox -e isort 80 | 81 | # or 82 | 83 | $ pip install isort 84 | $ isort --check --diff django_filters tests 85 | 86 | To sort the imports, simply remove the ``--check-only`` option. 87 | 88 | .. code-block:: bash 89 | 90 | $ isort --recursive django_filters tests 91 | -------------------------------------------------------------------------------- /docs/guide/install.txt: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Django-filter can be installed from PyPI with tools like ``pip``: 6 | 7 | .. code-block:: bash 8 | 9 | $ pip install django-filter 10 | 11 | Then add ``'django_filters'`` to your ``INSTALLED_APPS``. 12 | 13 | .. code-block:: python 14 | 15 | INSTALLED_APPS = [ 16 | ... 17 | 'django_filters', 18 | ] 19 | 20 | 21 | Requirements 22 | ------------ 23 | 24 | Django-filter requires a current version of `Django`__ and is tested against 25 | all supported versions of Python, as well as the latest version of Django REST 26 | Framework (`DRF`__). 27 | 28 | __ https://www.djangoproject.com/download/#supported-versions 29 | __ http://www.django-rest-framework.org/ 30 | -------------------------------------------------------------------------------- /docs/guide/rest_framework.txt: -------------------------------------------------------------------------------- 1 | .. _drf integration: 2 | 3 | ==================== 4 | Integration with DRF 5 | ==================== 6 | 7 | Integration with `Django Rest Framework`__ is provided through a DRF-specific ``FilterSet`` and a `filter backend`__. These may be found in the ``rest_framework`` sub-package. 8 | 9 | __ http://www.django-rest-framework.org/ 10 | __ http://www.django-rest-framework.org/api-guide/filtering/ 11 | 12 | Quickstart 13 | ---------- 14 | 15 | Using the new ``FilterSet`` simply requires changing the import path. Instead of importing from ``django_filters``, import from the ``rest_framework`` sub-package. 16 | 17 | .. code-block:: python 18 | 19 | from django_filters import rest_framework as filters 20 | 21 | class ProductFilter(filters.FilterSet): 22 | ... 23 | 24 | Your view class will also need to add ``DjangoFilterBackend`` to the ``filter_backends``. 25 | 26 | .. code-block:: python 27 | 28 | from django_filters import rest_framework as filters 29 | 30 | class ProductList(generics.ListAPIView): 31 | queryset = Product.objects.all() 32 | serializer_class = ProductSerializer 33 | filter_backends = (filters.DjangoFilterBackend,) 34 | filterset_fields = ('category', 'in_stock') 35 | 36 | If you want to use the django-filter backend by default, add it to the ``DEFAULT_FILTER_BACKENDS`` setting. 37 | 38 | .. code-block:: python 39 | 40 | # settings.py 41 | INSTALLED_APPS = [ 42 | # ... 43 | 'rest_framework', 44 | 'django_filters', 45 | ] 46 | 47 | REST_FRAMEWORK = { 48 | 'DEFAULT_FILTER_BACKENDS': ( 49 | 'django_filters.rest_framework.DjangoFilterBackend', 50 | # ... 51 | ), 52 | } 53 | 54 | 55 | Adding a FilterSet with ``filterset_class`` 56 | ------------------------------------------- 57 | 58 | To enable filtering with a ``FilterSet``, add it to the ``filterset_class`` parameter on your view class. 59 | 60 | .. code-block:: python 61 | 62 | from rest_framework import generics 63 | from django_filters import rest_framework as filters 64 | from myapp import Product 65 | 66 | 67 | class ProductFilter(filters.FilterSet): 68 | min_price = filters.NumberFilter(field_name="price", lookup_expr='gte') 69 | max_price = filters.NumberFilter(field_name="price", lookup_expr='lte') 70 | 71 | class Meta: 72 | model = Product 73 | fields = ['category', 'in_stock'] 74 | 75 | 76 | class ProductList(generics.ListAPIView): 77 | queryset = Product.objects.all() 78 | serializer_class = ProductSerializer 79 | filter_backends = (filters.DjangoFilterBackend,) 80 | filterset_class = ProductFilter 81 | 82 | 83 | Using the ``filterset_fields`` shortcut 84 | --------------------------------------- 85 | 86 | You may bypass creating a ``FilterSet`` by instead adding ``filterset_fields`` to your view class. This is equivalent to creating a ``FilterSet`` with just :ref:`Meta.fields `. 87 | 88 | 89 | .. code-block:: python 90 | 91 | from rest_framework import generics 92 | from django_filters import rest_framework as filters 93 | from myapp import Product 94 | 95 | 96 | class ProductList(generics.ListAPIView): 97 | queryset = Product.objects.all() 98 | filter_backends = (filters.DjangoFilterBackend,) 99 | filterset_fields = ('category', 'in_stock') 100 | 101 | 102 | # Equivalent FilterSet: 103 | class ProductFilter(filters.FilterSet): 104 | class Meta: 105 | model = Product 106 | fields = ('category', 'in_stock') 107 | 108 | 109 | Note that using ``filterset_fields`` and ``filterset_class`` together is not 110 | supported. 111 | 112 | 113 | Overriding FilterSet creation 114 | ----------------------------- 115 | 116 | ``FilterSet`` creation can be customized by overriding the following methods on the backend class: 117 | 118 | * ``.get_filterset(self, request, queryset, view)`` 119 | * ``.get_filterset_class(self, view, queryset=None)`` 120 | * ``.get_filterset_kwargs(self, request, queryset, view)`` 121 | 122 | You can override these methods on a case-by-case basis for each view, creating unique backends, or these methods can be used to write your own hooks to the view class. 123 | 124 | .. code-block:: python 125 | 126 | class MyFilterBackend(filters.DjangoFilterBackend): 127 | def get_filterset_kwargs(self, request, queryset, view): 128 | kwargs = super().get_filterset_kwargs(request, queryset, view) 129 | 130 | # merge filterset kwargs provided by view class 131 | if hasattr(view, 'get_filterset_kwargs'): 132 | kwargs.update(view.get_filterset_kwargs()) 133 | 134 | return kwargs 135 | 136 | 137 | class BookFilter(filters.FilterSet): 138 | def __init__(self, *args, author=None, **kwargs): 139 | super().__init__(*args, **kwargs) 140 | # do something w/ author 141 | 142 | 143 | class BookViewSet(viewsets.ModelViewSet): 144 | filter_backends = [MyFilterBackend] 145 | filterset_class = BookFilter 146 | 147 | def get_filterset_kwargs(self): 148 | return { 149 | 'author': self.get_author(), 150 | } 151 | 152 | 153 | Crispy Forms 154 | ------------ 155 | 156 | If you are using DRF's browsable API or admin API you may also want to install ``django-crispy-forms``, which will enhance the presentation of the filter forms in HTML views, by allowing them to render Bootstrap 3 HTML. Note that this isn't actively supported, although pull requests for bug fixes are welcome. 157 | 158 | .. code-block:: bash 159 | 160 | pip install django-crispy-forms 161 | 162 | With crispy forms installed and added to Django's ``INSTALLED_APPS``, the browsable API will present a filtering control for ``DjangoFilterBackend``, like so: 163 | 164 | .. image:: ../assets/form.png 165 | 166 | 167 | Additional ``FilterSet`` Features 168 | --------------------------------- 169 | 170 | The following features are specific to the rest framework FilterSet: 171 | 172 | - ``BooleanFilter``'s use the API-friendly ``BooleanWidget``, which accepts lowercase ``true``/``false``. 173 | - Filter generation uses ``IsoDateTimeFilter`` for datetime model fields. 174 | - Raised ``ValidationError``'s are reraised as their DRF equivalent. 175 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | ============= 2 | django-filter 3 | ============= 4 | 5 | Django-filter is a generic, reusable application to alleviate writing some of 6 | the more mundane bits of view code. Specifically, it allows users to filter 7 | down a queryset based on a model's fields, displaying the form to let them 8 | do this. 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :caption: User Guide 13 | 14 | guide/install 15 | guide/usage 16 | guide/rest_framework 17 | guide/tips 18 | guide/migration 19 | 20 | .. toctree:: 21 | :maxdepth: 1 22 | :caption: Reference Documentation 23 | 24 | ref/filterset 25 | ref/filters 26 | ref/fields 27 | ref/widgets 28 | ref/settings 29 | 30 | .. toctree:: 31 | :maxdepth: 1 32 | :caption: Developer Documentation 33 | 34 | dev/tests 35 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-filter.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-filter.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /docs/ref/fields.txt: -------------------------------------------------------------------------------- 1 | =============== 2 | Field Reference 3 | =============== 4 | 5 | .. module:: django_filters.fields 6 | :synopsis: Provided form fields and their arguments. 7 | 8 | .. currentmodule:: django_filters.fields 9 | 10 | .. class:: IsoDateTimeField 11 | 12 | Extends :class:`django.forms.DateTimeField` to allow parsing ISO 8601 formated dates, in addition to existing formats 13 | 14 | Defines a class level attribute ``ISO_8601`` as constant for the format. 15 | 16 | Sets ``input_formats = [ISO_8601]`` — this means that by default ``IsoDateTimeField`` will **only** parse ISO 8601 formated dates. 17 | 18 | You may set :attr:`~django.forms.DateTimeField.input_formats` to your list of required formats as per the :class:`~django.forms.DateTimeField` docs, using the ``ISO_8601`` class level attribute to specify the ISO 8601 format. 19 | 20 | .. code-block:: python 21 | 22 | f = IsoDateTimeField() 23 | f.input_formats = [IsoDateTimeField.ISO_8601] + DateTimeField.input_formats 24 | 25 | -------------------------------------------------------------------------------- /docs/ref/filterset.txt: -------------------------------------------------------------------------------- 1 | ================= 2 | FilterSet Options 3 | ================= 4 | 5 | .. module:: django_filters.filterset 6 | :synopsis: FilterSet features and options. 7 | 8 | .. currentmodule:: django_filters.filterset 9 | 10 | This document provides a guide on using additional FilterSet features. 11 | 12 | 13 | .. class:: FilterSet 14 | 15 | Meta options 16 | ------------ 17 | 18 | - :ref:`model ` 19 | - :ref:`fields ` 20 | - :ref:`exclude ` 21 | - :ref:`form
` 22 | - :ref:`filter_overrides ` 23 | - :ref:`unknown_field_behavior ` 24 | 25 | 26 | .. _model: 27 | 28 | Automatic filter generation with ``model`` 29 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30 | 31 | The ``FilterSet`` is capable of automatically generating filters for a given 32 | ``model``'s fields. Similar to Django's :class:`~django.forms.ModelForm`, filters are created 33 | based on the underlying model field's type. This option must be combined with 34 | either the ``fields`` or ``exclude`` option, which is the same requirement for 35 | Django's :class:`~django.forms.ModelForm` class, detailed :ref:`here `. 36 | 37 | .. code-block:: python 38 | 39 | class UserFilter(django_filters.FilterSet): 40 | class Meta: 41 | model = User 42 | fields = ['username', 'last_login'] 43 | 44 | 45 | .. _fields: 46 | 47 | Declaring filterable ``fields`` 48 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 49 | 50 | The ``fields`` option is combined with ``model`` to automatically generate 51 | filters. Note that generated filters will not overwrite filters declared on 52 | the ``FilterSet``. The ``fields`` option accepts two syntaxes: 53 | 54 | * a list of field names 55 | * a dictionary of field names mapped to a list of lookups 56 | 57 | .. code-block:: python 58 | 59 | class UserFilter(django_filters.FilterSet): 60 | class Meta: 61 | model = User 62 | fields = ['username', 'last_login'] 63 | 64 | # or 65 | 66 | class UserFilter(django_filters.FilterSet): 67 | class Meta: 68 | model = User 69 | fields = { 70 | 'username': ['exact', 'contains'], 71 | 'last_login': ['exact', 'year__gt'], 72 | } 73 | 74 | The list syntax will create an ``exact`` lookup filter for each field included 75 | in ``fields``. The dictionary syntax will create a filter for each lookup 76 | expression declared for its corresponding model field. These expressions may 77 | include both transforms and lookups, as detailed in the 78 | :mod:`lookup reference `. 79 | 80 | Note that it is **not** necessary to include declared filters in a ``fields`` 81 | list - doing so will only affect the order in which fields appear on a FilterSet's form. 82 | Including declarative aliases in a ``fields`` dict will raise an error. 83 | 84 | .. code-block:: python 85 | 86 | class UserFilter(django_filters.FilterSet): 87 | username = filters.CharFilter() 88 | login_timestamp = filters.IsoDateTimeFilter(field_name='last_login') 89 | 90 | class Meta: 91 | model = User 92 | fields = { 93 | 'username': ['exact', 'contains'], 94 | 'login_timestamp': ['exact'], 95 | } 96 | 97 | TypeError("'Meta.fields' contains fields that are not defined on this FilterSet: login_timestamp") 98 | 99 | 100 | .. _exclude: 101 | 102 | Disable filter fields with ``exclude`` 103 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 104 | 105 | The ``exclude`` option accepts a blacklist of field names to exclude from 106 | automatic filter generation. Note that this option will not disable filters 107 | declared directly on the ``FilterSet``. 108 | 109 | .. code-block:: python 110 | 111 | class UserFilter(django_filters.FilterSet): 112 | class Meta: 113 | model = User 114 | exclude = ['password'] 115 | 116 | 117 | .. _form: 118 | 119 | Custom Forms using ``form`` 120 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 121 | 122 | The inner ``Meta`` class also takes an optional ``form`` argument. This is a 123 | form class from which ``FilterSet.form`` will subclass. This works similar to 124 | the ``form`` option on a :class:`~django.contrib.admin.ModelAdmin`. 125 | 126 | 127 | .. _filter_overrides: 128 | 129 | Customise filter generation with ``filter_overrides`` 130 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 131 | 132 | The inner ``Meta`` class also takes an optional ``filter_overrides`` argument. 133 | This is a map of model fields to filter classes with options:: 134 | 135 | class ProductFilter(django_filters.FilterSet): 136 | 137 | class Meta: 138 | model = Product 139 | fields = ['name', 'release_date'] 140 | filter_overrides = { 141 | models.CharField: { 142 | 'filter_class': django_filters.CharFilter, 143 | 'extra': lambda f: { 144 | 'lookup_expr': 'icontains', 145 | }, 146 | }, 147 | models.BooleanField: { 148 | 'filter_class': django_filters.BooleanFilter, 149 | 'extra': lambda f: { 150 | 'widget': forms.CheckboxInput, 151 | }, 152 | }, 153 | } 154 | 155 | 156 | 157 | A possible usecase would be creating a custom filter to be able to filter on a 158 | :class:`~django.db.models.FileField` (This is hard to define in a generalised way, 159 | which is why there is no ``FileFilter``). 160 | 161 | This example shows an override used to filter on a :class:`~django.db.models.FileField`:: 162 | 163 | class Questionnaire(models.Model): 164 | file = models.FileField(upload_to=questionnaire_path) 165 | 166 | class QuestionnaireFilter(FilterSet): 167 | class Meta: 168 | model = Questionnaire 169 | fields = ['file'] 170 | filter_overrides = { 171 | models.FileField: { 172 | 'filter_class': CharFilter, 173 | 'extra': lambda f: {'lookup_expr': 'exact'}, 174 | }, 175 | } 176 | 177 | 178 | .. _unknown_field_behavior: 179 | 180 | Handling unknown fields with ``unknown_field_behavior`` 181 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 182 | 183 | The ``unknown_field_behavior`` option specifies how unknown fields are handled 184 | in a ``FilterSet``. You can set this option using the values of the 185 | ``UnknownFieldBehavior`` enum: 186 | 187 | - ``UnknownFieldBehavior.RAISE``: Raise an assertion error (default) 188 | - ``UnknownFieldBehavior.WARN``: Issue a warning and ignore the field 189 | - ``UnknownFieldBehavior.IGNORE``: Silently ignore the field 190 | 191 | Note that both the ``WARN`` and ``IGNORE`` options do not include the unknown 192 | field(s) in the list of filters. 193 | 194 | .. code-block:: python 195 | 196 | from django_filters import UnknownFieldBehavior 197 | 198 | class UserFilter(django_filters.FilterSet): 199 | class Meta: 200 | model = User 201 | fields = ['username', 'last_login'] 202 | unknown_field_behavior = UnknownFieldBehavior.WARN 203 | 204 | 205 | Overriding ``FilterSet`` methods 206 | -------------------------------- 207 | 208 | When overriding classmethods, calling ``super(MyFilterSet, cls)`` may result 209 | in a :exc:`NameError` exception. This is due to the ``FilterSetMetaclass`` calling 210 | these classmethods before the ``FilterSet`` class has been fully created. 211 | There are two recommmended workarounds: 212 | 213 | 1. If using python 3.6 or newer, use the argumentless ``super()`` syntax. 214 | 2. For older versions of python, use an intermediate class. Ex:: 215 | 216 | class Intermediate(django_filters.FilterSet): 217 | 218 | @classmethod 219 | def method(cls, arg): 220 | super(Intermediate, cls).method(arg) 221 | ... 222 | 223 | class ProductFilter(Intermediate): 224 | class Meta: 225 | model = Product 226 | fields = ['...'] 227 | 228 | 229 | .. method:: FilterSet.filter_for_lookup 230 | 231 | Prior to version 0.13.0, filter generation did not take into account the 232 | ``lookup_expr`` used. This commonly caused malformed filters to be generated 233 | for 'isnull', 'in', and 'range' lookups (as well as transformed lookups). The 234 | current implementation provides the following behavior: 235 | 236 | - 'isnull' lookups return a ``BooleanFilter`` 237 | - 'in' lookups return a filter derived from the CSV-based ``BaseInFilter``. 238 | - 'range' lookups return a filter derived from the CSV-based ``BaseRangeFilter``. 239 | 240 | If you want to override the ``filter_class`` and ``params`` used to instantiate 241 | filters for a model field, you can override ``filter_for_lookup()``. Ex:: 242 | 243 | class ProductFilter(django_filters.FilterSet): 244 | class Meta: 245 | model = Product 246 | fields = { 247 | 'release_date': ['exact', 'range'], 248 | } 249 | 250 | @classmethod 251 | def filter_for_lookup(cls, f, lookup_type): 252 | # override date range lookups 253 | if isinstance(f, models.DateField) and lookup_type == 'range': 254 | return django_filters.DateRangeFilter, {} 255 | 256 | # use default behavior otherwise 257 | return super().filter_for_lookup(f, lookup_type) 258 | 259 | 260 | .. _filterset_factory: 261 | 262 | Using ``filterset_factory`` 263 | --------------------------- 264 | 265 | .. function:: filterset_factory 266 | 267 | A ``FilterSet`` for a ``model`` can also be created by the 268 | ``filterset_factory``, which creates a ``FilterSet`` with the ``model`` set in 269 | the FilterSets Meta. You can pass a customized ``FilterSet`` class to the 270 | ``filterset_factory``, which then uses this class a a base for the created 271 | ``FilterSet``. Ex:: 272 | 273 | class CustomFilterSet(django_filters.FilterSet): 274 | class Meta: 275 | form = CustomFilterSetForm 276 | 277 | 278 | filterset = filterset_factory(Product, filterset=CustomFilterSet) 279 | -------------------------------------------------------------------------------- /docs/ref/settings.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | Settings Reference 3 | ================== 4 | 5 | Here is a list of all available settings of django-filters and their 6 | default values. All settings are prefixed with ``FILTERS_``, although this 7 | is a bit verbose it helps to make it easy to identify these settings. 8 | 9 | 10 | FILTERS_DEFAULT_LOOKUP_EXPR 11 | --------------------------- 12 | 13 | Default: ``'exact'`` 14 | 15 | Set the default lookup expression to be generated, when none is defined. 16 | 17 | 18 | FILTERS_EMPTY_CHOICE_LABEL 19 | -------------------------- 20 | 21 | Default: ``'---------'`` 22 | 23 | Set the default value for ``ChoiceFilter.empty_label``. You may disable the empty choice by setting this to ``None``. 24 | 25 | 26 | FILTERS_NULL_CHOICE_LABEL 27 | ------------------------- 28 | 29 | Default: ``None`` 30 | 31 | Set the default value for ``ChoiceFilter.null_label``. You may enable the null choice by setting a non-``None`` value. 32 | 33 | 34 | FILTERS_NULL_CHOICE_VALUE 35 | ------------------------- 36 | 37 | Default: ``'null'`` 38 | 39 | Set the default value for ``ChoiceFilter.null_value``. You may want to change this value if the default ``'null'`` string conflicts with an actual choice. 40 | 41 | 42 | FILTERS_DISABLE_HELP_TEXT 43 | ------------------------- 44 | 45 | Default: ``False`` 46 | 47 | Some filters provide informational ``help_text``. For example, csv-based 48 | filters (``filters.BaseCSVFilter``) inform users that "Multiple values may 49 | be separated by commas". 50 | 51 | You may set this to ``True`` to disable the ``help_text`` for **all** 52 | filters, removing the text from the rendered form's output. 53 | 54 | 55 | .. _verbose-lookups-setting: 56 | 57 | FILTERS_VERBOSE_LOOKUPS 58 | ----------------------- 59 | 60 | .. note:: 61 | 62 | This is considered an advanced setting and is subject to change. 63 | 64 | Default: 65 | 66 | .. code-block:: python 67 | 68 | # refer to 'django_filters.conf.DEFAULTS' 69 | 'VERBOSE_LOOKUPS': { 70 | 'exact': _(''), 71 | 'iexact': _(''), 72 | 'contains': _('contains'), 73 | 'icontains': _('contains'), 74 | ... 75 | } 76 | 77 | This setting controls the verbose output for generated filter labels. Instead 78 | of getting expression parts such as "lt" and "contained_by", the verbose label 79 | would contain "is less than" and "is contained by". Verbose output may be 80 | disabled by setting this to a falsy value. 81 | 82 | This setting also accepts callables. The callable should not require arguments 83 | and should return a dictionary. This is useful for extending or overriding the 84 | default terms without having to copy the entire set of terms to your settings. 85 | For example, you could add verbose output for "exact" lookups. 86 | 87 | .. code-block:: python 88 | 89 | # settings.py 90 | def FILTERS_VERBOSE_LOOKUPS(): 91 | from django_filters.conf import DEFAULTS 92 | 93 | verbose_lookups = DEFAULTS['VERBOSE_LOOKUPS'].copy() 94 | verbose_lookups.update({ 95 | 'exact': 'is equal to', 96 | }) 97 | return verbose_lookups 98 | -------------------------------------------------------------------------------- /docs/ref/widgets.txt: -------------------------------------------------------------------------------- 1 | ================ 2 | Widget Reference 3 | ================ 4 | 5 | .. module:: django_filters.widgets 6 | :synopsis: Provided form widgets and their arguments. 7 | 8 | .. currentmodule:: django_filters.widgets 9 | 10 | 11 | This is a reference document with a list of the provided widgets and their 12 | arguments. 13 | 14 | 15 | .. _link-widget: 16 | 17 | .. class:: LinkWidget 18 | 19 | This widget renders each option as a link, instead of an actual . It has 20 | one method that you can override for additional customizability. 21 | ``option_string()`` should return a string with 3 Python keyword argument 22 | placeholders: 23 | 24 | 1. ``attrs``: This is a string with all the attributes that will be on the 25 | final ```` tag. 26 | 2. ``query_string``: This is the query string for use in the ``href`` 27 | option on the ```` element. 28 | 3. ``label``: This is the text to be displayed to the user. 29 | 30 | 31 | .. _boolean-widget: 32 | .. class:: BooleanWidget 33 | 34 | This widget converts its input into Python's True/False values. It will convert 35 | all case variations of ``True`` and ``False`` into the internal Python values. 36 | To use it, pass this into the ``widgets`` argument of the :class:`~django_filters.filters.BooleanFilter`: 37 | 38 | .. code-block:: python 39 | 40 | active = BooleanFilter(widget=BooleanWidget()) 41 | 42 | 43 | .. _csv-widget: 44 | .. class:: CSVWidget 45 | 46 | This widget expects a comma separated value and converts it into a list of 47 | string values. It is expected that the field class handle a list of values as 48 | well as type conversion. 49 | 50 | 51 | .. _range-widget: 52 | .. class:: RangeWidget 53 | 54 | This widget is used with :class:`~django_filters.filters.RangeFilter` and its 55 | subclasses. It generates two form input elements which generally act as start/end 56 | values in a range. Under the hood, it is Django's :class:`~django.forms.TextInput` widget and 57 | accepts the same arguments and values. To use it, pass it to ``widget`` argument of 58 | a :class:`~django_filters.filters.RangeFilter`: 59 | 60 | .. code-block:: python 61 | 62 | date_range = DateFromToRangeFilter(widget=RangeWidget(attrs={'placeholder': 'YYYY/MM/DD'})) 63 | 64 | 65 | .. class:: SuffixedMultiWidget 66 | 67 | Extends Django's builtin :class:`~django.forms.MultiWidget` to append custom suffixes 68 | instead of indices. For example, take a range widget that accepts minimum and maximum 69 | bounds. By default, the resulting query params would look like the following: 70 | 71 | .. code-block:: http 72 | 73 | GET /products?price_0=10&price_1=25 HTTP/1.1 74 | 75 | By using ``SuffixedMultiWidget`` instead, you can provide human-friendly suffixes. 76 | 77 | .. code-block:: python 78 | 79 | class RangeWidget(SuffixedMultiWidget): 80 | suffixes = ['min', 'max'] 81 | 82 | The query names are now a little more ergonomic. 83 | 84 | .. code-block:: http 85 | 86 | GET /products?price_min=10&price_max=25 HTTP/1.1 87 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "django-filter" 7 | authors = [{name = "Alex Gaynor", email = "alex.gaynor@gmail.com"}] 8 | maintainers = [{name = "Carlton Gibson", email = "carlton.gibson@noumenal.es"}] 9 | license = {text = "BSD"} 10 | description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." 11 | readme = "README.rst" 12 | classifiers = [ 13 | "Development Status :: 5 - Production/Stable", 14 | "Environment :: Web Environment", 15 | "Intended Audience :: Developers", 16 | "License :: OSI Approved :: BSD License", 17 | "Operating System :: OS Independent", 18 | "Framework :: Django", 19 | "Framework :: Django :: 4.2", 20 | "Framework :: Django :: 5.0", 21 | "Framework :: Django :: 5.1", 22 | "Framework :: Django :: 5.2", 23 | "Programming Language :: Python", 24 | "Programming Language :: Python :: 3", 25 | "Programming Language :: Python :: 3.9", 26 | "Programming Language :: Python :: 3.10", 27 | "Programming Language :: Python :: 3.11", 28 | "Programming Language :: Python :: 3.12", 29 | ] 30 | requires-python = ">=3.9" 31 | dependencies = ["Django>=4.2"] 32 | dynamic = ["version"] 33 | 34 | [project.urls] 35 | Homepage = "https://github.com/carltongibson/django-filter/tree/main" 36 | Documentation = "https://django-filter.readthedocs.io/en/main/" 37 | Changelog = "https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst" 38 | "Bug Tracker" = "https://github.com/carltongibson/django-filter/issues" 39 | "Source Code" = "https://github.com/carltongibson/django-filter" 40 | 41 | [tool.setuptools] 42 | zip-safe = false 43 | include-package-data = true 44 | license-files = ["LICENSE"] 45 | 46 | [tool.setuptools.packages.find] 47 | exclude = ["tests*"] 48 | namespaces = false 49 | 50 | [tool.isort] 51 | profile = "black" 52 | skip = [".tox"] 53 | known_third_party = ["django", "pytz", "rest_framework"] 54 | known_first_party = ["django_filters"] 55 | 56 | [tool.flit.module] 57 | name = "django_filters" 58 | 59 | [tool.coverage.run] 60 | relative_files = true 61 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | Sphinx 2 | furo 3 | . 4 | -------------------------------------------------------------------------------- /requirements/maintainer.txt: -------------------------------------------------------------------------------- 1 | twine 2 | wheel 3 | Sphinx -------------------------------------------------------------------------------- /requirements/test-ci.txt: -------------------------------------------------------------------------------- 1 | markdown 2 | django-crispy-forms 3 | 4 | coverage[toml] 5 | pytz 6 | unittest-xml-reporting 7 | -------------------------------------------------------------------------------- /requirements/test.txt: -------------------------------------------------------------------------------- 1 | -r test-ci.txt 2 | django 3 | djangorestframework 4 | -------------------------------------------------------------------------------- /runshell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | from django.core.management import execute_from_command_line 6 | 7 | 8 | def runshell(): 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") 10 | execute_from_command_line(sys.argv[:1] + ["migrate", "--noinput", "-v", "0"]) 11 | execute_from_command_line(sys.argv[:1] + ["shell"] + sys.argv[1:]) 12 | 13 | 14 | if __name__ == "__main__": 15 | runshell() 16 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | from django.core.management import execute_from_command_line 6 | 7 | 8 | def runtests(): 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") 10 | argv = sys.argv[:1] + ["test"] + sys.argv[1:] 11 | execute_from_command_line(argv) 12 | 13 | 14 | if __name__ == "__main__": 15 | runtests() 16 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max_line_length = 120 3 | max_complexity = 10 4 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltongibson/django-filter/635343ec55c9928bfa297314711df77fa83ff6c7/tests/__init__.py -------------------------------------------------------------------------------- /tests/models.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | REGULAR = 0 6 | MANAGER = 1 7 | ADMIN = 2 8 | 9 | STATUS_CHOICES = ( 10 | (REGULAR, "Regular"), 11 | (MANAGER, "Manager"), 12 | (ADMIN, "Admin"), 13 | ) 14 | 15 | 16 | # classes for testing filters with inherited fields 17 | class SubCharField(models.CharField): 18 | pass 19 | 20 | 21 | class SubSubCharField(SubCharField): 22 | pass 23 | 24 | 25 | class SubnetMaskField(models.Field): 26 | empty_strings_allowed = False 27 | description = "Subnet Mask" 28 | 29 | def __init__(self, *args, **kwargs): 30 | kwargs["max_length"] = 15 31 | models.Field.__init__(self, *args, **kwargs) 32 | 33 | def get_internal_type(self): 34 | return "GenericIPAddressField" 35 | 36 | def formfield(self, **kwargs): 37 | defaults = {"form_class": forms.GenericIPAddressField} 38 | defaults.update(kwargs) 39 | return super().formfield(**defaults) 40 | 41 | 42 | class User(models.Model): 43 | username = models.CharField(_("username"), max_length=255) 44 | first_name = SubCharField(max_length=100) 45 | last_name = SubSubCharField(max_length=100) 46 | 47 | status = models.IntegerField(choices=STATUS_CHOICES, default=0) 48 | 49 | is_active = models.BooleanField(default=False) 50 | is_employed = models.BooleanField(null=True, default=False) 51 | 52 | favorite_books = models.ManyToManyField("Book", related_name="lovers") 53 | 54 | def __str__(self): 55 | return self.username 56 | 57 | 58 | class ManagerGroup(models.Model): 59 | users = models.ManyToManyField( 60 | User, limit_choices_to={"is_active": True}, related_name="member_of" 61 | ) 62 | manager = models.ForeignKey( 63 | User, 64 | limit_choices_to=lambda: {"status": MANAGER}, 65 | related_name="manager_of", 66 | on_delete=models.CASCADE, 67 | ) 68 | 69 | def __str__(self): 70 | return self.manager.name + " group" 71 | 72 | 73 | class AdminUser(User): 74 | class Meta: 75 | proxy = True 76 | 77 | def __str__(self): 78 | return "%s (ADMIN)" % self.username 79 | 80 | 81 | class Comment(models.Model): 82 | text = models.TextField() 83 | author = models.ForeignKey(User, related_name="comments", on_delete=models.CASCADE) 84 | 85 | date = models.DateField() 86 | time = models.TimeField() 87 | 88 | def __str__(self): 89 | return "%s said %s" % (self.author, self.text[:25]) 90 | 91 | 92 | class Article(models.Model): 93 | name = models.CharField(verbose_name="title", max_length=200, blank=True) 94 | published = models.DateTimeField() 95 | author = models.ForeignKey(User, null=True, on_delete=models.CASCADE) 96 | 97 | def __str__(self): 98 | if self.author_id: 99 | return "%s on %s" % (self.author, self.published) 100 | return "Anonymous on %s" % self.published 101 | 102 | 103 | class Book(models.Model): 104 | title = models.CharField(max_length=100) 105 | price = models.DecimalField(max_digits=6, decimal_places=2) 106 | average_rating = models.FloatField() 107 | 108 | def __str__(self): 109 | return self.title 110 | 111 | 112 | class Place(models.Model): 113 | name = models.CharField(max_length=100) 114 | 115 | class Meta: 116 | abstract = True 117 | 118 | 119 | class Restaurant(Place): 120 | serves_pizza = models.BooleanField(default=False) 121 | 122 | 123 | class NetworkSetting(models.Model): 124 | ip = models.GenericIPAddressField() 125 | mask = SubnetMaskField() 126 | 127 | cidr = models.CharField(max_length=18, blank=True, verbose_name="CIDR") 128 | 129 | 130 | class Company(models.Model): 131 | name = models.CharField(max_length=100) 132 | 133 | def __str__(self): 134 | return self.name 135 | 136 | class Meta: 137 | ordering = ["name"] 138 | 139 | 140 | class Location(models.Model): 141 | company = models.ForeignKey( 142 | Company, related_name="locations", on_delete=models.CASCADE 143 | ) 144 | name = models.CharField(max_length=100) 145 | zip_code = models.CharField(max_length=10) 146 | open_days = models.CharField(max_length=7) 147 | 148 | def __str__(self): 149 | return "%s: %s" % (self.company.name, self.name) 150 | 151 | 152 | class Account(models.Model): 153 | name = models.CharField(max_length=100) 154 | in_good_standing = models.BooleanField(default=False) 155 | friendly = models.BooleanField(default=False) 156 | 157 | 158 | class Profile(models.Model): 159 | account = models.OneToOneField( 160 | Account, related_name="profile", on_delete=models.CASCADE 161 | ) 162 | likes_coffee = models.BooleanField(default=False) 163 | likes_tea = models.BooleanField(default=False) 164 | 165 | 166 | class BankAccount(Account): 167 | amount_saved = models.IntegerField(default=0) 168 | 169 | 170 | class Node(models.Model): 171 | name = models.CharField(max_length=20) 172 | adjacents = models.ManyToManyField("self") 173 | 174 | 175 | class DirectedNode(models.Model): 176 | name = models.CharField(max_length=20) 177 | outbound_nodes = models.ManyToManyField( 178 | "self", symmetrical=False, related_name="inbound_nodes" 179 | ) 180 | 181 | 182 | class Worker(models.Model): 183 | name = models.CharField(max_length=100) 184 | 185 | 186 | class HiredWorker(models.Model): 187 | salary = models.IntegerField() 188 | hired_on = models.DateField() 189 | worker = models.ForeignKey(Worker, on_delete=models.CASCADE) 190 | business = models.ForeignKey("Business", on_delete=models.CASCADE) 191 | 192 | 193 | class Business(models.Model): 194 | name = models.CharField(max_length=100) 195 | employees = models.ManyToManyField( 196 | Worker, through=HiredWorker, related_name="employers" 197 | ) 198 | 199 | 200 | class UUIDTestModel(models.Model): 201 | uuid = models.UUIDField() 202 | 203 | 204 | class SpacewalkRecord(models.Model): 205 | """Cumulative space walk record. 206 | 207 | See: https://en.wikipedia.org/wiki/List_of_cumulative_spacewalk_records 208 | 209 | """ 210 | 211 | astronaut = models.CharField(max_length=100) 212 | duration = models.DurationField() 213 | -------------------------------------------------------------------------------- /tests/rest_framework/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "tests.rest_framework.apps.RestFrameworkTestConfig" 2 | -------------------------------------------------------------------------------- /tests/rest_framework/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RestFrameworkTestConfig(AppConfig): 5 | name = "tests.rest_framework" 6 | label = "drf_test_app" 7 | verbose_name = "Rest Framework Test App" 8 | -------------------------------------------------------------------------------- /tests/rest_framework/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class BasicModel(models.Model): 6 | text = models.CharField( 7 | max_length=100, 8 | verbose_name=_("Text comes here"), 9 | help_text=_("Text description."), 10 | ) 11 | 12 | 13 | class BaseFilterableItem(models.Model): 14 | text = models.CharField(max_length=100) 15 | 16 | 17 | class FilterableItem(BaseFilterableItem): 18 | decimal = models.DecimalField(max_digits=4, decimal_places=2) 19 | date = models.DateField() 20 | 21 | 22 | class DjangoFilterOrderingModel(models.Model): 23 | date = models.DateField() 24 | text = models.CharField(max_length=10) 25 | 26 | class Meta: 27 | ordering = ["-date"] 28 | 29 | 30 | class CategoryItem(BaseFilterableItem): 31 | category = models.CharField( 32 | max_length=10, choices=(("home", "Home"), ("office", "Office")) 33 | ) 34 | -------------------------------------------------------------------------------- /tests/rest_framework/templates/filter_template.html: -------------------------------------------------------------------------------- 1 | Test 2 | -------------------------------------------------------------------------------- /tests/rest_framework/test_backends.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | from django.db.models import BooleanField 4 | from django.test import TestCase 5 | from django.test.utils import override_settings 6 | from rest_framework import generics, serializers 7 | from rest_framework.test import APIRequestFactory 8 | 9 | from django_filters import filters 10 | from django_filters.rest_framework import DjangoFilterBackend, FilterSet, backends 11 | 12 | from ..models import Article 13 | from .models import CategoryItem, FilterableItem 14 | 15 | factory = APIRequestFactory() 16 | 17 | 18 | class FilterableItemSerializer(serializers.ModelSerializer): 19 | class Meta: 20 | model = FilterableItem 21 | fields = "__all__" 22 | 23 | 24 | class CategoryItemSerializer(serializers.ModelSerializer): 25 | class Meta: 26 | model = CategoryItem 27 | fields = "__all__" 28 | 29 | 30 | # These class are used to test a filter class. 31 | class SeveralFieldsFilter(FilterSet): 32 | text = filters.CharFilter(lookup_expr="icontains") 33 | decimal = filters.NumberFilter(lookup_expr="lt") 34 | date = filters.DateFilter(lookup_expr="gt") 35 | 36 | class Meta: 37 | model = FilterableItem 38 | fields = ["text", "decimal", "date"] 39 | 40 | 41 | # Basic filter on a list view. 42 | class FilterableItemView(generics.ListCreateAPIView): 43 | queryset = FilterableItem.objects.all() 44 | serializer_class = FilterableItemSerializer 45 | filter_backends = (DjangoFilterBackend,) 46 | 47 | 48 | class FilterFieldsRootView(FilterableItemView): 49 | filterset_fields = ["decimal", "date"] 50 | 51 | 52 | class FilterClassRootView(FilterableItemView): 53 | filterset_class = SeveralFieldsFilter 54 | 55 | 56 | class CategoryItemView(generics.ListCreateAPIView): 57 | queryset = CategoryItem.objects.all() 58 | serializer_class = CategoryItemSerializer 59 | filter_backends = (DjangoFilterBackend,) 60 | filterset_fields = ["category"] 61 | 62 | 63 | class GetFilterClassTests(TestCase): 64 | def test_filterset_class(self): 65 | class Filter(FilterSet): 66 | class Meta: 67 | model = FilterableItem 68 | fields = "__all__" 69 | 70 | backend = DjangoFilterBackend() 71 | view = FilterableItemView() 72 | view.filterset_class = Filter 73 | queryset = FilterableItem.objects.all() 74 | 75 | filterset_class = backend.get_filterset_class(view, queryset) 76 | self.assertIs(filterset_class, Filter) 77 | 78 | def test_filterset_class_no_meta(self): 79 | class Filter(FilterSet): 80 | pass 81 | 82 | backend = DjangoFilterBackend() 83 | view = FilterableItemView() 84 | view.filterset_class = Filter 85 | queryset = FilterableItem.objects.all() 86 | 87 | filterset_class = backend.get_filterset_class(view, queryset) 88 | self.assertIs(filterset_class, Filter) 89 | 90 | def test_filterset_class_no_queryset(self): 91 | class Filter(FilterSet): 92 | class Meta: 93 | model = FilterableItem 94 | fields = "__all__" 95 | 96 | backend = DjangoFilterBackend() 97 | view = FilterableItemView() 98 | view.filterset_class = Filter 99 | 100 | filterset_class = backend.get_filterset_class(view, None) 101 | self.assertIs(filterset_class, Filter) 102 | 103 | def test_filterset_fields(self): 104 | backend = DjangoFilterBackend() 105 | view = FilterableItemView() 106 | view.filterset_fields = ["text", "decimal", "date"] 107 | queryset = FilterableItem.objects.all() 108 | 109 | filterset_class = backend.get_filterset_class(view, queryset) 110 | self.assertEqual(filterset_class._meta.fields, view.filterset_fields) 111 | 112 | def test_filterset_fields_malformed(self): 113 | backend = DjangoFilterBackend() 114 | view = FilterableItemView() 115 | view.filterset_fields = ["non_existent"] 116 | queryset = FilterableItem.objects.all() 117 | 118 | msg = "'Meta.fields' must not contain non-model field names: non_existent" 119 | with self.assertRaisesMessage(TypeError, msg): 120 | backend.get_filterset_class(view, queryset) 121 | 122 | def test_filterset_fields_no_queryset(self): 123 | backend = DjangoFilterBackend() 124 | view = FilterableItemView() 125 | view.filterset_fields = ["text", "decimal", "date"] 126 | 127 | filterset_class = backend.get_filterset_class(view, None) 128 | self.assertIsNone(filterset_class) 129 | 130 | 131 | class TemplateTests(TestCase): 132 | def test_backend_output(self): 133 | """ 134 | Ensure backend renders default if template path does not exist 135 | """ 136 | view = FilterFieldsRootView() 137 | backend = view.filter_backends[0] 138 | request = view.initialize_request(factory.get("/")) 139 | html = backend().to_html(request, view.get_queryset(), view) 140 | 141 | self.assertHTMLEqual( 142 | html, 143 | """ 144 |

Field filters

145 | 146 |

147 | 148 | 149 |

150 |

151 | 152 | 153 |

154 | 155 | 156 | """, 157 | ) 158 | 159 | def test_template_path(self): 160 | view = FilterFieldsRootView() 161 | 162 | class Backend(view.filter_backends[0]): 163 | template = "filter_template.html" 164 | 165 | request = view.initialize_request(factory.get("/")) 166 | html = Backend().to_html(request, view.get_queryset(), view) 167 | 168 | self.assertHTMLEqual(html, "Test") 169 | 170 | @override_settings(TEMPLATES=[]) 171 | def test_DTL_missing(self): 172 | # The backend should be importable even if the DTL is not used. 173 | # See: https://github.com/carltongibson/django-filter/issues/506 174 | try: 175 | from importlib import reload # python 3.4 176 | except ImportError: 177 | from imp import reload 178 | 179 | reload(backends) 180 | 181 | def test_multiple_engines(self): 182 | # See: https://github.com/carltongibson/django-filter/issues/578 183 | DTL = { 184 | "BACKEND": "django.template.backends.django.DjangoTemplates", 185 | "APP_DIRS": True, 186 | } 187 | ALT = { 188 | "BACKEND": "django.template.backends.django.DjangoTemplates", 189 | "APP_DIRS": True, 190 | "NAME": "alt", 191 | } 192 | 193 | # multiple DTL backends 194 | with override_settings(TEMPLATES=[DTL, ALT]): 195 | self.test_backend_output() 196 | 197 | 198 | class AutoFilterSetTests(TestCase): 199 | def test_autofilter_meta_inheritance(self): 200 | # https://github.com/carltongibson/django-filter/issues/663 201 | 202 | class F(FilterSet): 203 | class Meta: 204 | filter_overrides = {BooleanField: {}} 205 | 206 | class Backend(DjangoFilterBackend): 207 | filterset_base = F 208 | 209 | view = FilterFieldsRootView() 210 | backend = Backend() 211 | 212 | filterset_class = backend.get_filterset_class(view, view.get_queryset()) 213 | filter_overrides = filterset_class._meta.filter_overrides 214 | 215 | # derived filterset_class.Meta should inherit from default_filter_set.Meta 216 | self.assertIn(BooleanField, filter_overrides) 217 | self.assertDictEqual(filter_overrides[BooleanField], {}) 218 | 219 | 220 | class ValidationErrorTests(TestCase): 221 | def test_errors(self): 222 | class F(FilterSet): 223 | class Meta: 224 | model = Article 225 | fields = ["id", "author", "name"] 226 | 227 | view = FilterFieldsRootView() 228 | backend = DjangoFilterBackend() 229 | request = factory.get("/?id=foo&author=bar&name=baz") 230 | request = view.initialize_request(request) 231 | queryset = Article.objects.all() 232 | view.filterset_class = F 233 | 234 | with self.assertRaises(serializers.ValidationError) as exc: 235 | backend.filter_queryset(request, queryset, view) 236 | 237 | # test output, does not include error code 238 | self.assertDictEqual( 239 | exc.exception.detail, 240 | { 241 | "id": ["Enter a number."], 242 | "author": [ 243 | "Select a valid choice. " 244 | "That choice is not one of the available choices." 245 | ], 246 | }, 247 | ) 248 | 249 | 250 | class DjangoFilterBackendTestCase(TestCase): 251 | @classmethod 252 | def setUpTestData(cls): 253 | cls.backend = DjangoFilterBackend() 254 | cls.backend.get_filterset_class = lambda x, y: None 255 | 256 | def test_get_filterset_none_filter_class(self): 257 | filterset = self.backend.get_filterset(mock.Mock(), mock.Mock(), mock.Mock()) 258 | self.assertIsNone(filterset) 259 | 260 | def test_filter_queryset_none_filter_class(self): 261 | prev_qs = mock.Mock() 262 | qs = self.backend.filter_queryset(mock.Mock(), prev_qs, mock.Mock()) 263 | self.assertIs(qs, prev_qs) 264 | 265 | def test_to_html_none_filter_class(self): 266 | html = self.backend.to_html(mock.Mock(), mock.Mock(), mock.Mock()) 267 | self.assertIsNone(html) 268 | 269 | @mock.patch("django_filters.compat.is_crispy", return_value=True) 270 | def test_template_crispy(self, _): 271 | self.assertEqual( 272 | self.backend.template, "django_filters/rest_framework/crispy_form.html" 273 | ) 274 | -------------------------------------------------------------------------------- /tests/rest_framework/test_filters.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | from django.test import TestCase 4 | 5 | from django_filters.rest_framework import filters 6 | from django_filters.widgets import BooleanWidget 7 | 8 | 9 | class ModuleImportTests(TestCase): 10 | def is_filter(self, name, value): 11 | return isinstance(value, type) and issubclass(value, filters.Filter) 12 | 13 | def test_imports(self): 14 | # msg = "Expected `filters.%s` to be imported in `filters.__all__`" 15 | filter_classes = [ 16 | key 17 | for key, value in inspect.getmembers(filters) 18 | if isinstance(value, type) and issubclass(value, filters.Filter) 19 | ] 20 | 21 | # sanity check 22 | self.assertIn("Filter", filter_classes) 23 | self.assertIn("BooleanFilter", filter_classes) 24 | 25 | for f in filter_classes: 26 | self.assertIn(f, filters.__all__) 27 | 28 | 29 | class BooleanFilterTests(TestCase): 30 | def test_widget(self): 31 | # Ensure that `BooleanFilter` uses the correct widget when importing 32 | # from `rest_framework.filters`. 33 | f = filters.BooleanFilter() 34 | 35 | self.assertEqual(f.extra["widget"], BooleanWidget) 36 | -------------------------------------------------------------------------------- /tests/rest_framework/test_filterset.py: -------------------------------------------------------------------------------- 1 | from unittest import skipIf 2 | 3 | from django.conf import settings 4 | from django.test import TestCase 5 | from django.test.utils import override_settings 6 | 7 | from django_filters.compat import crispy_forms 8 | from django_filters.rest_framework import FilterSet, filters 9 | from django_filters.widgets import BooleanWidget 10 | 11 | from ..models import Article, User 12 | 13 | 14 | class ArticleFilter(FilterSet): 15 | class Meta: 16 | model = Article 17 | fields = ["author"] 18 | 19 | 20 | class FilterSetFilterForFieldTests(TestCase): 21 | def test_isodatetimefilter(self): 22 | field = Article._meta.get_field("published") 23 | result = FilterSet.filter_for_field(field, "published") 24 | self.assertIsInstance(result, filters.IsoDateTimeFilter) 25 | self.assertEqual(result.field_name, "published") 26 | 27 | def test_booleanfilter_widget(self): 28 | field = User._meta.get_field("is_active") 29 | result = FilterSet.filter_for_field(field, "is_active") 30 | self.assertIsInstance(result, filters.BooleanFilter) 31 | self.assertEqual(result.extra["widget"], BooleanWidget) 32 | 33 | def test_booleanfilter_widget_nullbooleanfield(self): 34 | field = User._meta.get_field("is_employed") 35 | result = FilterSet.filter_for_field(field, "is_employed") 36 | self.assertIsInstance(result, filters.BooleanFilter) 37 | self.assertEqual(result.extra["widget"], BooleanWidget) 38 | 39 | 40 | @skipIf(crispy_forms is None, "django_crispy_forms must be installed") 41 | @override_settings(INSTALLED_APPS=settings.INSTALLED_APPS + ("crispy_forms",)) 42 | class CrispyFormsCompatTests(TestCase): 43 | def test_crispy_helper(self): 44 | # ensure the helper is present on the form 45 | self.assertTrue(hasattr(ArticleFilter().form, "helper")) 46 | 47 | def test_form_initialization(self): 48 | # ensure that crispy compat does not prematurely initialize the form 49 | self.assertFalse(hasattr(ArticleFilter(), "_form")) 50 | -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | # ensure package/conf is importable 2 | from django_filters.conf import DEFAULTS 3 | 4 | DATABASES = { 5 | "default": { 6 | "ENGINE": "django.db.backends.sqlite3", 7 | "NAME": ":memory:", 8 | }, 9 | } 10 | 11 | INSTALLED_APPS = ( 12 | "django.contrib.contenttypes", 13 | "django.contrib.staticfiles", 14 | "django.contrib.auth", 15 | "rest_framework", 16 | "django_filters", 17 | "tests.rest_framework", 18 | "tests", 19 | ) 20 | 21 | MIDDLEWARE = [] 22 | 23 | ROOT_URLCONF = "tests.urls" 24 | 25 | USE_TZ = True 26 | 27 | TIME_ZONE = "UTC" 28 | 29 | SECRET_KEY = "foobar" 30 | 31 | TEMPLATES = [ 32 | { 33 | "BACKEND": "django.template.backends.django.DjangoTemplates", 34 | "APP_DIRS": True, 35 | } 36 | ] 37 | 38 | 39 | STATIC_URL = "/static/" 40 | 41 | 42 | # XMLTestRunner output 43 | TEST_OUTPUT_DIR = ".xmlcoverage" 44 | 45 | 46 | # help verify that DEFAULTS is importable from conf. 47 | def FILTERS_VERBOSE_LOOKUPS(): 48 | return DEFAULTS["VERBOSE_LOOKUPS"] 49 | 50 | 51 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 52 | -------------------------------------------------------------------------------- /tests/templates/tests/book_filter.html: -------------------------------------------------------------------------------- 1 | {{ filter.form }} 2 | 3 | {% for obj in object_list %} 4 | {{ obj }} 5 | {% endfor %} 6 | -------------------------------------------------------------------------------- /tests/test_conf.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | from django.test import TestCase, override_settings 4 | 5 | from django_filters.conf import is_callable, settings 6 | 7 | 8 | class DefaultSettingsTests(TestCase): 9 | def test_verbose_lookups(self): 10 | self.assertIsInstance(settings.VERBOSE_LOOKUPS, dict) 11 | self.assertIn("exact", settings.VERBOSE_LOOKUPS) 12 | 13 | def test_default_lookup_expr(self): 14 | self.assertEqual(settings.DEFAULT_LOOKUP_EXPR, "exact") 15 | 16 | def test_disable_help_text(self): 17 | self.assertFalse(settings.DISABLE_HELP_TEXT) 18 | 19 | def test_empty_choice_label(self): 20 | self.assertEqual(settings.EMPTY_CHOICE_LABEL, "---------") 21 | 22 | def test_null_choice_label(self): 23 | self.assertIsNone(settings.NULL_CHOICE_LABEL) 24 | 25 | def test_null_choice_value(self): 26 | self.assertEqual(settings.NULL_CHOICE_VALUE, "null") 27 | 28 | 29 | class OverrideSettingsTests(TestCase): 30 | def test_attribute_override(self): 31 | self.assertIsInstance(settings.VERBOSE_LOOKUPS, dict) 32 | 33 | original = settings.VERBOSE_LOOKUPS 34 | 35 | with override_settings(FILTERS_VERBOSE_LOOKUPS=None): 36 | self.assertIsNone(settings.VERBOSE_LOOKUPS) 37 | 38 | self.assertIs(settings.VERBOSE_LOOKUPS, original) 39 | 40 | def test_missing_attribute_override(self): 41 | # ensure that changed setting behaves correctly when 42 | # not originally present in the user's settings. 43 | from django.conf import settings as dj_settings 44 | 45 | self.assertFalse(hasattr(dj_settings, "FILTERS_DISABLE_HELP_TEXT")) 46 | 47 | # Default value 48 | self.assertFalse(settings.DISABLE_HELP_TEXT) 49 | 50 | with override_settings(FILTERS_DISABLE_HELP_TEXT=True): 51 | self.assertTrue(settings.DISABLE_HELP_TEXT) 52 | 53 | # Revert to default 54 | self.assertFalse(settings.DISABLE_HELP_TEXT) 55 | 56 | def test_non_filters_setting(self): 57 | self.assertFalse(hasattr(settings, "USE_TZ")) 58 | 59 | with override_settings(USE_TZ=False): 60 | self.assertFalse(hasattr(settings, "USE_TZ")) 61 | 62 | self.assertFalse(hasattr(settings, "USE_TZ")) 63 | 64 | def test_non_existent_setting(self): 65 | self.assertFalse(hasattr(settings, "FILTERS_FOOBAR")) 66 | self.assertFalse(hasattr(settings, "FOOBAR")) 67 | 68 | with override_settings(FILTERS_FOOBAR="blah"): 69 | self.assertFalse(hasattr(settings, "FILTERS_FOOBAR")) 70 | self.assertFalse(hasattr(settings, "FOOBAR")) 71 | 72 | self.assertFalse(hasattr(settings, "FILTERS_FOOBAR")) 73 | self.assertFalse(hasattr(settings, "FOOBAR")) 74 | 75 | 76 | class IsCallableTests(TestCase): 77 | def test_behavior(self): 78 | def func(): 79 | pass 80 | 81 | class Class: 82 | def __call__(self): 83 | pass 84 | 85 | def method(self): 86 | pass 87 | 88 | c = Class() 89 | 90 | self.assertTrue(is_callable(func)) 91 | self.assertFalse(is_callable(Class)) 92 | self.assertTrue(is_callable(c)) 93 | self.assertTrue(is_callable(c.method)) 94 | 95 | 96 | class SettingsObjectTestCase(TestCase): 97 | @mock.patch("django_filters.conf.DEPRECATED_SETTINGS", ["TEST_123"]) 98 | @mock.patch.dict("django_filters.conf.DEFAULTS", {"TEST_123": True}) 99 | def test_get_setting_deprecated(self): 100 | with override_settings(FILTERS_TEST_123=True): 101 | with self.assertWarns(DeprecationWarning): 102 | settings.change_setting("FILTERS_TEST_123", True, True) 103 | test_setting = settings.get_setting("TEST_123") 104 | self.assertTrue(test_setting) 105 | -------------------------------------------------------------------------------- /tests/test_forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.test import TestCase, override_settings 3 | 4 | from django_filters.filters import CharFilter, ChoiceFilter 5 | from django_filters.filterset import FilterSet 6 | 7 | from .models import MANAGER, REGULAR, STATUS_CHOICES, Book, ManagerGroup, User 8 | 9 | 10 | class FilterSetFormTests(TestCase): 11 | def test_form_from_empty_filterset(self): 12 | class F(FilterSet): 13 | pass 14 | 15 | f = F(queryset=Book.objects.all()).form 16 | self.assertIsInstance(f, forms.Form) 17 | 18 | def test_form(self): 19 | class F(FilterSet): 20 | class Meta: 21 | model = Book 22 | fields = ("title",) 23 | 24 | f = F().form 25 | self.assertIsInstance(f, forms.Form) 26 | self.assertEqual(list(f.fields), ["title"]) 27 | 28 | def test_custom_form(self): 29 | class MyForm(forms.Form): 30 | pass 31 | 32 | class F(FilterSet): 33 | class Meta: 34 | model = Book 35 | fields = "__all__" 36 | form = MyForm 37 | 38 | f = F().form 39 | self.assertIsInstance(f, MyForm) 40 | 41 | def test_form_prefix(self): 42 | class F(FilterSet): 43 | class Meta: 44 | model = Book 45 | fields = ("title",) 46 | 47 | f = F().form 48 | self.assertIsNone(f.prefix) 49 | 50 | f = F(prefix="prefix").form 51 | self.assertEqual(f.prefix, "prefix") 52 | 53 | def test_form_fields(self): 54 | class F(FilterSet): 55 | class Meta: 56 | model = User 57 | fields = ["status"] 58 | 59 | f = F().form 60 | self.assertEqual(len(f.fields), 1) 61 | self.assertIn("status", f.fields) 62 | self.assertSequenceEqual( 63 | list(f.fields["status"].choices), (("", "---------"),) + STATUS_CHOICES 64 | ) 65 | 66 | def test_form_fields_exclusion(self): 67 | class F(FilterSet): 68 | title = CharFilter(exclude=True) 69 | 70 | class Meta: 71 | model = Book 72 | fields = ("title",) 73 | 74 | f = F().form 75 | self.assertEqual(f.fields["title"].label, "Exclude title") 76 | 77 | def test_complex_form_fields(self): 78 | class F(FilterSet): 79 | username = CharFilter(label="Filter for users with username") 80 | exclude_username = CharFilter( 81 | field_name="username", lookup_expr="iexact", exclude=True 82 | ) 83 | 84 | class Meta: 85 | model = User 86 | fields = { 87 | "status": ["exact", "lt", "gt"], 88 | "favorite_books__title": ["iexact", "in"], 89 | "manager_of__users__username": ["exact"], 90 | } 91 | 92 | fields = F().form.fields 93 | self.assertEqual(fields["username"].label, "Filter for users with username") 94 | self.assertEqual(fields["exclude_username"].label, "Exclude username") 95 | self.assertEqual(fields["status"].label, "Status") 96 | self.assertEqual(fields["status__lt"].label, "Status is less than") 97 | self.assertEqual(fields["status__gt"].label, "Status is greater than") 98 | self.assertEqual( 99 | fields["favorite_books__title__iexact"].label, "Favorite books title" 100 | ) 101 | self.assertEqual( 102 | fields["favorite_books__title__in"].label, "Favorite books title is in" 103 | ) 104 | self.assertEqual( 105 | fields["manager_of__users__username"].label, "Manager of users username" 106 | ) 107 | 108 | def test_form_fields_using_widget(self): 109 | class F(FilterSet): 110 | status = ChoiceFilter( 111 | widget=forms.RadioSelect, choices=STATUS_CHOICES, empty_label=None 112 | ) 113 | 114 | class Meta: 115 | model = User 116 | fields = ["status", "username"] 117 | 118 | f = F().form 119 | self.assertEqual(len(f.fields), 2) 120 | self.assertIn("status", f.fields) 121 | self.assertIn("username", f.fields) 122 | self.assertSequenceEqual(list(f.fields["status"].choices), STATUS_CHOICES) 123 | self.assertIsInstance(f.fields["status"].widget, forms.RadioSelect) 124 | 125 | def test_form_field_with_custom_label(self): 126 | class F(FilterSet): 127 | title = CharFilter(label="Book title") 128 | 129 | class Meta: 130 | model = Book 131 | fields = ("title",) 132 | 133 | f = F().form 134 | self.assertEqual(f.fields["title"].label, "Book title") 135 | self.assertEqual(f["title"].label, "Book title") 136 | 137 | def test_form_field_with_manual_name(self): 138 | class F(FilterSet): 139 | book_title = CharFilter(field_name="title") 140 | 141 | class Meta: 142 | model = Book 143 | fields = ("book_title",) 144 | 145 | f = F().form 146 | self.assertEqual(f.fields["book_title"].label, "Title") 147 | self.assertEqual(f["book_title"].label, "Title") 148 | 149 | def test_form_field_with_manual_name_and_label(self): 150 | class F(FilterSet): 151 | f1 = CharFilter(field_name="title", label="Book title") 152 | 153 | class Meta: 154 | model = Book 155 | fields = ("f1",) 156 | 157 | f = F().form 158 | self.assertEqual(f.fields["f1"].label, "Book title") 159 | self.assertEqual(f["f1"].label, "Book title") 160 | 161 | def test_filter_with_initial(self): 162 | class F(FilterSet): 163 | status = ChoiceFilter(choices=STATUS_CHOICES, initial=1) 164 | 165 | class Meta: 166 | model = User 167 | fields = ["status"] 168 | 169 | f = F().form 170 | self.assertEqual(f.fields["status"].initial, 1) 171 | 172 | def test_form_is_not_bound(self): 173 | class F(FilterSet): 174 | class Meta: 175 | model = Book 176 | fields = ("title",) 177 | 178 | f = F().form 179 | self.assertFalse(f.is_bound) 180 | self.assertEqual(f.data, {}) 181 | 182 | def test_form_is_bound(self): 183 | class F(FilterSet): 184 | class Meta: 185 | model = Book 186 | fields = ("title",) 187 | 188 | f = F({"title": "Some book"}).form 189 | self.assertTrue(f.is_bound) 190 | self.assertEqual(f.data, {"title": "Some book"}) 191 | 192 | def test_limit_choices_to(self): 193 | User.objects.create(username="inactive", is_active=False, status=REGULAR) 194 | u2 = User.objects.create(username="active", is_active=True, status=REGULAR) 195 | u3 = User.objects.create(username="manager", is_active=False, status=MANAGER) 196 | 197 | class F(FilterSet): 198 | class Meta: 199 | model = ManagerGroup 200 | fields = ["users", "manager"] 201 | 202 | f = F().form 203 | self.assertEqual(list(f.fields["users"].choices), [(u2.pk, "active")]) 204 | self.assertEqual( 205 | list(f.fields["manager"].choices), [("", "---------"), (u3.pk, "manager")] 206 | ) 207 | 208 | def test_disabled_help_text(self): 209 | class F(FilterSet): 210 | class Meta: 211 | model = Book 212 | fields = { 213 | # 'in' lookups are CSV-based, which have a `help_text`. 214 | "title": ["in"] 215 | } 216 | 217 | self.assertEqual( 218 | F().form.fields["title__in"].help_text, 219 | "Multiple values may be separated by commas.", 220 | ) 221 | 222 | with override_settings(FILTERS_DISABLE_HELP_TEXT=True): 223 | 224 | self.assertEqual(F().form.fields["title__in"].help_text, "") 225 | 226 | 227 | class FilterSetValidityTests(TestCase): 228 | class F(FilterSet): 229 | class Meta: 230 | model = Book 231 | fields = ["title", "price"] 232 | 233 | def test_not_bound(self): 234 | f = self.F() 235 | 236 | self.assertFalse(f.is_bound) 237 | self.assertFalse(f.is_valid()) 238 | self.assertEqual(f.data, {}) 239 | self.assertEqual(f.errors, {}) 240 | 241 | def test_is_bound_and_valid(self): 242 | f = self.F({"title": "Some book"}) 243 | 244 | self.assertTrue(f.is_bound) 245 | self.assertTrue(f.is_valid()) 246 | self.assertEqual(f.data, {"title": "Some book"}) 247 | self.assertEqual(f.errors, {}) 248 | 249 | def test_is_bound_and_not_valid(self): 250 | f = self.F({"price": "four dollars"}) 251 | 252 | self.assertTrue(f.is_bound) 253 | self.assertFalse(f.is_valid()) 254 | self.assertEqual(f.data, {"price": "four dollars"}) 255 | self.assertEqual(f.errors, {"price": ["Enter a number."]}) 256 | 257 | def test_number_filter_max_value_validation(self): 258 | class F(FilterSet): 259 | class Meta: 260 | model = Book 261 | fields = ["average_rating"] 262 | 263 | f = F({"average_rating": "1E1001"}) 264 | self.assertTrue(f.is_bound) 265 | self.assertFalse(f.is_valid()) 266 | self.assertEqual( 267 | f.errors, 268 | {"average_rating": ["Ensure this value is less than or equal to 1e+50."]}, 269 | ) 270 | -------------------------------------------------------------------------------- /tests/test_views.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ImproperlyConfigured 2 | from django.test import TestCase, override_settings 3 | from django.test.client import RequestFactory 4 | from django.utils import html 5 | 6 | from django_filters.filterset import FilterSet, filterset_factory 7 | from django_filters.views import FilterView 8 | 9 | from .models import Book 10 | 11 | 12 | @override_settings(ROOT_URLCONF="tests.urls") 13 | class GenericViewTestCase(TestCase): 14 | def setUp(self): 15 | Book.objects.create(title="Ender's Game", price="1.00", average_rating=3.0) 16 | Book.objects.create(title="Rainbow Six", price="1.00", average_rating=3.0) 17 | Book.objects.create(title="Snowcrash", price="1.00", average_rating=3.0) 18 | 19 | 20 | class GenericClassBasedViewTests(GenericViewTestCase): 21 | base_url = "/books/" 22 | 23 | def test_view(self): 24 | response = self.client.get(self.base_url) 25 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 26 | self.assertContains(response, html.escape(b)) 27 | 28 | def test_view_filtering_on_title(self): 29 | response = self.client.get(self.base_url + "?title=Snowcrash") 30 | for b in ["Ender's Game", "Rainbow Six"]: 31 | self.assertNotContains(response, html.escape(b)) 32 | self.assertContains(response, "Snowcrash") 33 | 34 | def test_view_with_filterset_not_model(self): 35 | factory = RequestFactory() 36 | request = factory.get(self.base_url) 37 | filterset = filterset_factory(Book) 38 | view = FilterView.as_view(filterset_class=filterset) 39 | response = view(request) 40 | self.assertEqual(response.status_code, 200) 41 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 42 | self.assertContains(response, html.escape(b)) 43 | 44 | def test_view_with_model_no_filterset(self): 45 | factory = RequestFactory() 46 | request = factory.get(self.base_url) 47 | view = FilterView.as_view(model=Book) 48 | response = view(request) 49 | self.assertEqual(response.status_code, 200) 50 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 51 | self.assertContains(response, html.escape(b)) 52 | 53 | def test_view_with_model_and_fields_no_filterset(self): 54 | factory = RequestFactory() 55 | request = factory.get(self.base_url + "?price=1.0") 56 | view = FilterView.as_view(model=Book, filterset_fields=["price"]) 57 | 58 | # filtering only by price 59 | response = view(request) 60 | self.assertEqual(response.status_code, 200) 61 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 62 | self.assertContains(response, html.escape(b)) 63 | 64 | # not filtering by title 65 | request = factory.get(self.base_url + "?title=Snowcrash") 66 | response = view(request) 67 | self.assertEqual(response.status_code, 200) 68 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 69 | self.assertContains(response, html.escape(b)) 70 | 71 | def test_view_with_strict_errors(self): 72 | factory = RequestFactory() 73 | request = factory.get(self.base_url + "?title=Snowcrash&price=four dollars") 74 | view = FilterView.as_view(model=Book) 75 | response = view(request) 76 | titles = [o.title for o in response.context_data["object_list"]] 77 | 78 | self.assertEqual(response.status_code, 200) 79 | self.assertEqual(titles, []) 80 | 81 | def test_view_with_non_strict_errors(self): 82 | factory = RequestFactory() 83 | request = factory.get(self.base_url + "?title=Snowcrash&price=four dollars") 84 | view = FilterView.as_view(model=Book, strict=False) 85 | response = view(request) 86 | titles = [o.title for o in response.context_data["object_list"]] 87 | 88 | self.assertEqual(response.status_code, 200) 89 | self.assertEqual( 90 | titles, 91 | ["Snowcrash"], 92 | ) 93 | 94 | def test_view_without_filterset_or_model(self): 95 | factory = RequestFactory() 96 | request = factory.get(self.base_url) 97 | view = FilterView.as_view() 98 | with self.assertRaises(ImproperlyConfigured): 99 | view(request) 100 | 101 | def test_view_with_bad_filterset(self): 102 | class MyFilterSet(FilterSet): 103 | pass 104 | 105 | factory = RequestFactory() 106 | request = factory.get(self.base_url) 107 | view = FilterView.as_view(filterset_class=MyFilterSet) 108 | with self.assertRaises(ImproperlyConfigured): 109 | view(request) 110 | 111 | def test_view_with_unbound_filter_form_returns_initial_queryset(self): 112 | factory = RequestFactory() 113 | request = factory.get(self.base_url) 114 | 115 | queryset = Book.objects.filter(title="Snowcrash") 116 | view = FilterView.as_view(model=Book, queryset=queryset) 117 | 118 | response = view(request) 119 | titles = [o.title for o in response.context_data["object_list"]] 120 | 121 | self.assertEqual(response.status_code, 200) 122 | self.assertEqual(titles, ["Snowcrash"]) 123 | 124 | 125 | class GenericFunctionalViewTests(GenericViewTestCase): 126 | base_url = "/books-legacy/" 127 | 128 | def test_view(self): 129 | response = self.client.get(self.base_url) 130 | for b in ["Ender's Game", "Rainbow Six", "Snowcrash"]: 131 | self.assertContains(response, html.escape(b)) 132 | # extra context 133 | self.assertEqual(response.context_data["foo"], "bar") 134 | self.assertEqual(response.context_data["bar"], "foo") 135 | 136 | def test_view_filtering_on_price(self): 137 | response = self.client.get(self.base_url + "?title=Snowcrash") 138 | for b in ["Ender's Game", "Rainbow Six"]: 139 | self.assertNotContains(response, html.escape(b)) 140 | self.assertContains(response, "Snowcrash") 141 | -------------------------------------------------------------------------------- /tests/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from django_filters.views import FilterView, object_filter 4 | 5 | from .models import Book 6 | 7 | 8 | def _foo(): 9 | return "bar" 10 | 11 | 12 | urlpatterns = [ 13 | path( 14 | "books-legacy/", 15 | object_filter, 16 | {"model": Book, "extra_context": {"foo": _foo, "bar": "foo"}}, 17 | ), 18 | path("books/", FilterView.as_view(model=Book)), 19 | ] 20 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | from django.db import models 4 | 5 | 6 | class QuerySet(models.QuerySet): 7 | def __bool__(self): 8 | return True 9 | 10 | 11 | class MockQuerySet: 12 | """ 13 | Generate a mock that is suitably similar to a QuerySet 14 | """ 15 | 16 | def __new__(self): 17 | m = mock.Mock(spec_set=QuerySet()) 18 | m.filter.return_value = m 19 | m.all.return_value = m 20 | return m 21 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | {py39, py310, py311, py312}-django42, 4 | {py310, py311, py312}-django50, 5 | {py310, py311, py312, py313}-django51, 6 | {py310, py311, py312, py313}-django52, 7 | {py312, py313}-latest, 8 | isort,lint,docs,warnings, 9 | isolated_build = true 10 | 11 | 12 | [latest] 13 | deps = 14 | https://github.com/django/django/archive/main.tar.gz 15 | https://github.com/encode/django-rest-framework/archive/master.tar.gz 16 | 17 | [testenv] 18 | commands = coverage run --parallel-mode --source django_filters ./runtests.py --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner {posargs} 19 | setenv = 20 | PYTHONDONTWRITEBYTECODE=1 21 | deps = 22 | django42: Django>=4.2,<5.0 23 | django50: Django>=5.0,<5.1 24 | django51: Django>=5.1,<5.2 25 | django52: Django>=5.2a1,<6.0 26 | !latest: djangorestframework 27 | latest: {[latest]deps} 28 | -r requirements/test-ci.txt 29 | 30 | [testenv:isort] 31 | commands = isort --check-only --diff django_filters tests {posargs} 32 | deps = isort 33 | 34 | [testenv:lint] 35 | commands = flake8 django_filters tests {posargs} 36 | deps = flake8 37 | 38 | [testenv:docs] 39 | commands = sphinx-build -WE docs _docs 40 | deps = 41 | -rrequirements/docs.txt 42 | 43 | [testenv:warnings] 44 | ignore_outcome = True 45 | unignore_outcomes = True 46 | commands = python -Werror ./runtests.py --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner {posargs} 47 | deps = 48 | {[latest]deps} 49 | -rrequirements/test-ci.txt 50 | --------------------------------------------------------------------------------