Skip to content

Commit

Permalink
Merge pull request #80 from jproffitt/master
Browse files Browse the repository at this point in the history
Support django 1.11
  • Loading branch information
jproffitt authored May 26, 2017
2 parents 9b223bc + bb135ae commit e27ad9a
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ _build/
*.pyc
test.db
example/example/settings/local.py
/.idea
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ 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.11,<2.0'

install:
- pip install $DJANGO
- pip install coverage coveralls

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
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# Django-parsley changelog

## 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 <a href="https://github.com/agiliq/Django-parsley/issues/72" target="_blank">here</a>.
- Added parsleyfy template tag

## 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 <a href="https://github.com/agiliq/Django-parsley/issues/56" target="_blank">here</a>.
- Changed RegexField attributes to confirm with parsley.js 2.x. More details <a href="https://github.com/agiliq/Django-parsley/issues/46" target="_blank">here</a>.
- Changed RegexField attributes to confirm with parsley.js 2.x. More details <a href="https://github.com/agiliq/Django-parsley/issues/46" target="_blank">here</a>.
31 changes: 26 additions & 5 deletions parsley/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import types

from django import forms
from parsley.widgets import ParsleyChoiceFieldRendererMixin
Expand All @@ -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)
Expand Down Expand Up @@ -66,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():
Expand All @@ -84,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):
Expand Down
12 changes: 10 additions & 2 deletions parsley/templatetags/parsley.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 0 additions & 1 deletion parsley/tests/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@


class TextForm(forms.Form):
"A simple form"
name = forms.CharField(required=True,)
university = forms.CharField(required=False)

Expand Down
41 changes: 34 additions & 7 deletions parsley/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -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__)


Expand Down Expand Up @@ -270,14 +270,41 @@ 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)

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, {})
11 changes: 8 additions & 3 deletions runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -19,16 +20,20 @@
INSTALLED_APPS=[
'parsley',
],
STATIC_URL = "/static/",
STATIC_URL="/static/",
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
},
]
)

# Setup Django 1.7+ (AppRegistryNotReady).
import django
if hasattr(django, 'setup'):
django.setup()

from django.test.runner import DiscoverRunner


def runtests(*test_args, **kwargs):
if not test_args:
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
VERSION = '0.6'
VERSION = '0.7'

import os
import sys
from fnmatch import fnmatchcase
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()
Expand Down
11 changes: 8 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -9,7 +11,10 @@ basepython =
py27: python2.7
py34: python3.4
py35: python3.5
py36: python3.6
deps =
coverage>=4
dj18: django==1.8.13
dj19: django==1.9.7
dj18: django==1.8.18
dj19: django==1.9.13
dj110: django==1.10.7
dj111: django==1.11.1

0 comments on commit e27ad9a

Please sign in to comment.