Merged parent repo and configured refurbished tests

This commit is contained in:
Andrew-Chen-Wang 2020-03-14 13:29:08 -04:00
commit 9cf72849e6
28 changed files with 173 additions and 174 deletions

4
.github/FUNDING.yml vendored
View File

@ -1,7 +1,7 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: danielroygreenfeld
github: pydanny
patreon: roygreenfeld
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel

View File

@ -15,10 +15,6 @@ matrix:
include:
- name: Test results
script: tox -e py37
- name: Run flake8 on result
script: tox -e flake8
- name: Run black on result
script: tox -e black
- name: Black template
script: tox -e black-template
- name: Basic Docker

View File

@ -88,6 +88,7 @@ Listed in alphabetical order.
Chris Pappalardo `@ChrisPappalardo`_
Christopher Clarke `@chrisdev`_
Cole Mackenzie `@cmackenzie1`_
Cole Maclean `@cole`_ @cole
Collederas `@Collederas`_
Craig Margieson `@cmargieson`_
Cristian Vargas `@cdvv7788`_
@ -176,6 +177,7 @@ Listed in alphabetical order.
Oleg Russkin `@rolep`_
Pablo `@oubiga`_
Parbhat Puri `@parbhat`_
Pawan Chaurasia `@rjsnh1522`_
Peter Bittner `@bittner`_
Peter Coles `@mrcoles`_
Philipp Matthies `@canonnervio`_
@ -206,6 +208,7 @@ Listed in alphabetical order.
Tubo Shi `@Tubo`_
Umair Ashraf `@umrashrf`_ @fabumair
Vadim Iskuchekov `@Egregors`_ @egregors
Vicente G. Reyes `@reyesvicente`_ @highcenburg
Vitaly Babiy
Vivian Guillen `@viviangb`_
Vlad Doster `@vladdoster`_
@ -260,6 +263,7 @@ Listed in alphabetical order.
.. _@chuckus: https://github.com/chuckus
.. _@cmackenzie1: https://github.com/cmackenzie1
.. _@cmargieson: https://github.com/cmargieson
.. _@cole: https://github.com/cole
.. _@Collederas: https://github.com/Collederas
.. _@curtisstpierre: https://github.com/curtisstpierre
.. _@dadokkio: https://github.com/dadokkio
@ -337,11 +341,13 @@ Listed in alphabetical order.
.. _@originell: https://github.com/originell
.. _@oubiga: https://github.com/oubiga
.. _@parbhat: https://github.com/parbhat
.. _@rjsnh1522: https://github.com/rjsnh1522
.. _@pchiquet: https://github.com/pchiquet
.. _@phiberjenz: https://github.com/phiberjenz
.. _@purplediane: https://github.com/purplediane
.. _@raonyguimaraes: https://github.com/raonyguimaraes
.. _@reggieriser: https://github.com/reggieriser
.. _@reyesvicente: https://github.com/reyesvicente
.. _@rm--: https://github.com/rm--
.. _@rolep: https://github.com/rolep
.. _@romanosipenko: https://github.com/romanosipenko

View File

@ -105,16 +105,16 @@ This project is run by volunteers. Please support them in their efforts to maint
Projects that provide financial support to the maintainers:
Two Scoops of Django 1.11
Django Crash Course
~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/products/2017-06-29-tsd11-sticker-02.png
:name: Two Scoops of Django 1.11 Cover
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/files/Django-Crash-Course-300x436.jpg
:name: Django Crash Course: Covers Django 3.0 and Python 3.8
:align: center
:alt: Two Scoops of Django
:target: http://twoscoopspress.com/products/two-scoops-of-django-1-11
:alt: Django Crash Course
:target: https://www.roygreenfeld.com/products/django-crash-course
Two Scoops of Django is the best dessert-themed Django reference in the universe
Django Crash Course for Django 3.0 and Python 3.8 is the best cheese-themed Django reference in the universe!
pyup
~~~~~~~~~~~~~~~~~~
@ -135,7 +135,7 @@ and then editing the results to include your name, email, and various configurat
First, get Cookiecutter. Trust me, it's awesome::
$ pip install "cookiecutter>=1.4.0"
$ pip install "cookiecutter>=1.7.0"
Now run it against this repo::

