diff --git a/{{ cookiecutter.name }}/Makefile b/{{ cookiecutter.name }}/Makefile index c804626b..f6207192 100644 --- a/{{ cookiecutter.name }}/Makefile +++ b/{{ cookiecutter.name }}/Makefile @@ -3,18 +3,16 @@ SIMULTANEOS_TEST_JOBS = 4 manage = poetry run python src/manage.py fmt: - poetry run autoflake --in-place --remove-all-unused-imports --recursive src - poetry run isort src - poetry run black src + poetry run ruff check src --fix --unsafe-fixes + poetry run ruff format src poetry run toml-sort pyproject.toml lint: $(manage) check $(manage) makemigrations --check --dry-run --no-input - poetry run isort --check-only src - poetry run black --check src - poetry run flake8 src + poetry run ruff check src + poetry run ruff format --check src poetry run mypy src poetry run toml-sort pyproject.toml --check poetry run pymarkdown scan README.md diff --git a/{{ cookiecutter.name }}/README.md b/{{ cookiecutter.name }}/README.md index b70090a9..7ba4eabe 100644 --- a/{{ cookiecutter.name }}/README.md +++ b/{{ cookiecutter.name }}/README.md @@ -46,7 +46,7 @@ make test # run tests ### Style * Obey [django's style guide](https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#model-style). -* Configure your IDE to use [flake8](https://pypi.python.org/pypi/flake8) for checking your python code. To run our linters manualy, do `make lint`. +* Configure your IDE to use [ruff](https://pypi.org/project/ruff) for checking your Python code. To run our linters manually, do `make lint`. Feel free to [adjust](https://docs.astral.sh/ruff/configuration/) ruff [rules](https://docs.astral.sh/ruff/rules/) in `pyproject.toml` section `tool.ruff.lint` for your needs. * Prefer English over your native language in comments and commit messages. * Commit messages should contain the unique id of issue they are linked to (refs #100500). * Every model, service and model method should have a docstring. diff --git a/{{ cookiecutter.name }}/pyproject.toml b/{{ cookiecutter.name }}/pyproject.toml index ca6d2f47..2bcf5215 100644 --- a/{{ cookiecutter.name }}/pyproject.toml +++ b/{{ cookiecutter.name }}/pyproject.toml @@ -2,6 +2,9 @@ build-backend = "poetry.core.masonry.api" requires = ["poetry-core"] +[project] +requires-python = "~=3.11" + [tool.poetry] authors = ["Fedor Borshev "] description = "" @@ -34,27 +37,11 @@ sentry-sdk = "^2.11.0" whitenoise = "^6.7.0" [tool.poetry.group.dev.dependencies] -autoflake = "^2.2.1" -black = "^24.4.2" django-stubs = "^5.1.1" djangorestframework-stubs = "^3.15.0" dotenv-linter = "^0.5.0" -flake8-absolute-import = "^1.0.0.2" -flake8-bugbear = "^24.4.26" -flake8-cognitive-complexity = "^0.1.0" -flake8-django = "^1.4" -flake8-eradicate = "^1.5.0" -flake8-fixme = "^1.1.1" -flake8-pep3101 = "^2.1.0" -flake8-pie = "^0.16.0" -flake8-print = "^5.0.0" -flake8-printf-formatting = "^1.1.2" -flake8-pyproject = "^1.2.3" -flake8-variables-names = "^0.0.6" -flake8-walrus = "^1.2.0" freezegun = "^1.5.1" ipython = "^8.26.0" -isort = "^5.12.0" jedi = "^0.19.1" mixer = {extras = ["django"], version = "^7.2.2"} mypy = "^1.11.0" @@ -66,42 +53,11 @@ pytest-freezer = "^0.4.8" pytest-mock = "^3.12.0" pytest-randomly = "^3.15.0" pytest-xdist = "^3.6.1" +ruff = "^0.8.0" toml-sort = "^0.23.1" types-freezegun = "^1.1.10" types-pillow = "^10.2.0.20240520" -[tool.black] -exclude = ''' -/( - | migrations -)/ -''' -line_length = 160 - -[tool.flake8] -exclude = ["__pycache__", "migrations"] -ignore = [ - "E203", # whitespace before ':' - "E265", # block comment should start with '#' - "E501", # line too long ({} > {} characters) - "E704", # multiple statements on one line (def) - "F811", # redefinition of unused name from line {} - "PT001", # use @pytest.fixture() over @pytest.fixture - "SIM102", # use a single if-statement instead of nested if-statements - "SIM113", # use enumerate instead of manually incrementing a counter -] -inline-quotes = '"' - -[tool.isort] -include_trailing_comma = true -line_length = 160 -multi_line_output = 3 -skip = [ - "migrations", -] -src_paths = ["src", "tests"] -use_parentheses = true - [tool.pymarkdown.plugins.md013] enabled = false @@ -123,6 +79,118 @@ filterwarnings = [ python_files = ["test*.py"] pythonpath = ". src" +[tool.ruff] +exclude = ["__pycache__", "migrations"] +line-length = 160 +src = ["src"] + +[tool.ruff.lint] +ignore = [ + "A001", # variable `{}` is shadowing a Python builtin + "A002", # argument `{}` is shadowing a Python builtin + "A003", # class attribute `{}` is shadowing a Python builtin + "ANN401", # dynamically typed expressions (typing.Any) are disallowed in `{}` + "ARG002", # unused method argument: `{}` + "ARG005", # unused lambda argument: `{}` + "B018", # found useless expression. Either assign it to a variable or remove it + "B904", # within an `except` clause, raise exceptions with [...] + "C408", # unnecessary `dict` call (rewrite as a literal) + "D100", # missing docstring in public module + "D101", # missing docstring in public class + "D102", # missing docstring in public method + "D103", # missing docstring in public function + "D104", # missing docstring in public package + "D105", # missing docstring in magic method + "D106", # missing docstring in public nested class + "D107", # missing docstring in `__init__` + "D200", # one-line docstring should fit on one line + "D202", # no blank lines allowed after function docstring (found {}) + "D203", # 1 blank line required before class docstring + "D205", # 1 blank line required between summary line and description + "D209", # multi-line docstring closing quotes should be on a separate line + "D210", # no whitespaces allowed surrounding docstring text + "D212", # multi-line docstring summary should start at the first line + "D213", # multi-line docstring summary should start at the second line + "D400", # first line should end with a period + "D401", # first line of docstring should be in imperative mood: "{}" + "D404", # first word of the docstring should not be "This" + "D415", # first line should end with a period, question mark, or exclamation point + "DTZ001", # the use of `datetime.datetime()` without `tzinfo` argument is not allowed + "E501", # line too long ({} > {}) + "EM101", # exception must not use a string literal, assign to variable first + "EM102", # expection must not use an f-string literal, assign to variable first + "FBT001", # boolean-typed position argument in function definition + "FBT002", # boolean default position argument in function definition + "FBT003", # boolean positional value in function call + "INP001", # file `{}` is part of an implicit namespace package. Add an `__init__.py` + "INT001", # f-string is resolved before function call; consider `_("string %s") % arg` + "N802", # function name `{}` should be lowercase + "N803", # argument name `{}` should be lowercase + "N804", # first argument of a class method should be named `cls` + "N806", # variable `{}` in function should be lowercase + "N812", # lowercase `{}` imported as non-lowercase `{}` + "N818", # exception name `{}` should be named with an Error suffix + "N999", # invalid module name: '{}' + "PERF401", # use a list comprehension to create a transformed list + "PGH003", # use specific rule codes when ignoring type issues + "PGH004", # use specific rule codes when using `noqa` + "PT001", # use `@pytest.fixture()` over `@pytest.fixture` + "RET501", # do not explicitly `return None` in function if it is the only possible return value + "RET502", # do not implicitly `return None` in function able to return non-`None` value + "RET503", # missing explicit `return` at the end of function able to return non-`None` value + "RUF012", # mutable class attributes should be annotated with `typing.ClassVar` + "RUF015", # prefer next({iterable}) over single element slice + "S101", # use of `assert` detected + "S311", # standart pseudo-random generators are not suitable for cryptographic purposes + "S324", # probable use of insecure hash functions in `{}`: `{}` + "SIM102", # use a single `if` statement instead of nested `if` statements + "SIM108", # use ternary operator `{}` instead of `if`-`else`-block + "SIM113", # use enumerate instead of manually incrementing a counter + "TC001", # move application import `{}` into a type-checking block + "TC002", # move third-party import `{}` into a type-checking block + "TC003", # move standart library import `{}` into a type-checking block + "TRY003", # avoid specifying long messages outside the exception class + "TRY300", # consider moving this statement to an `else` block +] +select = ["ALL"] + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[tool.ruff.lint.isort] +combine-as-imports = true +known-first-party = ["src"] +lines-after-imports = 2 + +[tool.ruff.lint.per-file-ignores] +"*/factory.py" = [ + "ANN", # flake8-annotations + "ARG001", +] +"*/fixtures.py" = [ + "ANN", # flake8-annotations + "ARG001", +] +"*/management/*" = [ + "ANN", # flake8-annotations +] +"*/migrations/*" = [ + "ANN", # flake8-annotations +] +"*/tests/*" = [ + "ANN", # flake8-annotations + "ARG001", + "PLR2004", +] +"src/app/conf/*" = [ + "ANN", # flake8-annotations +] +"src/app/testing/*" = [ + "ANN", # flake8-annotations + "ARG001", + "PLR2004", +] + [tool.tomlsort] all = true in_place = true diff --git a/{{ cookiecutter.name }}/src/.django-app-template/admin/__init__.py-tpl b/{{ cookiecutter.name }}/src/.django-app-template/admin/__init__.py-tpl index 4a790127..6312f38e 100644 --- a/{{ cookiecutter.name }}/src/.django-app-template/admin/__init__.py-tpl +++ b/{{ cookiecutter.name }}/src/.django-app-template/admin/__init__.py-tpl @@ -1,6 +1,7 @@ from app.admin import ModelAdmin, admin + __all__ = [ - "admin", "ModelAdmin", + "admin", ] diff --git a/{{ cookiecutter.name }}/src/.django-app-template/api/v1/urls.py-tpl b/{{ cookiecutter.name }}/src/.django-app-template/api/v1/urls.py-tpl index b0df5123..e91590f1 100644 --- a/{{ cookiecutter.name }}/src/.django-app-template/api/v1/urls.py-tpl +++ b/{{ cookiecutter.name }}/src/.django-app-template/api/v1/urls.py-tpl @@ -1,6 +1,7 @@ from django.urls import include, path from rest_framework.routers import SimpleRouter + router = SimpleRouter() urlpatterns = [ diff --git a/{{ cookiecutter.name }}/src/.django-app-template/models/__init__.py-tpl b/{{ cookiecutter.name }}/src/.django-app-template/models/__init__.py-tpl index 0d31b3d8..3447d87a 100644 --- a/{{ cookiecutter.name }}/src/.django-app-template/models/__init__.py-tpl +++ b/{{ cookiecutter.name }}/src/.django-app-template/models/__init__.py-tpl @@ -1,7 +1,8 @@ from app.models import DefaultModel, TimestampedModel, models + __all__ = [ - "models", "DefaultModel", "TimestampedModel", + "models", ] diff --git a/{{ cookiecutter.name }}/src/a12n/api/urls.py b/{{ cookiecutter.name }}/src/a12n/api/urls.py index 00c6ac72..7774e5b1 100644 --- a/{{ cookiecutter.name }}/src/a12n/api/urls.py +++ b/{{ cookiecutter.name }}/src/a12n/api/urls.py @@ -2,6 +2,7 @@ from a12n.api import views + app_name = "a12n" urlpatterns = [ diff --git a/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_obtain_jwt_view.py b/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_obtain_jwt_view.py index 329dd3d6..959c34ce 100644 --- a/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_obtain_jwt_view.py +++ b/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_obtain_jwt_view.py @@ -3,6 +3,7 @@ import pytest from axes.models import AccessAttempt + pytestmark = pytest.mark.django_db diff --git a/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_refresh_jwt_token.py b/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_refresh_jwt_token.py index a0129a34..104236b1 100644 --- a/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_refresh_jwt_token.py +++ b/{{ cookiecutter.name }}/src/a12n/tests/jwt_views/test_refresh_jwt_token.py @@ -3,6 +3,7 @@ from a12n.utils import get_jwt + pytestmark = [ pytest.mark.django_db, pytest.mark.freeze_time("2049-01-05"), diff --git a/{{ cookiecutter.name }}/src/app/admin/__init__.py b/{{ cookiecutter.name }}/src/app/admin/__init__.py index cb848394..9b4931a8 100644 --- a/{{ cookiecutter.name }}/src/app/admin/__init__.py +++ b/{{ cookiecutter.name }}/src/app/admin/__init__.py @@ -2,7 +2,8 @@ from app.admin.model_admin import ModelAdmin + __all__ = [ - "admin", "ModelAdmin", + "admin", ] diff --git a/{{ cookiecutter.name }}/src/app/api/viewsets.py b/{{ cookiecutter.name }}/src/app/api/viewsets.py index b9fc6527..7113271b 100644 --- a/{{ cookiecutter.name }}/src/app/api/viewsets.py +++ b/{{ cookiecutter.name }}/src/app/api/viewsets.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Protocol, Type +from typing import Any, Protocol from rest_framework import mixins, status from rest_framework.mixins import CreateModelMixin, UpdateModelMixin @@ -7,6 +7,7 @@ from rest_framework.serializers import BaseSerializer from rest_framework.viewsets import GenericViewSet + __all__ = ["DefaultModelViewSet"] @@ -53,7 +54,7 @@ def update(self: BaseGenericViewSet, request: Request, *args: Any, **kwargs: Any if getattr(instance, "_prefetched_objects_cache", None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. - instance._prefetched_objects_cache = {} + instance._prefetched_objects_cache = {} # noqa: SLF001 return self.get_response(instance, status.HTTP_200_OK) @@ -101,8 +102,8 @@ def get_response( def get_serializer_class( self: BaseGenericViewSet, - action: Optional[str] = None, - ) -> Type[BaseSerializer]: + action: str | None = None, + ) -> type[BaseSerializer]: if action is None: action = self.action # type: ignore diff --git a/{{ cookiecutter.name }}/src/app/celery.py b/{{ cookiecutter.name }}/src/app/celery.py index 405697f3..dca3ed5b 100644 --- a/{{ cookiecutter.name }}/src/app/celery.py +++ b/{{ cookiecutter.name }}/src/app/celery.py @@ -3,6 +3,7 @@ from celery import Celery from django.conf import settings + __all__ = ["celery"] os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") diff --git a/{{ cookiecutter.name }}/src/app/conf/api.py b/{{ cookiecutter.name }}/src/app/conf/api.py index 94d115c6..83e62a99 100644 --- a/{{ cookiecutter.name }}/src/app/conf/api.py +++ b/{{ cookiecutter.name }}/src/app/conf/api.py @@ -1,5 +1,6 @@ from app.conf.environ import env + # Django REST Framework # https://www.django-rest-framework.org/api-guide/settings/ diff --git a/{{ cookiecutter.name }}/src/app/conf/auth.py b/{{ cookiecutter.name }}/src/app/conf/auth.py index b69e08d4..cb4f0f7e 100644 --- a/{{ cookiecutter.name }}/src/app/conf/auth.py +++ b/{{ cookiecutter.name }}/src/app/conf/auth.py @@ -2,6 +2,7 @@ from app.conf.environ import env + AUTH_USER_MODEL = "users.User" AXES_ENABLED = env("AXES_ENABLED", cast=bool, default=True) diff --git a/{{ cookiecutter.name }}/src/app/conf/boilerplate.py b/{{ cookiecutter.name }}/src/app/conf/boilerplate.py index f16ff2a1..93170a60 100644 --- a/{{ cookiecutter.name }}/src/app/conf/boilerplate.py +++ b/{{ cookiecutter.name }}/src/app/conf/boilerplate.py @@ -1,5 +1,6 @@ from pathlib import Path + BASE_DIR = Path(__file__).resolve().parent.parent ROOT_URLCONF = "app.urls" diff --git a/{{ cookiecutter.name }}/src/app/conf/celery.py b/{{ cookiecutter.name }}/src/app/conf/celery.py index 578027dc..692d5bc6 100644 --- a/{{ cookiecutter.name }}/src/app/conf/celery.py +++ b/{{ cookiecutter.name }}/src/app/conf/celery.py @@ -1,6 +1,7 @@ from app.conf.environ import env from app.conf.timezone import TIME_ZONE + CELERY_BROKER_URL = env("CELERY_BROKER_URL", cast=str, default="redis://localhost:6379/0") CELERY_TASK_ALWAYS_EAGER = env("CELERY_TASK_ALWAYS_EAGER", cast=bool, default=env("DEBUG")) CELERY_TIMEZONE = TIME_ZONE diff --git a/{{ cookiecutter.name }}/src/app/conf/db.py b/{{ cookiecutter.name }}/src/app/conf/db.py index 935f531e..f4230128 100644 --- a/{{ cookiecutter.name }}/src/app/conf/db.py +++ b/{{ cookiecutter.name }}/src/app/conf/db.py @@ -3,6 +3,7 @@ from app.conf.environ import env + DATABASES = { # read os.environ["DATABASE_URL"] and raises ImproperlyConfigured exception if not found "default": env.db(), diff --git a/{{ cookiecutter.name }}/src/app/conf/environ.py b/{{ cookiecutter.name }}/src/app/conf/environ.py index ad55b162..5d98314f 100644 --- a/{{ cookiecutter.name }}/src/app/conf/environ.py +++ b/{{ cookiecutter.name }}/src/app/conf/environ.py @@ -2,6 +2,7 @@ from app.conf.boilerplate import BASE_DIR + env = environ.Env( DEBUG=(bool, False), CI=(bool, False), diff --git a/{{ cookiecutter.name }}/src/app/conf/http.py b/{{ cookiecutter.name }}/src/app/conf/http.py index ff265e4f..de84e241 100644 --- a/{{ cookiecutter.name }}/src/app/conf/http.py +++ b/{{ cookiecutter.name }}/src/app/conf/http.py @@ -1,5 +1,6 @@ from app.conf.environ import env + ALLOWED_HOSTS = ["*"] # host validation is not necessary in 2020 CSRF_TRUSTED_ORIGINS = [ "http://your.app.origin", diff --git a/{{ cookiecutter.name }}/src/app/conf/i18n.py b/{{ cookiecutter.name }}/src/app/conf/i18n.py index 6318ccc6..9c788e7d 100644 --- a/{{ cookiecutter.name }}/src/app/conf/i18n.py +++ b/{{ cookiecutter.name }}/src/app/conf/i18n.py @@ -2,6 +2,7 @@ from app.conf.boilerplate import BASE_DIR + LANGUAGE_CODE = "ru" LOCALE_PATHS = [Path(BASE_DIR).parent / "locale"] diff --git a/{{ cookiecutter.name }}/src/app/conf/media.py b/{{ cookiecutter.name }}/src/app/conf/media.py index 7c655753..10a5aaa9 100644 --- a/{{ cookiecutter.name }}/src/app/conf/media.py +++ b/{{ cookiecutter.name }}/src/app/conf/media.py @@ -1,4 +1,5 @@ from app.conf.environ import env + MEDIA_URL = "/media/" MEDIA_ROOT = env("MEDIA_ROOT", cast=str, default="media") diff --git a/{{ cookiecutter.name }}/src/app/conf/sentry.py b/{{ cookiecutter.name }}/src/app/conf/sentry.py index 3050ea95..c29de435 100644 --- a/{{ cookiecutter.name }}/src/app/conf/sentry.py +++ b/{{ cookiecutter.name }}/src/app/conf/sentry.py @@ -1,5 +1,6 @@ from app.conf.environ import env + # Sentry # https://sentry.io/for/django/ diff --git a/{{ cookiecutter.name }}/src/app/conf/static.py b/{{ cookiecutter.name }}/src/app/conf/static.py index 33530c17..14f494b5 100644 --- a/{{ cookiecutter.name }}/src/app/conf/static.py +++ b/{{ cookiecutter.name }}/src/app/conf/static.py @@ -1,5 +1,6 @@ from app.conf.environ import env + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ diff --git a/{{ cookiecutter.name }}/src/app/conf/storage.py b/{{ cookiecutter.name }}/src/app/conf/storage.py index d76063f7..e38342d9 100644 --- a/{{ cookiecutter.name }}/src/app/conf/storage.py +++ b/{{ cookiecutter.name }}/src/app/conf/storage.py @@ -1,5 +1,6 @@ from app.conf.environ import env + STORAGES = { "default": { "BACKEND": env( diff --git a/{{ cookiecutter.name }}/src/app/factory.py b/{{ cookiecutter.name }}/src/app/factory.py index cd7338a4..431b008d 100644 --- a/{{ cookiecutter.name }}/src/app/factory.py +++ b/{{ cookiecutter.name }}/src/app/factory.py @@ -4,6 +4,7 @@ from app.testing import register from app.testing.types import FactoryProtocol + faker = Faker() diff --git a/{{ cookiecutter.name }}/src/app/fixtures/__init__.py b/{{ cookiecutter.name }}/src/app/fixtures/__init__.py index 24efa781..4454c2e5 100644 --- a/{{ cookiecutter.name }}/src/app/fixtures/__init__.py +++ b/{{ cookiecutter.name }}/src/app/fixtures/__init__.py @@ -1,6 +1,7 @@ from app.fixtures.api import as_anon, as_user from app.fixtures.factory import factory + __all__ = [ "as_anon", "as_user", diff --git a/{{ cookiecutter.name }}/src/app/middleware/real_ip.py b/{{ cookiecutter.name }}/src/app/middleware/real_ip.py index 5fa01841..286c1c95 100644 --- a/{{ cookiecutter.name }}/src/app/middleware/real_ip.py +++ b/{{ cookiecutter.name }}/src/app/middleware/real_ip.py @@ -1,4 +1,4 @@ -from typing import Callable +from collections.abc import Callable from django.http import HttpRequest, HttpResponse from ipware import get_client_ip diff --git a/{{ cookiecutter.name }}/src/app/models.py b/{{ cookiecutter.name }}/src/app/models.py index 5f9c12b9..c7cfeef9 100644 --- a/{{ cookiecutter.name }}/src/app/models.py +++ b/{{ cookiecutter.name }}/src/app/models.py @@ -4,10 +4,11 @@ from django.contrib.contenttypes.models import ContentType from django.db import models + __all__ = [ - "models", "DefaultModel", "TimestampedModel", + "models", ] diff --git a/{{ cookiecutter.name }}/src/app/services.py b/{{ cookiecutter.name }}/src/app/services.py index f2de05c1..d8c3b1e8 100644 --- a/{{ cookiecutter.name }}/src/app/services.py +++ b/{{ cookiecutter.name }}/src/app/services.py @@ -1,5 +1,6 @@ from abc import ABCMeta, abstractmethod -from typing import Any, Callable +from collections.abc import Callable +from typing import Any class BaseService(metaclass=ABCMeta): diff --git a/{{ cookiecutter.name }}/src/app/settings.py b/{{ cookiecutter.name }}/src/app/settings.py index 83ce6640..8931afe4 100644 --- a/{{ cookiecutter.name }}/src/app/settings.py +++ b/{{ cookiecutter.name }}/src/app/settings.py @@ -6,6 +6,7 @@ from app.conf.environ import env + # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = env("SECRET_KEY") diff --git a/{{ cookiecutter.name }}/src/app/testing/__init__.py b/{{ cookiecutter.name }}/src/app/testing/__init__.py index 7652f55f..d6e2ad5a 100644 --- a/{{ cookiecutter.name }}/src/app/testing/__init__.py +++ b/{{ cookiecutter.name }}/src/app/testing/__init__.py @@ -1,6 +1,7 @@ from app.testing.api import ApiClient from app.testing.factory import FixtureFactory, register + __all__ = [ "ApiClient", "FixtureFactory", diff --git a/{{ cookiecutter.name }}/src/app/testing/api.py b/{{ cookiecutter.name }}/src/app/testing/api.py index 30fb48c5..965a7049 100644 --- a/{{ cookiecutter.name }}/src/app/testing/api.py +++ b/{{ cookiecutter.name }}/src/app/testing/api.py @@ -105,8 +105,7 @@ def _decode(self, response: Response): if self.is_json(response): return json.loads(content) - else: - return content + return content @staticmethod def is_json(response: Response) -> bool: diff --git a/{{ cookiecutter.name }}/src/app/testing/factory.py b/{{ cookiecutter.name }}/src/app/testing/factory.py index bbe5ae26..da4504de 100644 --- a/{{ cookiecutter.name }}/src/app/testing/factory.py +++ b/{{ cookiecutter.name }}/src/app/testing/factory.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from functools import partial -from typing import Callable from app.testing.mixer import mixer diff --git a/{{ cookiecutter.name }}/src/app/testing/mixer.py b/{{ cookiecutter.name }}/src/app/testing/mixer.py index 49e96dc2..f236533b 100644 --- a/{{ cookiecutter.name }}/src/app/testing/mixer.py +++ b/{{ cookiecutter.name }}/src/app/testing/mixer.py @@ -2,6 +2,7 @@ from mixer.backend.django import mixer + __all__ = [ "mixer", ] diff --git a/{{ cookiecutter.name }}/src/app/tests/test_health.py b/{{ cookiecutter.name }}/src/app/tests/test_health.py index 57bf52f6..43f8452b 100644 --- a/{{ cookiecutter.name }}/src/app/tests/test_health.py +++ b/{{ cookiecutter.name }}/src/app/tests/test_health.py @@ -1,5 +1,6 @@ import pytest + pytestmark = [ pytest.mark.django_db, pytest.mark.filterwarnings("ignore:.*inspect.getargspec().*:DeprecationWarning"), diff --git a/{{ cookiecutter.name }}/src/app/tests/test_remote_addr_midlleware.py b/{{ cookiecutter.name }}/src/app/tests/test_remote_addr_midlleware.py index 7aefd56d..a01a765e 100644 --- a/{{ cookiecutter.name }}/src/app/tests/test_remote_addr_midlleware.py +++ b/{{ cookiecutter.name }}/src/app/tests/test_remote_addr_midlleware.py @@ -3,14 +3,13 @@ from app.testing.api import ApiClient + pytestmark = [pytest.mark.django_db] @pytest.fixture(autouse=True) def _require_users_app_installed(settings): - assert apps.is_installed( - "users" - ), """ + assert apps.is_installed("users"), """ Stock f213/django users app should be installed to run this test. Make sure to test app.middleware.real_ip.real_ip_middleware on your own, if you drop diff --git a/{{ cookiecutter.name }}/src/app/urls/__init__.py b/{{ cookiecutter.name }}/src/app/urls/__init__.py index 659b1bc8..a6923df4 100644 --- a/{{ cookiecutter.name }}/src/app/urls/__init__.py +++ b/{{ cookiecutter.name }}/src/app/urls/__init__.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.urls import include, path + api = [ path("v1/", include("app.urls.v1", namespace="v1")), ] diff --git a/{{ cookiecutter.name }}/src/app/urls/v1.py b/{{ cookiecutter.name }}/src/app/urls/v1.py index dfd8d18e..2e5eab61 100644 --- a/{{ cookiecutter.name }}/src/app/urls/v1.py +++ b/{{ cookiecutter.name }}/src/app/urls/v1.py @@ -1,6 +1,7 @@ from django.urls import include, path from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView + app_name = "api_v1" urlpatterns = [ diff --git a/{{ cookiecutter.name }}/src/app/wsgi.py b/{{ cookiecutter.name }}/src/app/wsgi.py index d468f8da..f66d49a7 100644 --- a/{{ cookiecutter.name }}/src/app/wsgi.py +++ b/{{ cookiecutter.name }}/src/app/wsgi.py @@ -2,6 +2,7 @@ from django.core.wsgi import get_wsgi_application + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") application = get_wsgi_application() diff --git a/{{ cookiecutter.name }}/src/users/admin.py b/{{ cookiecutter.name }}/src/users/admin.py index 20908271..979ff06e 100644 --- a/{{ cookiecutter.name }}/src/users/admin.py +++ b/{{ cookiecutter.name }}/src/users/admin.py @@ -3,4 +3,5 @@ from users.models import User + admin.site.register(User, UserAdmin) diff --git a/{{ cookiecutter.name }}/src/users/api/urls.py b/{{ cookiecutter.name }}/src/users/api/urls.py index 4871b5e7..bf0e5e4a 100644 --- a/{{ cookiecutter.name }}/src/users/api/urls.py +++ b/{{ cookiecutter.name }}/src/users/api/urls.py @@ -2,6 +2,7 @@ from users.api import viewsets + app_name = "users" urlpatterns = [ diff --git a/{{ cookiecutter.name }}/src/users/fixtures.py b/{{ cookiecutter.name }}/src/users/fixtures.py index d63e4b8b..6e899c98 100644 --- a/{{ cookiecutter.name }}/src/users/fixtures.py +++ b/{{ cookiecutter.name }}/src/users/fixtures.py @@ -4,6 +4,7 @@ from users.models import User + if TYPE_CHECKING: from app.testing.factory import FixtureFactory diff --git a/{{ cookiecutter.name }}/src/users/models.py b/{{ cookiecutter.name }}/src/users/models.py index d1c036c5..7d7a4680 100644 --- a/{{ cookiecutter.name }}/src/users/models.py +++ b/{{ cookiecutter.name }}/src/users/models.py @@ -1,8 +1,7 @@ from typing import ClassVar -from django.contrib.auth.models import AbstractUser -from django.contrib.auth.models import UserManager as _UserManager +from django.contrib.auth.models import AbstractUser, UserManager as _UserManager -class User(AbstractUser): # noqa +class User(AbstractUser): objects: ClassVar[_UserManager] = _UserManager() diff --git a/{{ cookiecutter.name }}/src/users/tests/test_password_hashing.py b/{{ cookiecutter.name }}/src/users/tests/test_password_hashing.py index 85c12260..db2e3287 100644 --- a/{{ cookiecutter.name }}/src/users/tests/test_password_hashing.py +++ b/{{ cookiecutter.name }}/src/users/tests/test_password_hashing.py @@ -4,6 +4,7 @@ from users.models import User + pytestmark = [pytest.mark.django_db] diff --git a/{{ cookiecutter.name }}/src/users/tests/test_whoami.py b/{{ cookiecutter.name }}/src/users/tests/test_whoami.py index da6d257c..07fc27c3 100644 --- a/{{ cookiecutter.name }}/src/users/tests/test_whoami.py +++ b/{{ cookiecutter.name }}/src/users/tests/test_whoami.py @@ -1,5 +1,6 @@ import pytest + pytestmark = [pytest.mark.django_db]