diff --git a/.github/contributors.json b/.github/contributors.json
index 6fd74677e..ab1575605 100644
--- a/.github/contributors.json
+++ b/.github/contributors.json
@@ -1202,5 +1202,20 @@
"name": "Bogdan Mateescu",
"github_login": "mateesville93",
"twitter_username": ""
+ },
+ {
+ "name": "Fuzzwah",
+ "github_login": "Fuzzwah",
+ "twitter_username": ""
+ },
+ {
+ "name": "Thibault J.",
+ "github_login": "thibault",
+ "twitter_username": "thibault"
+ },
+ {
+ "name": "Pedro Campos",
+ "github_login": "pcampos119104",
+ "twitter_username": ""
}
]
\ No newline at end of file
diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml
index 2e6e65b86..335159444 100644
--- a/.github/workflows/update-contributors.yml
+++ b/.github/workflows/update-contributors.yml
@@ -26,7 +26,7 @@ jobs:
run: python scripts/update_contributors.py
- name: Commit changes
- uses: stefanzweifel/git-auto-commit-action@v4.12.0
+ uses: stefanzweifel/git-auto-commit-action@v4.13.1
with:
commit_message: Update Contributors
file_pattern: CONTRIBUTORS.md .github/contributors.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d9d07507e..cf4ffefac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,37 @@ All enhancements and patches to Cookiecutter Django will be documented in this f
+## 2022.01.14
+
+### Updated
+- Update uvicorn to 0.17.0 ([#3534](https://github.com/cookiecutter/cookiecutter-django/pull/3534))
+- Bump stefanzweifel/git-auto-commit-action from 4.13.0 to 4.13.1 ([#3532](https://github.com/cookiecutter/cookiecutter-django/pull/3532))
+
+## 2022.01.13
+
+### Changed
+- Add UserSignupForm and UserSocialSignupForm ([#3515](https://github.com/cookiecutter/cookiecutter-django/pull/3515))
+### Fixed
+- Fix high CPU usage when running `runserver_plus` in Docker ([#3531](https://github.com/cookiecutter/cookiecutter-django/pull/3531))
+- Fix out-of-sync sequence for Site ID ([#3511](https://github.com/cookiecutter/cookiecutter-django/pull/3511))
+
+## 2022.01.11
+
+### Updated
+- Bump stefanzweifel/git-auto-commit-action from 4.12.0 to 4.13.0 ([#3527](https://github.com/cookiecutter/cookiecutter-django/pull/3527))
+
+## 2022.01.10
+
+### Updated
+- Update django-cors-headers to 3.11.0 ([#3526](https://github.com/cookiecutter/cookiecutter-django/pull/3526))
+- Update sentry-sdk to 1.5.2 ([#3525](https://github.com/cookiecutter/cookiecutter-django/pull/3525))
+- Update gitpython to 3.1.26 ([#3524](https://github.com/cookiecutter/cookiecutter-django/pull/3524))
+
+## 2022.01.09
+
+### Changed
+- Fix broken center align of image links in README ([#3522](https://github.com/cookiecutter/cookiecutter-django/pull/3522))
+
## 2022.01.07
### Fixed
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index cbeda8503..fe69a718a 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -733,6 +733,13 @@ Listed in alphabetical order.
Théo Segonds |
diff --git a/README.md b/README.md
index 26f63fc3c..1e40a4190 100644
--- a/README.md
+++ b/README.md
@@ -63,13 +63,17 @@ Projects that provide financial support to the maintainers:
---
-[{#Two Scoops of Django 3.x .align-center}](https://www.feldroy.com/products//two-scoops-of-django-3-x)
+
+
+
Two Scoops of Django 3.x is the best ice cream-themed Django reference in the universe!
### PyUp
-[{#pyup .align-center}](https://pyup.io/)
+
+
+
PyUp brings you automated security and dependency updates used by Google and other organizations. Free for open source projects!
diff --git a/docs/developing-locally-docker.rst b/docs/developing-locally-docker.rst
index 0af97842b..f70363dc9 100644
--- a/docs/developing-locally-docker.rst
+++ b/docs/developing-locally-docker.rst
@@ -191,16 +191,18 @@ docker
The ``container_name`` from the yml file can be used to check on containers with docker commands, for example: ::
- $ docker logs worker
- $ docker top worker
+ $ docker logs _local_celeryworker
+ $ docker top _local_celeryworker
+Notice that the ``container_name`` is generated dynamically using your project slug as a prefix
+
Mailhog
~~~~~~~
When developing locally you can go with MailHog_ for email testing provided ``use_mailhog`` was set to ``y`` on setup. To proceed,
-#. make sure ``mailhog`` container is up and running;
+#. make sure ``_local_mailhog`` container is up and running;
#. open up ``http://127.0.0.1:8025``.
diff --git a/docs/requirements.txt b/docs/requirements.txt
index ed09babbb..d9286ce24 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,2 +1,2 @@
-sphinx==4.3.2
+sphinx==4.4.0
sphinx-rtd-theme==1.0.0
diff --git a/requirements.txt b/requirements.txt
index 4ef925e99..426091f9e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,6 +21,6 @@ pyyaml==6.0
# Scripting
# ------------------------------------------------------------------------------
PyGithub==1.55
-gitpython==3.1.25
+gitpython==3.1.26
jinja2==3.0.3
requests==2.27.1
diff --git a/setup.py b/setup.py
index e86a2cf71..28abc0d47 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ except ImportError:
from distutils.core import setup
# We use calendar versioning
-version = "2022.01.07"
+version = "2022.01.14"
with open("README.rst") as readme_file:
long_description = readme_file.read()
diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py
index 2d4dbdd03..afae0ec4f 100644
--- a/{{cookiecutter.project_slug}}/config/settings/base.py
+++ b/{{cookiecutter.project_slug}}/config/settings/base.py
@@ -312,8 +312,12 @@ ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter"
+# https://django-allauth.readthedocs.io/en/latest/forms.html
+ACCOUNT_FORMS = {"signup": "{{cookiecutter.project_slug}}.users.forms.UserSignupForm"}
# https://django-allauth.readthedocs.io/en/latest/configuration.html
SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter"
+# https://django-allauth.readthedocs.io/en/latest/forms.html
+SOCIALACCOUNT_FORMS = {"signup": "{{cookiecutter.project_slug}}.users.forms.UserSocialSignupForm"}
{% if cookiecutter.use_compressor == 'y' -%}
# django-compressor
# ------------------------------------------------------------------------------
diff --git a/{{cookiecutter.project_slug}}/local.yml b/{{cookiecutter.project_slug}}/local.yml
index 0de5eec8f..e4ce73d9f 100644
--- a/{{cookiecutter.project_slug}}/local.yml
+++ b/{{cookiecutter.project_slug}}/local.yml
@@ -9,14 +9,14 @@ volumes:
{{ cookiecutter.project_slug }}_local_mysql_data: {}
{{ cookiecutter.project_slug }}_local_mysql_data_backups: {}
{%- endif %}
-
+
services:
django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %}
build:
context: .
dockerfile: ./compose/local/django/Dockerfile
image: {{ cookiecutter.project_slug }}_local_django
- container_name: django
+ container_name: {{ cookiecutter.project_slug }}_local_django
depends_on:
{%- if cookiecutter.database_engine == 'postgresql' %}
- postgres
@@ -50,10 +50,10 @@ services:
context: .
dockerfile: ./compose/production/postgres/Dockerfile
image: {{ cookiecutter.project_slug }}_production_postgres
- container_name: postgres
+ container_name: {{ cookiecutter.project_slug }}_local_postgres
volumes:
- - local_postgres_data:/var/lib/postgresql/data:Z
- - local_postgres_data_backups:/backups:z
+ - {{ cookiecutter.project_slug }}_local_postgres_data:/var/lib/postgresql/data:Z
+ - {{ cookiecutter.project_slug }}_local_postgres_data_backups:/backups:z
env_file:
- ./.envs/.local/.postgres
{%- endif %}
@@ -73,7 +73,7 @@ services:
docs:
image: {{ cookiecutter.project_slug }}_local_docs
- container_name: docs
+ container_name: {{ cookiecutter.project_slug }}_local_docs
build:
context: .
dockerfile: ./compose/local/docs/Dockerfile
@@ -90,7 +90,7 @@ services:
mailhog:
image: mailhog/mailhog:v1.0.0
- container_name: mailhog
+ container_name: {{ cookiecutter.project_slug }}_local_mailhog
ports:
- "8025:8025"
@@ -99,12 +99,12 @@ services:
redis:
image: redis:6
- container_name: redis
+ container_name: {{ cookiecutter.project_slug }}_local_redis
celeryworker:
<<: *django
image: {{ cookiecutter.project_slug }}_local_celeryworker
- container_name: celeryworker
+ container_name: {{ cookiecutter.project_slug }}_local_celeryworker
depends_on:
- redis
{%- if cookiecutter.database_engine == 'postgresql' %}
@@ -122,7 +122,7 @@ services:
celerybeat:
<<: *django
image: {{ cookiecutter.project_slug }}_local_celerybeat
- container_name: celerybeat
+ container_name: {{ cookiecutter.project_slug }}_local_celerybeat
depends_on:
- redis
{%- if cookiecutter.database_engine == 'postgresql' %}
@@ -140,7 +140,7 @@ services:
flower:
<<: *django
image: {{ cookiecutter.project_slug }}_local_flower
- container_name: flower
+ container_name: {{ cookiecutter.project_slug }}_local_flower
ports:
- "5555:5555"
command: /start-flower
@@ -153,7 +153,7 @@ services:
context: .
dockerfile: ./compose/local/node/Dockerfile
image: {{ cookiecutter.project_slug }}_local_node
- container_name: node
+ container_name: {{ cookiecutter.project_slug }}_local_node
depends_on:
- django
volumes:
diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt
index 4dcd42605..7e4bcf545 100644
--- a/{{cookiecutter.project_slug}}/requirements/base.txt
+++ b/{{cookiecutter.project_slug}}/requirements/base.txt
@@ -24,7 +24,7 @@ flower==1.0.0 # https://github.com/mher/flower
{%- endif %}
{%- endif %}
{%- if cookiecutter.use_async == 'y' %}
-uvicorn[standard]==0.16.0 # https://github.com/encode/uvicorn
+uvicorn[standard]==0.17.0 # https://github.com/encode/uvicorn
{%- endif %}
# Django
@@ -42,5 +42,5 @@ django-redis==5.2.0 # https://github.com/jazzband/django-redis
{%- if cookiecutter.use_drf == "y" %}
# Django REST Framework
djangorestframework==3.13.1 # https://github.com/encode/django-rest-framework
-django-cors-headers==3.10.1 # https://github.com/adamchainz/django-cors-headers
+django-cors-headers==3.11.0 # https://github.com/adamchainz/django-cors-headers
{%- endif %}
diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt
index 00bd56cc1..ee99e04e9 100644
--- a/{{cookiecutter.project_slug}}/requirements/local.txt
+++ b/{{cookiecutter.project_slug}}/requirements/local.txt
@@ -1,6 +1,6 @@
-r base.txt
-Werkzeug==2.0.2 # https://github.com/pallets/werkzeug
+Werkzeug[watchdog]==2.0.2 # https://github.com/pallets/werkzeug
ipdb==0.13.9 # https://github.com/gotcha/ipdb
{%- if cookiecutter.database_engine == "postgresql" %}
{%- if cookiecutter.use_docker == 'y' %}
@@ -28,7 +28,7 @@ djangorestframework-stubs==1.4.0 # https://github.com/typeddjango/djangorestfra
# Documentation
# ------------------------------------------------------------------------------
-sphinx==4.3.2 # https://github.com/sphinx-doc/sphinx
+sphinx==4.4.0 # https://github.com/sphinx-doc/sphinx
sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild
# Code quality
diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt
index 25404ed5d..67d0ae0c5 100644
--- a/{{cookiecutter.project_slug}}/requirements/production.txt
+++ b/{{cookiecutter.project_slug}}/requirements/production.txt
@@ -12,7 +12,7 @@ mysqlclient==2.1.0 # https://github.com/PyMySQL/mysqlclient
Collectfast==2.2.0 # https://github.com/antonagestam/collectfast
{%- endif %}
{%- if cookiecutter.use_sentry == "y" %}
-sentry-sdk==1.5.1 # https://github.com/getsentry/sentry-python
+sentry-sdk==1.5.2 # https://github.com/getsentry/sentry-python
{%- endif %}
{%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %}
hiredis==2.0.0 # https://github.com/redis/hiredis-py
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/0003_set_site_domain_and_name.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/0003_set_site_domain_and_name.py
index 8f4a8f997..080c734bb 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/0003_set_site_domain_and_name.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/contrib/sites/migrations/0003_set_site_domain_and_name.py
@@ -7,23 +7,52 @@ from django.conf import settings
from django.db import migrations
+def _update_or_create_site_with_sequence(site_model, connection, domain, name):
+ """Update or create the site with default ID and keep the DB sequence in sync."""
+ site, created = site_model.objects.update_or_create(
+ id=settings.SITE_ID,
+ defaults={
+ "domain": domain,
+ "name": name,
+ },
+ )
+ if created:
+ # We provided the ID explicitly when creating the Site entry, therefore the DB
+ # sequence to auto-generate them wasn't used and is now out of sync. If we
+ # don't do anything, we'll get a unique constraint violation the next time a
+ # site is created.
+ # To avoid this, we need to manually update DB sequence and make sure it's
+ # greater than the maximum value.
+ max_id = site_model.objects.order_by('-id').first().id
+ with connection.cursor() as cursor:
+ cursor.execute("SELECT last_value from django_site_id_seq")
+ (current_id,) = cursor.fetchone()
+ if current_id <= max_id:
+ cursor.execute(
+ "alter sequence django_site_id_seq restart with %s",
+ [max_id + 1],
+ )
+
+
def update_site_forward(apps, schema_editor):
"""Set site domain and name."""
Site = apps.get_model("sites", "Site")
- Site.objects.update_or_create(
- id=settings.SITE_ID,
- defaults={
- "domain": "{{cookiecutter.domain_name}}",
- "name": "{{cookiecutter.project_name}}",
- },
+ _update_or_create_site_with_sequence(
+ Site,
+ schema_editor.connection,
+ "{{cookiecutter.domain_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.objects.update_or_create(
- id=settings.SITE_ID, defaults={"domain": "example.com", "name": "example.com"}
+ _update_or_create_site_with_sequence(
+ Site,
+ schema_editor.connection,
+ "example.com",
+ "example.com",
)
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py
index 8e8e3eb2f..6675f483a 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py
@@ -3,7 +3,7 @@ from django.contrib.auth import admin as auth_admin
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
-from {{ cookiecutter.project_slug }}.users.forms import UserChangeForm, UserCreationForm
+from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm, UserAdminCreationForm
User = get_user_model()
@@ -11,8 +11,8 @@ User = get_user_model()
@admin.register(User)
class UserAdmin(auth_admin.UserAdmin):
- form = UserChangeForm
- add_form = UserCreationForm
+ form = UserAdminChangeForm
+ add_form = UserAdminCreationForm
fieldsets = (
(None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("name", "email")}),
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/forms.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/forms.py
index 80cd97aed..6e1dd9d32 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/forms.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/forms.py
@@ -1,3 +1,5 @@
+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 _
@@ -5,15 +7,36 @@ from django.utils.translation import gettext_lazy as _
User = get_user_model()
-class UserChangeForm(admin_forms.UserChangeForm):
+class UserAdminChangeForm(admin_forms.UserChangeForm):
class Meta(admin_forms.UserChangeForm.Meta):
model = User
-class UserCreationForm(admin_forms.UserCreationForm):
+class UserAdminCreationForm(admin_forms.UserCreationForm):
+ """
+ Form for User Creation in the Admin Area.
+ To change user signup, see UserSignupForm and UserSocialSignupForm.
+ """
+
class Meta(admin_forms.UserCreationForm.Meta):
model = User
error_messages = {
"username": {"unique": _("This username has already been taken.")}
}
+
+
+class UserSignupForm(SignupForm):
+ """
+ Form that will be rendered on a user sign up section/screen.
+ Default fields will be added automatically.
+ Check UserSocialSignupForm for accounts created from social.
+ """
+
+
+class UserSocialSignupForm(SocialSignupForm):
+ """
+ Renders the form when user has signed up using social accounts.
+ Default fields will be added automatically.
+ See UserSignupForm otherwise.
+ """
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py
index 935eee9a2..1f6f61bc0 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py
@@ -5,7 +5,11 @@ from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
- """Default user for {{cookiecutter.project_name}}."""
+ """
+ Default custom user model for {{cookiecutter.project_name}}.
+ If adding fields that need to be filled at user signup,
+ check forms.SignupForm and forms.SocialSignupForms accordingly.
+ """
#: First and last name do not cover name patterns around the globe
name = CharField(_("Name of User"), blank=True, max_length=255)
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_forms.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_forms.py
index 35db91b4d..e51bb6bf2 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_forms.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_forms.py
@@ -4,20 +4,20 @@ Module for all Form Tests.
import pytest
from django.utils.translation import gettext_lazy as _
-from {{ cookiecutter.project_slug }}.users.forms import UserCreationForm
+from {{ cookiecutter.project_slug }}.users.forms import UserAdminCreationForm
from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db
-class TestUserCreationForm:
+class TestUserAdminCreationForm:
"""
- Test class for all tests related to the UserCreationForm
+ Test class for all tests related to the UserAdminCreationForm
"""
def test_username_validation_error_msg(self, user: User):
"""
- Tests UserCreation Form's unique validator functions correctly by testing:
+ Tests UserAdminCreation Form's unique validator functions correctly by testing:
1) A new user with an existing username cannot be added.
2) Only 1 error is raised by the UserCreation Form
3) The desired error message is raised
@@ -25,7 +25,7 @@ class TestUserCreationForm:
# The user already exists,
# hence cannot be created.
- form = UserCreationForm(
+ form = UserAdminCreationForm(
{
"username": user.username,
"password1": user.password,
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_views.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_views.py
index 7170bfae7..944daca52 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_views.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/test_views.py
@@ -8,7 +8,7 @@ from django.http import HttpRequest, HttpResponseRedirect
from django.test import RequestFactory
from django.urls import reverse
-from {{ cookiecutter.project_slug }}.users.forms import UserChangeForm
+from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm
from {{ cookiecutter.project_slug }}.users.models import User
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
from {{ cookiecutter.project_slug }}.users.views import (
@@ -62,7 +62,7 @@ class TestUserUpdateView:
view.request = request
# Initialize the form
- form = UserChangeForm()
+ form = UserAdminChangeForm()
form.cleaned_data = []
view.form_valid(form)
|