View File

@ -25,7 +25,9 @@ Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there a
* ``celeryworker`` running a Celery worker process;
* ``celerybeat`` running a Celery beat process;
* ``flower`` running Flower_ (for more info, check out :ref:`CeleryFlower` instructions for local environment).
* ``flower`` running Flower_.
The ``flower`` service is served by Traefik over HTTPS, through the port ``5555``. For more information about Flower and its login credentials, check out :ref:`CeleryFlower` instructions for local environment.
.. _`Flower`: https://github.com/mher/flower

View File

@ -1,7 +1,4 @@
[pytest]
addopts = -x --tb=short
addopts = -v --tb=short
python_paths = .
norecursedirs = .tox .git */migrations/* */static/* docs venv */{{cookiecutter.project_slug}}/*
markers =
flake8: Run flake8 on all possible template combinations
black: Run black on all possible template combinations

View File

@ -6,12 +6,12 @@ binaryornot==0.4.4
# ------------------------------------------------------------------------------
black==19.10b0
flake8==3.7.9
flake8-isort==2.8.0
# Testing
# ------------------------------------------------------------------------------
tox==3.14.3
pytest==5.3.5
pytest_cases==1.12.1
pytest-cookies==0.5.0
pytest-xdist==1.31.0
tox==3.14.5
pytest==5.4.1
pytest-cookies==0.5.1
pytest-instafail==0.4.1.post0
pyyaml==5.3

View File

@ -3,7 +3,6 @@ import re
import pytest
from cookiecutter.exceptions import FailedHookException
from pytest_cases import fixture_plus
import sh
import yaml
from binaryornot.check import is_binary
@ -26,67 +25,75 @@ def context():
}
@fixture_plus
@pytest.mark.parametrize("windows", ["y", "n"], ids=lambda yn: f"win:{yn}")
@pytest.mark.parametrize("use_docker", ["y", "n"], ids=lambda yn: f"docker:{yn}")
@pytest.mark.parametrize("use_celery", ["y", "n"], ids=lambda yn: f"celery:{yn}")
@pytest.mark.parametrize("use_mailhog", ["y", "n"], ids=lambda yn: f"mailhog:{yn}")
@pytest.mark.parametrize("use_sentry", ["y", "n"], ids=lambda yn: f"sentry:{yn}")
@pytest.mark.parametrize("use_compressor", ["y", "n"], ids=lambda yn: f"cmpr:{yn}")
@pytest.mark.parametrize("use_drf", ["y", "n"], ids=lambda yn: f"drf:{yn}")
@pytest.mark.parametrize(
"use_whitenoise,cloud_provider",
[
("y", "AWS"),
("y", "GCP"),
("y", "None"),
("n", "AWS"),
("n", "GCP"),
# no whitenoise + no cloud provider is not supported
],
ids=lambda id: f"wnoise:{id[0]}-cloud:{id[1]}",
)
@pytest.mark.parametrize(
"mail_service",
[
"Amazon SES",
"Mailgun",
"MailJet",
"Mandrill",
"Postmark",
"Sendgrid",
"SendinBlue",
"SparkPost",
"Plain/Vanilla Django-Anymail"
# GCP or None (i.e. no cloud provider) + Amazon SES is not supported
],
ids=lambda id: f"mail:{id[0]}",
)
def context_combination(
windows,
use_docker,
use_celery,
use_mailhog,
use_sentry,
use_compressor,
use_whitenoise,
use_drf,
cloud_provider,
mail_service,
):
"""Fixture that parametrize the function where it's used."""
return {
"windows": windows,
"use_docker": use_docker,
"use_compressor": use_compressor,
"use_celery": use_celery,
"use_mailhog": use_mailhog,
"use_sentry": use_sentry,
"use_whitenoise": use_whitenoise,
"use_drf": use_drf,
"cloud_provider": cloud_provider,
"mail_service": mail_service,
}
SUPPORTED_COMBINATIONS = [
{"open_source_license": "MIT"},
{"open_source_license": "BSD"},
{"open_source_license": "GPLv3"},
{"open_source_license": "Apache Software License 2.0"},
{"open_source_license": "Not open source"},
{"windows": "y"},
{"windows": "n"},
{"use_pycharm": "y"},
{"use_pycharm": "n"},
{"use_docker": "y"},
{"use_docker": "n"},
{"postgresql_version": "11.3"},
{"postgresql_version": "10.8"},
{"postgresql_version": "9.6"},
{"postgresql_version": "9.5"},
{"postgresql_version": "9.4"},
{"cloud_provider": "AWS", "use_whitenoise": "y"},
{"cloud_provider": "AWS", "use_whitenoise": "n"},
{"cloud_provider": "GCP", "use_whitenoise": "y"},
{"cloud_provider": "GCP", "use_whitenoise": "n"},
{"cloud_provider": "None", "use_whitenoise": "y"},
# Note: cloud_provider=None AND use_whitenoise=n is not supported
{"use_drf": "y"},
{"use_drf": "n"},
{"js_task_runner": "None"},
{"js_task_runner": "Gulp"},
{"custom_bootstrap_compilation": "y"},
{"custom_bootstrap_compilation": "n"},
{"use_compressor": "y"},
{"use_compressor": "n"},
{"use_celery": "y"},
{"use_celery": "n"},
{"use_mailhog": "y"},
{"use_mailhog": "n"},
{"use_sentry": "y"},
{"use_sentry": "n"},
{"use_whitenoise": "y"},
{"use_whitenoise": "n"},
{"use_heroku": "y"},
{"use_heroku": "n"},
{"ci_tool": "None"},
{"ci_tool": "Travis"},
{"ci_tool": "Gitlab"},
{"keep_local_envs_in_vcs": "y"},
{"keep_local_envs_in_vcs": "n"},
{"debug": "y"},
{"debug": "n"},
{"mail_service", "AWS SES"},
{"mail_service", "Mailgun"},
{"mail_service", "Mailjet"},
{"mail_service", "Mandrill"},
{"mail_service", "Postmark"},
{"mail_service", "Sendgrid"},
{"mail_service", "SendinBlue"},
{"mail_service", "SparkPost"},
{"mail_service", "Other SMTP"},
]
UNSUPPORTED_COMBINATIONS = [
{"cloud_provider": "None", "use_whitenoise": "n"},
{"cloud_provider": "GCP", "mail_service": "Amazon SES"},
{"cloud_provider": "None", "mail_service": "Amazon SES"}
]
def _fixture_id(ctx):
"""Helper to get a user friendly test name from the parametrized context."""
return "-".join(f"{key}:{value}" for key, value in ctx.items())
def build_files_list(root_dir):
@ -99,9 +106,7 @@ def build_files_list(root_dir):
def check_paths(paths):
"""Method to check all paths have correct substitutions,
used by other tests cases
"""
"""Method to check all paths have correct substitutions."""
# Assert that no match is found in any of the files
for path in paths:
if is_binary(path):
@ -113,13 +118,10 @@ def check_paths(paths):
assert match is None, msg.format(path)
def test_project_generation(cookies, context, context_combination):
"""
Test that project is generated and fully rendered.
This is parametrized for each combination from ``context_combination`` fixture
"""
result = cookies.bake(extra_context={**context, **context_combination})
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
def test_project_generation(cookies, context, context_override):
"""Test that project is generated and fully rendered."""
result = cookies.bake(extra_context={**context, **context_override})
assert result.exit_code == 0
assert result.exception is None
assert result.project.basename == context["project_slug"]
@ -130,34 +132,26 @@ def test_project_generation(cookies, context, context_combination):
check_paths(paths)
@pytest.mark.flake8
def test_flake8_passes(cookies, context_combination):
"""
Generated project should pass flake8.
This is parametrized for each combination from ``context_combination`` fixture
"""
result = cookies.bake(extra_context=context_combination)
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
def test_flake8_passes(cookies, context_override):
"""Generated project should pass flake8."""
result = cookies.bake(extra_context=context_override)
try:
sh.flake8(str(result.project))
except sh.ErrorReturnCode as e:
pytest.fail(e)
pytest.fail(e.stdout.decode())
@pytest.mark.black
def test_black_passes(cookies, context_combination):
"""
Generated project should pass black.
This is parametrized for each combination from ``context_combination`` fixture
"""
result = cookies.bake(extra_context=context_combination)
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
def test_black_passes(cookies, context_override):
"""Generated project should pass black."""
result = cookies.bake(extra_context=context_override)
try:
sh.black("--check", "--diff", "--exclude", "migrations", f"{result.project}/")
except sh.ErrorReturnCode as e:
pytest.fail(e)
pytest.fail(e.stdout.decode())
def test_travis_invokes_pytest(cookies, context):
@ -205,27 +199,10 @@ def test_invalid_slug(cookies, context, slug):
assert isinstance(result.exception, FailedHookException)
def test_no_whitenoise_and_no_cloud_provider(cookies, context):
"""It should not generate project if neither whitenoise or cloud provider are set"""
context.update({"use_whitenoise": "n", "cloud_provider": "None"})
result = cookies.bake(extra_context=context)
assert result.exit_code != 0
assert isinstance(result.exception, FailedHookException)
def test_gcp_with_aws_ses_mail_service(cookies, context):
"""It should not generate project if SES is set with GCP cloud provider"""
context.update({"cloud_provider": "GCP", "mail_service": "Amazon SES"})
result = cookies.bake(extra_context=context)
assert result.exit_code != 0
assert isinstance(result.exception, FailedHookException)
def test_no_cloud_provider_with_aws_ses_mail_service(cookies, context):
"""It should not generate project if SES is set with no cloud provider"""
context.update({"cloud_provider": "None", "mail_service": "Amazon SES"})
@pytest.mark.parametrize("invalid_context", UNSUPPORTED_COMBINATIONS)
def test_error_if_incompatible(cookies, context, invalid_context):
"""It should not generate project an incompatible combination is selected."""
context.update(invalid_context)
result = cookies.bake(extra_context=context)
assert result.exit_code != 0

12
tox.ini
View File

@ -1,18 +1,10 @@
[tox]
skipsdist = true
envlist = py37,flake8,black,black-template
envlist = py37,black-template
[testenv]
deps = -rrequirements.txt
commands = pytest -m "not flake8" -m "not black" {posargs:./tests}
[testenv:flake8]
deps = -rrequirements.txt
commands = pytest -m flake8 {posargs:./tests}
[testenv:black]
deps = -rrequirements.txt
commands = pytest -m black {posargs:./tests}
commands = pytest {posargs:./tests}
[testenv:black-template]
deps = black

View File

@ -13,8 +13,8 @@ indent_style = space
indent_size = 4
[*.py]
line_length = 120
known_first_party = {{ cookiecutter.project_slug }}
line_length = 88
known_first_party = {{cookiecutter.project_slug}},config
multi_line_output = 3
default_section = THIRDPARTY
recursive = true

View File

@ -16,4 +16,5 @@ repos:
entry: flake8
language: python
types: [python]
args: ['--config=setup.cfg']

View File

@ -2,4 +2,5 @@ release: python manage.py migrate
web: gunicorn config.wsgi:application
{% if cookiecutter.use_celery == "y" -%}
worker: celery worker --app=config.celery_app --loglevel=info
beat: celery beat --app=config.celery_app --loglevel=info
{%- endif %}

View File

@ -9,6 +9,11 @@ entryPoints:
web-secure:
# https
address: ":443"
{%- if cookiecutter.use_celery == 'y' %}
flower:
address: ":5555"
{%- endif %}
certificatesResolvers:
letsencrypt:
@ -41,6 +46,17 @@ http:
tls:
# https://docs.traefik.io/master/routing/routers/#certresolver
certResolver: letsencrypt
{%- if cookiecutter.use_celery == 'y' %}
flower-secure-router:
rule: "Host(`{{ cookiecutter.domain_name }}`)"
entryPoints:
- flower
service: flower
tls:
# https://docs.traefik.io/master/routing/routers/#certresolver
certResolver: letsencrypt
{%- endif %}
middlewares:
redirect:
@ -52,13 +68,20 @@ http:
# https://docs.traefik.io/master/middlewares/headers/#hostsproxyheaders
# https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
headers:
hostsProxyHeaders: ['X-CSRFToken']
hostsProxyHeaders: ["X-CSRFToken"]
services:
django:
loadBalancer:
servers:
- url: http://django:5000
{%- if cookiecutter.use_celery == 'y' %}
flower:
loadBalancer:
servers:
- url: http://flower:5555
{%- endif %}
providers:
# https://docs.traefik.io/master/providers/file/

View File

@ -1,5 +1,6 @@
from rest_framework.routers import DefaultRouter, SimpleRouter
from django.conf import settings
from rest_framework.routers import DefaultRouter, SimpleRouter
from {{ cookiecutter.project_slug }}.users.api.views import UserViewSet
if settings.DEBUG:

View File

@ -1,4 +1,5 @@
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.

View File

@ -1,3 +1,4 @@
"""isort:skip_file"""
{% if cookiecutter.use_sentry == 'y' -%}
import logging

View File

@ -1,10 +1,10 @@
from django.conf import settings
from django.urls import include, path
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic import TemplateView
from django.urls import include, path
from django.views import defaults as default_views
{% if cookiecutter.use_drf == 'y' -%}
from django.views.generic import TemplateView
{%- if cookiecutter.use_drf == 'y' %}
from rest_framework.authtoken.views import obtain_auth_token
{%- endif %}

View File

@ -42,6 +42,9 @@ services:
ports:
- "0.0.0.0:80:80"
- "0.0.0.0:443:443"
{%- if cookiecutter.use_celery == 'y' %}
- "0.0.0.0:5555:5555"
{%- endif %}
redis:
image: redis:5.0
@ -60,8 +63,6 @@ services:
flower:
<<: *django
image: {{ cookiecutter.project_slug }}_production_flower
ports:
- "5555:5555"
command: /start-flower
{%- endif %}

View File

@ -10,8 +10,8 @@ whitenoise==5.0.1 # https://github.com/evansd/whitenoise
{%- endif %}
redis==3.4.1 # https://github.com/andymccurdy/redis-py
{%- if cookiecutter.use_celery == "y" %}
celery==4.4.0 # pyup: < 5.0 # https://github.com/celery/celery
django-celery-beat==1.6.0 # https://github.com/celery/django-celery-beat
celery==4.4.1 # pyup: < 5.0 # https://github.com/celery/celery
django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat
{%- if cookiecutter.use_docker == 'y' %}
flower==0.9.3 # https://github.com/mher/flower
{%- endif %}
@ -19,11 +19,11 @@ flower==0.9.3 # https://github.com/mher/flower
# Django
# ------------------------------------------------------------------------------
django==2.2.10 # pyup: < 3.0 # https://www.djangoproject.com/
django==2.2.11 # pyup: < 3.0 # https://www.djangoproject.com/
django-environ==0.4.5 # https://github.com/joke2k/django-environ
django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils
django-allauth==0.41.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==1.8.1 # https://github.com/django-crispy-forms/django-crispy-forms
django-crispy-forms==1.9.0 # https://github.com/django-crispy-forms/django-crispy-forms
{%- if cookiecutter.use_compressor == "y" %}
django-compressor==2.4 # https://github.com/django-compressor/django-compressor
{%- endif %}
@ -31,4 +31,3 @@ django-redis==4.11.0 # https://github.com/niwinz/django-redis
# Django REST Framework
djangorestframework==3.11.0 # https://github.com/encode/django-rest-framework
coreapi==2.3.3 # https://github.com/core-api/python-client

View File

@ -1,8 +1,8 @@
-r ./base.txt
Werkzeug==0.16.1 # https://github.com/pallets/werkzeug
ipdb==0.12.3 # https://github.com/gotcha/ipdb
Sphinx==2.3.1 # https://github.com/sphinx-doc/sphinx
Werkzeug==1.0.0 # https://github.com/pallets/werkzeug
ipdb==0.13.2 # https://github.com/gotcha/ipdb
Sphinx==2.4.4 # https://github.com/sphinx-doc/sphinx
{%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- else %}
@ -11,7 +11,7 @@ psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2
# Testing
# ------------------------------------------------------------------------------
mypy==0.761 # https://github.com/python/mypy
mypy==0.770 # https://github.com/python/mypy
django-stubs==1.4.0 # https://github.com/typeddjango/django-stubs
pytest==5.3.5 # https://github.com/pytest-dev/pytest
pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
@ -19,19 +19,20 @@ pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
# Code quality
# ------------------------------------------------------------------------------
flake8==3.7.9 # https://github.com/PyCQA/flake8
flake8-isort==2.8.0 # https://github.com/gforcada/flake8-isort
coverage==5.0.3 # https://github.com/nedbat/coveragepy
black==19.10b0 # https://github.com/ambv/black
pylint-django==2.0.13 # https://github.com/PyCQA/pylint-django
pylint-django==2.0.14 # https://github.com/PyCQA/pylint-django
{%- if cookiecutter.use_celery == 'y' %}
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
{%- endif %}
pre-commit==2.0.1 # https://github.com/pre-commit/pre-commit
pre-commit==2.2.0 # https://github.com/pre-commit/pre-commit
# Django
# ------------------------------------------------------------------------------
factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==2.2 # https://github.com/jazzband/django-debug-toolbar
django-extensions==2.2.6 # https://github.com/django-extensions/django-extensions
django-extensions==2.2.8 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django

View File

@ -5,10 +5,10 @@
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- if cookiecutter.use_whitenoise == 'n' %}
Collectfast==1.3.1 # https://github.com/antonagestam/collectfast
Collectfast==2.1.0 # https://github.com/antonagestam/collectfast
{%- endif %}
{%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==0.14.1 # https://github.com/getsentry/sentry-python
sentry-sdk==0.14.2 # https://github.com/getsentry/sentry-python
{%- endif %}
# Django

View File

@ -1,4 +1,5 @@
from rest_framework import serializers
from {{ cookiecutter.project_slug }}.users.models import User

View File

@ -1,7 +1,7 @@
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, UpdateModelMixin
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

View File

@ -1,4 +1,4 @@
from django.contrib.auth import get_user_model, forms
from django.contrib.auth import forms, get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _

View File

@ -1,7 +1,6 @@
import pytest
from celery.result import EagerResult
from {{ cookiecutter.project_slug }}.users.tasks import get_users_count
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory

View File

@ -1,5 +1,5 @@
import pytest
from django.urls import reverse, resolve
from django.urls import resolve, reverse
from {{ cookiecutter.project_slug }}.users.models import User

View File

@ -1,9 +1,9 @@
from django.urls import path
from {{ cookiecutter.project_slug }}.users.views import (
user_detail_view,
user_redirect_view,
user_update_view,
user_detail_view,
)
app_name = "users"

View File

@ -1,9 +1,9 @@
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.views.generic import DetailView, RedirectView, UpdateView
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, RedirectView, UpdateView
User = get_user_model()