Merge branch 'master' into feature/mysql-support

This commit is contained in:
Abdullah Adeel 2022-01-17 16:21:09 +05:00 committed by GitHub
commit 9f99400b92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 180 additions and 47 deletions

View File

@ -1202,5 +1202,20 @@
"name": "Bogdan Mateescu", "name": "Bogdan Mateescu",
"github_login": "mateesville93", "github_login": "mateesville93",
"twitter_username": "" "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": ""
} }
] ]

View File

@ -26,7 +26,7 @@ jobs:
run: python scripts/update_contributors.py run: python scripts/update_contributors.py
- name: Commit changes - name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4.12.0 uses: stefanzweifel/git-auto-commit-action@v4.13.1
with: with:
commit_message: Update Contributors commit_message: Update Contributors
file_pattern: CONTRIBUTORS.md .github/contributors.json file_pattern: CONTRIBUTORS.md .github/contributors.json

View File

@ -3,6 +3,37 @@ All enhancements and patches to Cookiecutter Django will be documented in this f
<!-- GENERATOR_PLACEHOLDER --> <!-- GENERATOR_PLACEHOLDER -->
## 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 ## 2022.01.07
### Fixed ### Fixed

View File

@ -733,6 +733,13 @@ Listed in alphabetical order.
</td> </td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td>Fuzzwah</td>
<td>
<a href="https://github.com/Fuzzwah">Fuzzwah</a>
</td>
<td></td>
</tr>
<tr> <tr>
<td>Gabriel Mejia</td> <td>Gabriel Mejia</td>
<td> <td>
@ -1342,6 +1349,13 @@ Listed in alphabetical order.
</td> </td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td>Pedro Campos</td>
<td>
<a href="https://github.com/pcampos119104">pcampos119104</a>
</td>
<td></td>
</tr>
<tr> <tr>
<td>Peter Bittner</td> <td>Peter Bittner</td>
<td> <td>
@ -1531,6 +1545,13 @@ Listed in alphabetical order.
</td> </td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td>Thibault J.</td>
<td>
<a href="https://github.com/thibault">thibault</a>
</td>
<td>thibault</td>
</tr>
<tr> <tr>
<td>Théo Segonds</td> <td>Théo Segonds</td>
<td> <td>

View File

