mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-15 17:34:52 +03:00
Add username_type option
This commit is contained in:
parent
c59e3b337f
commit
a3f03ccdc0
|
@ -5,6 +5,7 @@
|
|||
"author_name": "Daniel Roy Greenfeld",
|
||||
"domain_name": "example.com",
|
||||
"email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com",
|
||||
"username_type": ["username", "email"],
|
||||
"version": "0.1.0",
|
||||
"open_source_license": [
|
||||
"MIT",
|
||||
|
|
|
@ -305,7 +305,7 @@ CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
|
|||
# ------------------------------------------------------------------------------
|
||||
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "username"
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "{{cookiecutter.username_type}}"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
{% if request.user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
{# URL provided by django-allauth/account/urls.py #}
|
||||
<a class="nav-link" href="{% url 'users:detail' request.user.username %}">{% translate "My Profile" %}</a>
|
||||
<a class="nav-link" href="{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ request.user.pk }}{% endraw %}{% else %}{% raw %}{{ request.user.username }}{% endraw %}{% endif %}{% raw %}">{% translate "My Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
{# URL provided by django-allauth/account/urls.py #}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% raw %}{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}User: {{ object.username }}{% endblock %}
|
||||
{% block title %}User: {% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
@ -9,7 +9,7 @@
|
|||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<h2>{{ object.username }}</h2>
|
||||
<h2>{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}</h2>
|
||||
{% if object.name %}
|
||||
<p>{{ object.name }}</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{% raw %}{% extends "base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{{ user.username }}{% endblock %}
|
||||
{% block title %}{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ user.username }}</h1>
|
||||
<h1>{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}</h1>
|
||||
<form class="form-horizontal" method="post" action="{% url 'users:update' %}">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
|
|
@ -14,8 +14,13 @@ class UserAdmin(auth_admin.UserAdmin):
|
|||
form = UserAdminChangeForm
|
||||
add_form = UserAdminCreationForm
|
||||
fieldsets = (
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
(None, {"fields": ("email", "password")}),
|
||||
(_("Personal info"), {"fields": ("name",)}),
|
||||
{%- else %}
|
||||
(None, {"fields": ("username", "password")}),
|
||||
(_("Personal info"), {"fields": ("name", "email")}),
|
||||
{%- endif %}
|
||||
(
|
||||
_("Permissions"),
|
||||
{
|
||||
|
@ -30,5 +35,5 @@ class UserAdmin(auth_admin.UserAdmin):
|
|||
),
|
||||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
list_display = ["username", "name", "is_superuser"]
|
||||
list_display = ["{{cookiecutter.username_type}}", "name", "is_superuser"]
|
||||
search_fields = ["name"]
|
||||
|
|
|
@ -7,8 +7,16 @@ User = get_user_model()
|
|||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
fields = ["name", "url"]
|
||||
|
||||
extra_kwargs = {
|
||||
"url": {"view_name": "api:user-detail", "lookup_field": "id"}
|
||||
}
|
||||
{%- else %}
|
||||
fields = ["username", "name", "url"]
|
||||
|
||||
extra_kwargs = {
|
||||
"url": {"view_name": "api:user-detail", "lookup_field": "username"}
|
||||
}
|
||||
{%- endif %}
|
||||
|
|
|
@ -13,7 +13,11 @@ User = get_user_model()
|
|||
class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
|
||||
serializer_class = UserSerializer
|
||||
queryset = User.objects.all()
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
lookup_field = "id"
|
||||
{%- else %}
|
||||
lookup_field = "username"
|
||||
{%- endif %}
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
assert isinstance(self.request.user.id, int)
|
||||
|
|
|
@ -2,7 +2,6 @@ from allauth.account.forms import SignupForm
|
|||
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
|
||||
from django.contrib.auth import forms as admin_forms
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
@ -21,10 +20,6 @@ class UserAdminCreationForm(admin_forms.UserCreationForm):
|
|||
class Meta(admin_forms.UserCreationForm.Meta):
|
||||
model = User
|
||||
|
||||
error_messages = {
|
||||
"username": {"unique": _("This username has already been taken.")}
|
||||
}
|
||||
|
||||
|
||||
class UserSignupForm(SignupForm):
|
||||
"""
|
||||
|
|
|
@ -3,6 +3,8 @@ import django.contrib.auth.validators
|
|||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
import {{cookiecutter.project_slug}}.users.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
|
@ -118,7 +120,11 @@ class Migration(migrations.Migration):
|
|||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
("objects", {{cookiecutter.project_slug}}.users.models.UserManager()),
|
||||
{%- else %}
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
{%- endif %}
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,9 +1,45 @@
|
|||
{% if cookiecutter.username_type == "email" -%}
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.models import AbstractUser, UserManager as DjangoUserManager
|
||||
{%- else %}
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
{%- endif %}
|
||||
from django.db.models import CharField
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class UserManager(DjangoUserManager):
|
||||
def _create_user(self, email, password, **extra_fields):
|
||||
"""
|
||||
Create and save a user with the given email and password.
|
||||
"""
|
||||
if not email:
|
||||
raise ValueError("The given email must be set")
|
||||
email = self.normalize_email(email)
|
||||
user = self.model(email=email, **extra_fields)
|
||||
user.password = make_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
def create_user(self, email, password=None, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", False)
|
||||
extra_fields.setdefault("is_superuser", False)
|
||||
return self._create_user(email, password, **extra_fields)
|
||||
|
||||
def create_superuser(self, email, password=None, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", True)
|
||||
extra_fields.setdefault("is_superuser", True)
|
||||
|
||||
if extra_fields.get("is_staff") is not True:
|
||||
raise ValueError("Superuser must have is_staff=True.")
|
||||
if extra_fields.get("is_superuser") is not True:
|
||||
raise ValueError("Superuser must have is_superuser=True.")
|
||||
|
||||
return self._create_user(email, password, **extra_fields)
|
||||
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""
|
||||
Default custom user model for {{cookiecutter.project_name}}.
|
||||
|
@ -13,9 +49,16 @@ class User(AbstractUser):
|
|||
|
||||
#: First and last name do not cover name patterns around the globe
|
||||
name = CharField(_("Name of User"), blank=True, max_length=255)
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
username = None # type: ignore
|
||||
{%- endif %}
|
||||
first_name = None # type: ignore
|
||||
last_name = None # type: ignore
|
||||
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
USERNAME_FIELD = "email"
|
||||
{%- endif %}
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Get url for user's detail view.
|
||||
|
||||
|
@ -23,4 +66,8 @@ class User(AbstractUser):
|
|||
str: URL for user detail.
|
||||
|
||||
"""
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
return reverse("users:detail", kwargs={"id": self.id})
|
||||
{%- else %}
|
||||
return reverse("users:detail", kwargs={"username": self.username})
|
||||
{%- endif %}
|
||||
|
|
|
@ -8,7 +8,9 @@ from factory.django import DjangoModelFactory
|
|||
|
||||
class UserFactory(DjangoModelFactory):
|
||||
|
||||
{% if cookiecutter.username_type == "username" -%}
|
||||
username = Faker("user_name")
|
||||
{%- endif %}
|
||||
email = Faker("email")
|
||||
name = Faker("name")
|
||||
|
||||
|
@ -30,4 +32,4 @@ class UserFactory(DjangoModelFactory):
|
|||
|
||||
class Meta:
|
||||
model = get_user_model()
|
||||
django_get_or_create = ["username"]
|
||||
django_get_or_create = ["{{cookiecutter.username_type}}"]
|
||||
|
|
|
@ -22,13 +22,21 @@ class TestUserAdmin:
|
|||
response = admin_client.post(
|
||||
url,
|
||||
data={
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
"email": "test@test.com",
|
||||
{%- else %}
|
||||
"username": "test",
|
||||
{%- endif %}
|
||||
"password1": "My_R@ndom-P@ssw0rd",
|
||||
"password2": "My_R@ndom-P@ssw0rd",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 302
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert User.objects.filter(email="test@test.com").exists()
|
||||
{%- else %}
|
||||
assert User.objects.filter(username="test").exists()
|
||||
{%- endif %}
|
||||
|
||||
def test_view_user(self, admin_client):
|
||||
user = User.objects.get(username="admin")
|
||||
|
|
|
@ -4,11 +4,19 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_user_detail(user: User):
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"pk": user.pk})
|
||||
== f"/api/users/{user.pk}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.pk}/").view_name == "api:user-detail"
|
||||
{%- else %}
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"username": user.username})
|
||||
== f"/api/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.username}/").view_name == "api:user-detail"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
def test_user_list():
|
||||
|
|
|
@ -24,7 +24,12 @@ class TestUserViewSet:
|
|||
response = view.me(request)
|
||||
|
||||
assert response.data == {
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
"email": user.email,
|
||||
"url": f"http://testserver/api/users/{user.pk}/",
|
||||
{%- else %}
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"url": f"http://testserver/api/users/{user.username}/",
|
||||
{%- endif %}
|
||||
"name": user.name,
|
||||
}
|
||||
|
|
|
@ -24,7 +24,11 @@ class TestUserAdminCreationForm:
|
|||
# hence cannot be created.
|
||||
form = UserAdminCreationForm(
|
||||
{
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
"email": user.email,
|
||||
{%- else %}
|
||||
"username": user.username,
|
||||
{%- endif %}
|
||||
"password1": user.password,
|
||||
"password2": user.password,
|
||||
}
|
||||
|
@ -32,5 +36,10 @@ class TestUserAdminCreationForm:
|
|||
|
||||
assert not form.is_valid()
|
||||
assert len(form.errors) == 1
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert "email" in form.errors
|
||||
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 %}
|
||||
|
|
|
@ -2,4 +2,8 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_user_get_absolute_url(user: User):
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert user.get_absolute_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert user.get_absolute_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
|
|
@ -4,11 +4,19 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_detail(user: User):
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"pk": user.pk})
|
||||
== f"/users/{user.pk}/"
|
||||
)
|
||||
assert resolve(f"/users/{user.pk}/").view_name == "users:detail"
|
||||
{%- else %}
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"username": user.username})
|
||||
== f"/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
def test_update():
|
||||
|
|
|
@ -39,7 +39,11 @@ class TestUserUpdateView:
|
|||
|
||||
view.request = request
|
||||
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert view.get_success_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert view.get_success_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
||||
def test_get_object(self, user: User, rf: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
|
@ -79,7 +83,11 @@ class TestUserRedirectView:
|
|||
|
||||
view.request = request
|
||||
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
assert view.get_redirect_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert view.get_redirect_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
class TestUserDetailView:
|
||||
|
@ -87,7 +95,11 @@ class TestUserDetailView:
|
|||
request = rf.get("/fake-url/")
|
||||
request.user = UserFactory()
|
||||
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
response = user_detail_view(request, pk=user.pk)
|
||||
{%- else %}
|
||||
response = user_detail_view(request, username=user.username)
|
||||
{%- endif %}
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
@ -95,7 +107,11 @@ class TestUserDetailView:
|
|||
request = rf.get("/fake-url/")
|
||||
request.user = AnonymousUser()
|
||||
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
response = user_detail_view(request, pk=user.pk)
|
||||
{%- else %}
|
||||
response = user_detail_view(request, username=user.username)
|
||||
{%- endif %}
|
||||
login_url = reverse(settings.LOGIN_URL)
|
||||
|
||||
assert isinstance(response, HttpResponseRedirect)
|
||||
|
|
|
@ -10,5 +10,9 @@ app_name = "users"
|
|||
urlpatterns = [
|
||||
path("~redirect/", view=user_redirect_view, name="redirect"),
|
||||
path("~update/", view=user_update_view, name="update"),
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
path("<int:id>/", view=user_detail_view, name="detail"),
|
||||
{%- else %}
|
||||
path("<str:username>/", view=user_detail_view, name="detail"),
|
||||
{%- endif %}
|
||||
]
|
||||
|
|
|
@ -11,8 +11,13 @@ User = get_user_model()
|
|||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
model = User
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
slug_field = "id"
|
||||
slug_url_kwarg = "id"
|
||||
{%- else %}
|
||||
slug_field = "username"
|
||||
slug_url_kwarg = "username"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
user_detail_view = UserDetailView.as_view()
|
||||
|
@ -42,7 +47,11 @@ class UserRedirectView(LoginRequiredMixin, RedirectView):
|
|||
permanent = False
|
||||
|
||||
def get_redirect_url(self):
|
||||
{% if cookiecutter.username_type == "email" -%}
|
||||
return reverse("users:detail", kwargs={"pk": self.request.user.pk})
|
||||
{%- else %}
|
||||
return reverse("users:detail", kwargs={"username": self.request.user.username})
|
||||
{%- endif %}
|
||||
|
||||
|
||||
user_redirect_view = UserRedirectView.as_view()
|
||||
|
|
Loading…
Reference in New Issue
Block a user