mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-09-20 19:02:37 +03:00
Uograde CC 240913
This commit is contained in:
parent
50946eedbd
commit
7a07ca17a7
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -74,8 +74,8 @@ jobs:
|
||||||
args: "frontend_pipeline=Gulp"
|
args: "frontend_pipeline=Gulp"
|
||||||
- name: Webpack
|
- name: Webpack
|
||||||
args: "frontend_pipeline=Webpack use_heroku=y"
|
args: "frontend_pipeline=Webpack use_heroku=y"
|
||||||
- name: Email
|
- name: Email Username
|
||||||
args: "ci_tool=Github project_name='Something superduper long - the great amazing project' project_slug=my_awesome_project"
|
args: "username_type=email ci_tool=Github project_name='Something superduper long - the great amazing project' project_slug=my_awesome_project"
|
||||||
|
|
||||||
name: "Bare metal ${{ matrix.script.name }}"
|
name: "Bare metal ${{ matrix.script.name }}"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -116,6 +116,10 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re
|
||||||
4 - Apache Software License 2.0
|
4 - Apache Software License 2.0
|
||||||
5 - Not open source
|
5 - Not open source
|
||||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||||
|
Select username_type:
|
||||||
|
1 - username
|
||||||
|
2 - email
|
||||||
|
Choose from 1, 2 [1]: 1
|
||||||
timezone [UTC]: America/Los_Angeles
|
timezone [UTC]: America/Los_Angeles
|
||||||
windows [n]: n
|
windows [n]: n
|
||||||
Select an editor to use. The choices are:
|
Select an editor to use. The choices are:
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"Apache Software License 2.0",
|
"Apache Software License 2.0",
|
||||||
"Not open source"
|
"Not open source"
|
||||||
],
|
],
|
||||||
|
"username_type": ["username", "email"],
|
||||||
"timezone": "UTC",
|
"timezone": "UTC",
|
||||||
"windows": "n",
|
"windows": "n",
|
||||||
"editor": ["None", "PyCharm", "VS Code"],
|
"editor": ["None", "PyCharm", "VS Code"],
|
||||||
|
@ -38,7 +39,6 @@
|
||||||
"use_sentry": "n",
|
"use_sentry": "n",
|
||||||
"use_whitenoise": "n",
|
"use_whitenoise": "n",
|
||||||
"use_heroku": "n",
|
"use_heroku": "n",
|
||||||
"username_type": ["email"],
|
|
||||||
"ci_tool": ["None", "Travis", "Gitlab", "Github", "Drone"],
|
"ci_tool": ["None", "Travis", "Gitlab", "Github", "Drone"],
|
||||||
"keep_local_envs_in_vcs": "y",
|
"keep_local_envs_in_vcs": "y",
|
||||||
"debug": "n"
|
"debug": "n"
|
||||||
|
|
|
@ -24,6 +24,13 @@ author_name:
|
||||||
email:
|
email:
|
||||||
The email address you want to identify yourself in the project.
|
The email address you want to identify yourself in the project.
|
||||||
|
|
||||||
|
username_type:
|
||||||
|
The type of username you want to use in the project. This can be either
|
||||||
|
``username`` or ``email``. If you choose ``username``, the ``email`` field
|
||||||
|
will be included. If you choose ``email``, the ``username`` field will be
|
||||||
|
excluded. It is best practice to always include an email field, so there is
|
||||||
|
no option for having just the ``username`` field.
|
||||||
|
|
||||||
domain_name:
|
domain_name:
|
||||||
The domain name you plan to use for your project once it goes live.
|
The domain name you plan to use for your project once it goes live.
|
||||||
Note that it can be safely changed later on whenever you need to.
|
Note that it can be safely changed later on whenever you need to.
|
||||||
|
|
|
@ -449,8 +449,8 @@ def main():
|
||||||
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
||||||
remove_gplv3_files()
|
remove_gplv3_files()
|
||||||
|
|
||||||
# if "{{ cookiecutter.username_type }}" == "username":
|
if "{{ cookiecutter.username_type }}" == "username":
|
||||||
# remove_custom_user_manager_files()
|
remove_custom_user_manager_files()
|
||||||
|
|
||||||
if "{{ cookiecutter.editor }}" != "PyCharm":
|
if "{{ cookiecutter.editor }}" != "PyCharm":
|
||||||
remove_pycharm_files()
|
remove_pycharm_files()
|
||||||
|
|
|
@ -43,6 +43,7 @@ def context():
|
||||||
|
|
||||||
|
|
||||||
SUPPORTED_COMBINATIONS = [
|
SUPPORTED_COMBINATIONS = [
|
||||||
|
{"username_type": "username"},
|
||||||
{"username_type": "email"},
|
{"username_type": "email"},
|
||||||
{"open_source_license": "MIT"},
|
{"open_source_license": "MIT"},
|
||||||
{"open_source_license": "BSD"},
|
{"open_source_license": "BSD"},
|
||||||
|
|
|
@ -7,8 +7,7 @@ set -o nounset
|
||||||
|
|
||||||
{% if cookiecutter.use_celery == 'y' %}
|
{% if cookiecutter.use_celery == 'y' %}
|
||||||
# N.B. If only .env files supported variable expansion...
|
# N.B. If only .env files supported variable expansion...
|
||||||
export CELERY_BROKER_URL="${REDIS_URL}"
|
export CELERY_BROKER_URL="${REDIS_URL}"
|
||||||
# Updated in .envs
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
if [ -z "${POSTGRES_USER}" ]; then
|
if [ -z "${POSTGRES_USER}" ]; then
|
||||||
|
@ -16,7 +15,6 @@ if [ -z "${POSTGRES_USER}" ]; then
|
||||||
export POSTGRES_USER="${base_postgres_image_default_user}"
|
export POSTGRES_USER="${base_postgres_image_default_user}"
|
||||||
fi
|
fi
|
||||||
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
||||||
# Updated in .envs
|
|
||||||
|
|
||||||
python << END
|
python << END
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -325,13 +325,15 @@ CELERY_TASK_SEND_SENT_EVENT = True
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
|
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
ACCOUNT_AUTHENTICATION_METHOD = "email"
|
ACCOUNT_AUTHENTICATION_METHOD = "{{cookiecutter.username_type}}"
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = True
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
ACCOUNT_USERNAME_REQUIRED = False
|
ACCOUNT_USERNAME_REQUIRED = False
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
|
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
|
||||||
|
{%- endif %}
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||||
# https://docs.allauth.org/en/latest/account/configuration.html
|
# https://docs.allauth.org/en/latest/account/configuration.html
|
||||||
|
|
|
@ -41,12 +41,12 @@ django_settings_module = "config.settings.test"
|
||||||
|
|
||||||
# ==== djLint ====
|
# ==== djLint ====
|
||||||
[tool.djlint]
|
[tool.djlint]
|
||||||
blank_line_after_tag = "load,extends,endblock"
|
blank_line_after_tag = "load,extends"
|
||||||
close_void_tags = true
|
close_void_tags = true
|
||||||
format_css = false
|
format_css = true
|
||||||
format_js = false
|
format_js = true
|
||||||
# TODO: remove T002 when fixed https://github.com/djlint/djLint/issues/687
|
# TODO: remove T002 when fixed https://github.com/djlint/djLint/issues/687
|
||||||
ignore = "H006,H030,H031,T002,H020,H023,H033,D018"
|
ignore = "H006,H030,H031,T002"
|
||||||
include = "H017,H035"
|
include = "H017,H035"
|
||||||
indent = 2
|
indent = 2
|
||||||
max_line_length = 119
|
max_line_length = 119
|
||||||
|
@ -65,10 +65,6 @@ extend-exclude = [
|
||||||
"*/migrations/*.py",
|
"*/migrations/*.py",
|
||||||
"staticfiles/*",
|
"staticfiles/*",
|
||||||
]
|
]
|
||||||
# Same as Django: https://github.com/cookiecutter/cookiecutter-django/issues/4792.
|
|
||||||
line-length = 88
|
|
||||||
indent-width = 4
|
|
||||||
target-version = "py312"
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = [
|
select = [
|
||||||
|
|
|
@ -52,5 +52,5 @@ django-webpack-loader==3.1.1 # https://github.com/django-webpack/django-webpack
|
||||||
|
|
||||||
# Project
|
# Project
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
fontawesomefree==6.5.2 # https://github.com/FortAwesome/Font-Awesome
|
fontawesomefree==6.6.0 # https://github.com/FortAwesome/Font-Awesome
|
||||||
django-import-export==4.0.8 # https://github.com/django-import-export/django-import-export
|
django-import-export==4.1.1 # https://github.com/django-import-export/django-import-export
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
User: {% endraw %}
|
User: {% endraw %}
|
||||||
|
{% if cookiecutter.username_type == "email" %}
|
||||||
{% raw %}{{ object.first_name }}{% endraw %}
|
{% raw %}{{ object.first_name }}{% endraw %}
|
||||||
|
{% else %}
|
||||||
|
{% raw %}{{ object.username }}{% endraw %}
|
||||||
|
{% endif %}
|
||||||
{% raw %}
|
{% raw %}
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
@ -13,11 +17,20 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>
|
<h2>
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
{% if cookiecutter.username_type == "email" %}
|
||||||
{% raw %}{{ object.first_name }}{% endraw %}
|
{% raw %}{{ object.first_name }}{% endraw %}
|
||||||
|
{% else %}
|
||||||
|
{% raw %}{{ object.username }}{% endraw %}
|
||||||
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
|
{%- if cookiecutter.username_type == "username" %}
|
||||||
|
{%- raw %}
|
||||||
|
<p>{{ object.first_name }}</p>
|
||||||
|
{%- endraw %}
|
||||||
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% raw %}
|
{%- raw %}
|
||||||
{% if object == request.user %}
|
{% if object == request.user %}
|
||||||
<!-- Action buttons -->
|
<!-- Action buttons -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -36,4 +49,4 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
{% endraw %}
|
{%- endraw %}
|
||||||
|
|
|
@ -2,18 +2,35 @@
|
||||||
|
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
{% block title %}{{ user }}{% endblock title %}
|
{% block title %}
|
||||||
|
{% endraw %}
|
||||||
|
{% if cookiecutter.username_type == "email" %}
|
||||||
|
{% raw %}{{ user.first_name }}{% endraw %}
|
||||||
|
{% else %}
|
||||||
|
{% raw %}{{ user.username }}{% endraw %}
|
||||||
|
{% endif %}
|
||||||
|
{% raw %}
|
||||||
|
{% endblock title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ user }}</h1>
|
<h1>
|
||||||
<form class="form-horizontal" method="post" action="{% url 'users:update' %}">
|
{% endraw %}
|
||||||
{% csrf_token %}
|
{% if cookiecutter.username_type == "email" %}
|
||||||
{{ form|crispy }}
|
{% raw %}{{ user.first_name }}{% endraw %}
|
||||||
<div class="control-group">
|
{% else %}
|
||||||
<div class="controls">
|
{% raw %}{{ user.username }}{% endraw %}
|
||||||
<button type="submit" class="btn btn-primary">Update</button>
|
{% endif %}
|
||||||
</div>
|
{% raw %}
|
||||||
|
</h1>
|
||||||
|
<form class="form-horizontal"
|
||||||
|
method="post"
|
||||||
|
action="{% url 'users:update' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
{%- endraw %}
|
{%- endraw %}
|
||||||
|
|
|
@ -55,10 +55,10 @@ class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||||
if request.user.is_authenticated and request.user.email != verified_email.email:
|
if request.user.is_authenticated and request.user.email != verified_email.email:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
"""
|
(
|
||||||
No es posible enlazar tu cuenta de {},
|
"No es posible enlazar tu cuenta de {}, "
|
||||||
ya que no coincide con tu correo en esta plataforma.
|
"ya que no coincide con tu correo en esta plataforma."
|
||||||
""".format(
|
).format(
|
||||||
list(request._socialapp_cache.keys())[0].capitalize()
|
list(request._socialapp_cache.keys())[0].capitalize()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,9 +21,13 @@ class UserAdmin(auth_admin.UserAdmin):
|
||||||
add_form = UserAdminCreationForm
|
add_form = UserAdminCreationForm
|
||||||
readonly_fields = ("uuid",)
|
readonly_fields = ("uuid",)
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
(None, {"fields": ("email", "password")}),
|
(None, {"fields": ("email", "password")}),
|
||||||
(_("Personal info"), {"fields": ("first_name", "last_name")}),
|
(_("Personal info"), {"fields": ("first_name", "last_name")}),
|
||||||
(None, {"fields": ("uuid",)}),
|
{%- else %}
|
||||||
|
(None, {"fields": ("username", "password")}),
|
||||||
|
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
|
||||||
|
{%- endif %}
|
||||||
(
|
(
|
||||||
_("Permissions"),
|
_("Permissions"),
|
||||||
{
|
{
|
||||||
|
@ -39,15 +43,16 @@ class UserAdmin(auth_admin.UserAdmin):
|
||||||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
||||||
)
|
)
|
||||||
list_display = [
|
list_display = [
|
||||||
"email",
|
"{{cookiecutter.username_type}}",
|
||||||
"first_name",
|
"first_name",
|
||||||
"last_name",
|
"last_name",
|
||||||
"is_active",
|
"is_active",
|
||||||
"is_staff",
|
"is_staff",
|
||||||
"is_superuser",
|
"is_superuser",
|
||||||
]
|
]
|
||||||
search_fields = ["email", "first_name", "last_name"]
|
search_fields = ["{{cookiecutter.username_type}}", "first_name", "last_name"]
|
||||||
ordering = ("email", "first_name", "last_name")
|
ordering = ("{{cookiecutter.username_type}}", "first_name", "last_name")
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
add_fieldsets = (
|
add_fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
@ -63,3 +68,21 @@ class UserAdmin(auth_admin.UserAdmin):
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
{%- else %}
|
||||||
|
add_fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"classes": ("wide",),
|
||||||
|
"fields": (
|
||||||
|
"username",
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"email",
|
||||||
|
"password1",
|
||||||
|
"password2",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
{%- endif %}
|
||||||
|
|
|
@ -6,5 +6,11 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
||||||
class UserSerializer(serializers.ModelSerializer[User]):
|
class UserSerializer(serializers.ModelSerializer[User]):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
fields = ["email", "uuid", "first_name", "last_name", "url"]
|
fields = ["email", "uuid", "first_name", "last_name", "url"]
|
||||||
extra_kwargs = {"url": {"view_name": "api:user-detail", "lookup_field": "uuid"}}
|
{%- else %}
|
||||||
|
fields = ["username", "email", "uuid", "first_name", "last_name", "url"]
|
||||||
|
{%- endif %}
|
||||||
|
extra_kwargs = {
|
||||||
|
"url": {"view_name": "api:user-detail", "lookup_field": "uuid"}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from allauth.account.forms import SignupForm
|
from allauth.account.forms import SignupForm
|
||||||
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
|
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
|
||||||
from django import forms
|
|
||||||
from django.contrib.auth import forms as admin_forms
|
from django.contrib.auth import forms as admin_forms
|
||||||
from django.contrib.auth import get_user_model
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
|
from django.forms import EmailField
|
||||||
|
{%- endif %}
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
@ -11,7 +12,9 @@ User = get_user_model()
|
||||||
class UserAdminChangeForm(admin_forms.UserChangeForm):
|
class UserAdminChangeForm(admin_forms.UserChangeForm):
|
||||||
class Meta(admin_forms.UserChangeForm.Meta): # type: ignore[name-defined]
|
class Meta(admin_forms.UserChangeForm.Meta): # type: ignore[name-defined]
|
||||||
model = User
|
model = User
|
||||||
fields = ("email", "first_name", "last_name")
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
|
field_classes = {"email": EmailField}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
|
||||||
class UserAdminCreationForm(admin_forms.UserCreationForm):
|
class UserAdminCreationForm(admin_forms.UserCreationForm):
|
||||||
|
@ -22,14 +25,18 @@ class UserAdminCreationForm(admin_forms.UserCreationForm):
|
||||||
|
|
||||||
class Meta(admin_forms.UserCreationForm.Meta): # type: ignore[name-defined]
|
class Meta(admin_forms.UserCreationForm.Meta): # type: ignore[name-defined]
|
||||||
model = User
|
model = User
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
fields = ("email", "first_name", "last_name")
|
fields = ("email", "first_name", "last_name")
|
||||||
|
field_classes = {"email": EmailField}
|
||||||
error_messages = {
|
error_messages = {
|
||||||
"email": {"unique": _("This email has already been taken.")}
|
"email": {"unique": _("This email has already been taken.")},
|
||||||
}
|
}
|
||||||
|
{%- else %}
|
||||||
|
fields = ("username", "first_name", "last_name")
|
||||||
error_messages = {
|
error_messages = {
|
||||||
"username": {"unique": _("This username has already been taken.")},
|
"username": {"unique": _("This username has already been taken.")},
|
||||||
}
|
}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
|
||||||
class UserSignupForm(SignupForm):
|
class UserSignupForm(SignupForm):
|
||||||
|
|
|
@ -44,6 +44,36 @@ class Migration(migrations.Migration):
|
||||||
verbose_name="superuser status",
|
verbose_name="superuser status",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
{%- if cookiecutter.username_type == "username" -%}
|
||||||
|
(
|
||||||
|
"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",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"email",
|
||||||
|
models.EmailField(
|
||||||
|
blank=True, max_length=254, verbose_name="email address",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
{%- else %}
|
||||||
|
(
|
||||||
|
"email",
|
||||||
|
models.EmailField(
|
||||||
|
unique=True, max_length=254, verbose_name="email address",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
{%- endif %}
|
||||||
(
|
(
|
||||||
"is_staff",
|
"is_staff",
|
||||||
models.BooleanField(
|
models.BooleanField(
|
||||||
|
@ -66,12 +96,6 @@ class Migration(migrations.Migration):
|
||||||
default=django.utils.timezone.now, verbose_name="date joined",
|
default=django.utils.timezone.now, verbose_name="date joined",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"email",
|
|
||||||
models.EmailField(
|
|
||||||
max_length=254, unique=True, verbose_name="email address"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"uuid",
|
"uuid",
|
||||||
models.UUIDField(
|
models.UUIDField(
|
||||||
|
@ -115,7 +139,11 @@ class Migration(migrations.Migration):
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
managers=[
|
managers=[
|
||||||
('objects', {{ cookiecutter.project_slug }}.users.models.UserManager()),
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
|
("objects", {{cookiecutter.project_slug}}.users.models.UserManager()),
|
||||||
|
{%- else %}
|
||||||
|
("objects", django.contrib.auth.models.UserManager()),
|
||||||
|
{%- endif %}
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import uuid as uuid_lib
|
import uuid as uuid_lib
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
|
|
||||||
from django.contrib.auth.base_user import BaseUserManager
|
{% endif -%}
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
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.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
|
|
||||||
from .managers import UserManager
|
from .managers import UserManager
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
|
@ -18,22 +23,23 @@ class User(AbstractUser):
|
||||||
check forms.SignupForm and forms.SocialSignupForms accordingly.
|
check forms.SignupForm and forms.SocialSignupForms accordingly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
email = models.EmailField(_("email address"), unique=True)
|
|
||||||
username = None
|
|
||||||
uuid = models.UUIDField(
|
uuid = models.UUIDField(
|
||||||
unique=True, db_index=True, default=uuid_lib.uuid4, editable=False
|
unique=True, db_index=True, default=uuid_lib.uuid4, editable=False
|
||||||
)
|
)
|
||||||
first_name = models.CharField(_("first name"), max_length=150)
|
first_name = models.CharField(_("first name"), max_length=150)
|
||||||
last_name = models.CharField(_("last name"), max_length=150)
|
last_name = models.CharField(_("last name"), max_length=150)
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
|
email = EmailField(_("email address"), unique=True)
|
||||||
|
username = None # type: ignore[assignment]
|
||||||
|
|
||||||
USERNAME_FIELD = "email"
|
USERNAME_FIELD = "email"
|
||||||
|
|
||||||
REQUIRED_FIELDS = [
|
REQUIRED_FIELDS = [
|
||||||
"first_name",
|
"first_name",
|
||||||
"last_name",
|
"last_name",
|
||||||
]
|
]
|
||||||
|
|
||||||
objects: ClassVar[UserManager] = UserManager()
|
objects: ClassVar[UserManager] = UserManager()
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
def get_absolute_url(self) -> str:
|
def get_absolute_url(self) -> str:
|
||||||
"""Get URL for user's detail view.
|
"""Get URL for user's detail view.
|
||||||
|
@ -45,7 +51,11 @@ class User(AbstractUser):
|
||||||
return reverse("users:detail", kwargs={"uuid": self.uuid})
|
return reverse("users:detail", kwargs={"uuid": self.uuid})
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
{ % - if cookiecutter.username_type == "email" %}
|
||||||
return f"{self.first_name} {self.last_name}"
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
{ % - else %}
|
||||||
|
return f"{self.username}"
|
||||||
|
{ % - endif %}
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.first_name = self.first_name.strip()
|
self.first_name = self.first_name.strip()
|
||||||
|
|
|
@ -32,16 +32,28 @@ class TestUserAdmin:
|
||||||
data={
|
data={
|
||||||
"first_name": "New",
|
"first_name": "New",
|
||||||
"last_name": "Admin",
|
"last_name": "Admin",
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
"email": "new-admin@example.com",
|
"email": "new-admin@example.com",
|
||||||
|
{%- else %}
|
||||||
|
"username": "test",
|
||||||
|
{%- endif %}
|
||||||
"password1": "My_R@ndom-P@ssw0rd",
|
"password1": "My_R@ndom-P@ssw0rd",
|
||||||
"password2": "My_R@ndom-P@ssw0rd",
|
"password2": "My_R@ndom-P@ssw0rd",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.FOUND
|
assert response.status_code == HTTPStatus.FOUND
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
assert User.objects.filter(email="new-admin@example.com").exists()
|
assert User.objects.filter(email="new-admin@example.com").exists()
|
||||||
|
{%- else %}
|
||||||
|
assert User.objects.filter(username="test").exists()
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
def test_view_user(self, admin_client):
|
def test_view_user(self, admin_client):
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
user = User.objects.get(email="admin@example.com")
|
user = User.objects.get(email="admin@example.com")
|
||||||
|
{%- else %}
|
||||||
|
user = User.objects.get(username="admin")
|
||||||
|
{%- endif %}
|
||||||
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
|
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
|
||||||
response = admin_client.get(url)
|
response = admin_client.get(url)
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == HTTPStatus.OK
|
||||||
|
|
|
@ -29,6 +29,9 @@ class TestUserViewSet:
|
||||||
response = view.me(request) # type: ignore[call-arg, arg-type, misc]
|
response = view.me(request) # type: ignore[call-arg, arg-type, misc]
|
||||||
|
|
||||||
assert response.data == {
|
assert response.data == {
|
||||||
|
{%- if cookiecutter.username_type == "username" %}
|
||||||
|
"username": user.username,
|
||||||
|
{%- endif %}
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
"uuid": str(user.uuid),
|
"uuid": str(user.uuid),
|
||||||
"first_name": user.first_name,
|
"first_name": user.first_name,
|
||||||
|
|
|
@ -23,7 +23,11 @@ class TestUserAdminCreationForm:
|
||||||
# hence cannot be created.
|
# hence cannot be created.
|
||||||
form = UserAdminCreationForm(
|
form = UserAdminCreationForm(
|
||||||
{
|
{
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
"email": user.email,
|
"email": user.email,
|
||||||
|
{%- else %}
|
||||||
|
"username": user.username,
|
||||||
|
{%- endif %}
|
||||||
"first_name": user.first_name,
|
"first_name": user.first_name,
|
||||||
"last_name": user.last_name,
|
"last_name": user.last_name,
|
||||||
"password1": user.password,
|
"password1": user.password,
|
||||||
|
@ -33,5 +37,10 @@ class TestUserAdminCreationForm:
|
||||||
|
|
||||||
assert not form.is_valid()
|
assert not form.is_valid()
|
||||||
assert len(form.errors) == 1
|
assert len(form.errors) == 1
|
||||||
|
{%- if cookiecutter.username_type == "email" %}
|
||||||
assert "email" in form.errors
|
assert "email" in form.errors
|
||||||
assert form.errors["email"][0] == _("This email has already been taken.")
|
assert form.errors["email"][0] == _("This email has already been taken.")
|
||||||
|
{%- else %}
|
||||||
|
assert "username" in form.errors
|
||||||
|
assert form.errors["username"][0] == _("This username has already been taken.")
|
||||||
|
{%- endif %}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user