@ -63,13 +63,17 @@ Projects that provide financial support to the maintainers:
--- ---
[![Two Scoops of Django](https://cdn.shopify.com/s/files/1/0304/6901/products/Two-Scoops-of-Django-3-Alpha-Cover_540x_26507b15-e489-470b-8a97-02773dd498d1_1080x.jpg){#Two Scoops of Django 3.x .align-center}](https://www.feldroy.com/products//two-scoops-of-django-3-x) <p align="center">
<a href="https://www.feldroy.com/products//two-scoops-of-django-3-x"><img src="https://cdn.shopify.com/s/files/1/0304/6901/products/Two-Scoops-of-Django-3-Alpha-Cover_540x_26507b15-e489-470b-8a97-02773dd498d1_1080x.jpg"></a>
</p>
Two Scoops of Django 3.x is the best ice cream-themed Django reference in the universe! Two Scoops of Django 3.x is the best ice cream-themed Django reference in the universe!
### PyUp ### PyUp
[![pyup](https://pyup.io/static/images/logo.png){#pyup .align-center}](https://pyup.io/) <p align="center">
<a href="https://pyup.io/"><img src="https://pyup.io/static/images/logo.png"></a>
</p>
PyUp brings you automated security and dependency updates used by Google and other organizations. Free for open source projects! PyUp brings you automated security and dependency updates used by Google and other organizations. Free for open source projects!

View File

@ -191,16 +191,18 @@ docker
The ``container_name`` from the yml file can be used to check on containers with docker commands, for example: :: The ``container_name`` from the yml file can be used to check on containers with docker commands, for example: ::
$ docker logs worker $ docker logs <project_slug>_local_celeryworker
$ docker top worker $ docker top <project_slug>_local_celeryworker
Notice that the ``container_name`` is generated dynamically using your project slug as a prefix
Mailhog Mailhog
~~~~~~~ ~~~~~~~
When developing locally you can go with MailHog_ for email testing provided ``use_mailhog`` was set to ``y`` on setup. To proceed, 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 ``<project_slug>_local_mailhog`` container is up and running;
#. open up ``http://127.0.0.1:8025``. #. open up ``http://127.0.0.1:8025``.

View File

@ -1,2 +1,2 @@
sphinx==4.3.2 sphinx==4.4.0
sphinx-rtd-theme==1.0.0 sphinx-rtd-theme==1.0.0

View File

@ -21,6 +21,6 @@ pyyaml==6.0
# Scripting # Scripting
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
PyGithub==1.55 PyGithub==1.55
gitpython==3.1.25 gitpython==3.1.26
jinja2==3.0.3 jinja2==3.0.3
requests==2.27.1 requests==2.27.1

View File

@ -5,7 +5,7 @@ except ImportError:
from distutils.core import setup from distutils.core import setup
# We use calendar versioning # We use calendar versioning
version = "2022.01.07" version = "2022.01.14"
with open("README.rst") as readme_file: with open("README.rst") as readme_file:
long_description = readme_file.read() long_description = readme_file.read()

View File

@ -312,8 +312,12 @@ ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# https://django-allauth.readthedocs.io/en/latest/configuration.html # https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter" 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 # https://django-allauth.readthedocs.io/en/latest/configuration.html
SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter" 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' -%} {% if cookiecutter.use_compressor == 'y' -%}
# django-compressor # django-compressor
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -16,7 +16,7 @@ services:
context: . context: .
dockerfile: ./compose/local/django/Dockerfile dockerfile: ./compose/local/django/Dockerfile
image: {{ cookiecutter.project_slug }}_local_django image: {{ cookiecutter.project_slug }}_local_django
container_name: django container_name: {{ cookiecutter.project_slug }}_local_django
depends_on: depends_on:
{%- if cookiecutter.database_engine == 'postgresql' %} {%- if cookiecutter.database_engine == 'postgresql' %}
- postgres - postgres
@ -50,10 +50,10 @@ services:
context: . context: .
dockerfile: ./compose/production/postgres/Dockerfile dockerfile: ./compose/production/postgres/Dockerfile
image: {{ cookiecutter.project_slug }}_production_postgres image: {{ cookiecutter.project_slug }}_production_postgres
container_name: postgres container_name: {{ cookiecutter.project_slug }}_local_postgres
volumes: volumes:
- local_postgres_data:/var/lib/postgresql/data:Z - {{ cookiecutter.project_slug }}_local_postgres_data:/var/lib/postgresql/data:Z
- local_postgres_data_backups:/backups:z - {{ cookiecutter.project_slug }}_local_postgres_data_backups:/backups:z
env_file: env_file:
- ./.envs/.local/.postgres - ./.envs/.local/.postgres
{%- endif %} {%- endif %}
@ -73,7 +73,7 @@ services:
docs: docs:
image: {{ cookiecutter.project_slug }}_local_docs image: {{ cookiecutter.project_slug }}_local_docs
container_name: docs container_name: {{ cookiecutter.project_slug }}_local_docs
build: build:
context: . context: .
dockerfile: ./compose/local/docs/Dockerfile dockerfile: ./compose/local/docs/Dockerfile
@ -90,7 +90,7 @@ services:
mailhog: mailhog:
image: mailhog/mailhog:v1.0.0 image: mailhog/mailhog:v1.0.0
container_name: mailhog container_name: {{ cookiecutter.project_slug }}_local_mailhog
ports: ports:
- "8025:8025" - "8025:8025"
@ -99,12 +99,12 @@ services:
redis: redis:
image: redis:6 image: redis:6
container_name: redis container_name: {{ cookiecutter.project_slug }}_local_redis
celeryworker: celeryworker:
<<: *django <<: *django
image: {{ cookiecutter.project_slug }}_local_celeryworker image: {{ cookiecutter.project_slug }}_local_celeryworker
container_name: celeryworker container_name: {{ cookiecutter.project_slug }}_local_celeryworker
depends_on: depends_on:
- redis - redis
{%- if cookiecutter.database_engine == 'postgresql' %} {%- if cookiecutter.database_engine == 'postgresql' %}
@ -122,7 +122,7 @@ services:
celerybeat: celerybeat:
<<: *django <<: *django
image: {{ cookiecutter.project_slug }}_local_celerybeat image: {{ cookiecutter.project_slug }}_local_celerybeat
container_name: celerybeat container_name: {{ cookiecutter.project_slug }}_local_celerybeat
depends_on: depends_on:
- redis - redis
{%- if cookiecutter.database_engine == 'postgresql' %} {%- if cookiecutter.database_engine == 'postgresql' %}
@ -140,7 +140,7 @@ services:
flower: flower:
<<: *django <<: *django
image: {{ cookiecutter.project_slug }}_local_flower image: {{ cookiecutter.project_slug }}_local_flower
container_name: flower container_name: {{ cookiecutter.project_slug }}_local_flower
ports: ports:
- "5555:5555" - "5555:5555"
command: /start-flower command: /start-flower
@ -153,7 +153,7 @@ services:
context: . context: .
dockerfile: ./compose/local/node/Dockerfile dockerfile: ./compose/local/node/Dockerfile
image: {{ cookiecutter.project_slug }}_local_node image: {{ cookiecutter.project_slug }}_local_node
container_name: node container_name: {{ cookiecutter.project_slug }}_local_node
depends_on: depends_on:
- django - django
volumes: volumes:

View File

@ -24,7 +24,7 @@ flower==1.0.0 # https://github.com/mher/flower
{%- endif %} {%- endif %}
{%- endif %} {%- endif %}
{%- if cookiecutter.use_async == 'y' %} {%- 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 %} {%- endif %}
# Django # Django
@ -42,5 +42,5 @@ django-redis==5.2.0 # https://github.com/jazzband/django-redis
{%- if cookiecutter.use_drf == "y" %} {%- if cookiecutter.use_drf == "y" %}
# Django REST Framework # Django REST Framework
djangorestframework==3.13.1 # https://github.com/encode/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 %} {%- endif %}

View File

@ -1,6 +1,6 @@
-r base.txt -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 ipdb==0.13.9 # https://github.com/gotcha/ipdb
{%- if cookiecutter.database_engine == "postgresql" %} {%- if cookiecutter.database_engine == "postgresql" %}
{%- if cookiecutter.use_docker == 'y' %} {%- if cookiecutter.use_docker == 'y' %}
@ -28,7 +28,7 @@ djangorestframework-stubs==1.4.0 # https://github.com/typeddjango/djangorestfra
# Documentation # 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 sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild
# Code quality # Code quality

View File

@ -12,7 +12,7 @@ mysqlclient==2.1.0 # https://github.com/PyMySQL/mysqlclient
Collectfast==2.2.0 # https://github.com/antonagestam/collectfast Collectfast==2.2.0 # https://github.com/antonagestam/collectfast
{%- endif %} {%- endif %}
{%- if cookiecutter.use_sentry == "y" %} {%- 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 %} {%- endif %}
{%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %} {%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %}
hiredis==2.0.0 # https://github.com/redis/hiredis-py hiredis==2.0.0 # https://github.com/redis/hiredis-py

View File

@ -7,23 +7,52 @@ from django.conf import settings
from django.db import migrations 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): def update_site_forward(apps, schema_editor):
"""Set site domain and name.""" """Set site domain and name."""
Site = apps.get_model("sites", "Site") Site = apps.get_model("sites", "Site")
Site.objects.update_or_create( _update_or_create_site_with_sequence(
id=settings.SITE_ID, Site,
defaults={ schema_editor.connection,
"domain": "{{cookiecutter.domain_name}}", "{{cookiecutter.domain_name}}",
"name": "{{cookiecutter.project_name}}", "{{cookiecutter.project_name}}",
},
) )
def update_site_backward(apps, schema_editor): def update_site_backward(apps, schema_editor):
"""Revert site domain and name to default.""" """Revert site domain and name to default."""
Site = apps.get_model("sites", "Site") Site = apps.get_model("sites", "Site")
Site.objects.update_or_create( _update_or_create_site_with_sequence(
id=settings.SITE_ID, defaults={"domain": "example.com", "name": "example.com"} Site,
schema_editor.connection,
"example.com",
"example.com",
) )

View File

@ -3,7 +3,7 @@ from django.contrib.auth import admin as auth_admin
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _ 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() User = get_user_model()
@ -11,8 +11,8 @@ User = get_user_model()
@admin.register(User) @admin.register(User)
class UserAdmin(auth_admin.UserAdmin): class UserAdmin(auth_admin.UserAdmin):
form = UserChangeForm form = UserAdminChangeForm
add_form = UserCreationForm add_form = UserAdminCreationForm
fieldsets = ( fieldsets = (
(None, {"fields": ("username", "password")}), (None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("name", "email")}), (_("Personal info"), {"fields": ("name", "email")}),

View File

@ -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 forms as admin_forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -5,15 +7,36 @@ from django.utils.translation import gettext_lazy as _
User = get_user_model() User = get_user_model()
class UserChangeForm(admin_forms.UserChangeForm): class UserAdminChangeForm(admin_forms.UserChangeForm):
class Meta(admin_forms.UserChangeForm.Meta): class Meta(admin_forms.UserChangeForm.Meta):
model = User 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): class Meta(admin_forms.UserCreationForm.Meta):
model = User model = User
error_messages = { error_messages = {
"username": {"unique": _("This username has already been taken.")} "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.
"""

View File

@ -5,7 +5,11 @@ from django.utils.translation import gettext_lazy as _
class User(AbstractUser): 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 #: First and last name do not cover name patterns around the globe
name = CharField(_("Name of User"), blank=True, max_length=255) name = CharField(_("Name of User"), blank=True, max_length=255)

View File

@ -4,20 +4,20 @@ Module for all Form Tests.
import pytest import pytest
from django.utils.translation import gettext_lazy as _ 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 from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db 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): 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. 1) A new user with an existing username cannot be added.
2) Only 1 error is raised by the UserCreation Form 2) Only 1 error is raised by the UserCreation Form
3) The desired error message is raised 3) The desired error message is raised
@ -25,7 +25,7 @@ class TestUserCreationForm:
# The user already exists, # The user already exists,
# hence cannot be created. # hence cannot be created.
form = UserCreationForm( form = UserAdminCreationForm(
{ {
"username": user.username, "username": user.username,
"password1": user.password, "password1": user.password,

View File

@ -8,7 +8,7 @@ from django.http import HttpRequest, HttpResponseRedirect
from django.test import RequestFactory from django.test import RequestFactory
from django.urls import reverse 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.models import User
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
from {{ cookiecutter.project_slug }}.users.views import ( from {{ cookiecutter.project_slug }}.users.views import (
@ -62,7 +62,7 @@ class TestUserUpdateView:
view.request = request view.request = request
# Initialize the form # Initialize the form
form = UserChangeForm() form = UserAdminChangeForm()
form.cleaned_data = [] form.cleaned_data = []
view.form_valid(form) view.form_valid(form)