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",
"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": ""
}
]

View File

@ -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

View File

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

View File

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

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: ::
$ docker logs worker
$ docker top worker
$ docker logs <project_slug>_local_celeryworker
$ docker top <project_slug>_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 ``<project_slug>_local_mailhog`` container is up and running;
#. 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

View File

@ -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

View File

@ -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()

View File

@ -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
# ------------------------------------------------------------------------------

View File

@ -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:

View File

@ -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 %}

View File

@ -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

View File

@ -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

View File

@ -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",
)

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.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")}),

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 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.
"""

View File

@ -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)

View File

@ -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,

View File

@ -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)