pax_global_header00006660000000000000000000000064143437753720014531gustar00rootroot0000000000000052 comment=8bd37e65c65b444cbc38e2360dfbd499a4b0c877 django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/000077500000000000000000000000001434377537200215125ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.flake8000066400000000000000000000001741434377537200226670ustar00rootroot00000000000000[flake8] ignore = E203, E266, E501, W503 max-line-length = 80 max-complexity = 18 select = B,C,E,F,W,T4,B9 exclude = tests/*django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.github/000077500000000000000000000000001434377537200230525ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.github/workflows/000077500000000000000000000000001434377537200251075ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.github/workflows/test-mysql.yml000066400000000000000000000034531434377537200277610ustar00rootroot00000000000000name: test-mysql on: push: branches: - main pull_request: branches: - main jobs: test-mysql: runs-on: ubuntu-20.04 strategy: max-parallel: 4 matrix: python-version: [3.9] django-version: [2.2.13, 3.0.7, 3.1.2, 3.2.4] include: - python-version: "3.10" django-version: "4.0.4" env: MYSQL_PASSWORD: mysql MYSQL_USER: mysql MYSQL_DATABASE: test_db MYSQL_HOST: 127.0.0.1 TEST_WITH_MYSQL: true services: mysql: image: mysql ports: ['3306:3306'] env: MYSQL_PASSWORD: mysql MYSQL_ROOT_PASSWORD: mysql MYSQL_USER: mysql MYSQL_DATABASE: test_db options: >- --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 10s --health-retries 5 steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install tooling run: | python -mpip install poetry codecov - name: Install mysqlclient run: | poetry add mysqlclient - name: Install dependencies run: | poetry install - name: Setup Django ${{ matrix.django-version }} run: | poetry run pip install django==${{ matrix.django-version }} - name: Lint with flake run: | poetry run flake8 django_hashids - name: Test with pytest run: | poetry run py.test --cov=./django_hashids/ --no-migrations - name: Upload coverage to codecov run: | codecov env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.github/workflows/test-pg.yml000066400000000000000000000034341434377537200272210ustar00rootroot00000000000000name: test-pg on: push: branches: - main pull_request: branches: - main jobs: test-pg: runs-on: ubuntu-20.04 strategy: max-parallel: 4 matrix: python-version: [3.9] django-version: [1.11.29, 2.2.13, 3.0.7, 3.1.2, 3.2.4] include: - python-version: "3.10" django-version: "4.0.4" env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres POSTGRES_DB: postgres POSTGRES_HOST: 127.0.0.1 TEST_WITH_PG: true services: postgres: image: postgres ports: ['5432:5432'] env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres POSTGRES_DB: postgres options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install tooling run: | python -mpip install poetry codecov - name: Install psycopg2 run: | poetry add psycopg2-binary - name: Install dependencies run: | poetry install - name: Setup Django ${{ matrix.django-version }} run: | poetry run pip install django==${{ matrix.django-version }} - name: Lint with flake run: | poetry run flake8 django_hashids - name: Test with pytest run: | poetry run py.test --cov=./django_hashids/ --no-migrations - name: Upload coverage to codecov run: | codecov env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.github/workflows/test.yml000066400000000000000000000023651434377537200266170ustar00rootroot00000000000000name: test on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-20.04 strategy: max-parallel: 4 matrix: python-version: ["3.6", "3.7", "3.8", "3.9"] django-version: ["1.11.29", "2.2.13", "3.0.7", "3.1.2", "3.2.4"] include: - python-version: "3.10" django-version: "4.0.4" steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install tooling run: | python -mpip install poetry codecov; python -mpip install six - name: Install dependencies run: | poetry install - name: Setup Django ${{ matrix.django-version }} run: | poetry run pip install django==${{ matrix.django-version }} - name: Lint with flake run: | poetry run flake8 django_hashids - name: Test with pytest run: | poetry run py.test --cov=./django_hashids/ - name: Upload coverage to codecov run: | codecov env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/.gitignore000066400000000000000000000067671434377537200235220ustar00rootroot00000000000000# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,django # Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,django ### Django ### *.log *.pot *.pyc __pycache__/ local_settings.py db.sqlite3 db.sqlite3-journal media # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ # in your Git repository. Update and uncomment the following line accordingly. # /staticfiles/ ### Django.Python Stack ### # Byte-compiled / optimized / DLL files *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo # Django stuff: # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ ### Python ### # Byte-compiled / optimized / DLL files # C extensions # Distribution / packaging # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. # Installer logs # Unit test / coverage reports # Translations # Django stuff: # Flask stuff: # Scrapy stuff: # Sphinx documentation # PyBuilder # Jupyter Notebook # IPython # pyenv # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. # PEP 582; used by e.g. github.com/David-OConnor/pyflow # Celery stuff # SageMath parsed files # Environments # Spyder project settings # Rope project settings # mkdocs documentation # mypy # Pyre type checker # pytype static type analyzer ### VisualStudioCode ### .vscode *.code-workspace ### VisualStudioCode Patch ### # Ignore all local history of files .history # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,django django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/LICENSE000066400000000000000000000020471434377537200225220ustar00rootroot00000000000000MIT License Copyright (c) 2020 Shen Li Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/README.md000066400000000000000000000072261434377537200230000ustar00rootroot00000000000000# Django Hashids [![Github Actions](https://github.com/ericls/django-hashids/workflows/test/badge.svg)](https://github.com/ericls/django-hashids/actions) [![Code Coverage](https://codecov.io/gh/ericls/django-hashids/branch/master/graph/badge.svg)](https://codecov.io/gh/ericls/django-hashids) [![Python Version](https://img.shields.io/pypi/pyversions/django-hashids.svg)](https://pypi.org/project/django-hashids/) [![PyPI Package](https://img.shields.io/pypi/v/django-hashids.svg)](https://pypi.org/project/django-hashids/) [![License](https://img.shields.io/pypi/l/django-hashids.svg)](https://github.com/ericls/django-hashids/blob/master/LICENSE) django-hashids is a simple and non-intrusive hashids library for Django. It acts as a model field, but it does not touch the database or change the model. # Features - Proxy the internal model `pk` field without storing the value in the database. - Allows lookups and filtering by hashid string. - Can be used as sort key - Allows specifying a salt, min_length and alphabet globally - Supports custom salt, min_length, and alphabet per field - Supports Django REST Framework Serializers - Supports exact ID searches in Django Admin when field is specified in search_fields. - Supports common filtering lookups, such as __iexact, __contains, __icontains, though matching is the same as __exact. - Supports other lookups: isnull, gt, gte, lt and lte. # Install ```bash pip install django-hashids ``` `django-hashids` is tested with Django 1.11, 2.2, 3.0, 3.1, 3.2, 4.0 and python 3.6, 3.7, 3.8, 3.9, 3.10. # Usage Add `HashidsField` to any model ```python from django_hashids import HashidsField class TestModel(Model): hashid = HashidsField(real_field_name="id") ``` `TestModel.hashid` field will proxy `TestModel.id` field but all queries will return and receive hashids strings. `TestModel.id` will work as before. ## Examples ```python instance = TestModel.objects.create() instance2 = TestModel.objects.create() instance.id # 1 instance2.id # 2 # Allows access to the field instance.hashid # '1Z' instance2.hashid # '4x' # Allows querying by the field TestModel.objects.get(hashid="1Z") TestModel.objects.filter(hashid="1Z") TestModel.objects.filter(hashid__in=["1Z", "4x"]) TestModel.objects.filter(hashid__gt="1Z") # same as id__gt=1, would return instance 2 # Allows usage in queryset.values TestModel.objects.values_list("hashid", flat=True) # ["1Z", "4x"] TestModel.objects.filter(hashid__in=TestModel.objects.values("hashid")) ``` ## Config The folloing attributes can be added in settings file to set default arguments of `HashidsField`: 1. `DJANGO_HASHIDS_SALT`: default salt 2. `DJANGO_HASHIDS_MIN_LENGTH`: default minimum length 3. `DJANGO_HASHIDS_ALPHABET`: default alphabet `HashidsField` does not reqiure any arguments but the followinig arguments can be supplied to modify its behavior. | Name | Description | | ------------------ | :-------------------------------------------------------: | | `real_field_name` | The proxied field name | | `hashids_instance` | The hashids instance used to encode/decode for this field | | `salt` | The salt used for this field to generate hashids | | `min_length` | The minimum length of hashids generated for this field | | `alphabet` | The alphabet used by this field to generate hashids | The argument `hashids_instance` is mutually exclusive to `salt`, `min_length` and `alphabet`. See [hashids-python](https://github.com/davidaurelio/hashids-python) for more info about the arguments. Some common Model arguments such as `verbose_name` are also supported. django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/django_hashids/000077500000000000000000000000001434377537200244575ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/django_hashids/__init__.py000066400000000000000000000000741434377537200265710ustar00rootroot00000000000000from .field import HashidsField __all__ = ["HashidsField"] django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/django_hashids/exceptions.py000066400000000000000000000001411434377537200272060ustar00rootroot00000000000000class ConfigError(Exception): pass class RealFieldDoesNotExistError(ConfigError): pass django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/django_hashids/field.py000066400000000000000000000121501434377537200261130ustar00rootroot00000000000000from django.conf import settings from django.core.exceptions import FieldError from django.db.models import Field from django.utils.functional import cached_property from hashids import Hashids from .exceptions import ConfigError, RealFieldDoesNotExistError class HashidsField(Field): concrete = False allowed_lookups = ("exact", "iexact", "in", "gt", "gte", "lt", "lte", "isnull") # these should never change, even when Hashids updates ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" MIN_LENGTH = 0 def __init__( self, real_field_name="id", *args, hashids_instance=None, salt=None, alphabet=None, min_length=None, **kwargs ): kwargs.pop("editable", None) super().__init__(*args, editable=False, **kwargs) self.real_field_name = real_field_name self.salt = salt self.min_length = min_length self.alphabet = alphabet self._explicit_hashids_instance = hashids_instance self.hashids_instance = None self.attached_to_model = None def contribute_to_class(self, cls, name): self.attname = name self.name = name if getattr(self, "model", None) is None and cls._meta.abstract is False: self.model = cls if self.attached_to_model is not None: # pragma: no cover raise FieldError( "Field '%s' is already attached to another model(%s)." % (self.name, self.attached_to_model) ) self.attached_to_model = cls self.column = None if self.verbose_name is None: self.verbose_name = self.name setattr(cls, name, self) cls._meta.add_field(self, private=True) self.hashids_instance = self.get_hashid_instance() def get_hashid_instance(self): if self._explicit_hashids_instance: if ( self.salt is not None or self.alphabet is not None or self.min_length is not None ): raise ConfigError( "if hashids_instance is set, salt, min_length and alphabet should not be set" ) return self._explicit_hashids_instance salt = self.salt min_length = self.min_length alphabet = self.alphabet if salt is None: salt = getattr(settings, "DJANGO_HASHIDS_SALT") if min_length is None: min_length = ( getattr(settings, "DJANGO_HASHIDS_MIN_LENGTH", None) or self.MIN_LENGTH ) if alphabet is None: alphabet = ( getattr(settings, "DJANGO_HASHIDS_ALPHABET", None) or self.ALPHABET ) return Hashids(salt=salt, min_length=min_length, alphabet=alphabet) def get_prep_value(self, value): decoded_values = self.hashids_instance.decode(value) if not decoded_values: return None return decoded_values[0] def from_db_value(self, value, expression, connection, *args): return self.hashids_instance.encode(value) def get_col(self, alias, output_field=None): if output_field is None: output_field = self col = self.real_col.get_col(alias, output_field) return col @cached_property def real_col(self): # `maybe_field` is intended for `pk`, which does not appear in `_meta.fields` maybe_field = getattr(self.attached_to_model._meta, self.real_field_name, None) if isinstance(maybe_field, Field): return maybe_field try: field = next( col for col in self.attached_to_model._meta.fields if col.name == self.real_field_name or col.attname == self.real_field_name ) except StopIteration: raise RealFieldDoesNotExistError( "%s(%s) can't find real field using real_field_name: %s" % (self.__class__.__name__, self, self.real_field_name) ) return field def __get__(self, instance, name=None): if not instance: return self real_value = getattr(instance, self.real_field_name, None) # the instance is not saved yet? if real_value is None: return "" assert isinstance(real_value, int) return self.hashids_instance.encode(real_value) def __set__(self, instance, value): pass def __deepcopy__(self, memo=None): new_instance = super().__deepcopy__(memo) for attr in ("hashids_instance", "attached_to_model"): if hasattr(new_instance, attr): setattr(new_instance, attr, None) # remove cached values from cached_property for key in ("real_col",): if key in new_instance.__dict__: del new_instance.__dict__[key] # pragma: no cover return new_instance @classmethod def get_lookups(cls): all_lookups = super().get_lookups() return {k: all_lookups[k] for k in cls.allowed_lookups} HashidField = HashidsField django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/poetry.lock000066400000000000000000001020001434377537200236770ustar00rootroot00000000000000[[package]] name = "asgiref" version = "3.4.1" description = "ASGI specs, helper code, and adapters" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] [[package]] name = "atomicwrites" version = "1.4.0" description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" version = "21.4.0" description = "Classes Without Boilerplate" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "black" version = "22.3.0" description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.6.2" [package.dependencies] click = ">=8.0.0" dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "click" version = "8.0.4" description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" version = "6.2" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "dataclasses" version = "0.8" description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false python-versions = ">=3.6, <3.7" [[package]] name = "django" version = "3.2.13" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] asgiref = ">=3.3.2,<4" pytz = "*" sqlparse = ">=0.2.2" [package.extras] argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] [[package]] name = "flake8" version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.7.0,<2.8.0" pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "hashids" version = "1.3.1" description = "Implements the hashids algorithm in python. For more information, visit http://hashids.org/" category = "main" optional = false python-versions = ">=2.7" [package.extras] test = ["pytest (>=2.1.0)"] [[package]] name = "importlib-metadata" version = "4.8.3" description = "Read metadata from Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] perf = ["ipython"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = "*" [[package]] name = "isort" version = "4.3.21" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] pipfile = ["pipreqs", "requirementslib"] pyproject = ["toml"] requirements = ["pipreqs", "pip-api"] xdg_home = ["appdirs (>=1.4.0)"] [[package]] name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = "*" [[package]] name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." category = "dev" optional = false python-versions = "*" [[package]] name = "packaging" version = "21.3" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pathspec" version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "platformdirs" version = "2.4.0" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.6" [package.extras] docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycodestyle" version = "2.7.0" description = "Python style guide checker" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pyflakes" version = "2.3.1" description = "passive checker of Python programs" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pyparsing" version = "3.0.7" description = "Python parsing module" category = "dev" optional = false python-versions = ">=3.6" [package.extras] diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] name = "pytest-cov" version = "3.0.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-django" version = "4.5.2" description = "A Django plugin for pytest." category = "dev" optional = false python-versions = ">=3.5" [package.dependencies] pytest = ">=5.4.0" [package.extras] docs = ["sphinx", "sphinx-rtd-theme"] testing = ["django", "django-configurations (>=2.0)"] [[package]] name = "pytz" version = "2022.1" description = "World timezone definitions, modern and historical" category = "dev" optional = false python-versions = "*" [[package]] name = "sqlparse" version = "0.4.2" description = "A non-validating SQL parser." category = "dev" optional = false python-versions = ">=3.5" [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomli" version = "1.2.3" description = "A lil' TOML parser" category = "dev" optional = false python-versions = ">=3.6" [[package]] name = "typed-ast" version = "1.5.3" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false python-versions = ">=3.6" [[package]] name = "typing-extensions" version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" category = "dev" optional = false python-versions = ">=3.6" [[package]] name = "zipp" version = "3.6.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" python-versions = ">=3.6.2,<4" content-hash = "a79733a0e4d251dee609457aec3827a53f3e161bc3cfee2771ea55996904be37" [metadata.files] asgiref = [ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] black = [ {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, ] click = [ {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, ] dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] django = [ {file = "Django-3.2.13-py3-none-any.whl", hash = "sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf"}, {file = "Django-3.2.13.tar.gz", hash = "sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6"}, ] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] hashids = [ {file = "hashids-1.3.1-py2.py3-none-any.whl", hash = "sha256:8bddd1acba501bfc9306e7e5a99a1667f4f2cacdc20cbd70bcc5ddfa5147c94c"}, {file = "hashids-1.3.1.tar.gz", hash = "sha256:6c3dc775e65efc2ce2c157a65acb776d634cb814598f406469abef00ae3f635c"}, ] importlib-metadata = [ {file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] platformdirs = [ {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pyparsing = [ {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, ] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] pytest-cov = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] pytest-django = [ {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, ] pytz = [ {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, ] sqlparse = [ {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomli = [ {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, ] typed-ast = [ {file = "typed_ast-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ad3b48cf2b487be140072fb86feff36801487d4abb7382bb1929aaac80638ea"}, {file = "typed_ast-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:542cd732351ba8235f20faa0fc7398946fe1a57f2cdb289e5497e1e7f48cfedb"}, {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc2c11ae59003d4a26dda637222d9ae924387f96acae9492df663843aefad55"}, {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd5df1313915dbd70eaaa88c19030b441742e8b05e6103c631c83b75e0435ccc"}, {file = "typed_ast-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:e34f9b9e61333ecb0f7d79c21c28aa5cd63bec15cb7e1310d7d3da6ce886bc9b"}, {file = "typed_ast-1.5.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f818c5b81966d4728fec14caa338e30a70dfc3da577984d38f97816c4b3071ec"}, {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3042bfc9ca118712c9809201f55355479cfcdc17449f9f8db5e744e9625c6805"}, {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fff9fdcce59dc61ec1b317bdb319f8f4e6b69ebbe61193ae0a60c5f9333dc49"}, {file = "typed_ast-1.5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8e0b8528838ffd426fea8d18bde4c73bcb4167218998cc8b9ee0a0f2bfe678a6"}, {file = "typed_ast-1.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ef1d96ad05a291f5c36895d86d1375c0ee70595b90f6bb5f5fdbee749b146db"}, {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed44e81517364cb5ba367e4f68fca01fba42a7a4690d40c07886586ac267d9b9"}, {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f60d9de0d087454c91b3999a296d0c4558c1666771e3460621875021bf899af9"}, {file = "typed_ast-1.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9e237e74fd321a55c90eee9bc5d44be976979ad38a29bbd734148295c1ce7617"}, {file = "typed_ast-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ee852185964744987609b40aee1d2eb81502ae63ee8eef614558f96a56c1902d"}, {file = "typed_ast-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27e46cdd01d6c3a0dd8f728b6a938a6751f7bd324817501c15fb056307f918c6"}, {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d64dabc6336ddc10373922a146fa2256043b3b43e61f28961caec2a5207c56d5"}, {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8cdf91b0c466a6c43f36c1964772918a2c04cfa83df8001ff32a89e357f8eb06"}, {file = "typed_ast-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:9cc9e1457e1feb06b075c8ef8aeb046a28ec351b1958b42c7c31c989c841403a"}, {file = "typed_ast-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e20d196815eeffb3d76b75223e8ffed124e65ee62097e4e73afb5fec6b993e7a"}, {file = "typed_ast-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:37e5349d1d5de2f4763d534ccb26809d1c24b180a477659a12c4bde9dd677d74"}, {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f1a27592fac87daa4e3f16538713d705599b0a27dfe25518b80b6b017f0a6d"}, {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8831479695eadc8b5ffed06fdfb3e424adc37962a75925668deeb503f446c0a3"}, {file = "typed_ast-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:20d5118e494478ef2d3a2702d964dae830aedd7b4d3b626d003eea526be18718"}, {file = "typed_ast-1.5.3.tar.gz", hash = "sha256:27f25232e2dd0edfe1f019d6bfaaf11e86e657d9bdb7b0956db95f560cceb2b3"}, ] typing-extensions = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] zipp = [ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/pyproject.toml000066400000000000000000000012301434377537200244220ustar00rootroot00000000000000[tool.poetry] name = "django-hashids" version = "0.7.0" readme = "README.md" description = "Non-intrusive hashids library for Django" homepage = "https://github.com/ericls/django-hashids" repository = "https://github.com/ericls/django-hashids" keywords = ["django", "hashids", "hashid"] authors = ["Shen Li "] license = "MIT" [tool.poetry.dependencies] python = ">=3.6.2,<4" hashids = ">=1.0.2" [tool.poetry.dev-dependencies] django = "^3.0.7" pytest = "^6.2.5" black = "^22.0.3" pytest-django = "^4.5.2" pytest-cov = "^3.0.0" flake8 = "^3.8.3" isort = "^4.3.21" [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/000077500000000000000000000000001434377537200226545ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/__init__.py000066400000000000000000000000001434377537200247530ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/settings.py000066400000000000000000000023171434377537200250710ustar00rootroot00000000000000import os SECRET_KEY = "1" DEBUG = True INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.auth", "tests.test_app", ] MIDDLEWARE = [] ROOT_URLCONF = "tests.urls" DJANGO_HASHIDS_SALT = "???!" DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", } } if os.environ.get("TEST_WITH_PG"): DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["POSTGRES_DB"], "USER": os.environ["POSTGRES_USER"], "PASSWORD": os.environ["POSTGRES_PASSWORD"], "HOST": os.environ["POSTGRES_HOST"], "PORT": "5432", } } elif os.environ.get("TEST_WITH_MYSQL"): DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": os.environ["MYSQL_DATABASE"], "USER": os.environ["MYSQL_USER"], "PASSWORD": os.environ["MYSQL_PASSWORD"], "HOST": os.environ["MYSQL_HOST"], "PORT": "3306", "TEST": { # the main database is also the test database "NAME": os.environ["MYSQL_DATABASE"], }, } } django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/test_app/000077500000000000000000000000001434377537200244735ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/test_app/__init__.py000066400000000000000000000000001434377537200265720ustar00rootroot00000000000000django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/test_app/models.py000066400000000000000000000022341434377537200263310ustar00rootroot00000000000000from django.contrib.auth.models import AbstractUser from django.db import models from django.db.models import Model from hashids import Hashids from django_hashids import HashidsField class TestModel(Model): hashid = HashidsField(real_field_name="id") class TestModelWithDifferentConfig(Model): hashid = HashidsField(salt="AAA", min_length=5, alphabet="OPQRST1234567890") this_hashids_instance = Hashids(salt="FOO") class TestModelWithOwnInstance(Model): hashid = HashidsField(hashids_instance=this_hashids_instance) class TestUser(AbstractUser): hashid = HashidsField(real_field_name="id") class TestUserRelated(Model): hashid = HashidsField(real_field_name="id") user = models.ForeignKey("TestUser", related_name="related", on_delete=models.CASCADE) class FirstSubClass(TestModel): pass class SecondSubClass(FirstSubClass): pass class TestAbstractModel(models.Model): hashid = HashidsField(real_field_name="id") class Meta: abstract = True class ModelA(TestAbstractModel): pass class ModelB(ModelA): pass class ModelUsingPKAsRealFieldName(Model): hashid = HashidsField(real_field_name="pk") django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/test_django_hashids.py000066400000000000000000000220161434377537200272330ustar00rootroot00000000000000import os import pytest from django import setup from django.db.models import ExpressionWrapper, F, IntegerField from django.test import override_settings from hashids import Hashids from django_hashids.exceptions import ConfigError, RealFieldDoesNotExistError os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" setup() pytestmark = pytest.mark.django_db def test_can_get_hashids(): from django.conf import settings from tests.test_app.models import TestModel instance = TestModel.objects.create() hashid = instance.hashid hashids_instance = Hashids(salt=settings.DJANGO_HASHIDS_SALT) assert hashids_instance.decode(hashid)[0] == instance.pk def test_can_get_field_from_model(): from tests.test_app.models import TestModel TestModel.hashid def test_can_use_per_field_config(): from tests.test_app.models import TestModelWithDifferentConfig instance = TestModelWithDifferentConfig.objects.create() hashid = instance.hashid hashids_instance = Hashids(salt="AAA", min_length=5, alphabet="OPQRST1234567890") assert hashids_instance.decode(hashid)[0] == instance.pk def test_can_use_per_field_instance(): from tests.test_app.models import TestModelWithOwnInstance, this_hashids_instance instance = TestModelWithOwnInstance.objects.create() assert this_hashids_instance.decode(instance.hashid)[0] == instance.pk def test_throws_when_setting_both_instance_and_config(): from django.db.models import Model from tests.test_app.models import this_hashids_instance from django_hashids import HashidsField with pytest.raises(ConfigError): class Foo(Model): class Meta: app_label = "tests.test_app" hash_id = HashidsField( salt="Anotherone", hashids_instance=this_hashids_instance ) def test_updates_when_changing_real_column_value(): from django.conf import settings from tests.test_app.models import TestModel instance = TestModel.objects.create() instance.id = 3 # works before saving hashids_instance = Hashids(salt=settings.DJANGO_HASHIDS_SALT) assert hashids_instance.decode(instance.hashid)[0] == 3 # works after saving instance.save() hashids_instance = Hashids(salt=settings.DJANGO_HASHIDS_SALT) assert hashids_instance.decode(instance.hashid)[0] == 3 def test_ignores_changes_to_value(): from django.conf import settings from tests.test_app.models import TestModel instance = TestModel.objects.create() instance.id = 3 instance.hashid = "FOO" hashids_instance = Hashids(salt=settings.DJANGO_HASHIDS_SALT) assert hashids_instance.decode(instance.hashid)[0] == 3 # works after saving instance.save() instance.hashid = "FOO" hashids_instance = Hashids(salt=settings.DJANGO_HASHIDS_SALT) assert hashids_instance.decode(instance.hashid)[0] == 3 def test_can_use_exact_lookup(): from tests.test_app.models import TestModel instance = TestModel.objects.create() got_instance = TestModel.objects.filter(hashid=instance.hashid).first() assert instance == got_instance # assert id field still works got_instance = TestModel.objects.filter(id=instance.id).first() assert instance == got_instance def test_can_use_in_lookup(): from tests.test_app.models import TestModel instance = TestModel.objects.create() instance2 = TestModel.objects.create() hashids = [instance.hashid, instance2.hashid] qs = TestModel.objects.filter(hashid__in=hashids) assert set([instance, instance2]) == set(qs) def test_can_use_lookup_when_value_does_not_exists(): # https://github.com/ericls/django-hashids/issues/4 from tests.test_app.models import TestModel # exact lookup instance = TestModel.objects.create() hashid = instance.hashid + "A" qs = TestModel.objects.filter(hashid=hashid) assert list(qs) == [] # lookup instance = TestModel.objects.create() instance2 = TestModel.objects.create() hashids = [instance.hashid + "A", instance2.hashid + "A"] qs = TestModel.objects.filter(hashid__in=hashids) assert list(qs) == [] def test_can_use_lt_gt_lte_gte_lookup(): from tests.test_app.models import TestModel instance = TestModel.objects.create() instance2 = TestModel.objects.create() qs = TestModel.objects.filter(hashid__lt=instance2.hashid) assert set([instance]) == set(qs) qs = TestModel.objects.filter(hashid__lte=instance2.hashid) assert set([instance, instance2]) == set(qs) qs = TestModel.objects.filter(hashid__gt=instance.hashid) assert set([instance2]) == set(qs) qs = TestModel.objects.filter(hashid__gte=instance.hashid) assert set([instance, instance2]) == set(qs) def test_can_get_values(): from tests.test_app.models import TestModel instance = TestModel.objects.create() instance2 = TestModel.objects.create() hashids = TestModel.objects.values("hashid") assert set([instance, instance2]) == set( TestModel.objects.filter(hashid__in=hashids) ) hashids = list(TestModel.objects.values_list("hashid", flat=True)) assert set([instance, instance2]) == set( TestModel.objects.filter(hashid__in=hashids) ) # assert id field still works ids = list(TestModel.objects.values_list("id", flat=True)) assert set([instance, instance2]) == set(TestModel.objects.filter(id__in=ids)) def test_can_select_as_integer(): from tests.test_app.models import TestModel instance = TestModel.objects.create() instance2 = TestModel.objects.create() integer_ids = list( TestModel.objects.annotate( hid=ExpressionWrapper(F("hashid"), output_field=IntegerField()) ).values_list("hid", flat=True) ) assert set([instance.id, instance2.id]) == set(integer_ids) @override_settings(DJANGO_HASHIDS_MIN_LENGTH=10) def test_can_use_min_length_from_settings(): from tests.test_app.models import TestModel TestModel.hashid.hashids_instance = None TestModel.hashid.hashids_instance = TestModel.hashid.get_hashid_instance() instance = TestModel.objects.create() assert len(instance.hashid) >= 10 @override_settings(DJANGO_HASHIDS_ALPHABET='!@#$%^&*(){}[]:"') def test_can_use_min_length_from_settings(): from tests.test_app.models import TestModel TestModel.hashid.hashids_instance = None TestModel.hashid.hashids_instance = TestModel.hashid.get_hashid_instance() instance = TestModel.objects.create() assert all(c in '!@#$%^&*(){}[]:"' for c in instance.hashid) def test_not_saved_instance(): from tests.test_app.models import TestModel instance = TestModel() assert instance.hashid == "" def test_create_user(): # https://github.com/ericls/django-hashids/issues/2 from tests.test_app.models import TestUser u = TestUser.objects.create_user("username", password="password") assert TestUser.hashid.hashids_instance.decode(u.hashid)[0] == u.id def test_multiple_level_inheritance(): # https://github.com/ericls/django-hashids/issues/25 from tests.test_app.models import SecondSubClass, FirstSubClass instance = SecondSubClass.objects.create() SecondSubClass.objects.filter(id=1).first() == SecondSubClass.objects.filter( hashid=instance.hashid ).first() instance = FirstSubClass.objects.create() FirstSubClass.objects.filter(id=1).first() == FirstSubClass.objects.filter( hashid=instance.hashid ).first() def test_multiple_level_inheritance_from_abstract_model(): # https://github.com/ericls/django-hashids/issues/25 from tests.test_app.models import ModelB, ModelA instance = ModelB.objects.create() ModelB.objects.filter(id=1).first() == ModelB.objects.filter( hashid=instance.hashid ).first() instance = ModelA.objects.create() ModelA.objects.filter(id=1).first() == ModelA.objects.filter( hashid=instance.hashid ).first() def test_related_queries(): from tests.test_app.models import TestUser, TestUserRelated u = TestUser.objects.create() r = TestUserRelated.objects.create(user=u) assert TestUserRelated.objects.filter(user__hashid=u.hashid).first() == r assert TestUser.objects.filter(related__hashid=r.hashid).first() == u def test_using_pk_as_real_field_name(): # https://github.com/ericls/django-hashids/issues/31 from tests.test_app.models import ModelUsingPKAsRealFieldName a = ModelUsingPKAsRealFieldName.objects.create() assert a.hashid assert ModelUsingPKAsRealFieldName.objects.get(hashid=a.hashid) == a assert ModelUsingPKAsRealFieldName.objects.get(hashid__lte=a.hashid) == a assert ( ModelUsingPKAsRealFieldName.objects.filter(hashid__lt=a.hashid).exists() is False ) def test_no_real_field_error_message(): from django.db.models import Model from django_hashids import HashidsField class Foo(Model): class Meta: app_label = "tests.test_app" hash_id = HashidsField(real_field_name="does_not_exist") with pytest.raises(RealFieldDoesNotExistError): Foo.objects.filter(hash_id="foo") django-hashids-8bd37e65c65b444cbc38e2360dfbd499a4b0c877/tests/urls.py000066400000000000000000000000211434377537200242040ustar00rootroot00000000000000urlpatterns = []