From 0b63e752d08e5bc2d9a1c025623c50cec9f4cf4e Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Wed, 8 Mar 2017 09:57:04 -0600 Subject: [PATCH 1/7] support django 1.11 --- parsley/decorators.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/parsley/decorators.py b/parsley/decorators.py index b074027..81bf7b0 100644 --- a/parsley/decorators.py +++ b/parsley/decorators.py @@ -1,4 +1,5 @@ import re +import types from django import forms from parsley.widgets import ParsleyChoiceFieldRendererMixin @@ -24,10 +25,25 @@ def update_widget_attrs(field, prefix='data'): attrs = field.widget.attrs if field.required: if isinstance(field.widget, forms.widgets.RadioSelect): - # Use a mixin, to try and support non-standard renderers if possible - class ParsleyChoiceFieldRenderer(ParsleyChoiceFieldRendererMixin, field.widget.renderer): - parsley_namespace = prefix - field.widget.renderer = ParsleyChoiceFieldRenderer + try: + # django >= 1.11 + original_create_option = field.widget.create_option + + def new_create_option(self, name, value, label, selected, index, subindex=None, attrs=None): + if index == len(self.choices) - 1: + attrs = attrs or {} + attrs["{prefix}-required".format(prefix=prefix)] = "true" + + return original_create_option(name, value, label, selected, index, subindex, attrs) + + field.widget.create_option = types.MethodType(new_create_option, field.widget) + except AttributeError: + # django < 1.11 + + # Use a mixin, to try and support non-standard renderers if possible + class ParsleyChoiceFieldRenderer(ParsleyChoiceFieldRendererMixin, field.widget.renderer): + parsley_namespace = prefix + field.widget.renderer = ParsleyChoiceFieldRenderer else: attrs["{prefix}-required".format(prefix=prefix)] = "true" error_message = field.error_messages.get('required', None) From 178bb6946becab88e98009db74fbbfad2daf9bb6 Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Wed, 8 Mar 2017 09:59:49 -0600 Subject: [PATCH 2/7] run on django 1.10 and 1.11 --- .travis.yml | 2 ++ tox.ini | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89e7f1a..903b80c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ python: env: - DJANGO='Django>=1.8,<1.9' - DJANGO='Django>=1.9,<1.10' + - DJANGO='Django>=1.10,<1.11' + - DJANGO='Django==1.11b1' install: - pip install --use-mirrors $DJANGO diff --git a/tox.ini b/tox.ini index edc3c6b..d645086 100644 --- a/tox.ini +++ b/tox.ini @@ -11,5 +11,7 @@ basepython = py35: python3.5 deps = coverage>=4 - dj18: django==1.8.13 - dj19: django==1.9.7 + dj18: django==1.8.17 + dj19: django==1.9.12 + dj10: django==1.10.6 + dj11: django==1.11b1 From 9ac8f3412ec5f45b491adec996ac4a8a447b7fdb Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Wed, 8 Mar 2017 10:05:23 -0600 Subject: [PATCH 3/7] remove --use-mirrors option --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 903b80c..6c18286 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,8 @@ env: - DJANGO='Django==1.11b1' install: - - pip install --use-mirrors $DJANGO - - pip install --use-mirrors coverage coveralls + - pip install $DJANGO + - pip install coverage coveralls script: coverage run setup.py test From 4acbe71b490faafb614e2a1f5c9425dfe193544f Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Fri, 26 May 2017 10:38:42 -0500 Subject: [PATCH 4/7] cleanup --- .gitignore | 1 + .travis.yml | 12 +++++++++++- runtests.py | 5 ++--- setup.py | 3 ++- tox.ini | 13 ++++++++----- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index f35fc07..91cdc57 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ _build/ *.pyc test.db example/example/settings/local.py +/.idea \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 6c18286..7aa11d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,13 @@ python: - 2.7 - 3.4 - 3.5 + - 3.6 env: - DJANGO='Django>=1.8,<1.9' - DJANGO='Django>=1.9,<1.10' - DJANGO='Django>=1.10,<1.11' - - DJANGO='Django==1.11b1' + - DJANGO='Django>=1.11,<2.0' install: - pip install $DJANGO @@ -17,4 +18,13 @@ install: script: coverage run setup.py test +matrix: + exclude: + - python: 3.6 + env: DJANGO='Django>=1.8,<1.9' + - python: 3.6 + env: DJANGO='Django>=1.9,<1.10' + - python: 3.6 + env: DJANGO='Django>=1.10,<1.11' + after_success: coveralls diff --git a/runtests.py b/runtests.py index 30913cb..3d26a2f 100755 --- a/runtests.py +++ b/runtests.py @@ -6,6 +6,7 @@ from optparse import OptionParser from django.conf import settings +from django.test.runner import DiscoverRunner # For convenience configure settings if they are not pre-configured or if we # haven't been provided settings to use by environment variable. @@ -19,7 +20,7 @@ INSTALLED_APPS=[ 'parsley', ], - STATIC_URL = "/static/", + STATIC_URL="/static/", ) # Setup Django 1.7+ (AppRegistryNotReady). @@ -27,8 +28,6 @@ if hasattr(django, 'setup'): django.setup() -from django.test.runner import DiscoverRunner - def runtests(*test_args, **kwargs): if not test_args: diff --git a/setup.py b/setup.py index f981fdb..a324581 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -VERSION = '0.6' +VERSION = '0.7b1' import os import sys @@ -6,6 +6,7 @@ from distutils.util import convert_path from setuptools import setup, find_packages + def read(fname): try: return open(os.path.join(os.path.dirname(__file__), fname)).read() diff --git a/tox.ini b/tox.ini index d645086..8f37d6d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,7 @@ [tox] -envlist = py{27,34,35}-dj{18,19} +envlist = + py{27,34,35}-dj{18,19,110} + py{27,34,35,36}-dj{111} skipsdist = True @@ -9,9 +11,10 @@ basepython = py27: python2.7 py34: python3.4 py35: python3.5 + py36: python3.6 deps = coverage>=4 - dj18: django==1.8.17 - dj19: django==1.9.12 - dj10: django==1.10.6 - dj11: django==1.11b1 + dj18: django==1.8.18 + dj19: django==1.9.13 + dj110: django==1.10.7 + dj111: django==1.11.1 From deeb5d14024bf1fc7840804e78898749d55a7eed Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Fri, 26 May 2017 10:52:03 -0500 Subject: [PATCH 5/7] remove warning for django > 1.9 --- parsley/templatetags/parsley.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/parsley/templatetags/parsley.py b/parsley/templatetags/parsley.py index e8112bc..71b8fae 100644 --- a/parsley/templatetags/parsley.py +++ b/parsley/templatetags/parsley.py @@ -1,9 +1,17 @@ -from django import template +from django import template, VERSION + from ..decorators import parsley_form register = template.Library() +if VERSION[:2] >= (1, 9): + # in django 1.9 and above, the simple_tag can do assignment + tag_decorator = register.simple_tag +else: + # django 1.8 and below needs the assignment_tag + tag_decorator = register.assignment_tag + -@register.assignment_tag() +@tag_decorator() def parsleyfy(form): return parsley_form(form) From b31aded377c2dc1daf4337e5046d0b8fb9f9d49f Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Fri, 26 May 2017 11:04:16 -0500 Subject: [PATCH 6/7] new version --- CHANGELOG.md | 7 ++++++- setup.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52be551..6609c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,13 @@ # Django-parsley changelog +## 0.7 + +- Added support for django 1.10 and 1.11 +- Fixed issue where django version would be forced to 1.8 when installing django-parsley. More details here. + ## 0.6 - Added checkbox and radio button validation support. - Updated parsley.js, included with this library, to 2.0.7 - Changed error message attributes to confirm with parsley.js 2.x. More details here. -- Changed RegexField attributes to confirm with parsley.js 2.x. More details here. \ No newline at end of file +- Changed RegexField attributes to confirm with parsley.js 2.x. More details here. diff --git a/setup.py b/setup.py index a324581..509e850 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -VERSION = '0.7b1' +VERSION = '0.7' import os import sys From bb135ae352b455a754a6951282851a81e1307a81 Mon Sep 17 00:00:00 2001 From: Jared Proffitt Date: Fri, 26 May 2017 11:49:56 -0500 Subject: [PATCH 7/7] add test for template tag, bump version --- CHANGELOG.md | 2 ++ parsley/decorators.py | 7 ++++++- parsley/tests/forms.py | 1 - parsley/tests/tests.py | 41 ++++++++++++++++++++++++++++++++++------- runtests.py | 6 ++++++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6609c55..793250d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ ## 0.7 - Added support for django 1.10 and 1.11 +- Dropped support for django 1.3 - 1.7 - Fixed issue where django version would be forced to 1.8 when installing django-parsley. More details here. +- Added parsleyfy template tag ## 0.6 diff --git a/parsley/decorators.py b/parsley/decorators.py index 81bf7b0..811ad88 100644 --- a/parsley/decorators.py +++ b/parsley/decorators.py @@ -82,6 +82,7 @@ class ParsleyChoiceFieldRenderer(ParsleyChoiceFieldRendererMixin, field.widget.r if error_message: attrs["{prefix}-type-message".format(field_type, prefix=prefix)] = error_message + def parsley_form(form): prefix = getattr(getattr(form, 'Meta', None), 'parsley_namespace', 'data-parsley') for _, field in form.fields.items(): @@ -100,8 +101,12 @@ def parsley_form(form): attrs["{prefix}-%s".format(prefix=prefix) % key] = value return form + def parsleyfy(klass): - "A decorator to add {prefix}-* attributes to your form.fields" + """ + A decorator to add {prefix}-* attributes to your form.fields + """ + old_init = klass.__init__ def new_init(self, *args, **kwargs): diff --git a/parsley/tests/forms.py b/parsley/tests/forms.py index 93936f8..57b51ef 100644 --- a/parsley/tests/forms.py +++ b/parsley/tests/forms.py @@ -6,7 +6,6 @@ class TextForm(forms.Form): - "A simple form" name = forms.CharField(required=True,) university = forms.CharField(required=False) diff --git a/parsley/tests/tests.py b/parsley/tests/tests.py index 7ac85de..80bb113 100644 --- a/parsley/tests/tests.py +++ b/parsley/tests/tests.py @@ -2,10 +2,11 @@ import six from django import forms +from django.template import Context, Template from django.test import TestCase from django.utils.translation import ugettext_lazy as _ -from parsley.decorators import parsleyfy +from parsley.decorators import parsleyfy, parsley_form from .forms import (TextForm, TextForm2, FieldTypeForm, ExtraDataForm, ExtraDataMissingFieldForm, FormWithWidgets, StudentModelForm, @@ -35,8 +36,7 @@ def test_basic(self): form = TextForm() self.assertEqual(form.fields["name"].widget.attrs, {}) self.assertEqual(form.fields["university"].widget.attrs, {}) - ParsleyForm = parsleyfy(TextForm) - form = ParsleyForm() + form = parsley_form(TextForm()) self.assertAttrsEqual(form.fields["name"].widget.attrs, { "data-parsley-required": "true", "data-parsley-required-message": _("This field is required.") @@ -117,17 +117,17 @@ def test_widgets(self): class TestMetadata(ParsleyTestCase): def test_docstring(self): form1 = TextForm() - form2 = parsleyfy(TextForm)() + form2 = parsley_form(TextForm()) self.assertEqual(form1.__doc__, form2.__doc__) def test_module(self): form1 = TextForm() - form2 = parsleyfy(TextForm)() + form2 = parsley_form(TextForm()) self.assertEqual(form1.__module__, form2.__module__) def test_name(self): form1 = TextForm() - form2 = parsleyfy(TextForm)() + form2 = parsley_form(TextForm()) self.assertEqual(form1.__class__.__name__, form2.__class__.__name__) @@ -270,10 +270,11 @@ def test_override_default_message(self): "data-parsley-required-message": "Favorite color is required" }) + class TestCustomPrefix(TestCase): def test_default_prefix(self): - form = TextForm() + form = TextForm2() attrs = form.fields['name'].widget.attrs self.assertTrue('data-parsley-required' in attrs) @@ -281,3 +282,29 @@ def test_custom_prefix(self): form = CustomPrefixForm() attrs = form.fields['name'].widget.attrs self.assertTrue('custom-required' in attrs) + + +class TemplateTagTest(ParsleyTestCase): + def test_basic(self): + """ + Tests that parsleyfy will work using the template tag + """ + + template = Template("{% load parsley %}") + form = TextForm() + context = Context({'form': form}) + template.render(context) + + self.assertEqual(context['form'].fields["name"].widget.attrs, {}) + self.assertEqual(context['form'].fields["university"].widget.attrs, {}) + + template = Template("{% load parsley %}{% parsleyfy form as form %}") + form = TextForm() + context = Context({'form': form}) + template.render(context) + + self.assertAttrsEqual(context['form'].fields["name"].widget.attrs, { + "data-parsley-required": "true", + "data-parsley-required-message": _("This field is required.") + }) + self.assertEqual(context['form'].fields["university"].widget.attrs, {}) \ No newline at end of file diff --git a/runtests.py b/runtests.py index 3d26a2f..ba38d46 100755 --- a/runtests.py +++ b/runtests.py @@ -21,6 +21,12 @@ 'parsley', ], STATIC_URL="/static/", + TEMPLATES=[ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + }, + ] ) # Setup Django 1.7+ (AppRegistryNotReady).