├── INSTALL.txt ├── LICENSE.txt ├── ajax_validation ├── __init__.py ├── media │ └── ajax_validation │ │ └── js │ │ ├── jquery-ajax-validation.js │ │ └── jquery-uni-form.js ├── models.py ├── templatetags │ ├── __init__.py │ └── jquery_validation.py ├── urls.py ├── utils.py └── views.py ├── docs ├── index.txt ├── serving-ajax-validation-media-server.txt └── usage.txt └── setup.py /INSTALL.txt: -------------------------------------------------------------------------------- 1 | Install 2 | ======= 3 | 4 | To install djanog-ajax-validation simply place ajax_validation/ on your 5 | PYTHONPATH and add 'ajax_validation' to your INSTALLED_APPS. 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2009, Alex Gaynor 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * The name of the names of its contributors may be used to endorse or 12 | * promote products derived from this software without specific prior 13 | * written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BYAlex Gaynor ''AS IS'' AND ANY 16 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL Alex Gatbir BE LIABLE FOR ANY 19 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /ajax_validation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/django-ajax-validation/e0490886c7b61e32adc9ebb43f14208e4aac38b3/ajax_validation/__init__.py -------------------------------------------------------------------------------- /ajax_validation/media/ajax_validation/js/jquery-ajax-validation.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | function inputs(form) { 3 | return form.find(":input:visible:not(:button)"); 4 | } 5 | 6 | $.fn.validate = function(url, settings) { 7 | settings = $.extend({ 8 | type: 'table', 9 | callback: false, 10 | fields: false, 11 | dom: this, 12 | event: 'submit', 13 | submitHandler: null 14 | }, settings); 15 | 16 | return this.each(function() { 17 | var form = $(this); 18 | settings.dom.bind(settings.event, function() { 19 | var status = false; 20 | var data = form.serialize(); 21 | if (settings.fields) { 22 | data += '&' + $.param({fields: settings.fields}); 23 | } 24 | $.ajax({ 25 | async: false, 26 | data: data, 27 | dataType: 'json', 28 | traditional: true, 29 | error: function(XHR, textStatus, errorThrown) { 30 | status = true; 31 | }, 32 | success: function(data, textStatus) { 33 | status = data.valid; 34 | if (!status) { 35 | if (settings.callback) { 36 | settings.callback(data, form); 37 | } 38 | else { 39 | var get_form_error_position = function(key) { 40 | key = key || '__all__'; 41 | if (key == '__all__') { 42 | var filter = ':first'; 43 | } else { 44 | var filter = ':first[id^=id_' + key.replace('__all__', '') + ']'; 45 | } 46 | return inputs(form).filter(filter).parent(); 47 | }; 48 | if (settings.type == 'p') { 49 | form.find('ul.errorlist').remove(); 50 | $.each(data.errors, function(key, val) { 51 | if (key.indexOf('__all__') >= 0) { 52 | var error = get_form_error_position(key); 53 | if (error.prev().is('ul.errorlist')) { 54 | error.prev().before(''); 55 | } 56 | else { 57 | error.before(''); 58 | } 59 | } 60 | else { 61 | $('#' + key).parent().before(''); 62 | } 63 | }); 64 | } 65 | if (settings.type == 'table') { 66 | inputs(form).prev('ul.errorlist').remove(); 67 | form.find('tr:has(ul.errorlist)').remove(); 68 | $.each(data.errors, function(key, val) { 69 | if (key.indexOf('__all__') >= 0) { 70 | get_form_error_position(key).parent().before(''); 71 | } 72 | else { 73 | $('#' + key).before(''); 74 | } 75 | }); 76 | } 77 | if (settings.type == 'ul') { 78 | inputs(form).prev().prev('ul.errorlist').remove(); 79 | form.find('li:has(ul.errorlist)').remove(); 80 | $.each(data.errors, function(key, val) { 81 | if (key.indexOf('__all__') >= 0) { 82 | get_form_error_position(key).before('
  • '); 83 | } 84 | else { 85 | $('#' + key).prev().before(''); 86 | } 87 | }); 88 | } 89 | } 90 | } 91 | }, 92 | type: 'POST', 93 | url: url 94 | }); 95 | if (status && settings.submitHandler) { 96 | return settings.submitHandler.apply(this); 97 | } 98 | return status; 99 | }); 100 | }); 101 | }; 102 | })(jQuery); 103 | -------------------------------------------------------------------------------- /ajax_validation/media/ajax_validation/js/jquery-uni-form.js: -------------------------------------------------------------------------------- 1 | function uniform_callback(data, form) { 2 | var field_divs = $(form).find(".ctrlHolder").filter(".error"); 3 | field_divs.removeClass("error"); 4 | field_divs.find(".errorField").remove(); 5 | 6 | $.each(data.errors, function(key, val) { 7 | var field_div = $(form).find(".ctrlHolder").filter("#div_" + key); 8 | field_div.addClass("error"); 9 | field_div.prepend('

    ' 10 | + val + '

    '); 11 | }); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /ajax_validation/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /ajax_validation/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/django-ajax-validation/e0490886c7b61e32adc9ebb43f14208e4aac38b3/ajax_validation/templatetags/__init__.py -------------------------------------------------------------------------------- /ajax_validation/templatetags/jquery_validation.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django import template 4 | 5 | import ajax_validation 6 | 7 | register = template.Library() 8 | 9 | VALIDATION_SCRIPT = None 10 | 11 | def include_validation(): 12 | global VALIDATION_SCRIPT 13 | if VALIDATION_SCRIPT is None: 14 | VALIDATION_SCRIPT = open(os.path.join(os.path.dirname(ajax_validation.__file__), 'media', 'ajax_validation', 'js', 'jquery-ajax-validation.js')).read() 15 | return '''''' % VALIDATION_SCRIPT 16 | register.simple_tag(include_validation) 17 | -------------------------------------------------------------------------------- /ajax_validation/urls.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex/django-ajax-validation/e0490886c7b61e32adc9ebb43f14208e4aac38b3/ajax_validation/urls.py -------------------------------------------------------------------------------- /ajax_validation/utils.py: -------------------------------------------------------------------------------- 1 | from django.utils.functional import Promise 2 | from django.utils.encoding import force_unicode 3 | try: 4 | from simplejson import JSONEncoder 5 | except ImportError: 6 | try: 7 | from json import JSONEncoder 8 | except ImportError: 9 | from django.utils.simplejson import JSONEncoder 10 | 11 | class LazyEncoder(JSONEncoder): 12 | def default(self, obj): 13 | if isinstance(obj, Promise): 14 | return force_unicode(obj) 15 | return obj 16 | -------------------------------------------------------------------------------- /ajax_validation/views.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.http import HttpResponse 3 | from django.views.decorators.http import require_POST 4 | from django.forms.formsets import BaseFormSet 5 | 6 | from ajax_validation.utils import LazyEncoder 7 | 8 | def validate(request, *args, **kwargs): 9 | form_class = kwargs.pop('form_class') 10 | defaults = { 11 | 'data': request.POST 12 | } 13 | extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {}) 14 | kwargs = extra_args_func(request, *args, **kwargs) 15 | defaults.update(kwargs) 16 | form = form_class(**defaults) 17 | if form.is_valid(): 18 | data = { 19 | 'valid': True, 20 | } 21 | else: 22 | # if we're dealing with a FormSet then walk over .forms to populate errors and formfields 23 | if isinstance(form, BaseFormSet): 24 | errors = {} 25 | formfields = {} 26 | for f in form.forms: 27 | for field in f.fields.keys(): 28 | formfields[f.add_prefix(field)] = f[field] 29 | for field, error in f.errors.iteritems(): 30 | errors[f.add_prefix(field)] = error 31 | if form.non_form_errors(): 32 | errors['__all__'] = form.non_form_errors() 33 | else: 34 | errors = form.errors 35 | formfields = dict([(fieldname, form[fieldname]) for fieldname in form.fields.keys()]) 36 | 37 | # if fields have been specified then restrict the error list 38 | if request.POST.getlist('fields'): 39 | fields = request.POST.getlist('fields') + ['__all__'] 40 | errors = dict([(key, val) for key, val in errors.iteritems() if key in fields]) 41 | 42 | final_errors = {} 43 | for key, val in errors.iteritems(): 44 | if '__all__' in key: 45 | final_errors[key] = val 46 | elif not isinstance(formfields[key].field, forms.FileField): 47 | html_id = formfields[key].field.widget.attrs.get('id') or formfields[key].auto_id 48 | html_id = formfields[key].field.widget.id_for_label(html_id) 49 | final_errors[html_id] = val 50 | data = { 51 | 'valid': False or not final_errors, 52 | 'errors': final_errors, 53 | } 54 | json_serializer = LazyEncoder() 55 | return HttpResponse(json_serializer.encode(data), mimetype='application/json') 56 | validate = require_POST(validate) 57 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | ###################### 2 | django-ajax-validation 3 | ###################### 4 | 5 | This is a fairly simple application for performing ajax validation of forms 6 | created using Django's forms system. Currently it only works with jQuery. 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | 12 | usage 13 | serving-ajax-validation-media-server 14 | 15 | -------------------------------------------------------------------------------- /docs/serving-ajax-validation-media-server.txt: -------------------------------------------------------------------------------- 1 | ===================================================== 2 | Serving Ajax Validation With Your Static Media Server 3 | ===================================================== 4 | 5 | By default, if you use the template tag included with Ajax Validation, the 6 | script will be placed inside HTML 16 | 17 | where ``PATH_TO_FILE_HERE`` is the location your media server is serving the 18 | file from. 19 | -------------------------------------------------------------------------------- /docs/usage.txt: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | To use, Ajax Validation requires only that you add a URL(one per form), and some javascript to any page with the form. 6 | 7 | For example, if you had the following form:: 8 | 9 | from django import forms 10 | 11 | class ContactForm(forms.Form): 12 | name = forms.CharField(label='Your Name') 13 | email = forms.EmailField(label='Your Email') 14 | message = forms.CharField(label='Your Message', widget=forms.Textarea) 15 | 16 | 17 | You would need to add the following url to your urls.py(you also need to import the form class):: 18 | 19 | (r'^SOME/URL/$', 'ajax_validation.views.validate', {'form_class': ContactForm}, 'contact_form_validate') 20 | 21 | 22 | The URL can take any arguments(named, or unamed), and you can also provide a 23 | callback function, this function is given request, \*args, and \*\*kwargs and 24 | should return a dictionary which is passed to the form constructor. 25 | 26 | And then in the template in which you are displaying the form, you should add:: 27 | 28 | 29 | {% load jquery_validation %} 30 | {% include_validation %} 31 | 37 | 38 | As you can see, you need to have jQuery for this to work(here it is being loaded 39 | from google). In the javascript you use jQuery's selectors to select where the 40 | form is, and the validate method takes 5 parameters, all optional, first is the 41 | URL of the validation, here we reverse the URL we set up earlier. The second 42 | parameter is a dictionary, it is optional and should either provide type(ul, 43 | table, or p), this is the type of renderer you used for django's forms(form.as_p, 44 | etc.), the default is table if nothing is provided. It can also take a callback 45 | option which is a function that recieves data, which is the JSON representation 46 | of any errors, and form, which is the jquery object that you provided. Finally, 47 | it takes fields, which is a list of the fields that should be validated, it will 48 | not display errors that aren't in that list of fields. The last 2 options are 49 | dom and event, these allow you to choose when the validation will occur, dom 50 | should be a jQuery object(i.e.: $('#my_field')), and event should be a jQuery 51 | event(listed `here`_). 52 | 53 | .. _here: http://docs.jquery.com/Events/bind#overview 54 | 55 | In addition you can provide a callable option, ``submitHandler``. This 56 | recieves the form DOM object and can do whatever it pleases. If it returns 57 | true the form will be submitted, else it won't. 58 | 59 | Using ``ajax-validation`` with ``django-uniform`` 60 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 61 | 62 | If you want to use ``django-ajax-validation`` with ``django-uniform`` there is 63 | an extra included handler that works with it. To use it simply include the 64 | ``jquery-uni-form.js`` file in your template and pass 65 | ``callback: uniform_callback`` to ``jQuery.validate``. 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='django-ajax-validation', 5 | version='0.1.4', 6 | description='Provides support for doing validation using Ajax(currently with jQuery) using your existing Django forms.', 7 | author='Alex Gaynor', 8 | author_email='alex.gaynor@gmail.com', 9 | url='http://github.com/alex/django-ajax-validation/tree/master', 10 | packages=find_packages(), 11 | classifiers=[ 12 | 'Development Status :: 3 - Alpha', 13 | 'Environment :: Web Environment', 14 | 'Intended Audience :: Developers', 15 | 'License :: OSI Approved :: BSD License', 16 | 'Operating System :: OS Independent', 17 | 'Programming Language :: Python', 18 | 'Framework :: Django', 19 | ], 20 | # Make setuptools include all data files under version control, 21 | # svn and CVS by default 22 | include_package_data=True, 23 | # Tells setuptools to download setuptools_git before running setup.py so 24 | # it can find the data files under Git version control. 25 | setup_requires=['setuptools_git'], 26 | zip_safe=False, 27 | ) 28 | --------------------------------------------------------------------------------