mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-02-03 05:04:25 +03:00
First pass at running black across the project (#1602)
This commit is contained in:
parent
4bdfbb4307
commit
3f753e0411
56
docs/conf.py
56
docs/conf.py
|
@ -29,19 +29,19 @@ now = datetime.now()
|
|||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = 'Cookiecutter Django'
|
||||
project = "Cookiecutter Django"
|
||||
copyright = "2013-2018, Daniel Roy Greenfeld".format(now.year)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -49,9 +49,9 @@ copyright = "2013-2018, Daniel Roy Greenfeld".format(now.year)
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '{}.{}.{}'.format(*now.isocalendar())
|
||||
version = "{}.{}.{}".format(*now.isocalendar())
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '{}.{}.{}'.format(*now.isocalendar())
|
||||
release = "{}.{}.{}".format(*now.isocalendar())
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -65,7 +65,7 @@ release = '{}.{}.{}'.format(*now.isocalendar())
|
|||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
exclude_patterns = ["_build"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
# default_role = None
|
||||
|
@ -82,7 +82,7 @@ exclude_patterns = ['_build']
|
|||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
@ -92,7 +92,7 @@ pygments_style = 'sphinx'
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = "default"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
@ -121,7 +121,7 @@ html_theme = 'default'
|
|||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
|
@ -165,7 +165,7 @@ html_static_path = ['_static']
|
|||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'cookiecutter-djangodoc'
|
||||
htmlhelp_basename = "cookiecutter-djangodoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
@ -173,10 +173,8 @@ htmlhelp_basename = 'cookiecutter-djangodoc'
|
|||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
@ -184,10 +182,13 @@ latex_elements = {
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'cookiecutter-django.tex',
|
||||
'cookiecutter-django Documentation',
|
||||
'cookiecutter-django', 'manual'),
|
||||
(
|
||||
"index",
|
||||
"cookiecutter-django.tex",
|
||||
"cookiecutter-django Documentation",
|
||||
"cookiecutter-django",
|
||||
"manual",
|
||||
)
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
@ -216,8 +217,13 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'Cookiecutter Django', 'Cookiecutter Django documentation',
|
||||
['Daniel Roy Greenfeld'], 1)
|
||||
(
|
||||
"index",
|
||||
"Cookiecutter Django",
|
||||
"Cookiecutter Django documentation",
|
||||
["Daniel Roy Greenfeld"],
|
||||
1,
|
||||
)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
@ -230,9 +236,15 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Cookiecutter Django', 'Cookiecutter Django documentation',
|
||||
'Daniel Roy Greenfeld', 'Cookiecutter Django',
|
||||
'A Cookiecutter template for creating production-ready Django projects quickly.', 'Miscellaneous'),
|
||||
(
|
||||
"index",
|
||||
"Cookiecutter Django",
|
||||
"Cookiecutter Django documentation",
|
||||
"Daniel Roy Greenfeld",
|
||||
"Cookiecutter Django",
|
||||
"A Cookiecutter template for creating production-ready Django projects quickly.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
|
|
@ -30,96 +30,77 @@ SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
|||
|
||||
|
||||
def remove_open_source_files():
|
||||
file_names = [
|
||||
'CONTRIBUTORS.txt',
|
||||
]
|
||||
file_names = ["CONTRIBUTORS.txt"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_gplv3_files():
|
||||
file_names = [
|
||||
'COPYING',
|
||||
]
|
||||
file_names = ["COPYING"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_pycharm_files():
|
||||
idea_dir_path = '.idea'
|
||||
idea_dir_path = ".idea"
|
||||
if os.path.exists(idea_dir_path):
|
||||
shutil.rmtree(idea_dir_path)
|
||||
|
||||
docs_dir_path = os.path.join('docs', 'pycharm')
|
||||
docs_dir_path = os.path.join("docs", "pycharm")
|
||||
if os.path.exists(docs_dir_path):
|
||||
shutil.rmtree(docs_dir_path)
|
||||
|
||||
|
||||
def remove_docker_files():
|
||||
shutil.rmtree('compose')
|
||||
shutil.rmtree("compose")
|
||||
|
||||
file_names = [
|
||||
'local.yml',
|
||||
'production.yml',
|
||||
'.dockerignore',
|
||||
]
|
||||
file_names = ["local.yml", "production.yml", ".dockerignore"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_heroku_files():
|
||||
file_names = [
|
||||
'Procfile',
|
||||
'runtime.txt',
|
||||
'requirements.txt',
|
||||
]
|
||||
file_names = ["Procfile", "runtime.txt", "requirements.txt"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_grunt_files():
|
||||
file_names = [
|
||||
'Gruntfile.js',
|
||||
]
|
||||
file_names = ["Gruntfile.js"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_gulp_files():
|
||||
file_names = [
|
||||
'gulpfile.js',
|
||||
]
|
||||
file_names = ["gulpfile.js"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_packagejson_file():
|
||||
file_names = [
|
||||
'package.json',
|
||||
]
|
||||
file_names = ["package.json"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_celery_app():
|
||||
shutil.rmtree(os.path.join('{{ cookiecutter.project_slug }}', 'taskapp'))
|
||||
shutil.rmtree(os.path.join("{{ cookiecutter.project_slug }}", "taskapp"))
|
||||
|
||||
|
||||
def remove_dottravisyml_file():
|
||||
os.remove('.travis.yml')
|
||||
os.remove(".travis.yml")
|
||||
|
||||
|
||||
def append_to_project_gitignore(path):
|
||||
gitignore_file_path = '.gitignore'
|
||||
with open(gitignore_file_path, 'a') as gitignore_file:
|
||||
gitignore_file_path = ".gitignore"
|
||||
with open(gitignore_file_path, "a") as gitignore_file:
|
||||
gitignore_file.write(path)
|
||||
gitignore_file.write(os.linesep)
|
||||
|
||||
|
||||
def generate_random_string(length,
|
||||
using_digits=False,
|
||||
using_ascii_letters=False,
|
||||
using_punctuation=False):
|
||||
def generate_random_string(
|
||||
length, using_digits=False, using_ascii_letters=False, using_punctuation=False
|
||||
):
|
||||
"""
|
||||
Example:
|
||||
opting out for 50 symbol-long, [a-z][A-Z][0-9] string
|
||||
|
@ -134,19 +115,13 @@ def generate_random_string(length,
|
|||
if using_ascii_letters:
|
||||
symbols += string.ascii_letters
|
||||
if using_punctuation:
|
||||
symbols += string.punctuation \
|
||||
.replace('"', '') \
|
||||
.replace("'", '') \
|
||||
.replace('\\', '')
|
||||
return ''.join([random.choice(symbols) for _ in range(length)])
|
||||
symbols += string.punctuation.replace('"', "").replace("'", "").replace(
|
||||
"\\", ""
|
||||
)
|
||||
return "".join([random.choice(symbols) for _ in range(length)])
|
||||
|
||||
|
||||
def set_flag(file_path,
|
||||
flag,
|
||||
value=None,
|
||||
formatted=None,
|
||||
*args,
|
||||
**kwargs):
|
||||
def set_flag(file_path, flag, value=None, formatted=None, *args, **kwargs):
|
||||
if value is None:
|
||||
random_string = generate_random_string(*args, **kwargs)
|
||||
if random_string is None:
|
||||
|
@ -159,7 +134,7 @@ def set_flag(file_path,
|
|||
random_string = formatted.format(random_string)
|
||||
value = random_string
|
||||
|
||||
with open(file_path, 'r+') as f:
|
||||
with open(file_path, "r+") as f:
|
||||
file_contents = f.read().replace(flag, value)
|
||||
f.seek(0)
|
||||
f.write(file_contents)
|
||||
|
@ -171,10 +146,10 @@ def set_flag(file_path,
|
|||
def set_django_secret_key(file_path):
|
||||
django_secret_key = set_flag(
|
||||
file_path,
|
||||
'!!!SET DJANGO_SECRET_KEY!!!',
|
||||
"!!!SET DJANGO_SECRET_KEY!!!",
|
||||
length=64,
|
||||
using_digits=True,
|
||||
using_ascii_letters=True
|
||||
using_ascii_letters=True,
|
||||
)
|
||||
return django_secret_key
|
||||
|
||||
|
@ -182,28 +157,22 @@ def set_django_secret_key(file_path):
|
|||
def set_django_admin_url(file_path):
|
||||
django_admin_url = set_flag(
|
||||
file_path,
|
||||
'!!!SET DJANGO_ADMIN_URL!!!',
|
||||
formatted='^{}/',
|
||||
"!!!SET DJANGO_ADMIN_URL!!!",
|
||||
formatted="^{}/",
|
||||
length=32,
|
||||
using_digits=True,
|
||||
using_ascii_letters=True
|
||||
using_ascii_letters=True,
|
||||
)
|
||||
return django_admin_url
|
||||
|
||||
|
||||
def generate_postgres_user():
|
||||
return generate_random_string(
|
||||
length=32,
|
||||
using_ascii_letters=True
|
||||
)
|
||||
return generate_random_string(length=32, using_ascii_letters=True)
|
||||
|
||||
|
||||
def set_postgres_user(file_path,
|
||||
value=None):
|
||||
def set_postgres_user(file_path, value=None):
|
||||
postgres_user = set_flag(
|
||||
file_path,
|
||||
'!!!SET POSTGRES_USER!!!',
|
||||
value=value or generate_postgres_user()
|
||||
file_path, "!!!SET POSTGRES_USER!!!", value=value or generate_postgres_user()
|
||||
)
|
||||
return postgres_user
|
||||
|
||||
|
@ -211,47 +180,47 @@ def set_postgres_user(file_path,
|
|||
def set_postgres_password(file_path):
|
||||
postgres_password = set_flag(
|
||||
file_path,
|
||||
'!!!SET POSTGRES_PASSWORD!!!',
|
||||
"!!!SET POSTGRES_PASSWORD!!!",
|
||||
length=64,
|
||||
using_digits=True,
|
||||
using_ascii_letters=True
|
||||
using_ascii_letters=True,
|
||||
)
|
||||
return postgres_password
|
||||
|
||||
|
||||
def append_to_gitignore_file(s):
|
||||
with open('.gitignore', 'a') as gitignore_file:
|
||||
with open(".gitignore", "a") as gitignore_file:
|
||||
gitignore_file.write(s)
|
||||
gitignore_file.write(os.linesep)
|
||||
|
||||
|
||||
def set_flags_in_envs(postgres_user):
|
||||
local_postgres_envs_path = os.path.join('.envs', '.local', '.postgres')
|
||||
local_postgres_envs_path = os.path.join(".envs", ".local", ".postgres")
|
||||
set_postgres_user(local_postgres_envs_path, value=postgres_user)
|
||||
set_postgres_password(local_postgres_envs_path)
|
||||
|
||||
production_django_envs_path = os.path.join('.envs', '.production', '.django')
|
||||
production_django_envs_path = os.path.join(".envs", ".production", ".django")
|
||||
set_django_secret_key(production_django_envs_path)
|
||||
set_django_admin_url(production_django_envs_path)
|
||||
|
||||
production_postgres_envs_path = os.path.join('.envs', '.production', '.postgres')
|
||||
production_postgres_envs_path = os.path.join(".envs", ".production", ".postgres")
|
||||
set_postgres_user(production_postgres_envs_path, value=postgres_user)
|
||||
set_postgres_password(production_postgres_envs_path)
|
||||
|
||||
|
||||
def set_flags_in_settings_files():
|
||||
set_django_secret_key(os.path.join('config', 'settings', 'local.py'))
|
||||
set_django_secret_key(os.path.join('config', 'settings', 'test.py'))
|
||||
set_django_secret_key(os.path.join("config", "settings", "local.py"))
|
||||
set_django_secret_key(os.path.join("config", "settings", "test.py"))
|
||||
|
||||
|
||||
def remove_envs_and_associated_files():
|
||||
shutil.rmtree('.envs')
|
||||
os.remove('merge_production_dotenvs_in_dotenv.py')
|
||||
shutil.rmtree(".envs")
|
||||
os.remove("merge_production_dotenvs_in_dotenv.py")
|
||||
|
||||
|
||||
def remove_celery_compose_dirs():
|
||||
shutil.rmtree(os.path.join('compose', 'local', 'django', 'celery'))
|
||||
shutil.rmtree(os.path.join('compose', 'production', 'django', 'celery'))
|
||||
shutil.rmtree(os.path.join("compose", "local", "django", "celery"))
|
||||
shutil.rmtree(os.path.join("compose", "production", "django", "celery"))
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -259,75 +228,67 @@ def main():
|
|||
set_flags_in_envs(postgres_user)
|
||||
set_flags_in_settings_files()
|
||||
|
||||
if '{{ cookiecutter.open_source_license }}' == 'Not open source':
|
||||
if "{{ cookiecutter.open_source_license }}" == "Not open source":
|
||||
remove_open_source_files()
|
||||
if '{{ cookiecutter.open_source_license}}' != 'GPLv3':
|
||||
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
||||
remove_gplv3_files()
|
||||
|
||||
if '{{ cookiecutter.use_pycharm }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_pycharm }}".lower() == "n":
|
||||
remove_pycharm_files()
|
||||
|
||||
if '{{ cookiecutter.use_docker }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||
remove_docker_files()
|
||||
|
||||
if '{{ cookiecutter.use_heroku }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||
remove_heroku_files()
|
||||
|
||||
if '{{ cookiecutter.use_docker }}'.lower() == 'n' and '{{ cookiecutter.use_heroku }}'.lower() == 'n':
|
||||
if '{{ cookiecutter.keep_local_envs_in_vcs }}'.lower() == 'y':
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "n" and "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
||||
print(
|
||||
INFO +
|
||||
".env(s) are only utilized when Docker Compose and/or "
|
||||
INFO + ".env(s) are only utilized when Docker Compose and/or "
|
||||
"Heroku support is enabled so keeping them does not "
|
||||
"make sense given your current setup." +
|
||||
TERMINATOR
|
||||
"make sense given your current setup." + TERMINATOR
|
||||
)
|
||||
remove_envs_and_associated_files()
|
||||
else:
|
||||
append_to_gitignore_file('.env')
|
||||
append_to_gitignore_file('.envs/*')
|
||||
if '{{ cookiecutter.keep_local_envs_in_vcs }}'.lower() == 'y':
|
||||
append_to_gitignore_file('!.envs/.local/')
|
||||
append_to_gitignore_file(".env")
|
||||
append_to_gitignore_file(".envs/*")
|
||||
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
||||
append_to_gitignore_file("!.envs/.local/")
|
||||
|
||||
if '{{ cookiecutter.js_task_runner}}'.lower() == 'gulp':
|
||||
if "{{ cookiecutter.js_task_runner}}".lower() == "gulp":
|
||||
remove_grunt_files()
|
||||
elif '{{ cookiecutter.js_task_runner}}'.lower() == 'grunt':
|
||||
elif "{{ cookiecutter.js_task_runner}}".lower() == "grunt":
|
||||
remove_gulp_files()
|
||||
else:
|
||||
remove_gulp_files()
|
||||
remove_grunt_files()
|
||||
remove_packagejson_file()
|
||||
if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] \
|
||||
and '{{ cookiecutter.use_docker }}'.lower() == 'y':
|
||||
if "{{ cookiecutter.js_task_runner }}".lower() in [
|
||||
"grunt", "gulp"
|
||||
] and "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||
print(
|
||||
WARNING +
|
||||
"Docker and {} JS task runner ".format(
|
||||
'{{ cookiecutter.js_task_runner }}'
|
||||
.lower()
|
||||
.capitalize()
|
||||
) +
|
||||
"working together not supported yet. "
|
||||
WARNING
|
||||
+ "Docker and {} JS task runner ".format(
|
||||
"{{ cookiecutter.js_task_runner }}".lower().capitalize()
|
||||
)
|
||||
+ "working together not supported yet. "
|
||||
"You can continue using the generated project like you "
|
||||
"normally would, however you would need to add a JS "
|
||||
"task runner service to your Docker Compose configuration "
|
||||
"manually." +
|
||||
TERMINATOR
|
||||
"manually." + TERMINATOR
|
||||
)
|
||||
|
||||
if '{{ cookiecutter.use_celery }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_celery }}".lower() == "n":
|
||||
remove_celery_app()
|
||||
if '{{ cookiecutter.use_docker }}'.lower() == 'y':
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||
remove_celery_compose_dirs()
|
||||
|
||||
if '{{ cookiecutter.use_travisci }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_travisci }}".lower() == "n":
|
||||
remove_dottravisyml_file()
|
||||
|
||||
print(
|
||||
SUCCESS +
|
||||
"Project initialized, keep up the good work!" +
|
||||
TERMINATOR
|
||||
)
|
||||
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -16,40 +16,41 @@ INFO = "\x1b[1;33m [INFO]: "
|
|||
HINT = "\x1b[3;33m"
|
||||
SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
||||
|
||||
project_slug = '{{ cookiecutter.project_slug }}'
|
||||
if hasattr(project_slug, 'isidentifier'):
|
||||
assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(project_slug)
|
||||
project_slug = "{{ cookiecutter.project_slug }}"
|
||||
if hasattr(project_slug, "isidentifier"):
|
||||
assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(
|
||||
project_slug
|
||||
)
|
||||
|
||||
assert "\\" not in "{{ cookiecutter.author_name }}", "Don't include backslashes in author name."
|
||||
|
||||
if '{{ cookiecutter.use_docker }}'.lower() == 'n':
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||
python_major_version = sys.version_info[0]
|
||||
if python_major_version == 2:
|
||||
print(
|
||||
WARNING +
|
||||
"Cookiecutter Django does not support Python 2. "
|
||||
WARNING + "Cookiecutter Django does not support Python 2. "
|
||||
"Stability is guaranteed with Python 3.6+ only, "
|
||||
"are you sure you want to proceed (y/n)? " +
|
||||
TERMINATOR
|
||||
"are you sure you want to proceed (y/n)? " + TERMINATOR
|
||||
)
|
||||
yes_options, no_options = frozenset(['y']), frozenset(['n'])
|
||||
yes_options, no_options = frozenset(["y"]), frozenset(["n"])
|
||||
while True:
|
||||
choice = raw_input().lower()
|
||||
if choice in yes_options:
|
||||
break
|
||||
|
||||
elif choice in no_options:
|
||||
print(
|
||||
INFO +
|
||||
"Generation process stopped as requested." +
|
||||
TERMINATOR
|
||||
)
|
||||
print(INFO + "Generation process stopped as requested." + TERMINATOR)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(
|
||||
HINT +
|
||||
"Please respond with {} or {}: ".format(
|
||||
', '.join(["'{}'".format(o) for o in yes_options if not o == '']),
|
||||
', '.join(["'{}'".format(o) for o in no_options if not o == ''])
|
||||
) +
|
||||
TERMINATOR
|
||||
HINT
|
||||
+ "Please respond with {} or {}: ".format(
|
||||
", ".join(
|
||||
["'{}'".format(o) for o in yes_options if not o == ""]
|
||||
),
|
||||
", ".join(
|
||||
["'{}'".format(o) for o in no_options if not o == ""]
|
||||
),
|
||||
)
|
||||
+ TERMINATOR
|
||||
)
|
||||
|
|
46
setup.py
46
setup.py
|
@ -10,42 +10,42 @@ except ImportError:
|
|||
|
||||
# Our version ALWAYS matches the version of Django we support
|
||||
# If Django has a new release, we branch, tag, then update this setting after the tag.
|
||||
version = '2.0.2'
|
||||
version = "2.0.2"
|
||||
|
||||
if sys.argv[-1] == 'tag':
|
||||
if sys.argv[-1] == "tag":
|
||||
os.system('git tag -a %s -m "version %s"' % (version, version))
|
||||
os.system('git push --tags')
|
||||
os.system("git push --tags")
|
||||
sys.exit()
|
||||
|
||||
with open('README.rst') as readme_file:
|
||||
with open("README.rst") as readme_file:
|
||||
long_description = readme_file.read()
|
||||
|
||||
setup(
|
||||
name='cookiecutter-django',
|
||||
name="cookiecutter-django",
|
||||
version=version,
|
||||
description='A Cookiecutter template for creating production-ready Django projects quickly.',
|
||||
description="A Cookiecutter template for creating production-ready Django projects quickly.",
|
||||
long_description=long_description,
|
||||
author='Daniel Roy Greenfeld',
|
||||
author_email='pydanny@gmail.com',
|
||||
url='https://github.com/pydanny/cookiecutter-django',
|
||||
author="Daniel Roy Greenfeld",
|
||||
author_email="pydanny@gmail.com",
|
||||
url="https://github.com/pydanny/cookiecutter-django",
|
||||
packages=[],
|
||||
license='BSD',
|
||||
license="BSD",
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Console',
|
||||
'Framework :: Django :: 2.0',
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Topic :: Software Development',
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Framework :: Django :: 2.0",
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: English",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Topic :: Software Development",
|
||||
],
|
||||
keywords=(
|
||||
'cookiecutter, Python, projects, project templates, django, '
|
||||
'skeleton, scaffolding, project directory, setup.py'
|
||||
"cookiecutter, Python, projects, project templates, django, "
|
||||
"skeleton, scaffolding, project directory, setup.py"
|
||||
),
|
||||
)
|
||||
|
|
|
@ -5,21 +5,21 @@ import sh
|
|||
import pytest
|
||||
from binaryornot.check import is_binary
|
||||
|
||||
PATTERN = '{{(\s?cookiecutter)[.](.*?)}}'
|
||||
PATTERN = "{{(\s?cookiecutter)[.](.*?)}}"
|
||||
RE_OBJ = re.compile(PATTERN)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def context():
|
||||
return {
|
||||
'project_name': 'My Test Project',
|
||||
'project_slug': 'my_test_project',
|
||||
'author_name': 'Test Author',
|
||||
'email': 'test@example.com',
|
||||
'description': 'A short description of the project.',
|
||||
'domain_name': 'example.com',
|
||||
'version': '0.1.0',
|
||||
'timezone': 'UTC',
|
||||
"project_name": "My Test Project",
|
||||
"project_slug": "my_test_project",
|
||||
"author_name": "Test Author",
|
||||
"email": "test@example.com",
|
||||
"description": "A short description of the project.",
|
||||
"domain_name": "example.com",
|
||||
"version": "0.1.0",
|
||||
"timezone": "UTC",
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,9 +40,10 @@ def check_paths(paths):
|
|||
for path in paths:
|
||||
if is_binary(path):
|
||||
continue
|
||||
for line in open(path, 'r'):
|
||||
|
||||
for line in open(path, "r"):
|
||||
match = RE_OBJ.search(line)
|
||||
msg = 'cookiecutter variable not replaced in {}'
|
||||
msg = "cookiecutter variable not replaced in {}"
|
||||
assert match is None, msg.format(path)
|
||||
|
||||
|
||||
|
@ -50,7 +51,7 @@ def test_default_configuration(cookies, context):
|
|||
result = cookies.bake(extra_context=context)
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project.basename == context['project_slug']
|
||||
assert result.project.basename == context["project_slug"]
|
||||
assert result.project.isdir()
|
||||
|
||||
paths = build_files_list(str(result.project))
|
||||
|
@ -58,9 +59,9 @@ def test_default_configuration(cookies, context):
|
|||
check_paths(paths)
|
||||
|
||||
|
||||
@pytest.fixture(params=['use_mailhog', 'use_celery', 'windows'])
|
||||
@pytest.fixture(params=["use_mailhog", "use_celery", "windows"])
|
||||
def feature_context(request, context):
|
||||
context.update({request.param: 'y'})
|
||||
context.update({request.param: "y"})
|
||||
return context
|
||||
|
||||
|
||||
|
@ -68,7 +69,7 @@ def test_enabled_features(cookies, feature_context):
|
|||
result = cookies.bake(extra_context=feature_context)
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project.basename == feature_context['project_slug']
|
||||
assert result.project.basename == feature_context["project_slug"]
|
||||
assert result.project.isdir()
|
||||
|
||||
paths = build_files_list(str(result.project))
|
||||
|
|
|
@ -10,47 +10,44 @@ from .base import env
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = False
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_SECRET_KEY', default='!!!SET DJANGO_SECRET_KEY!!!')
|
||||
SECRET_KEY = env("DJANGO_SECRET_KEY", default="!!!SET DJANGO_SECRET_KEY!!!")
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
|
||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
||||
TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': ''
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": ""
|
||||
}
|
||||
}
|
||||
|
||||
# PASSWORDS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||
]
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#templates
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa F405
|
||||
TEMPLATES[0]['OPTIONS']['loaders'] = [ # noqa F405
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG # noqa F405
|
||||
TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
|
||||
(
|
||||
'django.template.loaders.cached.Loader',
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
"django.template.loaders.filesystem.Loader",
|
||||
"django.template.loaders.app_directories.Loader",
|
||||
],
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
# EMAIL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||
EMAIL_HOST = 'localhost'
|
||||
EMAIL_HOST = "localhost"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
||||
EMAIL_PORT = 1025
|
||||
|
||||
|
|
|
@ -6,32 +6,47 @@ from django.views.generic import TemplateView
|
|||
from django.views import defaults as default_views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', TemplateView.as_view(template_name='pages/home.html'), name='home'),
|
||||
url(r'^about/$', TemplateView.as_view(template_name='pages/about.html'), name='about'),
|
||||
|
||||
url(r"^$", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||
url(
|
||||
r"^about/$",
|
||||
TemplateView.as_view(template_name="pages/about.html"),
|
||||
name="about",
|
||||
),
|
||||
# Django Admin, use {% raw %}{% url 'admin:index' %}{% endraw %}
|
||||
url(settings.ADMIN_URL, admin.site.urls),
|
||||
|
||||
# User management
|
||||
url(r'^users/', include('{{ cookiecutter.project_slug }}.users.urls', namespace='users')),
|
||||
url(r'^accounts/', include('allauth.urls')),
|
||||
|
||||
url(
|
||||
r"^users/",
|
||||
include("{{ cookiecutter.project_slug }}.users.urls", namespace="users"),
|
||||
),
|
||||
url(r"^accounts/", include("allauth.urls")),
|
||||
# Your stuff: custom urls includes go here
|
||||
|
||||
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
] + static(
|
||||
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
|
||||
)
|
||||
|
||||
if settings.DEBUG:
|
||||
# This allows the error pages to be debugged during development, just visit
|
||||
# these url in browser to see how these error pages look like.
|
||||
urlpatterns += [
|
||||
url(r'^400/$', default_views.bad_request, kwargs={'exception': Exception('Bad Request!')}),
|
||||
url(r'^403/$', default_views.permission_denied, kwargs={'exception': Exception('Permission Denied')}),
|
||||
url(r'^404/$', default_views.page_not_found, kwargs={'exception': Exception('Page not Found')}),
|
||||
url(r'^500/$', default_views.server_error),
|
||||
url(
|
||||
r"^400/$",
|
||||
default_views.bad_request,
|
||||
kwargs={"exception": Exception("Bad Request!")},
|
||||
),
|
||||
url(
|
||||
r"^403/$",
|
||||
default_views.permission_denied,
|
||||
kwargs={"exception": Exception("Permission Denied")},
|
||||
),
|
||||
url(
|
||||
r"^404/$",
|
||||
default_views.page_not_found,
|
||||
kwargs={"exception": Exception("Page not Found")},
|
||||
),
|
||||
url(r"^500/$", default_views.server_error),
|
||||
]
|
||||
if 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
if "debug_toolbar" in settings.INSTALLED_APPS:
|
||||
import debug_toolbar
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
] + urlpatterns
|
||||
|
||||
urlpatterns = [url(r"^__debug__/", include(debug_toolbar.urls))] + urlpatterns
|
||||
|
|
|
@ -27,19 +27,19 @@ import sys
|
|||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = '{{ cookiecutter.project_name }}'
|
||||
project = "{{ cookiecutter.project_name }}"
|
||||
copyright = """{% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}"""
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -47,9 +47,9 @@ copyright = """{% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}"""
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1'
|
||||
version = "0.1"
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1'
|
||||
release = "0.1"
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -63,7 +63,7 @@ release = '0.1'
|
|||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
exclude_patterns = ["_build"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
# default_role = None
|
||||
|
@ -80,7 +80,7 @@ exclude_patterns = ['_build']
|
|||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
@ -90,7 +90,7 @@ pygments_style = 'sphinx'
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = "default"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
@ -119,7 +119,7 @@ html_theme = 'default'
|
|||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
|
@ -163,7 +163,7 @@ html_static_path = ['_static']
|
|||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '{{ cookiecutter.project_slug }}doc'
|
||||
htmlhelp_basename = "{{ cookiecutter.project_slug }}doc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
@ -171,10 +171,8 @@ htmlhelp_basename = '{{ cookiecutter.project_slug }}doc'
|
|||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
@ -182,10 +180,13 @@ latex_elements = {
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'{{ cookiecutter.project_slug }}.tex',
|
||||
'{{ cookiecutter.project_name }} Documentation',
|
||||
"""{{ cookiecutter.author_name }}""", 'manual'),
|
||||
(
|
||||
"index",
|
||||
"{{ cookiecutter.project_slug }}.tex",
|
||||
"{{ cookiecutter.project_name }} Documentation",
|
||||
"""{{ cookiecutter.author_name }}""",
|
||||
"manual",
|
||||
)
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
@ -214,8 +215,13 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', '{{ cookiecutter.project_slug }}', '{{ cookiecutter.project_name }} Documentation',
|
||||
["""{{ cookiecutter.author_name }}"""], 1)
|
||||
(
|
||||
"index",
|
||||
"{{ cookiecutter.project_slug }}",
|
||||
"{{ cookiecutter.project_name }} Documentation",
|
||||
["""{{ cookiecutter.author_name }}"""],
|
||||
1,
|
||||
)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
@ -228,9 +234,15 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', '{{ cookiecutter.project_slug }}', '{{ cookiecutter.project_name }} Documentation',
|
||||
"""{{ cookiecutter.author_name }}""", '{{ cookiecutter.project_name }}',
|
||||
"""{{ cookiecutter.description }}""", 'Miscellaneous'),
|
||||
(
|
||||
"index",
|
||||
"{{ cookiecutter.project_slug }}",
|
||||
"{{ cookiecutter.project_name }} Documentation",
|
||||
"""{{ cookiecutter.author_name }}""",
|
||||
"{{ cookiecutter.project_name }}",
|
||||
"""{{ cookiecutter.description }}""",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
|
@ -19,11 +19,12 @@ if __name__ == '__main__':
|
|||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
)
|
||||
|
||||
raise
|
||||
|
||||
# This allows easy placement of apps within the interior
|
||||
# {{ cookiecutter.project_slug }} directory.
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(os.path.join(current_path, '{{ cookiecutter.project_slug }}'))
|
||||
sys.path.append(os.path.join(current_path, "{{ cookiecutter.project_slug }}"))
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
|
|
|
@ -4,21 +4,21 @@ from typing import Sequence
|
|||
import pytest
|
||||
|
||||
ROOT_DIR_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
PRODUCTION_DOTENVS_DIR_PATH = os.path.join(ROOT_DIR_PATH, '.envs', '.production')
|
||||
PRODUCTION_DOTENVS_DIR_PATH = os.path.join(ROOT_DIR_PATH, ".envs", ".production")
|
||||
PRODUCTION_DOTENV_FILE_PATHS = [
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, '.django'),
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, '.postgres'),
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, '.caddy'),
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".django"),
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".postgres"),
|
||||
os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".caddy"),
|
||||
]
|
||||
DOTENV_FILE_PATH = os.path.join(ROOT_DIR_PATH, '.env')
|
||||
DOTENV_FILE_PATH = os.path.join(ROOT_DIR_PATH, ".env")
|
||||
|
||||
|
||||
def merge(output_file_path: str,
|
||||
merged_file_paths: Sequence[str],
|
||||
append_linesep: bool = True) -> None:
|
||||
with open(output_file_path, 'w') as output_file:
|
||||
def merge(
|
||||
output_file_path: str, merged_file_paths: Sequence[str], append_linesep: bool = True
|
||||
) -> None:
|
||||
with open(output_file_path, "w") as output_file:
|
||||
for merged_file_path in merged_file_paths:
|
||||
with open(merged_file_path, 'r') as merged_file:
|
||||
with open(merged_file_path, "r") as merged_file:
|
||||
merged_file_content = merged_file.read()
|
||||
output_file.write(merged_file_content)
|
||||
if append_linesep:
|
||||
|
@ -29,26 +29,24 @@ def main():
|
|||
merge(DOTENV_FILE_PATH, PRODUCTION_DOTENV_FILE_PATHS)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('merged_file_count', range(3))
|
||||
@pytest.mark.parametrize('append_linesep', [True, False])
|
||||
def test_merge(tmpdir_factory,
|
||||
merged_file_count: int,
|
||||
append_linesep: bool):
|
||||
@pytest.mark.parametrize("merged_file_count", range(3))
|
||||
@pytest.mark.parametrize("append_linesep", [True, False])
|
||||
def test_merge(tmpdir_factory, merged_file_count: int, append_linesep: bool):
|
||||
tmp_dir_path = str(tmpdir_factory.getbasetemp())
|
||||
|
||||
output_file_path = os.path.join(tmp_dir_path, '.env')
|
||||
output_file_path = os.path.join(tmp_dir_path, ".env")
|
||||
|
||||
expected_output_file_content = ''
|
||||
expected_output_file_content = ""
|
||||
merged_file_paths = []
|
||||
for i in range(merged_file_count):
|
||||
merged_file_ord = i + 1
|
||||
|
||||
merged_filename = '.service{}'.format(merged_file_ord)
|
||||
merged_filename = ".service{}".format(merged_file_ord)
|
||||
merged_file_path = os.path.join(tmp_dir_path, merged_filename)
|
||||
|
||||
merged_file_content = merged_filename * merged_file_ord
|
||||
|
||||
with open(merged_file_path, 'w+') as file:
|
||||
with open(merged_file_path, "w+") as file:
|
||||
file.write(merged_file_content)
|
||||
|
||||
expected_output_file_content += merged_file_content
|
||||
|
@ -59,11 +57,11 @@ def test_merge(tmpdir_factory,
|
|||
|
||||
merge(output_file_path, merged_file_paths, append_linesep)
|
||||
|
||||
with open(output_file_path, 'r') as output_file:
|
||||
with open(output_file_path, "r") as output_file:
|
||||
actual_output_file_content = output_file.read()
|
||||
|
||||
assert actual_output_file_content == expected_output_file_content
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
__version__ = '{{ cookiecutter.version }}'
|
||||
__version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.', 1).split('.')])
|
||||
__version__ = "{{ cookiecutter.version }}"
|
||||
__version_info__ = tuple(
|
||||
[
|
||||
int(num) if num.isdigit() else num
|
||||
for num in __version__.replace("-", ".", 1).split(".")
|
||||
]
|
||||
)
|
||||
|
|
|
@ -9,23 +9,34 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Site',
|
||||
name="Site",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('domain', models.CharField(
|
||||
max_length=100, verbose_name='domain name', validators=[_simple_domain_name_validator]
|
||||
)),
|
||||
('name', models.CharField(max_length=50, verbose_name='display name')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"domain",
|
||||
models.CharField(
|
||||
max_length=100,
|
||||
verbose_name="domain name",
|
||||
validators=[_simple_domain_name_validator],
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=50, verbose_name="display name")),
|
||||
],
|
||||
options={
|
||||
'ordering': ('domain',),
|
||||
'db_table': 'django_site',
|
||||
'verbose_name': 'site',
|
||||
'verbose_name_plural': 'sites',
|
||||
"ordering": ("domain",),
|
||||
"db_table": "django_site",
|
||||
"verbose_name": "site",
|
||||
"verbose_name_plural": "sites",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
managers=[
|
||||
('objects', django.contrib.sites.models.SiteManager()),
|
||||
],
|
||||
),
|
||||
managers=[("objects", django.contrib.sites.models.SiteManager())],
|
||||
)
|
||||
]
|
||||
|
|
|
@ -4,17 +4,17 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sites', '0001_initial'),
|
||||
]
|
||||
dependencies = [("sites", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='domain',
|
||||
model_name="site",
|
||||
name="domain",
|
||||
field=models.CharField(
|
||||
max_length=100, unique=True, validators=[django.contrib.sites.models._simple_domain_name_validator],
|
||||
verbose_name='domain name'
|
||||
max_length=100,
|
||||
unique=True,
|
||||
validators=[django.contrib.sites.models._simple_domain_name_validator],
|
||||
verbose_name="domain name",
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -9,34 +9,26 @@ from django.db import migrations
|
|||
|
||||
def update_site_forward(apps, schema_editor):
|
||||
"""Set site domain and name."""
|
||||
Site = apps.get_model('sites', 'Site')
|
||||
Site = apps.get_model("sites", "Site")
|
||||
Site.objects.update_or_create(
|
||||
id=settings.SITE_ID,
|
||||
defaults={
|
||||
'domain': '{{cookiecutter.domain_name}}',
|
||||
'name': '{{cookiecutter.project_name}}'
|
||||
}
|
||||
"domain": "{{cookiecutter.domain_name}}",
|
||||
"name": "{{cookiecutter.project_name}}",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def update_site_backward(apps, schema_editor):
|
||||
"""Revert site domain and name to default."""
|
||||
Site = apps.get_model('sites', 'Site')
|
||||
Site = apps.get_model("sites", "Site")
|
||||
Site.objects.update_or_create(
|
||||
id=settings.SITE_ID,
|
||||
defaults={
|
||||
'domain': 'example.com',
|
||||
'name': 'example.com'
|
||||
}
|
||||
id=settings.SITE_ID, defaults={"domain": "example.com", "name": "example.com"}
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sites', '0002_alter_domain_unique'),
|
||||
]
|
||||
dependencies = [("sites", "0002_alter_domain_unique")]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_site_forward, update_site_backward),
|
||||
]
|
||||
operations = [migrations.RunPython(update_site_forward, update_site_backward)]
|
||||
|
|
|
@ -4,10 +4,12 @@ from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
|||
|
||||
|
||||
class AccountAdapter(DefaultAccountAdapter):
|
||||
|
||||
def is_open_for_signup(self, request):
|
||||
return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True)
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
||||
|
||||
class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
|
||||
def is_open_for_signup(self, request, sociallogin):
|
||||
return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True)
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
|
|
@ -6,15 +6,16 @@ from .models import User
|
|||
|
||||
|
||||
class MyUserChangeForm(UserChangeForm):
|
||||
|
||||
class Meta(UserChangeForm.Meta):
|
||||
model = User
|
||||
|
||||
|
||||
class MyUserCreationForm(UserCreationForm):
|
||||
|
||||
error_message = UserCreationForm.error_messages.update({
|
||||
'duplicate_username': 'This username has already been taken.'
|
||||
})
|
||||
error_message = UserCreationForm.error_messages.update(
|
||||
{"duplicate_username": "This username has already been taken."}
|
||||
)
|
||||
|
||||
class Meta(UserCreationForm.Meta):
|
||||
model = User
|
||||
|
@ -25,15 +26,14 @@ class MyUserCreationForm(UserCreationForm):
|
|||
User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return username
|
||||
raise forms.ValidationError(self.error_messages['duplicate_username'])
|
||||
|
||||
raise forms.ValidationError(self.error_messages["duplicate_username"])
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class MyUserAdmin(AuthUserAdmin):
|
||||
form = MyUserChangeForm
|
||||
add_form = MyUserCreationForm
|
||||
fieldsets = (
|
||||
('User Profile', {'fields': ('name',)}),
|
||||
) + AuthUserAdmin.fieldsets
|
||||
list_display = ('username', 'name', 'is_superuser')
|
||||
search_fields = ['name']
|
||||
fieldsets = (("User Profile", {"fields": ("name",)}),) + AuthUserAdmin.fieldsets
|
||||
list_display = ("username", "name", "is_superuser")
|
||||
search_fields = ["name"]
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = '{{cookiecutter.project_slug}}.users'
|
||||
name = "{{cookiecutter.project_slug}}.users"
|
||||
verbose_name = "Users"
|
||||
|
||||
def ready(self):
|
||||
|
|
|
@ -8,36 +8,125 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
]
|
||||
dependencies = [("auth", "0008_alter_user_username_max_length")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
name="User",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
blank=True, null=True, verbose_name="last login"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_superuser",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=30, verbose_name="first name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="last name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
blank=True, max_length=255, verbose_name="Name of User"
|
||||
),
|
||||
),
|
||||
(
|
||||
"groups",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Group",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_permissions",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="Specific permissions for this user.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Permission",
|
||||
verbose_name="user permissions",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'users',
|
||||
'verbose_name': 'user',
|
||||
'abstract': False,
|
||||
"verbose_name_plural": "users",
|
||||
"verbose_name": "user",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
managers=[("objects", django.contrib.auth.models.UserManager())],
|
||||
)
|
||||
]
|
||||
|
|
|
@ -8,10 +8,10 @@ class User(AbstractUser):
|
|||
|
||||
# First Name and Last Name do not cover name patterns
|
||||
# around the globe.
|
||||
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
||||
name = models.CharField(_("Name of User"), blank=True, max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('users:detail', kwargs={'username': self.username})
|
||||
return reverse("users:detail", kwargs={"username": self.username})
|
||||
|
|
|
@ -2,10 +2,10 @@ import factory
|
|||
|
||||
|
||||
class UserFactory(factory.django.DjangoModelFactory):
|
||||
username = factory.Sequence(lambda n: f'user-{n}')
|
||||
email = factory.Sequence(lambda n: f'user-{n}@example.com')
|
||||
password = factory.PostGenerationMethodCall('set_password', 'password')
|
||||
username = factory.Sequence(lambda n: f"user-{n}")
|
||||
email = factory.Sequence(lambda n: f"user-{n}@example.com")
|
||||
password = factory.PostGenerationMethodCall("set_password", "password")
|
||||
|
||||
class Meta:
|
||||
model = 'users.User'
|
||||
django_get_or_create = ('username', )
|
||||
model = "users.User"
|
||||
django_get_or_create = ("username",)
|
||||
|
|
|
@ -6,30 +6,34 @@ from ..admin import MyUserCreationForm
|
|||
class TestMyUserCreationForm(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.make_user('notalamode', 'notalamodespassword')
|
||||
self.user = self.make_user("notalamode", "notalamodespassword")
|
||||
|
||||
def test_clean_username_success(self):
|
||||
# Instantiate the form with a new username
|
||||
form = MyUserCreationForm({
|
||||
'username': 'alamode',
|
||||
'password1': '7jefB#f@Cc7YJB]2v',
|
||||
'password2': '7jefB#f@Cc7YJB]2v',
|
||||
})
|
||||
form = MyUserCreationForm(
|
||||
{
|
||||
"username": "alamode",
|
||||
"password1": "7jefB#f@Cc7YJB]2v",
|
||||
"password2": "7jefB#f@Cc7YJB]2v",
|
||||
}
|
||||
)
|
||||
# Run is_valid() to trigger the validation
|
||||
valid = form.is_valid()
|
||||
self.assertTrue(valid)
|
||||
|
||||
# Run the actual clean_username method
|
||||
username = form.clean_username()
|
||||
self.assertEqual('alamode', username)
|
||||
self.assertEqual("alamode", username)
|
||||
|
||||
def test_clean_username_false(self):
|
||||
# Instantiate the form with the same username as self.user
|
||||
form = MyUserCreationForm({
|
||||
'username': self.user.username,
|
||||
'password1': 'notalamodespassword',
|
||||
'password2': 'notalamodespassword',
|
||||
})
|
||||
form = MyUserCreationForm(
|
||||
{
|
||||
"username": self.user.username,
|
||||
"password1": "notalamodespassword",
|
||||
"password2": "notalamodespassword",
|
||||
}
|
||||
)
|
||||
# Run is_valid() to trigger the validation, which is going to fail
|
||||
# because the username is already taken
|
||||
valid = form.is_valid()
|
||||
|
@ -37,4 +41,4 @@ class TestMyUserCreationForm(TestCase):
|
|||
|
||||
# The form.errors dict should contain a single error called 'username'
|
||||
self.assertTrue(len(form.errors) == 1)
|
||||
self.assertTrue('username' in form.errors)
|
||||
self.assertTrue("username" in form.errors)
|
||||
|
|
|
@ -9,11 +9,8 @@ class TestUser(TestCase):
|
|||
def test__str__(self):
|
||||
self.assertEqual(
|
||||
self.user.__str__(),
|
||||
'testuser' # This is the default username for self.make_user()
|
||||
"testuser", # This is the default username for self.make_user()
|
||||
)
|
||||
|
||||
def test_get_absolute_url(self):
|
||||
self.assertEqual(
|
||||
self.user.get_absolute_url(),
|
||||
'/users/testuser/'
|
||||
)
|
||||
self.assertEqual(self.user.get_absolute_url(), "/users/testuser/")
|
||||
|
|
|
@ -11,41 +11,34 @@ class TestUserURLs(TestCase):
|
|||
|
||||
def test_list_reverse(self):
|
||||
"""users:list should reverse to /users/."""
|
||||
self.assertEqual(reverse('users:list'), '/users/')
|
||||
self.assertEqual(reverse("users:list"), "/users/")
|
||||
|
||||
def test_list_resolve(self):
|
||||
"""/users/ should resolve to users:list."""
|
||||
self.assertEqual(resolve('/users/').view_name, 'users:list')
|
||||
self.assertEqual(resolve("/users/").view_name, "users:list")
|
||||
|
||||
def test_redirect_reverse(self):
|
||||
"""users:redirect should reverse to /users/~redirect/."""
|
||||
self.assertEqual(reverse('users:redirect'), '/users/~redirect/')
|
||||
self.assertEqual(reverse("users:redirect"), "/users/~redirect/")
|
||||
|
||||
def test_redirect_resolve(self):
|
||||
"""/users/~redirect/ should resolve to users:redirect."""
|
||||
self.assertEqual(
|
||||
resolve('/users/~redirect/').view_name,
|
||||
'users:redirect'
|
||||
)
|
||||
self.assertEqual(resolve("/users/~redirect/").view_name, "users:redirect")
|
||||
|
||||
def test_detail_reverse(self):
|
||||
"""users:detail should reverse to /users/testuser/."""
|
||||
self.assertEqual(
|
||||
reverse('users:detail', kwargs={'username': 'testuser'}),
|
||||
'/users/testuser/'
|
||||
reverse("users:detail", kwargs={"username": "testuser"}), "/users/testuser/"
|
||||
)
|
||||
|
||||
def test_detail_resolve(self):
|
||||
"""/users/testuser/ should resolve to users:detail."""
|
||||
self.assertEqual(resolve('/users/testuser/').view_name, 'users:detail')
|
||||
self.assertEqual(resolve("/users/testuser/").view_name, "users:detail")
|
||||
|
||||
def test_update_reverse(self):
|
||||
"""users:update should reverse to /users/~update/."""
|
||||
self.assertEqual(reverse('users:update'), '/users/~update/')
|
||||
self.assertEqual(reverse("users:update"), "/users/~update/")
|
||||
|
||||
def test_update_resolve(self):
|
||||
"""/users/~update/ should resolve to users:update."""
|
||||
self.assertEqual(
|
||||
resolve('/users/~update/').view_name,
|
||||
'users:update'
|
||||
)
|
||||
self.assertEqual(resolve("/users/~update/").view_name, "users:update")
|
||||
|
|
|
@ -2,10 +2,7 @@ from django.test import RequestFactory
|
|||
|
||||
from test_plus.test import TestCase
|
||||
|
||||
from ..views import (
|
||||
UserRedirectView,
|
||||
UserUpdateView
|
||||
)
|
||||
from ..views import (UserRedirectView, UserUpdateView)
|
||||
|
||||
|
||||
class BaseUserTestCase(TestCase):
|
||||
|
@ -21,17 +18,14 @@ class TestUserRedirectView(BaseUserTestCase):
|
|||
# Instantiate the view directly. Never do this outside a test!
|
||||
view = UserRedirectView()
|
||||
# Generate a fake request
|
||||
request = self.factory.get('/fake-url')
|
||||
request = self.factory.get("/fake-url")
|
||||
# Attach the user to the request
|
||||
request.user = self.user
|
||||
# Attach the request to the view
|
||||
view.request = request
|
||||
# Expect: '/users/testuser/', as that is the default username for
|
||||
# self.make_user()
|
||||
self.assertEqual(
|
||||
view.get_redirect_url(),
|
||||
'/users/testuser/'
|
||||
)
|
||||
self.assertEqual(view.get_redirect_url(), "/users/testuser/")
|
||||
|
||||
|
||||
class TestUserUpdateView(BaseUserTestCase):
|
||||
|
@ -42,7 +36,7 @@ class TestUserUpdateView(BaseUserTestCase):
|
|||
# Instantiate the view directly. Never do this outside a test!
|
||||
self.view = UserUpdateView()
|
||||
# Generate a fake request
|
||||
request = self.factory.get('/fake-url')
|
||||
request = self.factory.get("/fake-url")
|
||||
# Attach the user to the request
|
||||
request.user = self.user
|
||||
# Attach the request to the view
|
||||
|
@ -51,14 +45,8 @@ class TestUserUpdateView(BaseUserTestCase):
|
|||
def test_get_success_url(self):
|
||||
# Expect: '/users/testuser/', as that is the default username for
|
||||
# self.make_user()
|
||||
self.assertEqual(
|
||||
self.view.get_success_url(),
|
||||
'/users/testuser/'
|
||||
)
|
||||
self.assertEqual(self.view.get_success_url(), "/users/testuser/")
|
||||
|
||||
def test_get_object(self):
|
||||
# Expect: self.user, as that is the request's user object
|
||||
self.assertEqual(
|
||||
self.view.get_object(),
|
||||
self.user
|
||||
)
|
||||
self.assertEqual(self.view.get_object(), self.user)
|
||||
|
|
|
@ -2,26 +2,14 @@ from django.conf.urls import url
|
|||
|
||||
from . import views
|
||||
|
||||
app_name = 'users'
|
||||
app_name = "users"
|
||||
urlpatterns = [
|
||||
url(regex=r"^$", view=views.UserListView.as_view(), name="list"),
|
||||
url(regex=r"^~redirect/$", view=views.UserRedirectView.as_view(), name="redirect"),
|
||||
url(regex=r"^~update/$", view=views.UserUpdateView.as_view(), name="update"),
|
||||
url(
|
||||
regex=r'^$',
|
||||
view=views.UserListView.as_view(),
|
||||
name='list'
|
||||
),
|
||||
url(
|
||||
regex=r'^~redirect/$',
|
||||
view=views.UserRedirectView.as_view(),
|
||||
name='redirect'
|
||||
),
|
||||
url(
|
||||
regex=r'^~update/$',
|
||||
view=views.UserUpdateView.as_view(),
|
||||
name='update'
|
||||
),
|
||||
url(
|
||||
regex=r'^(?P<username>[\w.@+-]+)/$',
|
||||
regex=r"^(?P<username>[\w.@+-]+)/$",
|
||||
view=views.UserDetailView.as_view(),
|
||||
name='detail'
|
||||
name="detail",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -8,29 +8,28 @@ from .models import User
|
|||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
# These next two lines tell the view to index lookups by username
|
||||
slug_field = 'username'
|
||||
slug_url_kwarg = 'username'
|
||||
slug_field = "username"
|
||||
slug_url_kwarg = "username"
|
||||
|
||||
|
||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||
permanent = False
|
||||
|
||||
def get_redirect_url(self):
|
||||
return reverse('users:detail',
|
||||
kwargs={'username': self.request.user.username})
|
||||
return reverse("users:detail", kwargs={"username": self.request.user.username})
|
||||
|
||||
|
||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
fields = ['name', ]
|
||||
fields = ["name"]
|
||||
|
||||
# we already imported User in the view code above, remember?
|
||||
model = User
|
||||
|
||||
# send the user back to their own page after a successful update
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:detail',
|
||||
kwargs={'username': self.request.user.username})
|
||||
return reverse("users:detail", kwargs={"username": self.request.user.username})
|
||||
|
||||
def get_object(self):
|
||||
# Only get the User record for the user making the request
|
||||
|
@ -40,5 +39,5 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
|||
class UserListView(LoginRequiredMixin, ListView):
|
||||
model = User
|
||||
# These next two lines tell the view to index lookups by username
|
||||
slug_field = 'username'
|
||||
slug_url_kwarg = 'username'
|
||||
slug_field = "username"
|
||||
slug_url_kwarg = "username"
|
||||
|
|
Loading…
Reference in New Issue
Block a user