Repository: pydanny/cookiecutter-djangopackage

Issue:
Add Django 1.11 to the generated project build matrix
### Description

Django 1.11 was [released April 4th](https://docs.djangoproject.com/en/1.11/releases/1.11/), it would be good to add it to the build matrix!

### To note

- This is the first version to support Python 3.6, so ideally we would test with Python 3.6 only Django 1.11.
- Django master branch dropped support for Python 2.7, so we should not run tests with that combination.
- From my experience a few months ago, Travis doesn't play well when specifying `python: 3.5` and running tox environment for 3.6 (or the other way round), it crashed with Interpreter not found, but they've updated their image [recently](https://blog.travis-ci.com/2017-05-04-precise-image-updates), that might have solved that.

### Suggested solution 

A solution that is used by [DRF](https://github.com/encode/django-rest-framework/blob/master/.travis.yml) is to use `travis-tox`. However, it's adding another dependency and the build matrix is quite complex, with a few moving parts.

I'm happy to put together a PR if that sounds reasonable.


Discussion:

Resolving patch:
diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py
--- a/hooks/post_gen_project.py
+++ b/hooks/post_gen_project.py
@@ -8,6 +8,7 @@
 
 PROJECT_DIRECTORY = os.path.realpath(os.path.curdir)
 
+
 def remove_example_project(project_directory):
     """Removes the taskapp if celery isn't going to be used"""
     # Determine the local_setting_file_location
diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py
--- a/hooks/pre_gen_project.py
+++ b/hooks/pre_gen_project.py
@@ -14,9 +14,9 @@
     logger.error('Invalid value for app_name "{}"'.format(app_name))
     sys.exit(1)
 
-ALLOWED_VERSIONS = ["1.8", "1.9", "1.10", "master"]
+ALLOWED_VERSIONS = ["1.8", "1.9", "1.10", "1.11", "master"]
 
 for django_version in '{{cookiecutter.django_versions}}'.split(","):
-	if str(django_version).strip() not in ALLOWED_VERSIONS:
-		logger.error('Invalid Django version "{}". '.format(django_version))
-		sys.exit(1)
+    if str(django_version).strip() not in ALLOWED_VERSIONS:
+        logger.error('Invalid Django version "{}". '.format(django_version))
+        sys.exit(1)
diff --git a/{{cookiecutter.repo_name}}/setup.py b/{{cookiecutter.repo_name}}/setup.py
--- a/{{cookiecutter.repo_name}}/setup.py
+++ b/{{cookiecutter.repo_name}}/setup.py
@@ -28,7 +28,7 @@ def get_version(*file_paths):
     raise RuntimeError('Unable to find version string.')
 
 
-version = get_version("{{ cookiecutter.app_name }}", "__init__.py")
+version = '{{ cookiecutter.version }}'
 
 
 if sys.argv[-1] == 'publish':
@@ -74,15 +74,16 @@ def get_version(*file_paths):
         'Framework :: Django',{% if '1.8' in cookiecutter.django_versions %}
         'Framework :: Django :: 1.8',{% endif %}{% if '1.9' in cookiecutter.django_versions %}
         'Framework :: Django :: 1.9',{% endif %}{% if '1.10' in cookiecutter.django_versions %}
-        'Framework :: Django :: 1.10',{% endif %}
+        'Framework :: Django :: 1.10',{% endif %}{% if '1.11' in cookiecutter.django_versions %}
+        'Framework :: Django :: 1.11',{% endif %}
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
         'Natural Language :: English',
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3',{% if '1.8' in cookiecutter.django_versions %}
-        'Programming Language :: Python :: 3.3',{% endif %}
+        'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
     ],
 )


Test patch:
diff --git a/tests/test_bake_project.py b/tests/test_bake_project.py
--- a/tests/test_bake_project.py
+++ b/tests/test_bake_project.py
@@ -24,7 +24,8 @@ def inside_dir(dirpath):
 def bake_in_temp_dir(cookies, *args, **kwargs):
     """
     Delete the temporal directory that is created when executing the tests
-    :param cookies: pytest_cookies.Cookies, cookie to be baked and its temporal files will be removed
+    :param cookies: pytest_cookies.Cookies, cookie to be baked and its
+    temporal files will be removed
     """
     result = cookies.bake(*args, **kwargs)
     try:
@@ -39,12 +40,12 @@ def test_bake_selecting_license(cookies):
     """
     license_strings = {
         'Apache Software License 2.0': 'Apache',
-        'BSD': 'Redistributions of source code must retain the above copyright notice, this',
-        'ISCL': 'Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee',
+        'BSD': 'Redistributions of source code must retain the above copyright notice, this',  # noqa
+        'ISCL': 'Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee',  # noqa
         'MIT': 'MIT ',
     }
     for license, target_string in license_strings.items():
-        with bake_in_temp_dir(cookies, extra_context={'open_source_license': license}) as result:
+        with bake_in_temp_dir(cookies, extra_context={'open_source_license': license}) as result:  # noqa
             assert target_string in result.project.join('LICENSE').read()
             assert license in result.project.join('setup.py').read()
 
@@ -60,7 +61,7 @@ def test_readme(cookies):
 
 
 def test_models(cookies):
-    extra_context = {'models': 'ChocolateChip,Zimsterne', 'app_name': 'cookies'}
+    extra_context = {'models': 'ChocolateChip,Zimsterne', 'app_name': 'cookies'}  # noqa
     with bake_in_temp_dir(cookies, extra_context=extra_context) as result:
 
         model_file = result.project.join('cookies', 'models.py')
@@ -68,7 +69,6 @@ def test_models(cookies):
         assert 'TimeStampedModel' in model_txt
 
 
-
 def test_views_with_models(cookies):
     """
     Test case to assert if the views are created when the models are passed
@@ -86,7 +86,8 @@ def test_views_with_models(cookies):
 
 def test_views_without_models(cookies):
     """
-    Test case to assert that the views.py file is empty when there are no models defined
+    Test case to assert that the views.py file is empty when there are no
+    models defined
     """
     extra_context = {'app_name': 'cookies'}
     with bake_in_temp_dir(cookies, extra_context=extra_context) as result:
@@ -113,13 +114,14 @@ def test_urls_regex_with_model(cookies):
 
 def test_urls_without_model(cookies):
     """
-    Test case to assert that the urls.py file has the basic template when there are no models defined
+    Test case to assert that the urls.py file has the basic template when
+    there are no models defined
     """
     extra_context = {'app_name': 'cookies'}
     with bake_in_temp_dir(cookies, extra_context=extra_context) as result:
         urls_file = result.project.join('cookies', 'urls.py')
         urls_file_txt = urls_file.read()
-        basic_url = "url(r'', TemplateView.as_view(template_name=\"base.html\"))"
+        basic_url = "url(r'', TemplateView.as_view(template_name=\"base.html\"))"  # noqa
         assert basic_url in urls_file_txt
 
 
@@ -142,7 +144,7 @@ def test_tox(cookies):
 
         tox_file = result.project.join('tox.ini')
         tox_text = tox_file.read()
-        assert 'commands = coverage run --source cookie_lover runtests.py' in tox_text
+        assert 'commands = coverage run --source cookie_lover runtests.py' in tox_text  # noqa
 
 
 def test_authors(cookies):
@@ -153,17 +155,18 @@ def test_authors(cookies):
         authors_text = authors_file.read()
         assert 'Cookie McCookieface' in authors_text
 
+
 def test_manifest(cookies):
     extra_context = {'app_name': 'cookie_lover'}
     with bake_in_temp_dir(cookies, extra_context=extra_context) as result:
 
         manifest_file = result.project.join('MANIFEST.in')
         manifest_text = manifest_file.read()
-        assert 'recursive-include cookie_lover *.html *.png *.gif *js *.css *jpg *jpeg *svg *py' in manifest_text
+        assert 'recursive-include cookie_lover *.html *.png *.gif *js *.css *jpg *jpeg *svg *py' in manifest_text  # noqa
 
 
 def test_setup_py(cookies):
-    extra_context = {'app_name': 'cookie_lover', 'full_name': 'Cookie McCookieface'}
+    extra_context = {'app_name': 'cookie_lover', 'full_name': 'Cookie McCookieface'}  # noqa
     with bake_in_temp_dir(cookies, extra_context=extra_context) as result:
 
         setup_file = result.project.join('setup.py')
@@ -174,39 +177,52 @@ def test_setup_py(cookies):
 
 def test_django_versions_default(cookies):
     """
-    Test case to assert that the tox.ini & setup.py files are generated with correct versions w default Django versions
+    Test case to assert that the tox.ini & setup.py files are generated
+    with correct versions w default Django versions
     """
 
     with bake_in_temp_dir(cookies) as result:
 
         tox_file = result.project.join('tox.ini')
         tox_text = tox_file.read()
-        assert "{py27,py33,py34,py35}-django-18" in tox_text
-        assert "{py27,py34,py35}-django-19" in tox_text
+        assert "{py27,py34,py35}-django-18" in tox_text
+        assert "{py27,py34,py35,py36}-django-19" in tox_text
+        assert "{py27,py34,py35,py36}-django-110" in tox_text
+        assert "{py27,py34,py35,py36}-django-111" in tox_text
         travis_file = result.project.join('.travis.yml')
         travis_text = travis_file.read()
         assert 'py27-django-18' in travis_text
-        assert 'py33-django-18' in travis_text
         assert 'py34-django-18' in travis_text
         assert 'py35-django-18' in travis_text
         assert 'py27-django-19' in travis_text
         assert 'py34-django-19' in travis_text
         assert 'py35-django-19' in travis_text
+        assert 'py36-django-19' in travis_text
+        assert 'py27-django-110' in travis_text
+        assert 'py34-django-110' in travis_text
+        assert 'py35-django-110' in travis_text
+        assert 'py36-django-110' in travis_text
+        assert 'py27-django-111' in travis_text
+        assert 'py34-django-111' in travis_text
+        assert 'py35-django-111' in travis_text
+        assert 'py36-django-111' in travis_text
         setup_file = result.project.join('setup.py')
         setup_text = setup_file.read()
         assert "'Framework :: Django :: 1.8'," in setup_text
         assert "'Framework :: Django :: 1.9'," in setup_text
+        assert "'Framework :: Django :: 1.10'," in setup_text
+        assert "'Framework :: Django :: 1.11'," in setup_text
         assert "'Programming Language :: Python :: 2'," in setup_text
         assert "'Programming Language :: Python :: 2.7'," in setup_text
         assert "'Programming Language :: Python :: 3'," in setup_text
-        assert "'Programming Language :: Python :: 3.3'," in setup_text
         assert "'Programming Language :: Python :: 3.4'," in setup_text
         assert "'Programming Language :: Python :: 3.5'," in setup_text
 
 
 def test_new_django_versions(cookies):
     """
-    Test case to assert that the tox.ini & setup.py files are generated with correct versions with a new Django version
+    Test case to assert that the tox.ini & setup.py files are generated
+    with correct versions with a new Django version
     """
 
     extra_context = {'django_versions': '1.10'}
@@ -214,7 +230,7 @@ def test_new_django_versions(cookies):
 
         tox_file = result.project.join('tox.ini')
         tox_text = tox_file.read()
-        assert "{py27,py34,py35}-django-110" in tox_text
+        assert "{py27,py34,py35,py36}-django-110" in tox_text
         assert 'django19' not in tox_text
         travis_file = result.project.join('.travis.yml')
         travis_text = travis_file.read()
@@ -265,6 +281,7 @@ def test_app_config(cookies):
 
 # example project tests from here on
 
+
 def test_make_migrations(cookies):
     """generated project should be able to generate migrations"""
     with bake_in_temp_dir(cookies, extra_context={}) as result: