diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 2e7b11e6..3ab939c2 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -122,6 +122,7 @@ Listed in alphabetical order. Garry Cairns `@garry-cairns`_ Garry Polley `@garrypolley`_ Gilbishkosma `@Gilbishkosma`_ + Glenn Wiskur `@gwiskur`_ Guilherme Guy `@guilherme1guy`_ Hamish Durkin `@durkode`_ Hana Quadara `@hanaquadara`_ @@ -298,6 +299,7 @@ Listed in alphabetical order. .. _@garry-cairns: https://github.com/garry-cairns .. _@garrypolley: https://github.com/garrypolley .. _@Gilbishkosma: https://github.com/Gilbishkosma +.. _@gwiskur: https://github.com/gwiskur .. _@glasslion: https://github.com/glasslion .. _@goldhand: https://github.com/goldhand .. _@hackebrot: https://github.com/hackebrot diff --git a/LICENSE b/LICENSE index 28466d40..a67e4da2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2018, Daniel Roy Greenfeld +Copyright (c) 2013-2020, Daniel Roy Greenfeld All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/README.rst b/README.rst index 64919923..1855f59f 100644 --- a/README.rst +++ b/README.rst @@ -272,7 +272,7 @@ If you do rename your fork, I encourage you to submit it to the following places * cookiecutter_ so it gets listed in the README as a template. * The cookiecutter grid_ on Django Packages. -.. _cookiecutter: https://github.com/audreyr/cookiecutter +.. _cookiecutter: https://github.com/cookiecutter/cookiecutter .. _grid: https://www.djangopackages.com/grids/g/cookiecutters/ Submit a Pull Request diff --git a/docs/deployment-on-pythonanywhere.rst b/docs/deployment-on-pythonanywhere.rst index 4738d5a5..a924d69a 100644 --- a/docs/deployment-on-pythonanywhere.rst +++ b/docs/deployment-on-pythonanywhere.rst @@ -15,7 +15,7 @@ Full instructions follow, but here's a high-level view. 2. Set your config variables in the *postactivate* script -3. Run the *manage.py* ``migrate`` and ``collectstatic`` commands +3. Run the *manage.py* ``migrate`` and ``collectstatic`` {%- if cookiecutter.use_compressor == "y" %}and ``compress`` {%- endif %}commands 4. Add an entry to the PythonAnywhere *Web tab* @@ -109,6 +109,7 @@ Now run the migration, and collectstatic: source $VIRTUAL_ENV/bin/postactivate python manage.py migrate python manage.py collectstatic + {%- if cookiecutter.use_compressor == "y" %}python manage.py compress {%- endif %} # and, optionally python manage.py createsuperuser @@ -175,6 +176,7 @@ For subsequent deployments, the procedure is much simpler. In a Bash console: git pull python manage.py migrate python manage.py collectstatic + {%- if cookiecutter.use_compressor == "y" %}python manage.py compress {%- endif %} And then go to the Web tab and hit **Reload** diff --git a/docs/faq.rst b/docs/faq.rst index 1481a8ba..59e82465 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -24,4 +24,4 @@ Why doesn't this follow the layout from Two Scoops of Django? You may notice that some elements of this project do not exactly match what we describe in chapter 3 of `Two Scoops of Django 1.11`_. The reason for that is this project, amongst other things, serves as a test bed for trying out new ideas and concepts. Sometimes they work, sometimes they don't, but the end result is that it won't necessarily match precisely what is described in the book I co-authored. -.. _Two Scoops of Django 1.11: https://www.twoscoopspress.com/collections/django/products/two-scoops-of-django-1-11 +.. _Two Scoops of Django 1.11: https://www.feldroy.com/collections/django/products/two-scoops-of-django-1-11 diff --git a/docs/index.rst b/docs/index.rst index 8e0d04aa..b29e728e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,7 @@ Welcome to Cookiecutter Django's documentation! A Cookiecutter_ template for Django. -.. _cookiecutter: https://github.com/audreyr/cookiecutter +.. _cookiecutter: https://github.com/cookiecutter/cookiecutter Contents: diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 2e2c19e1..ad1db3ee 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -301,6 +301,10 @@ def remove_drf_starter_files(): shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "users", "api")) +def remove_storages_module(): + os.remove(os.path.join("{{cookiecutter.project_slug}}", "utils", "storages.py")) + + def main(): debug = "{{ cookiecutter.debug }}".lower() == "y" @@ -361,6 +365,7 @@ def main(): WARNING + "You chose not to use a cloud provider, " "media files won't be served in production." + TERMINATOR ) + remove_storages_module() if "{{ cookiecutter.use_celery }}".lower() == "n": remove_celery_files() diff --git a/requirements.txt b/requirements.txt index 81fbf524..f4d5a931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,11 +6,11 @@ binaryornot==0.4.4 # ------------------------------------------------------------------------------ black==19.10b0 flake8==3.7.9 -flake8-isort==2.9.0 +flake8-isort==2.9.1 # Testing # ------------------------------------------------------------------------------ -tox==3.14.5 +tox==3.14.6 pytest==5.4.1 pytest-cookies==0.5.1 pytest-instafail==0.4.1.post0 diff --git a/setup.py b/setup.py index e311e82a..d1051d8b 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ except ImportError: # Our version ALWAYS matches the version of Django we support # If Django has a new release, we branch, tag, then update this setting after the tag. -version = "3.0.4" +version = "3.0.5" if sys.argv[-1] == "tag": os.system(f'git tag -a {version} -m "version {version}"') diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index 22832d34..cea29b8e 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -1,9 +1,7 @@ -"""isort:skip_file""" {% if cookiecutter.use_sentry == 'y' -%} import logging import sentry_sdk - from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.logging import LoggingIntegration {%- if cookiecutter.use_celery == 'y' %} @@ -104,11 +102,11 @@ GS_DEFAULT_ACL = "publicRead" {% if cookiecutter.use_whitenoise == 'y' -%} STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" {% elif cookiecutter.cloud_provider == 'AWS' -%} -STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage" +STATICFILES_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.StaticRootS3Boto3Storage" COLLECTFAST_STRATEGY = "collectfast.strategies.boto3.Boto3Strategy" STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/" {% elif cookiecutter.cloud_provider == 'GCP' -%} -STATICFILES_STORAGE = "config.settings.production.StaticRootGoogleCloudStorage" +STATICFILES_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.StaticRootGoogleCloudStorage" COLLECTFAST_STRATEGY = "collectfast.strategies.gcloud.GoogleCloudStrategy" STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/" {% endif -%} @@ -116,39 +114,10 @@ STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/" # MEDIA # ------------------------------------------------------------------------------ {%- if cookiecutter.cloud_provider == 'AWS' %} -# region http://stackoverflow.com/questions/10390244/ -# Full-fledge class: https://stackoverflow.com/a/18046120/104731 -from storages.backends.s3boto3 import S3Boto3Storage # noqa E402 - - -class StaticRootS3Boto3Storage(S3Boto3Storage): - location = "static" - default_acl = "public-read" - - -class MediaRootS3Boto3Storage(S3Boto3Storage): - location = "media" - file_overwrite = False - - -# endregion -DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage" +DEFAULT_FILE_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.MediaRootS3Boto3Storage" MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/" {%- elif cookiecutter.cloud_provider == 'GCP' %} -from storages.backends.gcloud import GoogleCloudStorage # noqa E402 - - -class StaticRootGoogleCloudStorage(GoogleCloudStorage): - location = "static" - default_acl = "publicRead" - - -class MediaRootGoogleCloudStorage(GoogleCloudStorage): - location = "media" - file_overwrite = False - - -DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootGoogleCloudStorage" +DEFAULT_FILE_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.MediaRootGoogleCloudStorage" MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/" {%- endif %} diff --git a/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst b/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst index 1c20d023..d8e76916 100644 --- a/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst +++ b/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst @@ -14,7 +14,7 @@ This repository comes with already prepared "Run/Debug Configurations" for docke .. image:: images/2.png -But as you can see, at the beginning there is something wrong with them. They have red X on django icon, and they cannot be used, without configuring remote python interpteter. To do that, you have to go to *Settings > Build, Execution, Deployment* first. +But as you can see, at the beginning there is something wrong with them. They have red X on django icon, and they cannot be used, without configuring remote python interpreter. To do that, you have to go to *Settings > Build, Execution, Deployment* first. Next, you have to add new remote python interpreter, based on already tested deployment settings. Go to *Settings > Project > Project Interpreter*. Click on the cog icon, and click *Add Remote*. diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 2d47c6e1..896110c2 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,6 +1,6 @@ pytz==2019.3 # https://github.com/stub42/pytz python-slugify==4.0.0 # https://github.com/un33k/python-slugify -Pillow==7.0.0 # https://github.com/python-pillow/Pillow +Pillow==7.1.1 # https://github.com/python-pillow/Pillow {%- if cookiecutter.use_compressor == "y" %} rcssmin==1.0.6{% if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin {%- endif %} @@ -13,7 +13,7 @@ redis==3.4.1 # https://github.com/andymccurdy/redis-py celery==4.4.2 # pyup: < 5.0 # https://github.com/celery/celery django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat {%- if cookiecutter.use_docker == 'y' %} -flower==0.9.3 # https://github.com/mher/flower +flower==0.9.4 # https://github.com/mher/flower {%- endif %} {%- endif %} {%- if cookiecutter.use_async == 'y' %} @@ -23,7 +23,7 @@ gunicorn==20.0.4 # https://github.com/benoitc/gunicorn # Django # ------------------------------------------------------------------------------ -django==3.0.4 # pyup: < 3.1 # https://www.djangoproject.com/ +django==3.0.5 # pyup: < 3.1 # https://www.djangoproject.com/ django-environ==0.4.5 # https://github.com/joke2k/django-environ django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils django-allauth==0.41.0 # https://github.com/pennersr/django-allauth diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index 81ef97c7..6dbf448e 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -1,12 +1,12 @@ -r ./base.txt -Werkzeug==1.0.0 # https://github.com/pallets/werkzeug +Werkzeug==1.0.1 # https://github.com/pallets/werkzeug ipdb==0.13.2 # https://github.com/gotcha/ipdb -Sphinx==2.4.4 # https://github.com/sphinx-doc/sphinx +Sphinx==3.0.0 # https://github.com/sphinx-doc/sphinx {%- if cookiecutter.use_docker == 'y' %} -psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 +psycopg2==2.8.5 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- else %} -psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2 +psycopg2-binary==2.8.5 # https://github.com/psycopg/psycopg2 {%- endif %} # Testing @@ -19,7 +19,7 @@ pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar # Code quality # ------------------------------------------------------------------------------ flake8==3.7.9 # https://github.com/PyCQA/flake8 -flake8-isort==2.9.0 # https://github.com/gforcada/flake8-isort +flake8-isort==2.9.1 # https://github.com/gforcada/flake8-isort coverage==5.0.4 # https://github.com/nedbat/coveragepy black==19.10b0 # https://github.com/ambv/black pylint-django==2.0.14 # https://github.com/PyCQA/pylint-django @@ -33,6 +33,6 @@ pre-commit==2.2.0 # https://github.com/pre-commit/pre-commit factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy django-debug-toolbar==2.2 # https://github.com/jazzband/django-debug-toolbar -django-extensions==2.2.8 # https://github.com/django-extensions/django-extensions +django-extensions==2.2.9 # https://github.com/django-extensions/django-extensions django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin -pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django +pytest-django==3.9.0 # https://github.com/pytest-dev/pytest-django diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index 75ae8c30..ebdaaf66 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -2,10 +2,10 @@ -r ./base.txt -psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- if cookiecutter.use_async == 'n' %} gunicorn==20.0.4 # https://github.com/benoitc/gunicorn {%- endif %} +psycopg2==2.8.5 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- if cookiecutter.use_whitenoise == 'n' %} Collectfast==2.1.0 # https://github.com/antonagestam/collectfast {%- endif %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/conftest.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/conftest.py index 8cd51d7f..335648e0 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/conftest.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/conftest.py @@ -1,5 +1,4 @@ import pytest -from django.test import RequestFactory from {{ cookiecutter.project_slug }}.users.models import User from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory @@ -13,8 +12,3 @@ def media_storage(settings, tmpdir): @pytest.fixture def user() -> User: return UserFactory() - - -@pytest.fixture -def request_factory() -> RequestFactory: - return RequestFactory() 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 9e868899..ca006059 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 @@ -16,18 +16,18 @@ class TestUserUpdateView: https://github.com/pytest-dev/pytest-django/pull/258 """ - def test_get_success_url(self, user: User, request_factory: RequestFactory): + def test_get_success_url(self, user: User, rf: RequestFactory): view = UserUpdateView() - request = request_factory.get("/fake-url/") + request = rf.get("/fake-url/") request.user = user view.request = request assert view.get_success_url() == f"/users/{user.username}/" - def test_get_object(self, user: User, request_factory: RequestFactory): + def test_get_object(self, user: User, rf: RequestFactory): view = UserUpdateView() - request = request_factory.get("/fake-url/") + request = rf.get("/fake-url/") request.user = user view.request = request @@ -36,9 +36,9 @@ class TestUserUpdateView: class TestUserRedirectView: - def test_get_redirect_url(self, user: User, request_factory: RequestFactory): + def test_get_redirect_url(self, user: User, rf: RequestFactory): view = UserRedirectView() - request = request_factory.get("/fake-url") + request = rf.get("/fake-url") request.user = user view.request = request diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/utils/storages.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/utils/storages.py new file mode 100644 index 00000000..b712d323 --- /dev/null +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/utils/storages.py @@ -0,0 +1,25 @@ +{% if cookiecutter.cloud_provider == 'AWS' -%} +from storages.backends.s3boto3 import S3Boto3Storage + + +class StaticRootS3Boto3Storage(S3Boto3Storage): + location = "static" + default_acl = "public-read" + + +class MediaRootS3Boto3Storage(S3Boto3Storage): + location = "media" + file_overwrite = False +{%- elif cookiecutter.cloud_provider == 'GCP' -%} +from storages.backends.gcloud import GoogleCloudStorage + + +class StaticRootGoogleCloudStorage(GoogleCloudStorage): + location = "static" + default_acl = "publicRead" + + +class MediaRootGoogleCloudStorage(GoogleCloudStorage): + location = "media" + file_overwrite = False +{%- endif %}