mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2024-11-21 17:16:51 +03:00
Apply ruff to codebase
This commit is contained in:
parent
6ba6104f09
commit
0fc4ea6165
|
@ -180,28 +180,25 @@ def test_project_generation(cookies, context, context_override):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_flake8_passes(cookies, context_override):
|
||||
"""Generated project should pass flake8."""
|
||||
def test_ruff_check_passes(cookies, context_override):
|
||||
"""Generated project should pass ruff check."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.flake8(_cwd=str(result.project_path))
|
||||
sh.ruff("check", ".", _cwd=str(result.project_path))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@auto_fixable
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_black_passes(cookies, context_override):
|
||||
"""Check whether generated project passes black style."""
|
||||
def test_ruff_format_passes(cookies, context_override):
|
||||
"""Check whether generated project passes ruff format."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.black(
|
||||
"--check",
|
||||
"--diff",
|
||||
"--exclude",
|
||||
"migrations",
|
||||
sh.ruff(
|
||||
"format",
|
||||
".",
|
||||
_cwd=str(result.project_path),
|
||||
)
|
||||
|
@ -287,7 +284,7 @@ def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_scrip
|
|||
with open(f"{result.project_path}/.travis.yml") as travis_yml:
|
||||
try:
|
||||
yml = yaml.safe_load(travis_yml)["jobs"]["include"]
|
||||
assert yml[0]["script"] == ["flake8"]
|
||||
assert yml[0]["script"] == ["ruff check ."]
|
||||
assert yml[1]["script"] == [expected_test_script]
|
||||
except yaml.YAMLError as e:
|
||||
pytest.fail(str(e))
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
from django.conf import settings
|
||||
from rest_framework.routers import DefaultRouter, SimpleRouter
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from rest_framework.routers import SimpleRouter
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.api.views import UserViewSet
|
||||
|
||||
if settings.DEBUG:
|
||||
router = DefaultRouter()
|
||||
else:
|
||||
router = SimpleRouter()
|
||||
router = DefaultRouter() if settings.DEBUG else SimpleRouter()
|
||||
|
||||
router.register("users", UserViewSet)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa
|
||||
"""
|
||||
ASGI config for {{ cookiecutter.project_name }} project.
|
||||
|
||||
|
@ -29,7 +30,7 @@ django_application = get_asgi_application()
|
|||
# application = HelloWorldApplication(application)
|
||||
|
||||
# Import websocket application here, so apps from django_application are loaded first
|
||||
from config.websocket import websocket_application # noqa isort:skip
|
||||
from config.websocket import websocket_application
|
||||
|
||||
|
||||
async def application(scope, receive, send):
|
||||
|
@ -38,4 +39,5 @@ async def application(scope, receive, send):
|
|||
elif scope["type"] == "websocket":
|
||||
await websocket_application(scope, receive, send)
|
||||
else:
|
||||
raise NotImplementedError(f"Unknown scope type {scope['type']}")
|
||||
msg = f"Unknown scope type {scope['type']}"
|
||||
raise NotImplementedError(msg)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa: ERA001, E501
|
||||
"""Base settings to build other settings files upon."""
|
||||
|
||||
from pathlib import Path
|
||||
|
@ -136,7 +137,9 @@ PASSWORD_HASHERS = [
|
|||
]
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||
|
@ -209,7 +212,7 @@ TEMPLATES = [
|
|||
"{{cookiecutter.project_slug}}.users.context_processors.allauth_settings",
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer
|
||||
|
@ -273,7 +276,7 @@ LOGGING = {
|
|||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
}
|
||||
},
|
||||
},
|
||||
"root": {"level": "INFO", "handlers": ["console"]},
|
||||
}
|
||||
|
@ -379,7 +382,7 @@ WEBPACK_LOADER = {
|
|||
"STATS_FILE": BASE_DIR / "webpack-stats.json",
|
||||
"POLL_INTERVAL": 0.1,
|
||||
"IGNORE": [r".+\.hot-update.js", r".+\.map"],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
from .base import * # noqa
|
||||
# ruff: noqa: E501
|
||||
from .base import * # noqa: F403
|
||||
from .base import INSTALLED_APPS
|
||||
from .base import MIDDLEWARE
|
||||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
from .base import WEBPACK_LOADER
|
||||
{%- endif %}
|
||||
from .base import env
|
||||
|
||||
# GENERAL
|
||||
|
@ -11,7 +17,7 @@ SECRET_KEY = env(
|
|||
default="!!!SET DJANGO_SECRET_KEY!!!",
|
||||
)
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
|
||||
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] # noqa: S104
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -20,7 +26,7 @@ CACHES = {
|
|||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
"LOCATION": "",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
# EMAIL
|
||||
|
@ -37,7 +43,9 @@ EMAIL_HOST = "localhost"
|
|||
EMAIL_PORT = 1025
|
||||
{%- else -%}
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
EMAIL_BACKEND = env("DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend")
|
||||
EMAIL_BACKEND = env(
|
||||
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend",
|
||||
)
|
||||
{%- endif %}
|
||||
|
||||
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||
|
@ -45,15 +53,15 @@ EMAIL_BACKEND = env("DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.c
|
|||
# WhiteNoise
|
||||
# ------------------------------------------------------------------------------
|
||||
# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
|
||||
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa: F405
|
||||
INSTALLED_APPS = ["whitenoise.runserver_nostatic", *INSTALLED_APPS]
|
||||
{% endif %}
|
||||
|
||||
# django-debug-toolbar
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
|
||||
INSTALLED_APPS += ["debug_toolbar"] # noqa: F405
|
||||
INSTALLED_APPS += ["debug_toolbar"]
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
|
||||
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405
|
||||
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
"DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"],
|
||||
|
@ -80,7 +88,7 @@ if env("USE_DOCKER") == "yes":
|
|||
# django-extensions
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
||||
INSTALLED_APPS += ["django_extensions"] # noqa: F405
|
||||
INSTALLED_APPS += ["django_extensions"]
|
||||
{% if cookiecutter.use_celery == 'y' -%}
|
||||
|
||||
# Celery
|
||||
|
@ -96,7 +104,7 @@ CELERY_TASK_EAGER_PROPAGATES = True
|
|||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
# django-webpack-loader
|
||||
# ------------------------------------------------------------------------------
|
||||
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG # noqa: F405
|
||||
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG
|
||||
|
||||
{%- endif %}
|
||||
# Your stuff...
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa: E501
|
||||
{% if cookiecutter.use_sentry == 'y' -%}
|
||||
import logging
|
||||
|
||||
|
@ -12,7 +13,12 @@ from sentry_sdk.integrations.logging import LoggingIntegration
|
|||
from sentry_sdk.integrations.redis import RedisIntegration
|
||||
|
||||
{% endif -%}
|
||||
from .base import * # noqa
|
||||
from .base import * # noqa: F403
|
||||
from .base import DATABASES
|
||||
from .base import INSTALLED_APPS
|
||||
{%- if cookiecutter.use_drf == "y" %}
|
||||
from .base import SPECTACULAR_SETTINGS
|
||||
{%- endif %}
|
||||
from .base import env
|
||||
|
||||
# GENERAL
|
||||
|
@ -24,7 +30,7 @@ ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domai
|
|||
|
||||
# DATABASES
|
||||
# ------------------------------------------------------------------------------
|
||||
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa: F405
|
||||
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60)
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -38,7 +44,7 @@ CACHES = {
|
|||
# https://github.com/jazzband/django-redis#memcached-exceptions-behavior
|
||||
"IGNORE_EXCEPTIONS": True,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
# SECURITY
|
||||
|
@ -56,17 +62,23 @@ CSRF_COOKIE_SECURE = True
|
|||
# TODO: set this to 60 seconds first and then to 518400 once you prove the former works
|
||||
SECURE_HSTS_SECONDS = 60
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool("DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True)
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
|
||||
"DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS",
|
||||
default=True,
|
||||
)
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload
|
||||
SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True)
|
||||
# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = env.bool("DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True)
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
|
||||
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF",
|
||||
default=True,
|
||||
)
|
||||
|
||||
{% if cookiecutter.cloud_provider != 'None' -%}
|
||||
# STORAGES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-storages.readthedocs.io/en/latest/#installation
|
||||
INSTALLED_APPS += ["storages"] # noqa: F405
|
||||
INSTALLED_APPS += ["storages"]
|
||||
{%- endif -%}
|
||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
||||
|
@ -197,7 +209,7 @@ ADMIN_URL = env("DJANGO_ADMIN_URL")
|
|||
# Anymail
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
|
||||
INSTALLED_APPS += ["anymail"] # noqa: F405
|
||||
INSTALLED_APPS += ["anymail"]
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
|
||||
{%- if cookiecutter.mail_service == 'Mailgun' %}
|
||||
|
@ -273,7 +285,7 @@ COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
|
|||
COMPRESS_STORAGE = STORAGES["staticfiles"]["BACKEND"]
|
||||
{%- endif %}
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL
|
||||
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %} # noqa: F405{% endif %}
|
||||
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %}{% endif %}
|
||||
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_OFFLINE
|
||||
COMPRESS_OFFLINE = True # Offline compression is required when using Whitenoise
|
||||
|
@ -291,7 +303,7 @@ COMPRESS_FILTERS = {
|
|||
# Collectfast
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://github.com/antonagestam/collectfast#installation
|
||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa: F405
|
||||
INSTALLED_APPS = ["collectfast", *INSTALLED_APPS]
|
||||
{% endif %}
|
||||
# LOGGING
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -351,7 +363,7 @@ LOGGING = {
|
|||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
}
|
||||
},
|
||||
},
|
||||
"root": {"level": "INFO", "handlers": ["console"]},
|
||||
"loggers": {
|
||||
|
@ -403,7 +415,7 @@ sentry_sdk.init(
|
|||
# django-rest-framework
|
||||
# -------------------------------------------------------------------------------
|
||||
# Tools that generate code samples can use SERVERS to point to the correct domain
|
||||
SPECTACULAR_SETTINGS["SERVERS"] = [ # noqa: F405
|
||||
SPECTACULAR_SETTINGS["SERVERS"] = [
|
||||
{"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"},
|
||||
]
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
With these settings, tests run faster.
|
||||
"""
|
||||
|
||||
from .base import * # noqa
|
||||
from .base import * # noqa: F403
|
||||
from .base import TEMPLATES
|
||||
from .base import env
|
||||
|
||||
# GENERAL
|
||||
|
@ -27,7 +28,7 @@ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|||
|
||||
# DEBUGGING FOR TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore # noqa: F405
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore[index]
|
||||
|
||||
# MEDIA
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,27 +1,37 @@
|
|||
# ruff: noqa
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
{%- endif %}
|
||||
from django.urls import include, path
|
||||
from django.urls import include
|
||||
from django.urls import path
|
||||
from django.views import defaults as default_views
|
||||
from django.views.generic import TemplateView
|
||||
{%- if cookiecutter.use_drf == 'y' %}
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
from drf_spectacular.views import SpectacularAPIView
|
||||
from drf_spectacular.views import SpectacularSwaggerView
|
||||
from rest_framework.authtoken.views import obtain_auth_token
|
||||
{%- endif %}
|
||||
|
||||
urlpatterns = [
|
||||
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||
path("about/", TemplateView.as_view(template_name="pages/about.html"), name="about"),
|
||||
path(
|
||||
"about/",
|
||||
TemplateView.as_view(template_name="pages/about.html"),
|
||||
name="about",
|
||||
),
|
||||
# Django Admin, use {% raw %}{% url 'admin:index' %}{% endraw %}
|
||||
path(settings.ADMIN_URL, admin.site.urls),
|
||||
# User management
|
||||
path("users/", include("{{ cookiecutter.project_slug }}.users.urls", namespace="users")),
|
||||
path("accounts/", include("allauth.urls")),
|
||||
# Your stuff: custom urls includes go here
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
# ...
|
||||
# Media files
|
||||
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
|
||||
]
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
if settings.DEBUG:
|
||||
# Static file serving when using Gunicorn + Uvicorn for local web socket development
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa
|
||||
"""
|
||||
WSGI config for {{ cookiecutter.project_name }} project.
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# ruff: noqa
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
@ -13,7 +14,7 @@ if __name__ == "__main__":
|
|||
# issue is really that Django is missing to avoid masking other
|
||||
# exceptions on Python 2.
|
||||
try:
|
||||
import django # noqa
|
||||
import django
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# ruff: noqa
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
|
|
0
{{cookiecutter.project_slug}}/tests/__init__.py
Normal file
0
{{cookiecutter.project_slug}}/tests/__init__.py
Normal file
|
@ -1,2 +1,5 @@
|
|||
__version__ = "{{ cookiecutter.version }}"
|
||||
__version_info__ = tuple(int(num) if num.isdigit() else num for num in __version__.replace("-", ".", 1).split("."))
|
||||
__version_info__ = tuple(
|
||||
int(num) if num.isdigit() else num
|
||||
for num in __version__.replace("-", ".", 1).split(".")
|
||||
)
|
||||
|
|
|
@ -5,10 +5,10 @@ from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
|||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def media_storage(settings, tmpdir):
|
||||
def _media_storage(settings, tmpdir) -> None:
|
||||
settings.MEDIA_ROOT = tmpdir.strpath
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def user(db) -> User:
|
||||
return UserFactory()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import django.contrib.sites.models
|
||||
from django.contrib.sites.models import _simple_domain_name_validator
|
||||
from django.db import migrations, models
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -38,5 +39,5 @@ class Migration(migrations.Migration):
|
|||
},
|
||||
bases=(models.Model,),
|
||||
managers=[("objects", django.contrib.sites.models.SiteManager())],
|
||||
)
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import django.contrib.sites.models
|
||||
from django.db import migrations, models
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
|
|
@ -23,7 +23,7 @@ def _update_or_create_site_with_sequence(site_model, connection, domain, name):
|
|||
# site is created.
|
||||
# To avoid this, we need to manually update DB sequence and make sure it's
|
||||
# greater than the maximum value.
|
||||
max_id = site_model.objects.order_by('-id').first().id
|
||||
max_id = site_model.objects.order_by("-id").first().id
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT last_value from django_site_id_seq")
|
||||
(current_id,) = cursor.fetchone()
|
||||
|
|
|
@ -5,10 +5,11 @@ import typing
|
|||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||
from django.conf import settings
|
||||
from django.http import HttpRequest
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from allauth.socialaccount.models import SocialLogin
|
||||
from django.http import HttpRequest
|
||||
|
||||
from {{cookiecutter.project_slug}}.users.models import User
|
||||
|
||||
|
||||
|
@ -18,10 +19,19 @@ class AccountAdapter(DefaultAccountAdapter):
|
|||
|
||||
|
||||
class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
def is_open_for_signup(self, request: HttpRequest, sociallogin: SocialLogin) -> bool:
|
||||
def is_open_for_signup(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
sociallogin: SocialLogin,
|
||||
) -> bool:
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
||||
def populate_user(self, request: HttpRequest, sociallogin: SocialLogin, data: dict[str, typing.Any]) -> User:
|
||||
def populate_user(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
sociallogin: SocialLogin,
|
||||
data: dict[str, typing.Any],
|
||||
) -> User:
|
||||
"""
|
||||
Populates user information from social provider info.
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import admin as auth_admin
|
||||
from django.contrib.auth import get_user_model, decorators
|
||||
from django.contrib.auth import decorators
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm, UserAdminCreationForm
|
||||
from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm
|
||||
from {{ cookiecutter.project_slug }}.users.forms import UserAdminCreationForm
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ from rest_framework import serializers
|
|||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User as UserType
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.mixins import RetrieveModelMixin
|
||||
from rest_framework.mixins import UpdateModelMixin
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import contextlib
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -7,7 +9,5 @@ class UsersConfig(AppConfig):
|
|||
verbose_name = _("Users")
|
||||
|
||||
def ready(self):
|
||||
try:
|
||||
with contextlib.suppress(ImportError):
|
||||
import {{ cookiecutter.project_slug }}.users.signals # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
@ -15,7 +15,8 @@ class UserManager(DjangoUserManager["User"]):
|
|||
Create and save a user with the given email and password.
|
||||
"""
|
||||
if not email:
|
||||
raise ValueError("The given email must be set")
|
||||
msg = "The given email must be set"
|
||||
raise ValueError(msg)
|
||||
email = self.normalize_email(email)
|
||||
user = self.model(email=email, **extra_fields)
|
||||
user.password = make_password(password)
|
||||
|
@ -32,8 +33,10 @@ class UserManager(DjangoUserManager["User"]):
|
|||
extra_fields.setdefault("is_superuser", True)
|
||||
|
||||
if extra_fields.get("is_staff") is not True:
|
||||
raise ValueError("Superuser must have is_staff=True.")
|
||||
msg = "Superuser must have is_staff=True."
|
||||
raise ValueError(msg)
|
||||
if extra_fields.get("is_superuser") is not True:
|
||||
raise ValueError("Superuser must have is_superuser=True.")
|
||||
msg = "Superuser must have is_superuser=True."
|
||||
raise ValueError(msg)
|
||||
|
||||
return self._create_user(email, password, **extra_fields)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
import {{cookiecutter.project_slug}}.users.models
|
||||
|
||||
|
@ -31,7 +32,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
blank=True, null=True, verbose_name="last login"
|
||||
blank=True, null=True, verbose_name="last login",
|
||||
),
|
||||
),
|
||||
(
|
||||
|
@ -61,14 +62,14 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
blank=True, max_length=254, verbose_name="email address",
|
||||
),
|
||||
),
|
||||
{%- else %}
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
unique=True, max_length=254, verbose_name="email address"
|
||||
unique=True, max_length=254, verbose_name="email address",
|
||||
),
|
||||
),
|
||||
{%- endif %}
|
||||
|
@ -91,13 +92,13 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
default=django.utils.timezone.now, verbose_name="date joined",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Name of User"
|
||||
blank=True, max_length=255, verbose_name="Name of User",
|
||||
),
|
||||
),
|
||||
(
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{%- if cookiecutter.username_type == "email" %}
|
||||
from typing import ClassVar
|
||||
{%- endif %}
|
||||
|
||||
{% endif -%}
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db.models import CharField{% if cookiecutter.username_type == "email" %}, EmailField{% endif %}
|
||||
from django.db.models import CharField
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
from django.db.models import EmailField
|
||||
{%- endif %}
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
|
@ -21,11 +24,11 @@ class User(AbstractUser):
|
|||
|
||||
# First and last name do not cover name patterns around the globe
|
||||
name = CharField(_("Name of User"), blank=True, max_length=255)
|
||||
first_name = None # type: ignore
|
||||
last_name = None # type: ignore
|
||||
first_name = None # type: ignore[assignment]
|
||||
last_name = None # type: ignore[assignment]
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
email = EmailField(_("email address"), unique=True)
|
||||
username = None # type: ignore
|
||||
username = None # type: ignore[assignment]
|
||||
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS = []
|
||||
|
|
|
@ -2,7 +2,8 @@ from collections.abc import Sequence
|
|||
from typing import Any
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from factory import Faker, post_generation
|
||||
from factory import Faker
|
||||
from factory import post_generation
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
|
||||
|
@ -14,7 +15,7 @@ class UserFactory(DjangoModelFactory):
|
|||
name = Faker("name")
|
||||
|
||||
@post_generation
|
||||
def password(self, create: bool, extracted: Sequence[Any], **kwargs):
|
||||
def password(self, create: bool, extracted: Sequence[Any], **kwargs): # noqa: FBT001
|
||||
password = (
|
||||
extracted
|
||||
if extracted
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import contextlib
|
||||
from http import HTTPStatus
|
||||
from importlib import reload
|
||||
|
||||
import pytest
|
||||
|
@ -13,17 +15,17 @@ class TestUserAdmin:
|
|||
def test_changelist(self, admin_client):
|
||||
url = reverse("admin:users_user_changelist")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
def test_search(self, admin_client):
|
||||
url = reverse("admin:users_user_changelist")
|
||||
response = admin_client.get(url, data={"q": "test"})
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
def test_add(self, admin_client):
|
||||
url = reverse("admin:users_user_add")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
response = admin_client.post(
|
||||
url,
|
||||
|
@ -37,7 +39,7 @@ class TestUserAdmin:
|
|||
"password2": "My_R@ndom-P@ssw0rd",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert response.status_code == HTTPStatus.FOUND
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert User.objects.filter(email="new-admin@example.com").exists()
|
||||
{%- else %}
|
||||
|
@ -52,21 +54,19 @@ class TestUserAdmin:
|
|||
{%- endif %}
|
||||
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
@pytest.fixture
|
||||
def force_allauth(self, settings):
|
||||
@pytest.fixture()
|
||||
def _force_allauth(self, settings):
|
||||
settings.DJANGO_ADMIN_FORCE_ALLAUTH = True
|
||||
# Reload the admin module to apply the setting change
|
||||
import {{ cookiecutter.project_slug }}.users.admin as users_admin
|
||||
|
||||
try:
|
||||
with contextlib.suppress(admin.sites.AlreadyRegistered):
|
||||
reload(users_admin)
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.usefixtures("force_allauth")
|
||||
@pytest.mark.django_db()
|
||||
@pytest.mark.usefixtures("_force_allauth")
|
||||
def test_allauth_login(self, rf, settings):
|
||||
request = rf.get("/fake-url")
|
||||
request.user = AnonymousUser()
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
from django.urls import resolve, reverse
|
||||
from django.urls import resolve
|
||||
from django.urls import reverse
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
|
||||
def test_user_detail(user: User):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert reverse("api:user-detail", kwargs={"pk": user.pk}) == f"/api/users/{user.pk}/"
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"pk": user.pk}) == f"/api/users/{user.pk}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.pk}/").view_name == "api:user-detail"
|
||||
{%- else %}
|
||||
assert reverse("api:user-detail", kwargs={"username": user.username}) == f"/api/users/{user.username}/"
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"username": user.username})
|
||||
== f"/api/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.username}/").view_name == "api:user-detail"
|
||||
{%- endif %}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
class TestUserViewSet:
|
||||
@pytest.fixture
|
||||
@pytest.fixture()
|
||||
def api_rf(self) -> APIRequestFactory:
|
||||
return APIRequestFactory()
|
||||
|
||||
|
@ -26,7 +26,7 @@ class TestUserViewSet:
|
|||
|
||||
view.request = request
|
||||
|
||||
response = view.me(request) # type: ignore
|
||||
response = view.me(request) # type: ignore[call-arg, arg-type, misc]
|
||||
|
||||
assert response.data == {
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
|
|
|
@ -30,7 +30,7 @@ class TestUserAdminCreationForm:
|
|||
{%- endif %}
|
||||
"password1": user.password,
|
||||
"password2": user.password,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
assert not form.is_valid()
|
||||
|
|
|
@ -6,12 +6,12 @@ from django.core.management import call_command
|
|||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.django_db()
|
||||
class TestUserManager:
|
||||
def test_create_user(self):
|
||||
user = User.objects.create_user(
|
||||
email="john@example.com",
|
||||
password="something-r@nd0m!",
|
||||
password="something-r@nd0m!", # noqa: S106
|
||||
)
|
||||
assert user.email == "john@example.com"
|
||||
assert not user.is_staff
|
||||
|
@ -22,7 +22,7 @@ class TestUserManager:
|
|||
def test_create_superuser(self):
|
||||
user = User.objects.create_superuser(
|
||||
email="admin@example.com",
|
||||
password="something-r@nd0m!",
|
||||
password="something-r@nd0m!", # noqa: S106
|
||||
)
|
||||
assert user.email == "admin@example.com"
|
||||
assert user.is_staff
|
||||
|
@ -32,12 +32,12 @@ class TestUserManager:
|
|||
def test_create_superuser_username_ignored(self):
|
||||
user = User.objects.create_superuser(
|
||||
email="test@example.com",
|
||||
password="something-r@nd0m!",
|
||||
password="something-r@nd0m!", # noqa: S106
|
||||
)
|
||||
assert user.username is None
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.django_db()
|
||||
def test_createsuperuser_command():
|
||||
"""Ensure createsuperuser command works with our custom manager."""
|
||||
out = StringIO()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
|
||||
|
@ -5,17 +7,17 @@ from django.urls import reverse
|
|||
def test_swagger_accessible_by_admin(admin_client):
|
||||
url = reverse("api-docs")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.django_db()
|
||||
def test_swagger_ui_not_accessible_by_normal_user(client):
|
||||
url = reverse("api-docs")
|
||||
response = client.get(url)
|
||||
assert response.status_code == 403
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
|
||||
def test_api_schema_generated_successfully(admin_client):
|
||||
url = reverse("api-schema")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
|
|
@ -9,8 +9,9 @@ pytestmark = pytest.mark.django_db
|
|||
|
||||
def test_user_count(settings):
|
||||
"""A basic test to execute the get_users_count Celery task."""
|
||||
UserFactory.create_batch(3)
|
||||
batch_size = 3
|
||||
UserFactory.create_batch(batch_size)
|
||||
settings.CELERY_TASK_ALWAYS_EAGER = True
|
||||
task_result = get_users_count.delay()
|
||||
assert isinstance(task_result, EagerResult)
|
||||
assert task_result.result == 3
|
||||
assert task_result.result == batch_size
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.urls import resolve, reverse
|
||||
from django.urls import resolve
|
||||
from django.urls import reverse
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
|
@ -8,7 +9,10 @@ def test_detail(user: User):
|
|||
assert reverse("users:detail", kwargs={"pk": user.pk}) == f"/users/{user.pk}/"
|
||||
assert resolve(f"/users/{user.pk}/").view_name == "users:detail"
|
||||
{%- else %}
|
||||
assert reverse("users:detail", kwargs={"username": user.username}) == f"/users/{user.username}/"
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"username": user.username})
|
||||
== f"/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
|
||||
{%- endif %}
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.http import HttpRequest, HttpResponseRedirect
|
||||
from django.http import HttpRequest
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -12,11 +15,9 @@ from django.utils.translation import gettext_lazy as _
|
|||
from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
||||
from {{ cookiecutter.project_slug }}.users.views import (
|
||||
UserRedirectView,
|
||||
UserUpdateView,
|
||||
user_detail_view,
|
||||
)
|
||||
from {{ cookiecutter.project_slug }}.users.views import UserRedirectView
|
||||
from {{ cookiecutter.project_slug }}.users.views import UserUpdateView
|
||||
from {{ cookiecutter.project_slug }}.users.views import user_detail_view
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
@ -102,7 +103,7 @@ class TestUserDetailView:
|
|||
response = user_detail_view(request, username=user.username)
|
||||
{%- endif %}
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
def test_not_authenticated(self, user: User, rf: RequestFactory):
|
||||
request = rf.get("/fake-url/")
|
||||
|
@ -116,5 +117,5 @@ class TestUserDetailView:
|
|||
login_url = reverse(settings.LOGIN_URL)
|
||||
|
||||
assert isinstance(response, HttpResponseRedirect)
|
||||
assert response.status_code == 302
|
||||
assert response.status_code == HTTPStatus.FOUND
|
||||
assert response.url == f"{login_url}?next=/fake-url/"
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
from django.urls import path
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.views import (
|
||||
user_detail_view,
|
||||
user_redirect_view,
|
||||
user_update_view,
|
||||
)
|
||||
from {{ cookiecutter.project_slug }}.users.views import user_detail_view
|
||||
from {{ cookiecutter.project_slug }}.users.views import user_redirect_view
|
||||
from {{ cookiecutter.project_slug }}.users.views import user_update_view
|
||||
|
||||
app_name = "users"
|
||||
urlpatterns = [
|
||||
|
|
|
@ -3,7 +3,9 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
|||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, RedirectView, UpdateView
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.generic import UpdateView
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
@ -28,7 +30,8 @@ class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||
success_message = _("Information successfully updated")
|
||||
|
||||
def get_success_url(self):
|
||||
assert self.request.user.is_authenticated # for mypy to know that the user is authenticated
|
||||
# for mypy to know that the user is authenticated
|
||||
assert self.request.user.is_authenticated
|
||||
return self.request.user.get_absolute_url()
|
||||
|
||||
def get_object(self):
|
||||
|
|
Loading…
Reference in New Issue
Block a user