mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-18 02:44:51 +03:00
Merge branch 'master' into feature/mysql-support
This commit is contained in:
commit
9f99400b92
15
.github/contributors.json
vendored
15
.github/contributors.json
vendored
|
@ -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": ""
|
||||
}
|
||||
]
|
2
.github/workflows/update-contributors.yml
vendored
2
.github/workflows/update-contributors.yml
vendored
|
@ -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
|
||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
<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 .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!
|
||||
|
||||
|
|
|
@ -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``.
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
sphinx==4.3.2
|
||||
sphinx==4.4.0
|
||||
sphinx-rtd-theme==1.0.0
|
||||
|
|
|
@ -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
|
||||
|
|
2
setup.py
2
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()
|
||||
|
|
|
@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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")}),
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user