├── requirements.txt ├── example ├── __init__.py ├── sample_app │ ├── __init__.py │ ├── admin.py │ └── models.py ├── urls.py ├── manage.py └── settings.py ├── CHANGELOG ├── formfield ├── views.py ├── urls.py ├── models.py ├── __init__.py ├── widgets.py ├── fields.py └── tests.py ├── doc_src ├── ss001.png ├── ss002.png ├── ss003.png ├── _static │ ├── scrn1.png │ ├── scrn2.png │ ├── documentation.png │ ├── header_sm_mid.png │ ├── triangle_left.png │ ├── triangle_open.png │ ├── triangle_closed.png │ ├── searchfield_repeat.png │ ├── title_background.png │ ├── searchfield_leftcap.png │ ├── searchfield_rightcap.png │ ├── breadcrumb_background.png │ ├── toc.js │ └── default.css ├── _build │ └── doctrees │ │ ├── index.doctree │ │ ├── environment.pickle │ │ ├── installation.doctree │ │ ├── reference │ │ ├── api.doctree │ │ ├── index.doctree │ │ └── settings.doctree │ │ └── getting_started.doctree ├── reference │ ├── index.rst │ └── api.rst ├── installation.rst ├── index.rst ├── getting_started.rst ├── Makefile ├── make.bat ├── _templates │ └── layout.html └── conf.py ├── docs ├── objects.inv ├── _static │ ├── up.png │ ├── down.png │ ├── file.png │ ├── minus.png │ ├── plus.png │ ├── scrn1.png │ ├── scrn2.png │ ├── comment.png │ ├── ajax-loader.gif │ ├── up-pressed.png │ ├── comment-close.png │ ├── documentation.png │ ├── down-pressed.png │ ├── header_sm_mid.png │ ├── triangle_left.png │ ├── triangle_open.png │ ├── comment-bright.png │ ├── title_background.png │ ├── triangle_closed.png │ ├── searchfield_repeat.png │ ├── breadcrumb_background.png │ ├── searchfield_leftcap.png │ ├── searchfield_rightcap.png │ ├── toc.js │ ├── pygments.css │ ├── classic.css │ ├── sidebar.js │ ├── doctools.js │ ├── underscore.js │ ├── basic.css │ └── default.css ├── _images │ ├── ss001.png │ ├── ss002.png │ └── ss003.png ├── .buildinfo ├── _sources │ ├── reference │ │ ├── index.txt │ │ └── api.txt │ ├── installation.txt │ ├── index.txt │ └── getting_started.txt ├── searchindex.js ├── genindex.html ├── search.html ├── reference │ ├── index.html │ └── api.html ├── installation.html ├── index.html └── getting_started.html ├── Makefile ├── MANIFEST.in ├── .gitignore ├── .travis.yml ├── setup.py ├── README.rst └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/sample_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.1 2 | === 3 | 4 | Initial version -------------------------------------------------------------------------------- /formfield/views.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /doc_src/ss001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/ss001.png -------------------------------------------------------------------------------- /doc_src/ss002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/ss002.png -------------------------------------------------------------------------------- /doc_src/ss003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/ss003.png -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/objects.inv -------------------------------------------------------------------------------- /formfield/urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | urlpatterns = [] 4 | -------------------------------------------------------------------------------- /docs/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/up.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | DJANGO_SETTINGS_MODULE=formfield.example.settings django-admin.py test formfield -------------------------------------------------------------------------------- /docs/_images/ss001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_images/ss001.png -------------------------------------------------------------------------------- /docs/_images/ss002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_images/ss002.png -------------------------------------------------------------------------------- /docs/_images/ss003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_images/ss003.png -------------------------------------------------------------------------------- /docs/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/down.png -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/scrn1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/scrn1.png -------------------------------------------------------------------------------- /docs/_static/scrn2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/scrn2.png -------------------------------------------------------------------------------- /doc_src/_static/scrn1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/scrn1.png -------------------------------------------------------------------------------- /doc_src/_static/scrn2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/scrn2.png -------------------------------------------------------------------------------- /docs/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/comment.png -------------------------------------------------------------------------------- /formfield/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django.db import models 4 | -------------------------------------------------------------------------------- /docs/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_static/documentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/documentation.png -------------------------------------------------------------------------------- /docs/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_static/header_sm_mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/header_sm_mid.png -------------------------------------------------------------------------------- /docs/_static/triangle_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/triangle_left.png -------------------------------------------------------------------------------- /docs/_static/triangle_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/triangle_open.png -------------------------------------------------------------------------------- /doc_src/_static/documentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/documentation.png -------------------------------------------------------------------------------- /doc_src/_static/header_sm_mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/header_sm_mid.png -------------------------------------------------------------------------------- /doc_src/_static/triangle_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/triangle_left.png -------------------------------------------------------------------------------- /doc_src/_static/triangle_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/triangle_open.png -------------------------------------------------------------------------------- /docs/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_static/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/title_background.png -------------------------------------------------------------------------------- /docs/_static/triangle_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/triangle_closed.png -------------------------------------------------------------------------------- /doc_src/_static/triangle_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/triangle_closed.png -------------------------------------------------------------------------------- /docs/_static/searchfield_repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/searchfield_repeat.png -------------------------------------------------------------------------------- /doc_src/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /doc_src/_static/searchfield_repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/searchfield_repeat.png -------------------------------------------------------------------------------- /doc_src/_static/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/title_background.png -------------------------------------------------------------------------------- /docs/_static/breadcrumb_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/breadcrumb_background.png -------------------------------------------------------------------------------- /docs/_static/searchfield_leftcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/searchfield_leftcap.png -------------------------------------------------------------------------------- /docs/_static/searchfield_rightcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/docs/_static/searchfield_rightcap.png -------------------------------------------------------------------------------- /doc_src/_static/searchfield_leftcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/searchfield_leftcap.png -------------------------------------------------------------------------------- /doc_src/_static/searchfield_rightcap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/searchfield_rightcap.png -------------------------------------------------------------------------------- /doc_src/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /doc_src/_static/breadcrumb_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_static/breadcrumb_background.png -------------------------------------------------------------------------------- /doc_src/_build/doctrees/installation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/installation.doctree -------------------------------------------------------------------------------- /doc_src/_build/doctrees/reference/api.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/reference/api.doctree -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include doc_src * 2 | recursive-include docs * 3 | 4 | include LICENSE 5 | include README 6 | include *.txt 7 | 8 | prune example/ -------------------------------------------------------------------------------- /doc_src/_build/doctrees/getting_started.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/getting_started.doctree -------------------------------------------------------------------------------- /doc_src/_build/doctrees/reference/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/reference/index.doctree -------------------------------------------------------------------------------- /doc_src/_build/doctrees/reference/settings.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsoa/django-formfield/HEAD/doc_src/_build/doctrees/reference/settings.doctree -------------------------------------------------------------------------------- /example/sample_app/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django.contrib import admin 4 | 5 | from .models import Person 6 | 7 | 8 | admin.site.register(Person) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | dev.db 3 | local_settings.py 4 | media/ugc 5 | docs/_build/ 6 | src/ 7 | pip-log.txt 8 | media/js/*.r*.js 9 | media/css/*.r*.css 10 | *DS_Store 11 | *.egg-info 12 | dist/ 13 | example/static -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 36968ab64785cd7edf6f4af7824df71d 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /doc_src/reference/index.rst: -------------------------------------------------------------------------------- 1 | .. The reference section is for low-level documentation for programmers. 2 | There should be a file for each class 3 | Include Docstring and parameter expectations for each class 4 | 5 | Reference 6 | ========= 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :glob: 11 | 12 | api -------------------------------------------------------------------------------- /docs/_sources/reference/index.txt: -------------------------------------------------------------------------------- 1 | .. The reference section is for low-level documentation for programmers. 2 | There should be a file for each class 3 | Include Docstring and parameter expectations for each class 4 | 5 | Reference 6 | ========= 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :glob: 11 | 12 | api -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.4" 5 | - "3.5" 6 | env: 7 | - DJANGO_VERSION=1.8.17 8 | - DJANGO_VERSION=1.9.12 9 | - DJANGO_VERSION=1.10.6 10 | - DJANGO_VERSION=1.11 11 | install: 12 | - pip install Django==$DJANGO_VERSION 13 | - pip install six 14 | - pip install -e . 15 | script: 16 | - ./example/manage.py test formfield 17 | branches: 18 | only: 19 | - master 20 | -------------------------------------------------------------------------------- /doc_src/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | Installation is easy using ``pip`` or ``easy_install``. 8 | 9 | .. code-block:: bash 10 | 11 | pip install django-formfield 12 | 13 | or 14 | 15 | .. code-block:: bash 16 | 17 | easy_install django-formfield 18 | 19 | 20 | Add to installed apps 21 | ===================== 22 | 23 | :: 24 | 25 | INSTALLED_APPS = ( 26 | ... 27 | formfield, 28 | ... 29 | ) 30 | -------------------------------------------------------------------------------- /docs/_sources/installation.txt: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | Installation is easy using ``pip`` or ``easy_install``. 8 | 9 | .. code-block:: bash 10 | 11 | pip install django-formfield 12 | 13 | or 14 | 15 | .. code-block:: bash 16 | 17 | easy_install django-formfield 18 | 19 | 20 | Add to installed apps 21 | ===================== 22 | 23 | :: 24 | 25 | INSTALLED_APPS = ( 26 | ... 27 | formfield, 28 | ... 29 | ) 30 | -------------------------------------------------------------------------------- /example/urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django.conf.urls import include, url 4 | 5 | from django.contrib import admin 6 | 7 | admin.autodiscover() 8 | 9 | 10 | urlpatterns = [ 11 | url(r'^formfield/', include('formfield.urls')), 12 | url(r'^admin/', include(admin.site.urls)), 13 | ] 14 | 15 | # 16 | # urlpatterns = urlpatterns + patterns('', 17 | # (r'^static/(?P.*)$', 'django.views.static.serve', 18 | # {'document_root': settings.MEDIA_ROOT}), 19 | # ) if settings.DEBUG else urlpatterson 20 | -------------------------------------------------------------------------------- /docs/_static/toc.js: -------------------------------------------------------------------------------- 1 | var TOC = { 2 | load: function () { 3 | $('#toc_button').click(TOC.toggle); 4 | }, 5 | 6 | toggle: function () { 7 | if ($('#sphinxsidebar').toggle().is(':hidden')) { 8 | $('div.document').css('left', "0px"); 9 | $('toc_button').removeClass("open"); 10 | } else { 11 | $('div.document').css('left', "230px"); 12 | $('#toc_button').addClass("open"); 13 | } 14 | return $('#sphinxsidebar'); 15 | } 16 | }; 17 | 18 | $(document).ready(function () { 19 | TOC.load(); 20 | }); -------------------------------------------------------------------------------- /doc_src/_static/toc.js: -------------------------------------------------------------------------------- 1 | var TOC = { 2 | load: function () { 3 | $('#toc_button').click(TOC.toggle); 4 | }, 5 | 6 | toggle: function () { 7 | if ($('#sphinxsidebar').toggle().is(':hidden')) { 8 | $('div.document').css('left', "0px"); 9 | $('toc_button').removeClass("open"); 10 | } else { 11 | $('div.document').css('left', "230px"); 12 | $('#toc_button').addClass("open"); 13 | } 14 | return $('#sphinxsidebar'); 15 | } 16 | }; 17 | 18 | $(document).ready(function () { 19 | TOC.load(); 20 | }); -------------------------------------------------------------------------------- /example/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | 12 | 13 | # #!/usr/bin/env python 14 | # import os, sys 15 | 16 | # if __name__ == "__main__": 17 | # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 18 | 19 | # from django.core.management import execute_from_command_line 20 | 21 | # execute_from_command_line(sys.argv) 22 | -------------------------------------------------------------------------------- /doc_src/index.rst: -------------------------------------------------------------------------------- 1 | .. app documentation master file, created by 2 | sphinx-quickstart on Wed Oct 21 13:18:22 2009. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to django-formfield's documentation! 7 | ============================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :glob: 14 | 15 | installation 16 | getting_started 17 | 18 | reference/index 19 | 20 | Contributing 21 | ============ 22 | 23 | * `Source `_ 24 | * `Issues `_ 25 | 26 | 27 | Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | 34 | -------------------------------------------------------------------------------- /docs/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. app documentation master file, created by 2 | sphinx-quickstart on Wed Oct 21 13:18:22 2009. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to django-formfield's documentation! 7 | ============================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :glob: 14 | 15 | installation 16 | getting_started 17 | 18 | reference/index 19 | 20 | Contributing 21 | ============ 22 | 23 | * `Source `_ 24 | * `Issues `_ 25 | 26 | 27 | Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | 34 | -------------------------------------------------------------------------------- /formfield/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | django-formfield 4 | """ 5 | from __future__ import unicode_literals 6 | 7 | __version_info__ = { 8 | 'major': 0, 9 | 'minor': 4, 10 | 'micro': 0, 11 | 'releaselevel': 'final', 12 | 'serial': 1 13 | } 14 | 15 | 16 | def get_version(): 17 | """ 18 | Return the formatted version information 19 | """ 20 | vers = ["%(major)i.%(minor)i" % __version_info__, ] 21 | 22 | if __version_info__['micro']: 23 | vers.append(".%(micro)i" % __version_info__) 24 | if __version_info__['releaselevel'] != 'final': 25 | vers.append('%(releaselevel)s%(serial)i' % __version_info__) 26 | return ''.join(vers) 27 | 28 | __version__ = get_version() 29 | 30 | try: 31 | from .fields import FormField, ModelFormField # NOQA 32 | except (Exception, ): 33 | pass 34 | -------------------------------------------------------------------------------- /example/sample_app/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django.db import models 4 | from django import forms 5 | 6 | from formfield import ModelFormField, FormField 7 | 8 | 9 | class Coordinates(forms.Form): 10 | lng = forms.CharField(required=False) 11 | lat = forms.CharField(required=False) 12 | 13 | 14 | class Address(forms.Form): 15 | street = forms.CharField(widget=forms.Textarea) 16 | country = forms.CharField(required=False) 17 | zipcode = forms.IntegerField(required=False) 18 | 19 | coords = FormField(Coordinates) 20 | 21 | 22 | class SubPersonMetaForm(forms.Form): 23 | middle_name = forms.CharField() 24 | suffix = forms.CharField(required=False) 25 | 26 | address = FormField(Address) 27 | 28 | 29 | class PersonMetaForm(forms.Form): 30 | age = forms.IntegerField() 31 | sex = forms.ChoiceField(required=False, choices=((1, 'male'), (2, 'female'))) 32 | name = forms.CharField() 33 | date = forms.DateTimeField(required=False) 34 | 35 | subform = FormField(SubPersonMetaForm) 36 | 37 | 38 | class Person(models.Model): 39 | name = models.CharField(max_length=255) 40 | 41 | meta_info = ModelFormField(form=PersonMetaForm) 42 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from os import path 4 | from setuptools import setup, find_packages 5 | import formfield 6 | 7 | 8 | def read_file(filename): 9 | """Read a file into a string""" 10 | p = path.abspath(path.dirname(__file__)) 11 | filepath = path.join(p, filename) 12 | try: 13 | return open(filepath).read() 14 | except IOError: 15 | return '' 16 | 17 | 18 | def get_readme(): 19 | """Return the README file contents. Supports text,rst, and markdown""" 20 | for name in ('README', 'README.rst', 'README.md'): 21 | if path.exists(name): 22 | return read_file(name) 23 | return '' 24 | 25 | # Use the docstring of the __init__ file to be the description 26 | DESC = " ".join(formfield.__doc__.splitlines()).strip() 27 | 28 | setup( 29 | name="django-formfield", 30 | version=formfield.get_version().replace(' ', '-'), 31 | url='http://github.com/jsoa/django-formfield/', 32 | author='Jose Soares', 33 | author_email='jose@linux.com', 34 | description=DESC, 35 | long_description=get_readme(), 36 | packages=find_packages(), 37 | include_package_data=True, 38 | install_requires=read_file('requirements.txt'), 39 | classifiers=[ 40 | 'License :: OSI Approved :: Apache Software License', 41 | 'Framework :: Django', 42 | ], 43 | ) 44 | -------------------------------------------------------------------------------- /docs/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({envversion:49,filenames:["getting_started","index","installation","reference/api","reference/index"],objects:{},objnames:{},objtypes:{},terms:{"class":[0,3],"default":3,"import":[0,3],"null":0,"return":[0,3],"true":0,abov:0,accept:[0,3],add:1,admin:0,alia:3,ani:0,api:1,app:[0,1],append:3,argument:[0,3],automat:0,avail:0,becaus:0,behind:0,blank:0,bound:3,callabl:3,can:[0,3],chang:[0,3],charfield:[0,3],choic:0,choicefield:0,cleaned_data:0,code:3,contact:3,content:1,control:3,counter:3,creat:0,css:3,data:[0,3],def:3,dictionari:0,django:0,don:3,done:0,dream:0,each:0,easi:2,easy_instal:2,email:3,emailfield:3,entir:3,enumer:3,expect:0,fals:[0,3],femal:0,field:[0,3],first:[0,3],follow:0,forc:0,form:[0,3],format:3,format_output:3,formfield:0,friendli:3,from:[0,3],have:3,help:[0,3],help_text:3,here:[0,3],hidden:0,hit:0,howev:0,id_formfield_:3,index:1,inner:0,input:0,instal:1,installat:1,installed_apps:2,integerfield:0,intens:0,issue:1,joan:0,join:3,json:[0,3],kwarg:[0,3],label:3,level:0,like:0,made:0,main:3,make:0,male:0,match:0,max_length:[0,3],meta_data:3,meta_info:0,metadataform:3,model:[0,3],modelformfield:0,modul:1,more:3,must:[0,3],myform:3,name:[0,3],none:0,normal:3,object:0,odd:3,onli:0,other_info:3,other_nam:3,otherinfoform:3,output:3,overrid:3,overridden:0,page:1,path:3,person:0,personmetaform:0,phone:3,pip:2,python:0,readonli:0,realli:0,refer:1,render:[0,3],rendered_widget:3,requir:[0,3],result:0,ret:3,retriev:0,sample_app:0,save:0,scene:0,search:1,see:0,seem:3,self:3,serial:[0,3],set:0,sex:0,should:0,simpli:[0,3],six:3,some:[0,3],someth:0,sourc:1,store:0,string:3,submit:0,suppli:[0,3],tag:3,therefor:0,thi:[0,3],thing:0,two:0,type:0,unexpect:0,usage:3,user:3,valid:0,valu:0,via:0,wai:3,want:3,well:[0,3],what:0,when:[0,3],where:3,which:[0,3],widget:3,within:[0,3],wrap:3,wrong:0,yea:3,you:[0,3]},titles:["Getting Started","Welcome to django-formfield’s documentation!","Installation","API","Reference"],titleterms:{add:2,api:3,app:2,contribut:1,django:1,document:1,exampl:0,extra:3,format_help_text:3,format_label:3,formfield:[1,3],formfieldwidget:3,get:0,indice:1,instal:2,installat:2,method:3,modelformfield:3,refer:4,start:0,tabl:1,welcom:1}}) -------------------------------------------------------------------------------- /formfield/widgets.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django import forms 4 | 5 | 6 | class FormFieldWidget(forms.MultiWidget): 7 | """ 8 | This widget will render each field found in the supplied form. 9 | """ 10 | def __init__(self, fields, attrs=None): 11 | self.fields = fields 12 | # Retreive each field widget for the form 13 | widgets = [f.field.widget for f in self.fields] 14 | 15 | super(FormFieldWidget, self).__init__(widgets, attrs) 16 | 17 | def value_from_datadict(self, data, files, name): 18 | """Ensure the payload is a list of values. In the case of a sub 19 | form, we need to ensure the data is returned as a list and not a 20 | dictionary. 21 | 22 | When a dict is found in the given data, we need to ensure the data 23 | is converted to a list perseving the field order. 24 | 25 | """ 26 | if name in data: 27 | payload = data.get(name) 28 | if isinstance(payload, (dict,)): 29 | # Make sure we get the data in the correct roder 30 | return [payload.get(f.name) for f in self.fields] 31 | return payload 32 | return super(FormFieldWidget, self).value_from_datadict(data, files, name) 33 | 34 | def decompress(self, value): 35 | """ 36 | Retreieve each field value or provide the initial values 37 | """ 38 | if value: 39 | return [value.get(field.name, None) for field in self.fields] 40 | return [field.field.initial for field in self.fields] 41 | 42 | def format_label(self, field, counter): 43 | """ 44 | Format the label for each field 45 | """ 46 | return '' % ( 47 | counter, field.field.required and 'class="required"', field.label) 48 | 49 | def format_help_text(self, field, counter): 50 | """ 51 | Format the help text for the bound field 52 | """ 53 | return '

%s

' % field.help_text 54 | 55 | def format_output(self, rendered_widgets): 56 | """ 57 | This output will yeild all widgets grouped in a un-ordered list 58 | """ 59 | ret = [u'
    '] 60 | for i, field in enumerate(self.fields): 61 | label = self.format_label(field, i) 62 | help_text = self.format_help_text(field, i) 63 | ret.append(u'
  • %s %s %s
  • ' % ( 64 | label, rendered_widgets[i], field.help_text and help_text)) 65 | 66 | ret.append(u'
') 67 | return ''.join(ret) 68 | -------------------------------------------------------------------------------- /doc_src/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | =============== 4 | Getting Started 5 | =============== 6 | 7 | django-formfield is a form field that accepts a django form as its first argument, and validates 8 | as well as render's each form field as expected. Yes a form within a form, *within a dream*? There 9 | are two types of fields available, :ref:`api_formfield` and :ref:`api_model_formfield`. For 10 | :ref:`api_model_formfield` the data is stored in json. For :ref:`api_formfield` data is simply 11 | returned as a python dictionary (form.cleaned_data) 12 | 13 | Example 14 | ======= 15 | 16 | :: 17 | 18 | from django.db import models 19 | from django import forms 20 | 21 | from formfield import ModelFormField 22 | 23 | class PersonMetaForm(forms.Form): 24 | age = forms.IntegerField() 25 | sex = forms.ChoiceField(choices=((1, 'male'), (2, 'female')), required=False) 26 | 27 | 28 | class Person(models.Model): 29 | name = CharField(max_length=200) 30 | 31 | meta_info = ModelFormField(form=PersonMetaForm) 32 | 33 | .. note:: 34 | 35 | Changed in 0.3: You must supply the kwarg argument `form` to `ModelFormField` 36 | 37 | 38 | Which will result in something like this (using the admin) 39 | 40 | .. image:: ss001.png 41 | 42 | The :ref:`api_model_formfield` is automatically set to `null=True`, `blank=True`, this is 43 | because validation is done on the inner form. As a result you will see something like the 44 | following if we hit save on the change form: 45 | 46 | .. image:: ss002.png 47 | 48 | If we supply the change for valid data you should get a python dictionary when retrieving 49 | the data:: 50 | 51 | >>> person = Person.objects.get(pk=1) 52 | >>> person.meta_info 53 | {u'age': 12, u'sex': u'1'} 54 | 55 | The form is the only thing forcing valid input, behind the scenes the 56 | data is being serialized into json. Therefore on the python level we can supply meta_info 57 | any valid json::: 58 | 59 | >>> from sample_app.models import Person 60 | >>> data = {'some': 'thing', 'is': 'wrong', 'here': 'help!'} 61 | >>> p = Person.objects.create(name="Joan", meta_info=data) 62 | >>> p.meta_info 63 | {'is': 'wrong', 'some': 'thing', 'here': 'help!'} 64 | 65 | .. note:: 66 | 67 | If the form field is being made available via a change form, such as the admin, any 68 | unexpected value will be overridden by what the form returns . For example, the 69 | `PersonMetaForm` above only expects `age` and `sex`, so none of the values above 70 | ('is', 'some' and 'here') match and will be overridden when the form submitted. 71 | 72 | We can however, make the field hidden or readonly and use it to supply any 73 | valid json, but its not really the intension of this app. 74 | -------------------------------------------------------------------------------- /docs/_sources/getting_started.txt: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | =============== 4 | Getting Started 5 | =============== 6 | 7 | django-formfield is a form field that accepts a django form as its first argument, and validates 8 | as well as render's each form field as expected. Yes a form within a form, *within a dream*? There 9 | are two types of fields available, :ref:`api_formfield` and :ref:`api_model_formfield`. For 10 | :ref:`api_model_formfield` the data is stored in json. For :ref:`api_formfield` data is simply 11 | returned as a python dictionary (form.cleaned_data) 12 | 13 | Example 14 | ======= 15 | 16 | :: 17 | 18 | from django.db import models 19 | from django import forms 20 | 21 | from formfield import ModelFormField 22 | 23 | class PersonMetaForm(forms.Form): 24 | age = forms.IntegerField() 25 | sex = forms.ChoiceField(choices=((1, 'male'), (2, 'female')), required=False) 26 | 27 | 28 | class Person(models.Model): 29 | name = CharField(max_length=200) 30 | 31 | meta_info = ModelFormField(form=PersonMetaForm) 32 | 33 | .. note:: 34 | 35 | Changed in 0.3: You must supply the kwarg argument `form` to `ModelFormField` 36 | 37 | 38 | Which will result in something like this (using the admin) 39 | 40 | .. image:: ss001.png 41 | 42 | The :ref:`api_model_formfield` is automatically set to `null=True`, `blank=True`, this is 43 | because validation is done on the inner form. As a result you will see something like the 44 | following if we hit save on the change form: 45 | 46 | .. image:: ss002.png 47 | 48 | If we supply the change for valid data you should get a python dictionary when retrieving 49 | the data:: 50 | 51 | >>> person = Person.objects.get(pk=1) 52 | >>> person.meta_info 53 | {u'age': 12, u'sex': u'1'} 54 | 55 | The form is the only thing forcing valid input, behind the scenes the 56 | data is being serialized into json. Therefore on the python level we can supply meta_info 57 | any valid json::: 58 | 59 | >>> from sample_app.models import Person 60 | >>> data = {'some': 'thing', 'is': 'wrong', 'here': 'help!'} 61 | >>> p = Person.objects.create(name="Joan", meta_info=data) 62 | >>> p.meta_info 63 | {'is': 'wrong', 'some': 'thing', 'here': 'help!'} 64 | 65 | .. note:: 66 | 67 | If the form field is being made available via a change form, such as the admin, any 68 | unexpected value will be overridden by what the form returns . For example, the 69 | `PersonMetaForm` above only expects `age` and `sex`, so none of the values above 70 | ('is', 'some' and 'here') match and will be overridden when the form submitted. 71 | 72 | We can however, make the field hidden or readonly and use it to supply any 73 | valid json, but its not really the intension of this app. 74 | -------------------------------------------------------------------------------- /doc_src/reference/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | === 4 | API 5 | === 6 | 7 | .. _api_formfield: 8 | 9 | FormField 10 | ========= 11 | 12 | A form field which accepts a `django.forms.Form`, a string in the format 'path.to.Class' or a callable that returns a `django.forms.Form` as the first argument. 13 | :ref:`api_widget_formfield` is used as this fields widget. 14 | 15 | Example Usage:: 16 | 17 | from django import forms 18 | from formfield import FormField 19 | 20 | class OtherInfoForm(forms.Form): 21 | other_name = forms.CharField() 22 | 23 | 24 | class MyForm(forms.Form): 25 | 26 | name = forms.CharField() 27 | 28 | other_info = FormField(form=OtherInfoForm) 29 | 30 | .. note:: 31 | 32 | Changed in 0.3: You must supply the kwarg argument `form` to `ModelFormField` 33 | 34 | 35 | Seems odd to have a form within a form yea? Its more useful when using it with 36 | a model where the data is serialized to json. 37 | 38 | 39 | .. _api_model_formfield: 40 | 41 | ModelFormField 42 | ============== 43 | 44 | A model form field which accepts a `django.forms.Form`, a string in the format 'path.to.Class' or a callable that returns a `django.forms.Form` as the first argument. 45 | :ref:`api_formfield` is used as form field. 46 | 47 | 48 | Example Usage:: 49 | 50 | from django.db import models 51 | from django import forms 52 | from formfield import ModelFormField 53 | 54 | class MetaDataForm(forms.Form): 55 | alias = forms.CharField(required=False) 56 | phone = forms.CharField(required=False) 57 | email = forms.EmailField(required=False) 58 | 59 | 60 | class Contact(models.Model): 61 | 62 | name = models.CharField(max_length=200) 63 | 64 | meta_data = ModelFormField(form=MetaDataForm) 65 | 66 | 67 | .. _api_widget_formfield: 68 | 69 | FormFieldWidget 70 | =============== 71 | 72 | This is the widget used to render the output in a user friendly way. We added 73 | some methods to help render the output. The main method to override is the normal 74 | `format_output`, here is the default code:: 75 | 76 | ret = ['
    '] 77 | for i, field in enumerate(self.fields): 78 | label = self.format_label(field, i) 79 | help_text = self.format_help_text(field, i) 80 | ret.append('
  • %s %s %s
  • ' % ( 81 | label, rendered_widgets[i], field.help_text and help_text)) 82 | 83 | ret.append('
') 84 | return six.u(''.join(ret)) 85 | 86 | It simply wraps the entire form in a
    tag with a css class of `formfield`, you 87 | can override this for more control. 88 | 89 | Extra methods 90 | ------------- 91 | 92 | If you don't want to override the entire method you can override `format_label` and 93 | `format_help_text` as well. These methods accept to arguments, the bound field and 94 | a counter 95 | 96 | FormFieldWidget.format_label 97 | **************************** 98 | 99 | :: 100 | 101 | def format_label(self, field, counter): 102 | return '' % ( 103 | counter, field.field.required and 'class="required"', field.label) 104 | 105 | 106 | FormFieldWidget.format_help_text 107 | ******************************** 108 | 109 | :: 110 | 111 | def format_help_text(self, field, counter): 112 | return '

    %s

    ' % field.help_text 113 | -------------------------------------------------------------------------------- /docs/_sources/reference/api.txt: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | === 4 | API 5 | === 6 | 7 | .. _api_formfield: 8 | 9 | FormField 10 | ========= 11 | 12 | A form field which accepts a `django.forms.Form`, a string in the format 'path.to.Class' or a callable that returns a `django.forms.Form` as the first argument. 13 | :ref:`api_widget_formfield` is used as this fields widget. 14 | 15 | Example Usage:: 16 | 17 | from django import forms 18 | from formfield import FormField 19 | 20 | class OtherInfoForm(forms.Form): 21 | other_name = forms.CharField() 22 | 23 | 24 | class MyForm(forms.Form): 25 | 26 | name = forms.CharField() 27 | 28 | other_info = FormField(form=OtherInfoForm) 29 | 30 | .. note:: 31 | 32 | Changed in 0.3: You must supply the kwarg argument `form` to `ModelFormField` 33 | 34 | 35 | Seems odd to have a form within a form yea? Its more useful when using it with 36 | a model where the data is serialized to json. 37 | 38 | 39 | .. _api_model_formfield: 40 | 41 | ModelFormField 42 | ============== 43 | 44 | A model form field which accepts a `django.forms.Form`, a string in the format 'path.to.Class' or a callable that returns a `django.forms.Form` as the first argument. 45 | :ref:`api_formfield` is used as form field. 46 | 47 | 48 | Example Usage:: 49 | 50 | from django.db import models 51 | from django import forms 52 | from formfield import ModelFormField 53 | 54 | class MetaDataForm(forms.Form): 55 | alias = forms.CharField(required=False) 56 | phone = forms.CharField(required=False) 57 | email = forms.EmailField(required=False) 58 | 59 | 60 | class Contact(models.Model): 61 | 62 | name = models.CharField(max_length=200) 63 | 64 | meta_data = ModelFormField(form=MetaDataForm) 65 | 66 | 67 | .. _api_widget_formfield: 68 | 69 | FormFieldWidget 70 | =============== 71 | 72 | This is the widget used to render the output in a user friendly way. We added 73 | some methods to help render the output. The main method to override is the normal 74 | `format_output`, here is the default code:: 75 | 76 | ret = ['
      '] 77 | for i, field in enumerate(self.fields): 78 | label = self.format_label(field, i) 79 | help_text = self.format_help_text(field, i) 80 | ret.append('
    • %s %s %s
    • ' % ( 81 | label, rendered_widgets[i], field.help_text and help_text)) 82 | 83 | ret.append('
    ') 84 | return six.u(''.join(ret)) 85 | 86 | It simply wraps the entire form in a
      tag with a css class of `formfield`, you 87 | can override this for more control. 88 | 89 | Extra methods 90 | ------------- 91 | 92 | If you don't want to override the entire method you can override `format_label` and 93 | `format_help_text` as well. These methods accept to arguments, the bound field and 94 | a counter 95 | 96 | FormFieldWidget.format_label 97 | **************************** 98 | 99 | :: 100 | 101 | def format_label(self, field, counter): 102 | return '' % ( 103 | counter, field.field.required and 'class="required"', field.label) 104 | 105 | 106 | FormFieldWidget.format_help_text 107 | ******************************** 108 | 109 | :: 110 | 111 | def format_help_text(self, field, counter): 112 | return '

      %s

      ' % field.help_text 113 | -------------------------------------------------------------------------------- /doc_src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | DESTDIR = .. 10 | 11 | # Internal variables. 12 | PAPEROPT_a4 = -D latex_paper_size=a4 13 | PAPEROPT_letter = -D latex_paper_size=letter 14 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 15 | 16 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 17 | 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 27 | @echo " changes to make an overview of all changed/added/deprecated items" 28 | @echo " linkcheck to check all external links for integrity" 29 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 30 | 31 | clean: 32 | -rm -rf $(BUILDDIR)/* 33 | 34 | html: 35 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DESTDIR)/docs 36 | @echo 37 | @echo "Build finished. The HTML pages are in $(DESTDIR)/docs." 38 | 39 | dirhtml: 40 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 43 | 44 | pickle: 45 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 46 | @echo 47 | @echo "Build finished; now you can process the pickle files." 48 | 49 | json: 50 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 51 | @echo 52 | @echo "Build finished; now you can process the JSON files." 53 | 54 | htmlhelp: 55 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 56 | @echo 57 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 58 | ".hhp project file in $(BUILDDIR)/htmlhelp." 59 | 60 | qthelp: 61 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 62 | @echo 63 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 64 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 65 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/app.qhcp" 66 | @echo "To view the help file:" 67 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/app.qhc" 68 | 69 | latex: 70 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 71 | @echo 72 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 73 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 74 | "run these through (pdf)latex." 75 | 76 | changes: 77 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 78 | @echo 79 | @echo "The overview file is in $(BUILDDIR)/changes." 80 | 81 | linkcheck: 82 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 83 | @echo 84 | @echo "Link check complete; look for any errors in the above output " \ 85 | "or in $(BUILDDIR)/linkcheck/output.txt." 86 | 87 | doctest: 88 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 89 | @echo "Testing of doctests in the sources finished, look at the " \ 90 | "results in $(BUILDDIR)/doctest/output.txt." 91 | -------------------------------------------------------------------------------- /doc_src/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | set SPHINXBUILD=sphinx-build 6 | set BUILDDIR=_build 7 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 8 | if NOT "%PAPER%" == "" ( 9 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 10 | ) 11 | 12 | if "%1" == "" goto help 13 | 14 | if "%1" == "help" ( 15 | :help 16 | echo.Please use `make ^` where ^ is one of 17 | echo. html to make standalone HTML files 18 | echo. dirhtml to make HTML files named index.html in directories 19 | echo. pickle to make pickle files 20 | echo. json to make JSON files 21 | echo. htmlhelp to make HTML files and a HTML help project 22 | echo. qthelp to make HTML files and a qthelp project 23 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 24 | echo. changes to make an overview over all changed/added/deprecated items 25 | echo. linkcheck to check all external links for integrity 26 | echo. doctest to run all doctests embedded in the documentation if enabled 27 | goto end 28 | ) 29 | 30 | if "%1" == "clean" ( 31 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 32 | del /q /s %BUILDDIR%\* 33 | goto end 34 | ) 35 | 36 | if "%1" == "html" ( 37 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 38 | echo. 39 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 40 | goto end 41 | ) 42 | 43 | if "%1" == "dirhtml" ( 44 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 45 | echo. 46 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 47 | goto end 48 | ) 49 | 50 | if "%1" == "pickle" ( 51 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 52 | echo. 53 | echo.Build finished; now you can process the pickle files. 54 | goto end 55 | ) 56 | 57 | if "%1" == "json" ( 58 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 59 | echo. 60 | echo.Build finished; now you can process the JSON files. 61 | goto end 62 | ) 63 | 64 | if "%1" == "htmlhelp" ( 65 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 66 | echo. 67 | echo.Build finished; now you can run HTML Help Workshop with the ^ 68 | .hhp project file in %BUILDDIR%/htmlhelp. 69 | goto end 70 | ) 71 | 72 | if "%1" == "qthelp" ( 73 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 74 | echo. 75 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 76 | .qhcp project file in %BUILDDIR%/qthelp, like this: 77 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\app.qhcp 78 | echo.To view the help file: 79 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\app.ghc 80 | goto end 81 | ) 82 | 83 | if "%1" == "latex" ( 84 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 85 | echo. 86 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 87 | goto end 88 | ) 89 | 90 | if "%1" == "changes" ( 91 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 92 | echo. 93 | echo.The overview file is in %BUILDDIR%/changes. 94 | goto end 95 | ) 96 | 97 | if "%1" == "linkcheck" ( 98 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 99 | echo. 100 | echo.Link check complete; look for any errors in the above output ^ 101 | or in %BUILDDIR%/linkcheck/output.txt. 102 | goto end 103 | ) 104 | 105 | if "%1" == "doctest" ( 106 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 107 | echo. 108 | echo.Testing of doctests in the sources finished, look at the ^ 109 | results in %BUILDDIR%/doctest/output.txt. 110 | goto end 111 | ) 112 | 113 | :end 114 | -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Index — django-formfield 0.4 documentation 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
      31 |

      django-formfield 0.4 documentation

      32 |
      33 | 42 | 43 |
      44 |
      45 | 46 | 51 | 52 | 53 | 54 | 55 | 71 | 72 |
      73 |
      74 | 75 | 76 | 77 |
      78 |
      79 |
      80 |
      81 | 82 | 83 |

      Index

      84 | 85 |
      86 | 87 |
      88 | 89 | 90 |
      91 |
      92 |
      93 | 99 |
      100 |
      101 |
    104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /example/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for example project. 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | import os, sys 6 | APP = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 7 | PROJ_ROOT = os.path.abspath(os.path.dirname(__file__)) 8 | sys.path.append(APP) 9 | 10 | ADMINS = ( 11 | # ('Your Name', 'your_email@domain.com'), 12 | ) 13 | 14 | MANAGERS = ADMINS 15 | 16 | DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 17 | DATABASE_NAME = 'dev.db' # Or path to database file if using sqlite3. 18 | DATABASE_USER = '' # Not used with sqlite3. 19 | DATABASE_PASSWORD = '' # Not used with sqlite3. 20 | DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. 21 | DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. 22 | 23 | DATABASES = { 24 | 'default': { 25 | 'ENGINE': 'django.db.backends.sqlite3', 26 | 'NAME': 'dev.db', 27 | } 28 | } 29 | 30 | # Local time zone for this installation. Choices can be found here: 31 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 32 | # although not all choices may be available on all operating systems. 33 | # If running in a Windows environment this must be set to the same as your 34 | # system time zone. 35 | TIME_ZONE = 'America/Chicago' 36 | 37 | # Language code for this installation. All choices can be found here: 38 | # http://www.i18nguy.com/unicode/language-identifiers.html 39 | LANGUAGE_CODE = 'en-us' 40 | 41 | SITE_ID = 1 42 | 43 | # If you set this to False, Django will make some optimizations so as not 44 | # to load the internationalization machinery. 45 | USE_I18N = True 46 | 47 | # Absolute path to the directory that holds media. 48 | # Example: "/home/media/media.lawrence.com/" 49 | MEDIA_ROOT = os.path.abspath(os.path.join('media')) 50 | 51 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 52 | # trailing slash if there is a path component (optional in other cases). 53 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 54 | 55 | STATIC_ROOT = os.path.abspath(os.path.join('static'))#os.path.join(PROJ_ROOT, "static"), 56 | 57 | MEDIA_URL = '/static/' 58 | 59 | STATIC_URL = '/static-media/' 60 | 61 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 62 | # trailing slash. 63 | # Examples: "http://foo.com/media/", "/media/". 64 | ADMIN_MEDIA_PREFIX = '/media/' 65 | 66 | # Make this unique, and don't share it with anybody. 67 | SECRET_KEY = 'g2_39yupn*6j4p*cg2%w643jiq-1n_annua*%i8+rq0dx9p=$n' 68 | 69 | # List of callables that know how to import templates from various sources. 70 | TEMPLATE_LOADERS = ( 71 | 'django.template.loaders.filesystem.Loader', 72 | 'django.template.loaders.app_directories.Loader', 73 | # 'django.template.loaders.eggs.load_template_source', 74 | ) 75 | 76 | MIDDLEWARE_CLASSES = ( 77 | 'django.middleware.common.CommonMiddleware', 78 | 'django.contrib.sessions.middleware.SessionMiddleware', 79 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 80 | 'django.contrib.messages.middleware.MessageMiddleware' 81 | ) 82 | 83 | ROOT_URLCONF = 'example.urls' 84 | 85 | TEMPLATE_DIRS = ( 86 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 87 | # Always use forward slashes, even on Windows. 88 | # Don't forget to use absolute paths, not relative paths. 89 | ) 90 | 91 | INSTALLED_APPS = ( 92 | 'django.contrib.admin', 93 | 'django.contrib.auth', 94 | 'django.contrib.contenttypes', 95 | 'django.contrib.sessions', 96 | 'django.contrib.sites', 97 | 'django.contrib.staticfiles', 98 | 'formfield', 99 | 'sample_app', 100 | ) 101 | 102 | TEST_RUNNER = 'django.test.runner.DiscoverRunner' 103 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Search — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    38 |

    django-formfield 0.4 documentation

    39 |
    40 | 49 | 50 |
    51 |
    52 | 53 | 58 | 59 |
    60 |
    61 | 62 | 63 | 64 |
    65 |
    66 |
    67 |
    68 | 69 |

    Search

    70 |
    71 | 72 |

    73 | Please activate JavaScript to enable the search 74 | functionality. 75 |

    76 |
    77 |

    78 | From here you can search these documents. Enter your search 79 | words into the box below and click "search". Note that the search 80 | function will automatically search for all of the words. Pages 81 | containing fewer words won't appear in the result list. 82 |

    83 |
    84 | 85 | 86 | 87 |
    88 | 89 |
    90 | 91 |
    92 | 93 |
    94 |
    95 |
    96 | 102 |
    103 |
    104 |
107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | |BUILD|_ 2 | 3 | .. |BUILD| image:: 4 | https://secure.travis-ci.org/jsoa/django-formfield.png?branch=master 5 | .. _BUILD: http://travis-ci.org/#!/jsoa/django-formfield 6 | 7 | 8 | :Version: 0.4 9 | :Docs: https://django-formfield.readthedocs.org/en/latest/ 10 | :Download: http://pypi.python.org/pypi/django-formfield/ 11 | :Source: https://github.com/jsoa/django-formfield 12 | 13 | ========== 14 | Change Log 15 | ========== 16 | * **0.4*** 17 | * Django 1.9/1.10/1.11 compatibility 18 | * Cleaner error messages 19 | * Form within a form fixes 20 | * **0.3** 21 | * Django 1.6/1.7/1.8 compatibility 22 | * Python 3.4 compatibility 23 | * Form argument can now be dynamic 24 | 25 | * **0.2** 26 | * Fix Django 1.5 install issue 27 | 28 | * **0.1.3** 29 | * Fixed bug when a form's initial value evaludated to ``False`` 30 | * pep8 related fixes 31 | 32 | =============== 33 | Getting Started 34 | =============== 35 | 36 | django-formfield is a form field that accepts a django form as its first argument, and validates 37 | as well as render's each form field as expected. Yes a form within a form, *within a dream*? There 38 | are two types of fields available, `FormField` and `ModelFormField`. For 39 | `ModelFormField` the data is stored in json. For `FormField` data is simply 40 | returned as a python dictionary (form.cleaned_data) 41 | 42 | ============ 43 | Installation 44 | ============ 45 | 46 | Installation is easy using ``pip`` or ``easy_install``. 47 | 48 | :: 49 | 50 | pip install django-formfield 51 | 52 | or 53 | 54 | :: 55 | 56 | easy_install django-formfield 57 | 58 | 59 | Add to installed apps 60 | ===================== 61 | 62 | :: 63 | 64 | INSTALLED_APPS = ( 65 | ... 66 | 'formfield', 67 | ... 68 | ) 69 | 70 | 71 | Example 72 | ======= 73 | 74 | :: 75 | 76 | from django.db import models 77 | from django import forms 78 | 79 | from formfield import ModelFormField 80 | 81 | class PersonMetaForm(forms.Form): 82 | age = forms.IntegerField() 83 | sex = forms.ChoiceField(choices=((1, 'male'), (2, 'female')), required=False) 84 | 85 | 86 | class Person(models.Model): 87 | name = CharField(max_length=200) 88 | 89 | meta_info = ModelFormField(form=PersonMetaForm) 90 | 91 | Which will result in something like this (using the admin) 92 | 93 | .. image:: https://github.com/jsoa/django-formfield/raw/master/docs/_images/ss001.png 94 | 95 | The `ModelFormField` is automatically set to `null=True`, `blank=True`, this is 96 | because validation is done on the inner form. As a result you will see something like the 97 | following if we hit save on the change form: 98 | 99 | .. image:: https://github.com/jsoa/django-formfield/raw/master/docs/_images/ss002.png 100 | 101 | If we supply the change for valid data you should get a python dictionary when retrieving 102 | the data:: 103 | 104 | >>> person = Person.objects.get(pk=1) 105 | >>> person.meta_info 106 | {u'age': 12, u'sex': u'1'} 107 | 108 | The form is the only thing forcing valid input, behind the scenes the 109 | data is being serialized into json. Therefore on the python level we can supply meta_info 110 | any valid json::: 111 | 112 | >>> from sample_app.models import Person 113 | >>> data = {'some': 'thing', 'is': 'wrong', 'here': 'help!'} 114 | >>> p = Person.objects.create(name="Joan", meta_info=data) 115 | >>> p.meta_info 116 | {'is': 'wrong', 'some': 'thing', 'here': 'help!'} 117 | 118 | .. note:: 119 | 120 | If the form field is being made available via a change form, such as the admin, any 121 | unexpected value will be overridden by what the form returns . For example, the 122 | `PersonMetaForm` above only expects `age` and `sex`, so none of the values above 123 | ('is', 'some' and 'here') match and will be overridden when the form submitted. 124 | 125 | We can however, make the field hidden or readonly and use it to supply any 126 | valid json, but its not really the intension of this app. 127 | -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 51 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 52 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 53 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 54 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 55 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 56 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 57 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 58 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 59 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 60 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 61 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 62 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 63 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 64 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 65 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Reference — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

django-formfield 0.4 documentation

33 |
34 | 45 | 46 |
47 |
48 | 49 | 57 | 58 | 74 | 75 |
76 |
77 | 78 | 79 | 80 |
81 |
82 |
83 |
84 | 85 |
86 |

Reference

87 |
88 | 96 |
97 |
98 | 99 | 100 |
101 |
102 |
103 | 109 |
110 |
111 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /formfield/fields.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import unicode_literals 4 | import json 5 | import six 6 | 7 | from django import forms 8 | from django.core.exceptions import ValidationError 9 | from django.core.serializers.json import DjangoJSONEncoder 10 | from django.db import models 11 | from django.utils.safestring import mark_safe 12 | 13 | from .widgets import FormFieldWidget 14 | 15 | 16 | class JSONField(models.TextField): 17 | """ 18 | JSONField is a generic textfield that serializes/unserializes 19 | the data from our form fields 20 | """ 21 | 22 | def __init__(self, *args, **kwargs): 23 | self.dump_kwargs = kwargs.pop('dump_kwargs', 24 | {'cls': DjangoJSONEncoder}) 25 | self.load_kwargs = kwargs.pop('load_kwargs', {}) 26 | 27 | super(JSONField, self).__init__(*args, **kwargs) 28 | 29 | def to_python(self, value): 30 | 31 | if isinstance(value, six.string_types): 32 | try: 33 | return json.loads(value, **self.load_kwargs) 34 | except ValueError: 35 | pass 36 | 37 | return value 38 | 39 | def get_db_prep_value(self, value, *args, **kwargs): 40 | 41 | if isinstance(value, six.string_types): 42 | return value 43 | 44 | return json.dumps(value, **self.dump_kwargs) 45 | 46 | def value_to_string(self, obj): 47 | value = self._get_val_from_obj(obj) 48 | return self.get_db_prep_value(value) 49 | 50 | 51 | class FormField(forms.MultiValueField): 52 | """The form field we can use in forms""" 53 | 54 | def __init__(self, form, **kwargs): 55 | import inspect 56 | if inspect.isclass(form) and issubclass(form, forms.Form): 57 | form_class = form 58 | elif callable(form): 59 | form_class = form() 60 | self.form = form_class() 61 | elif isinstance(form, six.string_types): 62 | from django.utils import module_loading 63 | if hasattr(module_loading, 'import_by_path'): 64 | form_class = module_loading.import_by_path(form) 65 | else: 66 | form_class = module_loading.import_string(form) 67 | self.form = form_class() 68 | 69 | # Set the widget and initial data 70 | kwargs['widget'] = FormFieldWidget([f for f in self.form]) 71 | kwargs['initial'] = [f.field.initial for f in self.form] 72 | # The field it self should not be required, this allows us to 73 | # have optional fields in a sub form 74 | kwargs['required'] = False 75 | 76 | self.max_length = kwargs.pop('max_length', None) 77 | 78 | super(FormField, self).__init__(**kwargs) 79 | 80 | self.fields = [f.field for f in self.form] 81 | 82 | def compress(self, data_list): 83 | """ 84 | Return the cleaned_data of the form, everything should already be valid 85 | """ 86 | data = {} 87 | if data_list: 88 | return dict( 89 | (f.name, data_list[i]) for i, f in enumerate(self.form)) 90 | return data 91 | 92 | def clean(self, value): 93 | """ 94 | Call the form is_valid to ensure every value supplied is valid 95 | """ 96 | if not value: 97 | raise ValidationError( 98 | 'Error found in Form Field: Nothing to validate') 99 | 100 | data = dict((bf.name, value[i]) for i, bf in enumerate(self.form)) 101 | self.form = form = self.form.__class__(data) 102 | if not form.is_valid(): 103 | error_dict = list(form.errors.items()) 104 | raise ValidationError([ 105 | ValidationError(mark_safe('{} {}'.format( 106 | k.title(), v)), code=k) for k, v in error_dict]) 107 | 108 | # This call will ensure compress is called as expected. 109 | return super(FormField, self).clean(value) 110 | 111 | 112 | class ModelFormField(JSONField): 113 | """The json backed field we can use in our models""" 114 | 115 | def __init__(self, *args, **kwargs): 116 | """ 117 | This field needs to be nullable and blankable. The supplied form 118 | will provide the validation. 119 | """ 120 | self.form = kwargs.pop('form', None) 121 | 122 | kwargs['null'] = True 123 | kwargs['blank'] = True 124 | 125 | super(ModelFormField, self).__init__(*args, **kwargs) 126 | 127 | def formfield(self, form_class=FormField, **kwargs): 128 | # Need to supply form to FormField 129 | return super(ModelFormField, self).formfield(form_class=form_class, 130 | form=self.form, **kwargs) 131 | 132 | try: 133 | from south.modelsinspector import add_introspection_rules 134 | add_introspection_rules([], ["^formfield\.fields\.JSONField"]) 135 | add_introspection_rules([], ["^formfield\.fields\.ModelFormField"]) 136 | except ImportError: 137 | pass 138 | -------------------------------------------------------------------------------- /docs/installation.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Installation — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

django-formfield 0.4 documentation

33 |
34 | 45 | 46 |
47 |
48 | 49 | 57 | 58 | 74 | 75 |
76 |
77 | 78 | 79 | 80 |
81 |
82 |
83 |
84 | 85 |
86 |

Installation

87 |

Installation is easy using pip or easy_install.

88 |
pip install django-formfield
 89 | 
90 |
91 |

or

92 |
easy_install django-formfield
 93 | 
94 |
95 |
96 |

Add to installed apps

97 |
INSTALLED_APPS = (
 98 |     ...
 99 |     formfield,
100 |     ...
101 | )
102 | 
103 |
104 |
105 |
106 | 107 | 108 |
109 |
110 |
111 | 117 |
118 |
119 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/_static/classic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * default.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- default theme. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: sans-serif; 18 | font-size: 100%; 19 | background-color: #11303d; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: #1c4e63; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 230px; 36 | } 37 | 38 | div.body { 39 | background-color: #ffffff; 40 | color: #000000; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | div.footer { 45 | color: #ffffff; 46 | width: 100%; 47 | padding: 9px 0 9px 0; 48 | text-align: center; 49 | font-size: 75%; 50 | } 51 | 52 | div.footer a { 53 | color: #ffffff; 54 | text-decoration: underline; 55 | } 56 | 57 | div.related { 58 | background-color: #133f52; 59 | line-height: 30px; 60 | color: #ffffff; 61 | } 62 | 63 | div.related a { 64 | color: #ffffff; 65 | } 66 | 67 | div.sphinxsidebar { 68 | } 69 | 70 | div.sphinxsidebar h3 { 71 | font-family: 'Trebuchet MS', sans-serif; 72 | color: #ffffff; 73 | font-size: 1.4em; 74 | font-weight: normal; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | 79 | div.sphinxsidebar h3 a { 80 | color: #ffffff; 81 | } 82 | 83 | div.sphinxsidebar h4 { 84 | font-family: 'Trebuchet MS', sans-serif; 85 | color: #ffffff; 86 | font-size: 1.3em; 87 | font-weight: normal; 88 | margin: 5px 0 0 0; 89 | padding: 0; 90 | } 91 | 92 | div.sphinxsidebar p { 93 | color: #ffffff; 94 | } 95 | 96 | div.sphinxsidebar p.topless { 97 | margin: 5px 10px 10px 10px; 98 | } 99 | 100 | div.sphinxsidebar ul { 101 | margin: 10px; 102 | padding: 0; 103 | color: #ffffff; 104 | } 105 | 106 | div.sphinxsidebar a { 107 | color: #98dbcc; 108 | } 109 | 110 | div.sphinxsidebar input { 111 | border: 1px solid #98dbcc; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | 116 | 117 | 118 | /* -- hyperlink styles ------------------------------------------------------ */ 119 | 120 | a { 121 | color: #355f7c; 122 | text-decoration: none; 123 | } 124 | 125 | a:visited { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:hover { 131 | text-decoration: underline; 132 | } 133 | 134 | 135 | 136 | /* -- body styles ----------------------------------------------------------- */ 137 | 138 | div.body h1, 139 | div.body h2, 140 | div.body h3, 141 | div.body h4, 142 | div.body h5, 143 | div.body h6 { 144 | font-family: 'Trebuchet MS', sans-serif; 145 | background-color: #f2f2f2; 146 | font-weight: normal; 147 | color: #20435c; 148 | border-bottom: 1px solid #ccc; 149 | margin: 20px -20px 10px -20px; 150 | padding: 3px 0 3px 10px; 151 | } 152 | 153 | div.body h1 { margin-top: 0; font-size: 200%; } 154 | div.body h2 { font-size: 160%; } 155 | div.body h3 { font-size: 140%; } 156 | div.body h4 { font-size: 120%; } 157 | div.body h5 { font-size: 110%; } 158 | div.body h6 { font-size: 100%; } 159 | 160 | a.headerlink { 161 | color: #c60f0f; 162 | font-size: 0.8em; 163 | padding: 0 4px 0 4px; 164 | text-decoration: none; 165 | } 166 | 167 | a.headerlink:hover { 168 | background-color: #c60f0f; 169 | color: white; 170 | } 171 | 172 | div.body p, div.body dd, div.body li, div.body blockquote { 173 | text-align: justify; 174 | line-height: 130%; 175 | } 176 | 177 | div.admonition p.admonition-title + p { 178 | display: inline; 179 | } 180 | 181 | div.admonition p { 182 | margin-bottom: 5px; 183 | } 184 | 185 | div.admonition pre { 186 | margin-bottom: 5px; 187 | } 188 | 189 | div.admonition ul, div.admonition ol { 190 | margin-bottom: 5px; 191 | } 192 | 193 | div.note { 194 | background-color: #eee; 195 | border: 1px solid #ccc; 196 | } 197 | 198 | div.seealso { 199 | background-color: #ffc; 200 | border: 1px solid #ff6; 201 | } 202 | 203 | div.topic { 204 | background-color: #eee; 205 | } 206 | 207 | div.warning { 208 | background-color: #ffe4e4; 209 | border: 1px solid #f66; 210 | } 211 | 212 | p.admonition-title { 213 | display: inline; 214 | } 215 | 216 | p.admonition-title:after { 217 | content: ":"; 218 | } 219 | 220 | pre { 221 | padding: 5px; 222 | background-color: #eeffcc; 223 | color: #333333; 224 | line-height: 120%; 225 | border: 1px solid #ac9; 226 | border-left: none; 227 | border-right: none; 228 | } 229 | 230 | code { 231 | background-color: #ecf0f3; 232 | padding: 0 1px 0 1px; 233 | font-size: 0.95em; 234 | } 235 | 236 | th { 237 | background-color: #ede; 238 | } 239 | 240 | .warning code { 241 | background: #efc2c2; 242 | } 243 | 244 | .note code { 245 | background: #d6d6d6; 246 | } 247 | 248 | .viewcode-back { 249 | font-family: sans-serif; 250 | } 251 | 252 | div.viewcode-block:target { 253 | background-color: #f4debf; 254 | border-top: 1px solid #ac9; 255 | border-bottom: 1px solid #ac9; 256 | } 257 | 258 | div.code-block-caption { 259 | color: #efefef; 260 | background-color: #1c4e63; 261 | } -------------------------------------------------------------------------------- /docs/_static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | // global elements used by the functions. 34 | // the 'sidebarbutton' element is defined as global after its 35 | // creation, in the add_sidebar_button function 36 | var bodywrapper = $('.bodywrapper'); 37 | var sidebar = $('.sphinxsidebar'); 38 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 39 | 40 | // for some reason, the document has no sidebar; do not run into errors 41 | if (!sidebar.length) return; 42 | 43 | // original margin-left of the bodywrapper and width of the sidebar 44 | // with the sidebar expanded 45 | var bw_margin_expanded = bodywrapper.css('margin-left'); 46 | var ssb_width_expanded = sidebar.width(); 47 | 48 | // margin-left of the bodywrapper and width of the sidebar 49 | // with the sidebar collapsed 50 | var bw_margin_collapsed = '.8em'; 51 | var ssb_width_collapsed = '.8em'; 52 | 53 | // colors used by the current theme 54 | var dark_color = $('.related').css('background-color'); 55 | var light_color = $('.document').css('background-color'); 56 | 57 | function sidebar_is_collapsed() { 58 | return sidebarwrapper.is(':not(:visible)'); 59 | } 60 | 61 | function toggle_sidebar() { 62 | if (sidebar_is_collapsed()) 63 | expand_sidebar(); 64 | else 65 | collapse_sidebar(); 66 | } 67 | 68 | function collapse_sidebar() { 69 | sidebarwrapper.hide(); 70 | sidebar.css('width', ssb_width_collapsed); 71 | bodywrapper.css('margin-left', bw_margin_collapsed); 72 | sidebarbutton.css({ 73 | 'margin-left': '0', 74 | 'height': bodywrapper.height() 75 | }); 76 | sidebarbutton.find('span').text('»'); 77 | sidebarbutton.attr('title', _('Expand sidebar')); 78 | document.cookie = 'sidebar=collapsed'; 79 | } 80 | 81 | function expand_sidebar() { 82 | bodywrapper.css('margin-left', bw_margin_expanded); 83 | sidebar.css('width', ssb_width_expanded); 84 | sidebarwrapper.show(); 85 | sidebarbutton.css({ 86 | 'margin-left': ssb_width_expanded-12, 87 | 'height': bodywrapper.height() 88 | }); 89 | sidebarbutton.find('span').text('«'); 90 | sidebarbutton.attr('title', _('Collapse sidebar')); 91 | document.cookie = 'sidebar=expanded'; 92 | } 93 | 94 | function add_sidebar_button() { 95 | sidebarwrapper.css({ 96 | 'float': 'left', 97 | 'margin-right': '0', 98 | 'width': ssb_width_expanded - 28 99 | }); 100 | // create the button 101 | sidebar.append( 102 | '
«
' 103 | ); 104 | var sidebarbutton = $('#sidebarbutton'); 105 | light_color = sidebarbutton.css('background-color'); 106 | // find the height of the viewport to center the '<<' in the page 107 | var viewport_height; 108 | if (window.innerHeight) 109 | viewport_height = window.innerHeight; 110 | else 111 | viewport_height = $(window).height(); 112 | sidebarbutton.find('span').css({ 113 | 'display': 'block', 114 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 115 | }); 116 | 117 | sidebarbutton.click(toggle_sidebar); 118 | sidebarbutton.attr('title', _('Collapse sidebar')); 119 | sidebarbutton.css({ 120 | 'color': '#FFFFFF', 121 | 'border-left': '1px solid ' + dark_color, 122 | 'font-size': '1.2em', 123 | 'cursor': 'pointer', 124 | 'height': bodywrapper.height(), 125 | 'padding-top': '1px', 126 | 'margin-left': ssb_width_expanded - 12 127 | }); 128 | 129 | sidebarbutton.hover( 130 | function () { 131 | $(this).css('background-color', dark_color); 132 | }, 133 | function () { 134 | $(this).css('background-color', light_color); 135 | } 136 | ); 137 | } 138 | 139 | function set_position_from_cookie() { 140 | if (!document.cookie) 141 | return; 142 | var items = document.cookie.split(';'); 143 | for(var k=0; k 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to django-formfield’s documentation! — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |

django-formfield 0.4 documentation

32 |
33 | 43 | 44 |
45 |
46 | 47 | 52 | 53 | 69 | 70 |
71 |
72 | 73 | 74 | 75 |
76 |
77 |
78 |
79 | 80 |
81 |

Welcome to django-formfield’s documentation!

82 |

Contents:

83 |
84 | 98 |
99 |
100 |
101 |

Contributing

102 | 106 |
107 |
108 |

Indices and tables

109 | 114 |
115 | 116 | 117 |
118 |
119 |
120 | 126 |
127 |
128 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /doc_src/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {%- block doctype -%} 3 | 5 | {%- endblock %} 6 | {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} 7 | {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} 8 | {%- block linktags %} 9 | {%- if hasdoc('about') %} 10 | 11 | {%- endif %} 12 | {%- if hasdoc('genindex') %} 13 | 14 | {%- endif %} 15 | {%- if hasdoc('search') %} 16 | 17 | {%- endif %} 18 | {%- if hasdoc('copyright') %} 19 | 20 | {%- endif %} 21 | 22 | {%- if parents %} 23 | 24 | {%- endif %} 25 | {%- if next %} 26 | 27 | {%- endif %} 28 | {%- if prev %} 29 | 30 | {%- endif %} 31 | {%- endblock %} 32 | {%- block extrahead %} {% endblock %} 33 | {%- block header %}{% endblock %} 34 | {%- block relbar1 %} 35 |
36 |

{{docstitle}}

37 |
38 | 49 | {% endblock %} 50 | 51 | {%- block sidebar1 %} 52 | {%- if not embedded %}{% if not theme_nosidebar|tobool %} 53 |
54 |
55 | {%- block sidebarlogo %} 56 | {%- if logo %} 57 | 60 | {%- endif %} 61 | {%- endblock %} 62 | {%- block sidebartoc %} 63 | 64 | {{ toctree() }} 65 | {%- endblock %} 66 | {%- block sidebarrel %} 67 | {%- endblock %} 68 | {%- block sidebarsourcelink %} 69 | {%- if show_source and has_source and sourcename %} 70 |

{{ _('This Page') }}

71 | 75 | {%- endif %} 76 | {%- endblock %} 77 | {%- if customsidebar %} 78 | {% include customsidebar %} 79 | {%- endif %} 80 | {%- block sidebarsearch %} 81 | {%- if pagename != "search" %} 82 | 98 | 99 | {%- endif %} 100 | {%- endblock %} 101 |
102 |
103 | {%- endif %}{% endif %} 104 | 105 | {% endblock %} 106 | {%- block document %} 107 |
108 | {%- if not embedded %}{% if not theme_nosidebar|tobool %} 109 |
110 | {%- endif %}{% endif %} 111 |
112 | {% block body %} {% endblock %} 113 |
114 | {%- if not embedded %}{% if not theme_nosidebar|tobool %} 115 |
116 | {%- endif %}{% endif %} 117 |
118 | 132 | {%- endblock %} 133 | {%- block sidebar2 %}{% endblock %} 134 | {%- block relbar2 %}{% endblock %} 135 | {%- block footer %} 136 | 143 | 144 | {%- endblock %} 145 | -------------------------------------------------------------------------------- /formfield/tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from django.test import TestCase 4 | from django.db import models 5 | from django import forms 6 | from django.core.exceptions import ValidationError 7 | 8 | from .fields import ModelFormField, FormField 9 | 10 | 11 | class MetaForm(forms.Form): 12 | age = forms.IntegerField() 13 | sex = forms.ChoiceField( 14 | required=False, choices=((1, 'Make'), (2, 'Female'))) 15 | 16 | 17 | class Coordinates(forms.Form): 18 | lng = forms.CharField(required=False) 19 | lat = forms.CharField(required=False) 20 | 21 | 22 | class Address(forms.Form): 23 | street = forms.CharField() 24 | country = forms.CharField(required=False) 25 | zipcode = forms.IntegerField(required=False) 26 | 27 | coords = FormField(Coordinates) 28 | 29 | 30 | class PersonForm(forms.Form): 31 | first_name = forms.CharField() 32 | last_name = forms.CharField() 33 | suffix = forms.CharField(required=False) 34 | age = forms.IntegerField(required=False) 35 | 36 | address = FormField(Address) 37 | 38 | 39 | class TestModel(models.Model): 40 | name = models.CharField(max_length=255) 41 | meta_data = ModelFormField(MetaForm) 42 | 43 | personal = ModelFormField(PersonForm) 44 | 45 | 46 | class FormFieldTests(TestCase): 47 | """ 48 | Tests for django-formfield 49 | """ 50 | maxDiff = None 51 | 52 | def setUp(self): 53 | self.model = TestModel.objects.create( 54 | name="John", 55 | meta_data={'age': 32, 'sex': 1}) 56 | 57 | def test_01_get_field(self): 58 | """Ensure the data is a proper dictionary""" 59 | self.assertEqual(self.model.meta_data, {'age': 32, 'sex': 1}) 60 | 61 | def test_02_modify_field(self): 62 | """After a change is made, ensure the data is still a dictionary""" 63 | self.model.meta_data = {'age': 18, 'sex': 2} 64 | self.model.save() 65 | self.assertEqual(self.model.meta_data, {'age': 18, 'sex': 2}) 66 | 67 | # Ensure we can also pass set a string representation of the json 68 | self.model.meta_data = {"age": 15, "sex": 1} 69 | self.model.save() 70 | self.assertEqual(self.model.meta_data, {'age': 15, 'sex': 1}) 71 | 72 | def test_03_formfield(self): 73 | """Ensure the field cleans properly""" 74 | initial_data = {'age': 23, 'sex': 1} 75 | field = FormField(MetaForm, initial=initial_data) 76 | 77 | # A multi-value field will receive the values as a list 78 | # in the correct order of the form 79 | self.assertEqual(field.clean([10, 1]), {'age': 10, 'sex': '1'}) 80 | 81 | self.assertRaises(ValidationError, field.clean, []) 82 | 83 | def test_04_form(self): 84 | """ 85 | Ensure a django form with the form field is valid if supplied with the 86 | correct data 87 | """ 88 | class MyForm(forms.Form): 89 | name = forms.CharField() 90 | meta_data = FormField(MetaForm) 91 | 92 | form = MyForm() 93 | self.assertFalse(form.is_valid()) 94 | 95 | # We need to supply the django form with the proper multi value 96 | # data i.e. _0 _1 that represent each (inner) form field 97 | form = MyForm({'name': 'john', 'meta_data_0': 12, 'meta_data_1': 1}) 98 | self.assertTrue(form.is_bound) 99 | self.assertTrue(form.is_valid()) 100 | 101 | # Ensure the cleaned data is what we expect 102 | self.assertEqual(form.cleaned_data, 103 | {'name': 'john', 'meta_data': {'age': 12, 'sex': '1'}}) 104 | 105 | class MyFormToo(forms.Form): 106 | name = forms.CharField() 107 | meta_data = FormField('formfield.tests.MetaForm') 108 | 109 | form = MyFormToo() 110 | self.assertFalse(form.is_valid()) 111 | 112 | form = MyFormToo({'name': 'john', 'meta_data_0': 12, 'meta_data_1': 1}) 113 | self.assertTrue(form.is_bound) 114 | self.assertTrue(form.is_valid()) 115 | 116 | # Ensure the cleaned data is what we expect 117 | self.assertEqual(form.cleaned_data, 118 | {'name': 'john', 'meta_data': {'age': 12, 'sex': '1'}}) 119 | 120 | class MyFormThree(forms.Form): 121 | name = forms.CharField() 122 | meta_data = FormField(lambda: MetaForm) 123 | 124 | form = MyFormThree() 125 | self.assertFalse(form.is_valid()) 126 | 127 | form = MyFormThree({'name': 'john', 'meta_data_0': 12, 'meta_data_1': 1}) 128 | self.assertTrue(form.is_bound) 129 | self.assertTrue(form.is_valid()) 130 | 131 | # Ensure the cleaned data is what we expect 132 | self.assertEqual(form.cleaned_data, 133 | {'name': 'john', 'meta_data': {'age': 12, 'sex': '1'}}) 134 | 135 | def test_05_sub_formfield(self): 136 | """Ensure sub formfields validate""" 137 | payload = { 138 | 'first_name': '1', 139 | 'last_name': '2', 140 | 'suffix': '3', 141 | 'age': 4, 142 | 'address': { 143 | 'street': '5', 144 | 'country': '6', 145 | 'zipcode': 7, 146 | 'coords': { 147 | 'lng': '8', 148 | 'lat': '9' 149 | } 150 | } 151 | } 152 | field = FormField(PersonForm) 153 | data_to_clean = ['1', '2', '3', '4', ['5', '6', '7', ['8', '9']]] 154 | self.assertEqual(field.clean(data_to_clean), payload) 155 | 156 | def test_06_invalidate_required_subform_field(self): 157 | """Ensure a proper validation error is raised with sub formfield is 158 | invalid (required field)""" 159 | 160 | field = FormField(PersonForm) 161 | # Invalidate a required field (street) 162 | data_to_clean = ['1', '2', '3', '4', [None, '6', '7', ['8', '9']]] 163 | self.assertRaises(ValidationError, field.clean, data_to_clean) 164 | 165 | def test_07_invalidate_int_subform_field(self): 166 | """Ensure a proper validation error is raised with sub formfield is 167 | invalid (integer only)""" 168 | field = FormField(PersonForm) 169 | # Invalidate a integer field (zipcode) 170 | data_to_clean = ['1', '2', '3', '4', ['5', '6', 'aaa', ['8', '9']]] 171 | self.assertRaises(ValidationError, field.clean, data_to_clean) 172 | -------------------------------------------------------------------------------- /doc_src/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # app documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Oct 21 13:18:22 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.append(os.path.abspath('..')) 20 | import formfield 21 | os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings' 22 | 23 | # -- General configuration ----------------------------------------------------- 24 | 25 | # Add any Sphinx extension module names here, as strings. They can be extensions 26 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 27 | extensions = ['sphinx.ext.autodoc'] 28 | 29 | # Add any paths that contain templates here, relative to this directory. 30 | templates_path = ['_templates'] 31 | 32 | # The suffix of source filenames. 33 | source_suffix = '.rst' 34 | 35 | # The encoding of source files. 36 | #source_encoding = 'utf-8' 37 | 38 | # The master toctree document. 39 | master_doc = 'index' 40 | 41 | # General information about the project. 42 | project = u'django-formfield' 43 | copyright = u'2010, Jose Soares' 44 | 45 | # The version info for the project you're documenting, acts as replacement for 46 | # |version| and |release|, also used in various other places throughout the 47 | # built documents. 48 | # 49 | # The short X.Y version. 50 | version = formfield.get_version() 51 | # The full version, including alpha/beta/rc tags. 52 | release = formfield.get_version() 53 | 54 | # The language for content autogenerated by Sphinx. Refer to documentation 55 | # for a list of supported languages. 56 | #language = None 57 | 58 | # There are two options for replacing |today|: either, you set today to some 59 | # non-false value, then it is used: 60 | #today = '' 61 | # Else, today_fmt is used as the format for a strftime call. 62 | #today_fmt = '%B %d, %Y' 63 | 64 | # List of documents that shouldn't be included in the build. 65 | #unused_docs = [] 66 | 67 | # List of directories, relative to source directory, that shouldn't be searched 68 | # for source files. 69 | exclude_trees = ['_build'] 70 | 71 | # The reST default role (used for this markup: `text`) to use for all documents. 72 | #default_role = None 73 | 74 | # If true, '()' will be appended to :func: etc. cross-reference text. 75 | #add_function_parentheses = True 76 | 77 | # If true, the current module name will be prepended to all description 78 | # unit titles (such as .. function::). 79 | #add_module_names = True 80 | 81 | # If true, sectionauthor and moduleauthor directives will be shown in the 82 | # output. They are ignored by default. 83 | #show_authors = False 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # A list of ignored prefixes for module index sorting. 89 | #modindex_common_prefix = [] 90 | 91 | 92 | # -- Options for HTML output --------------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. Major themes that come with 95 | # Sphinx are currently 'default' and 'sphinxdoc'. 96 | html_theme = 'default' 97 | 98 | # Theme options are theme-specific and customize the look and feel of a theme 99 | # further. For a list of options available for each theme, see the 100 | # documentation. 101 | #html_theme_options = {} 102 | 103 | # Add any paths that contain custom themes here, relative to this directory. 104 | #html_theme_path = [] 105 | 106 | # The name for this set of Sphinx documents. If None, it defaults to 107 | # " v documentation". 108 | #html_title = None 109 | 110 | # A shorter title for the navigation bar. Default is the same as html_title. 111 | #html_short_title = None 112 | 113 | # The name of an image file (relative to this directory) to place at the top 114 | # of the sidebar. 115 | #html_logo = None 116 | 117 | # The name of an image file (within the static path) to use as favicon of the 118 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 119 | # pixels large. 120 | #html_favicon = None 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named "default.css" will overwrite the builtin "default.css". 125 | html_static_path = ['_static'] 126 | 127 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 128 | # using the given strftime format. 129 | #html_last_updated_fmt = '%b %d, %Y' 130 | 131 | # If true, SmartyPants will be used to convert quotes and dashes to 132 | # typographically correct entities. 133 | #html_use_smartypants = True 134 | 135 | # Custom sidebar templates, maps document names to template names. 136 | #html_sidebars = {} 137 | 138 | # Additional templates that should be rendered to pages, maps page names to 139 | # template names. 140 | #html_additional_pages = {} 141 | 142 | # If false, no module index is generated. 143 | #html_use_modindex = True 144 | 145 | # If false, no index is generated. 146 | #html_use_index = True 147 | 148 | # If true, the index is split into individual pages for each letter. 149 | #html_split_index = False 150 | 151 | # If true, links to the reST sources are added to the pages. 152 | html_show_sourcelink = False 153 | 154 | # If true, an OpenSearch description file will be output, and all pages will 155 | # contain a tag referring to it. The value of this option must be the 156 | # base URL from which the finished HTML is served. 157 | #html_use_opensearch = '' 158 | 159 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 160 | #html_file_suffix = '' 161 | 162 | # Output file base name for HTML help builder. 163 | htmlhelp_basename = 'django-formfielddoc' 164 | 165 | 166 | # -- Options for LaTeX output -------------------------------------------------- 167 | 168 | # The paper size ('letter' or 'a4'). 169 | #latex_paper_size = 'letter' 170 | 171 | # The font size ('10pt', '11pt' or '12pt'). 172 | #latex_font_size = '10pt' 173 | 174 | # Grouping the document tree into LaTeX files. List of tuples 175 | # (source start file, target name, title, author, documentclass [howto/manual]). 176 | latex_documents = [ 177 | ('index', 'app.tex', u'django-formfield Documentation', 178 | u'Jose Soares', 'manual'), 179 | ] 180 | 181 | # The name of an image file (relative to this directory) to place at the top of 182 | # the title page. 183 | #latex_logo = None 184 | 185 | # For "manual" documents, if this is true, then toplevel headings are parts, 186 | # not chapters. 187 | #latex_use_parts = False 188 | 189 | # Additional stuff for the LaTeX preamble. 190 | #latex_preamble = '' 191 | 192 | # Documents to append as an appendix to all manuals. 193 | #latex_appendices = [] 194 | 195 | # If false, no module index is generated. 196 | #latex_use_modindex = True 197 | -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s == 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node) { 70 | if (node.nodeType == 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { 74 | var span = document.createElement("span"); 75 | span.className = className; 76 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 77 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 78 | document.createTextNode(val.substr(pos + text.length)), 79 | node.nextSibling)); 80 | node.nodeValue = val.substr(0, pos); 81 | } 82 | } 83 | else if (!jQuery(node).is("button, select, textarea")) { 84 | jQuery.each(node.childNodes, function() { 85 | highlight(this); 86 | }); 87 | } 88 | } 89 | return this.each(function() { 90 | highlight(this); 91 | }); 92 | }; 93 | 94 | /* 95 | * backward compatibility for jQuery.browser 96 | * This will be supported until firefox bug is fixed. 97 | */ 98 | if (!jQuery.browser) { 99 | jQuery.uaMatch = function(ua) { 100 | ua = ua.toLowerCase(); 101 | 102 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 103 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 104 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 105 | /(msie) ([\w.]+)/.exec(ua) || 106 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 107 | []; 108 | 109 | return { 110 | browser: match[ 1 ] || "", 111 | version: match[ 2 ] || "0" 112 | }; 113 | }; 114 | jQuery.browser = {}; 115 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 116 | } 117 | 118 | /** 119 | * Small JavaScript module for the documentation. 120 | */ 121 | var Documentation = { 122 | 123 | init : function() { 124 | this.fixFirefoxAnchorBug(); 125 | this.highlightSearchWords(); 126 | this.initIndexTable(); 127 | 128 | }, 129 | 130 | /** 131 | * i18n support 132 | */ 133 | TRANSLATIONS : {}, 134 | PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, 135 | LOCALE : 'unknown', 136 | 137 | // gettext and ngettext don't access this so that the functions 138 | // can safely bound to a different name (_ = Documentation.gettext) 139 | gettext : function(string) { 140 | var translated = Documentation.TRANSLATIONS[string]; 141 | if (typeof translated == 'undefined') 142 | return string; 143 | return (typeof translated == 'string') ? translated : translated[0]; 144 | }, 145 | 146 | ngettext : function(singular, plural, n) { 147 | var translated = Documentation.TRANSLATIONS[singular]; 148 | if (typeof translated == 'undefined') 149 | return (n == 1) ? singular : plural; 150 | return translated[Documentation.PLURALEXPR(n)]; 151 | }, 152 | 153 | addTranslations : function(catalog) { 154 | for (var key in catalog.messages) 155 | this.TRANSLATIONS[key] = catalog.messages[key]; 156 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 157 | this.LOCALE = catalog.locale; 158 | }, 159 | 160 | /** 161 | * add context elements like header anchor links 162 | */ 163 | addContextElements : function() { 164 | $('div[id] > :header:first').each(function() { 165 | $('\u00B6'). 166 | attr('href', '#' + this.id). 167 | attr('title', _('Permalink to this headline')). 168 | appendTo(this); 169 | }); 170 | $('dt[id]').each(function() { 171 | $('\u00B6'). 172 | attr('href', '#' + this.id). 173 | attr('title', _('Permalink to this definition')). 174 | appendTo(this); 175 | }); 176 | }, 177 | 178 | /** 179 | * workaround a firefox stupidity 180 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 181 | */ 182 | fixFirefoxAnchorBug : function() { 183 | if (document.location.hash) 184 | window.setTimeout(function() { 185 | document.location.href += ''; 186 | }, 10); 187 | }, 188 | 189 | /** 190 | * highlight the search words provided in the url in the text 191 | */ 192 | highlightSearchWords : function() { 193 | var params = $.getQueryParameters(); 194 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 195 | if (terms.length) { 196 | var body = $('div.body'); 197 | if (!body.length) { 198 | body = $('body'); 199 | } 200 | window.setTimeout(function() { 201 | $.each(terms, function() { 202 | body.highlightText(this.toLowerCase(), 'highlighted'); 203 | }); 204 | }, 10); 205 | $('') 207 | .appendTo($('#searchbox')); 208 | } 209 | }, 210 | 211 | /** 212 | * init the domain index toggle buttons 213 | */ 214 | initIndexTable : function() { 215 | var togglers = $('img.toggler').click(function() { 216 | var src = $(this).attr('src'); 217 | var idnum = $(this).attr('id').substr(7); 218 | $('tr.cg-' + idnum).toggle(); 219 | if (src.substr(-9) == 'minus.png') 220 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 221 | else 222 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 223 | }).css('display', ''); 224 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 225 | togglers.click(); 226 | } 227 | }, 228 | 229 | /** 230 | * helper function to hide the search marks again 231 | */ 232 | hideSearchWords : function() { 233 | $('#searchbox .highlight-link').fadeOut(300); 234 | $('span.highlighted').removeClass('highlighted'); 235 | }, 236 | 237 | /** 238 | * make the url absolute 239 | */ 240 | makeURL : function(relativeURL) { 241 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 242 | }, 243 | 244 | /** 245 | * get the current relative url 246 | */ 247 | getCurrentURL : function() { 248 | var path = document.location.pathname; 249 | var parts = path.split(/\//); 250 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 251 | if (this == '..') 252 | parts.pop(); 253 | }); 254 | var url = parts.join('/'); 255 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 256 | }, 257 | 258 | initOnKeyListeners: function() { 259 | $(document).keyup(function(event) { 260 | var activeElementType = document.activeElement.tagName; 261 | // don't navigate when in search box or textarea 262 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { 263 | switch (event.keyCode) { 264 | case 37: // left 265 | var prevHref = $('link[rel="prev"]').prop('href'); 266 | if (prevHref) { 267 | window.location.href = prevHref; 268 | return false; 269 | } 270 | case 39: // right 271 | var nextHref = $('link[rel="next"]').prop('href'); 272 | if (nextHref) { 273 | window.location.href = nextHref; 274 | return false; 275 | } 276 | } 277 | } 278 | }); 279 | } 280 | }; 281 | 282 | // quick alias for translations 283 | _ = Documentation.gettext; 284 | 285 | $(document).ready(function() { 286 | Documentation.init(); 287 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /docs/getting_started.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Getting Started — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

django-formfield 0.4 documentation

33 |
34 | 45 | 46 |
47 |
48 | 49 | 57 | 58 | 74 | 75 |
76 |
77 | 78 | 79 | 80 |
81 |
82 |
83 |
84 | 85 |
86 |

Getting Started

87 |

django-formfield is a form field that accepts a django form as its first argument, and validates 88 | as well as render’s each form field as expected. Yes a form within a form, within a dream? There 89 | are two types of fields available, FormField and ModelFormField. For 90 | ModelFormField the data is stored in json. For FormField data is simply 91 | returned as a python dictionary (form.cleaned_data)

92 |
93 |

Example

94 |
from django.db import models
 95 | from django import forms
 96 | 
 97 | from formfield import ModelFormField
 98 | 
 99 | class PersonMetaForm(forms.Form):
100 |     age = forms.IntegerField()
101 |     sex = forms.ChoiceField(choices=((1, 'male'), (2, 'female')), required=False)
102 | 
103 | 
104 | class Person(models.Model):
105 |     name = CharField(max_length=200)
106 | 
107 |     meta_info = ModelFormField(form=PersonMetaForm)
108 | 
109 |
110 |
111 |

Note

112 |

Changed in 0.3: You must supply the kwarg argument form to ModelFormField

113 |
114 |

Which will result in something like this (using the admin)

115 | _images/ss001.png 116 |

The ModelFormField is automatically set to null=True, blank=True, this is 117 | because validation is done on the inner form. As a result you will see something like the 118 | following if we hit save on the change form:

119 | _images/ss002.png 120 |

If we supply the change for valid data you should get a python dictionary when retrieving 121 | the data:

122 |
>>> person = Person.objects.get(pk=1)
123 | >>> person.meta_info
124 | {u'age': 12, u'sex': u'1'}
125 | 
126 |
127 |

The form is the only thing forcing valid input, behind the scenes the 128 | data is being serialized into json. Therefore on the python level we can supply meta_info 129 | any valid json::

130 |
>>> from sample_app.models import Person
131 | >>> data = {'some': 'thing', 'is': 'wrong', 'here': 'help!'}
132 | >>> p = Person.objects.create(name="Joan", meta_info=data)
133 | >>> p.meta_info
134 | {'is': 'wrong', 'some': 'thing', 'here': 'help!'}
135 | 
136 |
137 |
138 |

Note

139 |

If the form field is being made available via a change form, such as the admin, any 140 | unexpected value will be overridden by what the form returns . For example, the 141 | PersonMetaForm above only expects age and sex, so none of the values above 142 | (‘is’, ‘some’ and ‘here’) match and will be overridden when the form submitted.

143 |

We can however, make the field hidden or readonly and use it to supply any 144 | valid json, but its not really the intension of this app.

145 |
146 |
147 |
148 | 149 | 150 |
151 |
152 |
153 | 159 |
160 |
161 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/_static/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.1 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, 10 | h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= 11 | b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 32 | -------------------------------------------------------------------------------- /docs/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 230px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | word-wrap: break-word; 56 | overflow-wrap : break-word; 57 | } 58 | 59 | div.sphinxsidebar ul { 60 | list-style: none; 61 | } 62 | 63 | div.sphinxsidebar ul ul, 64 | div.sphinxsidebar ul.want-points { 65 | margin-left: 20px; 66 | list-style: square; 67 | } 68 | 69 | div.sphinxsidebar ul ul { 70 | margin-top: 0; 71 | margin-bottom: 0; 72 | } 73 | 74 | div.sphinxsidebar form { 75 | margin-top: 10px; 76 | } 77 | 78 | div.sphinxsidebar input { 79 | border: 1px solid #98dbcc; 80 | font-family: sans-serif; 81 | font-size: 1em; 82 | } 83 | 84 | div.sphinxsidebar #searchbox input[type="text"] { 85 | width: 170px; 86 | } 87 | 88 | img { 89 | border: 0; 90 | max-width: 100%; 91 | } 92 | 93 | /* -- search page ----------------------------------------------------------- */ 94 | 95 | ul.search { 96 | margin: 10px 0 0 20px; 97 | padding: 0; 98 | } 99 | 100 | ul.search li { 101 | padding: 5px 0 5px 20px; 102 | background-image: url(file.png); 103 | background-repeat: no-repeat; 104 | background-position: 0 7px; 105 | } 106 | 107 | ul.search li a { 108 | font-weight: bold; 109 | } 110 | 111 | ul.search li div.context { 112 | color: #888; 113 | margin: 2px 0 0 30px; 114 | text-align: left; 115 | } 116 | 117 | ul.keywordmatches li.goodmatch a { 118 | font-weight: bold; 119 | } 120 | 121 | /* -- index page ------------------------------------------------------------ */ 122 | 123 | table.contentstable { 124 | width: 90%; 125 | } 126 | 127 | table.contentstable p.biglink { 128 | line-height: 150%; 129 | } 130 | 131 | a.biglink { 132 | font-size: 1.3em; 133 | } 134 | 135 | span.linkdescr { 136 | font-style: italic; 137 | padding-top: 5px; 138 | font-size: 90%; 139 | } 140 | 141 | /* -- general index --------------------------------------------------------- */ 142 | 143 | table.indextable { 144 | width: 100%; 145 | } 146 | 147 | table.indextable td { 148 | text-align: left; 149 | vertical-align: top; 150 | } 151 | 152 | table.indextable dl, table.indextable dd { 153 | margin-top: 0; 154 | margin-bottom: 0; 155 | } 156 | 157 | table.indextable tr.pcap { 158 | height: 10px; 159 | } 160 | 161 | table.indextable tr.cap { 162 | margin-top: 10px; 163 | background-color: #f2f2f2; 164 | } 165 | 166 | img.toggler { 167 | margin-right: 3px; 168 | margin-top: 3px; 169 | cursor: pointer; 170 | } 171 | 172 | div.modindex-jumpbox { 173 | border-top: 1px solid #ddd; 174 | border-bottom: 1px solid #ddd; 175 | margin: 1em 0 1em 0; 176 | padding: 0.4em; 177 | } 178 | 179 | div.genindex-jumpbox { 180 | border-top: 1px solid #ddd; 181 | border-bottom: 1px solid #ddd; 182 | margin: 1em 0 1em 0; 183 | padding: 0.4em; 184 | } 185 | 186 | /* -- general body styles --------------------------------------------------- */ 187 | 188 | div.body p, div.body dd, div.body li, div.body blockquote { 189 | -moz-hyphens: auto; 190 | -ms-hyphens: auto; 191 | -webkit-hyphens: auto; 192 | hyphens: auto; 193 | } 194 | 195 | a.headerlink { 196 | visibility: hidden; 197 | } 198 | 199 | h1:hover > a.headerlink, 200 | h2:hover > a.headerlink, 201 | h3:hover > a.headerlink, 202 | h4:hover > a.headerlink, 203 | h5:hover > a.headerlink, 204 | h6:hover > a.headerlink, 205 | dt:hover > a.headerlink, 206 | caption:hover > a.headerlink, 207 | p.caption:hover > a.headerlink, 208 | div.code-block-caption:hover > a.headerlink { 209 | visibility: visible; 210 | } 211 | 212 | div.body p.caption { 213 | text-align: inherit; 214 | } 215 | 216 | div.body td { 217 | text-align: left; 218 | } 219 | 220 | .field-list ul { 221 | padding-left: 1em; 222 | } 223 | 224 | .first { 225 | margin-top: 0 !important; 226 | } 227 | 228 | p.rubric { 229 | margin-top: 30px; 230 | font-weight: bold; 231 | } 232 | 233 | img.align-left, .figure.align-left, object.align-left { 234 | clear: left; 235 | float: left; 236 | margin-right: 1em; 237 | } 238 | 239 | img.align-right, .figure.align-right, object.align-right { 240 | clear: right; 241 | float: right; 242 | margin-left: 1em; 243 | } 244 | 245 | img.align-center, .figure.align-center, object.align-center { 246 | display: block; 247 | margin-left: auto; 248 | margin-right: auto; 249 | } 250 | 251 | .align-left { 252 | text-align: left; 253 | } 254 | 255 | .align-center { 256 | text-align: center; 257 | } 258 | 259 | .align-right { 260 | text-align: right; 261 | } 262 | 263 | /* -- sidebars -------------------------------------------------------------- */ 264 | 265 | div.sidebar { 266 | margin: 0 0 0.5em 1em; 267 | border: 1px solid #ddb; 268 | padding: 7px 7px 0 7px; 269 | background-color: #ffe; 270 | width: 40%; 271 | float: right; 272 | } 273 | 274 | p.sidebar-title { 275 | font-weight: bold; 276 | } 277 | 278 | /* -- topics ---------------------------------------------------------------- */ 279 | 280 | div.topic { 281 | border: 1px solid #ccc; 282 | padding: 7px 7px 0 7px; 283 | margin: 10px 0 10px 0; 284 | } 285 | 286 | p.topic-title { 287 | font-size: 1.1em; 288 | font-weight: bold; 289 | margin-top: 10px; 290 | } 291 | 292 | /* -- admonitions ----------------------------------------------------------- */ 293 | 294 | div.admonition { 295 | margin-top: 10px; 296 | margin-bottom: 10px; 297 | padding: 7px; 298 | } 299 | 300 | div.admonition dt { 301 | font-weight: bold; 302 | } 303 | 304 | div.admonition dl { 305 | margin-bottom: 0; 306 | } 307 | 308 | p.admonition-title { 309 | margin: 0px 10px 5px 0px; 310 | font-weight: bold; 311 | } 312 | 313 | div.body p.centered { 314 | text-align: center; 315 | margin-top: 25px; 316 | } 317 | 318 | /* -- tables ---------------------------------------------------------------- */ 319 | 320 | table.docutils { 321 | border: 0; 322 | border-collapse: collapse; 323 | } 324 | 325 | table caption span.caption-number { 326 | font-style: italic; 327 | } 328 | 329 | table caption span.caption-text { 330 | } 331 | 332 | table.docutils td, table.docutils th { 333 | padding: 1px 8px 1px 5px; 334 | border-top: 0; 335 | border-left: 0; 336 | border-right: 0; 337 | border-bottom: 1px solid #aaa; 338 | } 339 | 340 | table.field-list td, table.field-list th { 341 | border: 0 !important; 342 | } 343 | 344 | table.footnote td, table.footnote th { 345 | border: 0 !important; 346 | } 347 | 348 | th { 349 | text-align: left; 350 | padding-right: 5px; 351 | } 352 | 353 | table.citation { 354 | border-left: solid 1px gray; 355 | margin-left: 1px; 356 | } 357 | 358 | table.citation td { 359 | border-bottom: none; 360 | } 361 | 362 | /* -- figures --------------------------------------------------------------- */ 363 | 364 | div.figure { 365 | margin: 0.5em; 366 | padding: 0.5em; 367 | } 368 | 369 | div.figure p.caption { 370 | padding: 0.3em; 371 | } 372 | 373 | div.figure p.caption span.caption-number { 374 | font-style: italic; 375 | } 376 | 377 | div.figure p.caption span.caption-text { 378 | } 379 | 380 | 381 | /* -- other body styles ----------------------------------------------------- */ 382 | 383 | ol.arabic { 384 | list-style: decimal; 385 | } 386 | 387 | ol.loweralpha { 388 | list-style: lower-alpha; 389 | } 390 | 391 | ol.upperalpha { 392 | list-style: upper-alpha; 393 | } 394 | 395 | ol.lowerroman { 396 | list-style: lower-roman; 397 | } 398 | 399 | ol.upperroman { 400 | list-style: upper-roman; 401 | } 402 | 403 | dl { 404 | margin-bottom: 15px; 405 | } 406 | 407 | dd p { 408 | margin-top: 0px; 409 | } 410 | 411 | dd ul, dd table { 412 | margin-bottom: 10px; 413 | } 414 | 415 | dd { 416 | margin-top: 3px; 417 | margin-bottom: 10px; 418 | margin-left: 30px; 419 | } 420 | 421 | dt:target, .highlighted { 422 | background-color: #fbe54e; 423 | } 424 | 425 | dl.glossary dt { 426 | font-weight: bold; 427 | font-size: 1.1em; 428 | } 429 | 430 | .field-list ul { 431 | margin: 0; 432 | padding-left: 1em; 433 | } 434 | 435 | .field-list p { 436 | margin: 0; 437 | } 438 | 439 | .optional { 440 | font-size: 1.3em; 441 | } 442 | 443 | .sig-paren { 444 | font-size: larger; 445 | } 446 | 447 | .versionmodified { 448 | font-style: italic; 449 | } 450 | 451 | .system-message { 452 | background-color: #fda; 453 | padding: 5px; 454 | border: 3px solid red; 455 | } 456 | 457 | .footnote:target { 458 | background-color: #ffa; 459 | } 460 | 461 | .line-block { 462 | display: block; 463 | margin-top: 1em; 464 | margin-bottom: 1em; 465 | } 466 | 467 | .line-block .line-block { 468 | margin-top: 0; 469 | margin-bottom: 0; 470 | margin-left: 1.5em; 471 | } 472 | 473 | .guilabel, .menuselection { 474 | font-family: sans-serif; 475 | } 476 | 477 | .accelerator { 478 | text-decoration: underline; 479 | } 480 | 481 | .classifier { 482 | font-style: oblique; 483 | } 484 | 485 | abbr, acronym { 486 | border-bottom: dotted 1px; 487 | cursor: help; 488 | } 489 | 490 | /* -- code displays --------------------------------------------------------- */ 491 | 492 | pre { 493 | overflow: auto; 494 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 495 | } 496 | 497 | td.linenos pre { 498 | padding: 5px 0px; 499 | border: 0; 500 | background-color: transparent; 501 | color: #aaa; 502 | } 503 | 504 | table.highlighttable { 505 | margin-left: 0.5em; 506 | } 507 | 508 | table.highlighttable td { 509 | padding: 0 0.5em 0 0.5em; 510 | } 511 | 512 | div.code-block-caption { 513 | padding: 2px 5px; 514 | font-size: small; 515 | } 516 | 517 | div.code-block-caption code { 518 | background-color: transparent; 519 | } 520 | 521 | div.code-block-caption + div > div.highlight > pre { 522 | margin-top: 0; 523 | } 524 | 525 | div.code-block-caption span.caption-number { 526 | padding: 0.1em 0.3em; 527 | font-style: italic; 528 | } 529 | 530 | div.code-block-caption span.caption-text { 531 | } 532 | 533 | div.literal-block-wrapper { 534 | padding: 1em 1em 0; 535 | } 536 | 537 | div.literal-block-wrapper div.highlight { 538 | margin: 0; 539 | } 540 | 541 | code.descname { 542 | background-color: transparent; 543 | font-weight: bold; 544 | font-size: 1.2em; 545 | } 546 | 547 | code.descclassname { 548 | background-color: transparent; 549 | } 550 | 551 | code.xref, a code { 552 | background-color: transparent; 553 | font-weight: bold; 554 | } 555 | 556 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 557 | background-color: transparent; 558 | } 559 | 560 | .viewcode-link { 561 | float: right; 562 | } 563 | 564 | .viewcode-back { 565 | float: right; 566 | font-family: sans-serif; 567 | } 568 | 569 | div.viewcode-block:target { 570 | margin: -1px -10px; 571 | padding: 0 10px; 572 | } 573 | 574 | /* -- math display ---------------------------------------------------------- */ 575 | 576 | img.math { 577 | vertical-align: middle; 578 | } 579 | 580 | div.body div.math p { 581 | text-align: center; 582 | } 583 | 584 | span.eqno { 585 | float: right; 586 | } 587 | 588 | /* -- printout stylesheet --------------------------------------------------- */ 589 | 590 | @media print { 591 | div.document, 592 | div.documentwrapper, 593 | div.bodywrapper { 594 | margin: 0 !important; 595 | width: 100%; 596 | } 597 | 598 | div.sphinxsidebar, 599 | div.related, 600 | div.footer, 601 | #top-link { 602 | display: none; 603 | } 604 | } -------------------------------------------------------------------------------- /docs/reference/api.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API — django-formfield 0.4 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

django-formfield 0.4 documentation

33 |
34 | 44 | 45 |
46 |
47 | 48 | 56 | 57 | 73 | 74 |
75 |
76 | 77 | 78 | 79 |
80 |
81 |
82 |
83 | 84 |
85 |

API

86 |
87 |

FormField

88 |

A form field which accepts a django.forms.Form, a string in the format ‘path.to.Class’ or a callable that returns a django.forms.Form as the first argument. 89 | FormFieldWidget is used as this fields widget.

90 |

Example Usage:

91 |
from django import forms
 92 | from formfield import FormField
 93 | 
 94 | class OtherInfoForm(forms.Form):
 95 |     other_name = forms.CharField()
 96 | 
 97 | 
 98 | class MyForm(forms.Form):
 99 | 
100 |     name = forms.CharField()
101 | 
102 |     other_info = FormField(form=OtherInfoForm)
103 | 
104 |
105 |
106 |

Note

107 |

Changed in 0.3: You must supply the kwarg argument form to ModelFormField

108 |
109 |

Seems odd to have a form within a form yea? Its more useful when using it with 110 | a model where the data is serialized to json.

111 |
112 |
113 |

ModelFormField

114 |

A model form field which accepts a django.forms.Form, a string in the format ‘path.to.Class’ or a callable that returns a django.forms.Form as the first argument. 115 | FormField is used as form field.

116 |

Example Usage:

117 |
from django.db import models
118 | from django import forms
119 | from formfield import ModelFormField
120 | 
121 | class MetaDataForm(forms.Form):
122 |     alias = forms.CharField(required=False)
123 |     phone = forms.CharField(required=False)
124 |     email = forms.EmailField(required=False)
125 | 
126 | 
127 | class Contact(models.Model):
128 | 
129 |     name = models.CharField(max_length=200)
130 | 
131 |     meta_data = ModelFormField(form=MetaDataForm)
132 | 
133 |
134 |
135 |
136 |

FormFieldWidget

137 |

This is the widget used to render the output in a user friendly way. We added 138 | some methods to help render the output. The main method to override is the normal 139 | format_output, here is the default code:

140 |
ret = ['<ul class="formfield">']
141 | for i, field in enumerate(self.fields):
142 |     label = self.format_label(field, i)
143 |     help_text = self.format_help_text(field, i)
144 |     ret.append('<li>%s %s %s</li>' % (
145 |         label, rendered_widgets[i], field.help_text and help_text))
146 | 
147 | ret.append('</ul>')
148 | return six.u(''.join(ret))
149 | 
150 |
151 |

It simply wraps the entire form in a <ul> tag with a css class of formfield, you 152 | can override this for more control.

153 |
154 |

Extra methods

155 |

If you don’t want to override the entire method you can override format_label and 156 | format_help_text as well. These methods accept to arguments, the bound field and 157 | a counter

158 |
159 |

FormFieldWidget.format_label

160 |
def format_label(self, field, counter):
161 |     return '<label for="id_formfield_%s" %s>%s</label>' % (
162 |         counter, field.field.required and 'class="required"', field.label)
163 | 
164 |
165 |
166 |
167 |

FormFieldWidget.format_help_text

168 |
def format_help_text(self, field, counter):
169 |     return '<p class="help">%s</p>' % field.help_text
170 | 
171 |
172 |
173 |
174 |
175 |
176 | 177 | 178 |
179 |
180 |
181 | 187 |
188 |
189 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /doc_src/_static/default.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Sphinx stylesheet -- basic theme 3 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | */ 5 | h3 { 6 | color:#000000; 7 | font-size: 17px; 8 | margin-bottom:0.5em; 9 | margin-top:2em; 10 | } 11 | /* -- main layout ----------------------------------------------------------- */ 12 | 13 | div.clearer { 14 | clear: both; 15 | } 16 | 17 | /* -- header ---------------------------------------------------------------- */ 18 | 19 | #header #title { 20 | background:#29334F url(title_background.png) repeat-x scroll 0 0; 21 | border-bottom:1px solid #B6B6B6; 22 | height:25px; 23 | overflow:hidden; 24 | } 25 | #headerButtons { 26 | position: absolute; 27 | list-style: none outside; 28 | top: 26px; 29 | left: 0px; 30 | right: 0px; 31 | margin: 0px; 32 | padding: 0px; 33 | border-top: 1px solid #2B334F; 34 | border-bottom: 1px solid #EDEDED; 35 | height: 20px; 36 | font-size: 8pt; 37 | overflow: hidden; 38 | background-color: #D8D8D8; 39 | } 40 | 41 | #headerButtons li { 42 | background-repeat:no-repeat; 43 | display:inline; 44 | margin-top:0; 45 | padding:0; 46 | } 47 | 48 | .headerButton { 49 | display: inline; 50 | height:20px; 51 | } 52 | 53 | .headerButton a { 54 | text-decoration: none; 55 | float: right; 56 | height: 20px; 57 | padding: 4px 15px; 58 | border-left: 1px solid #ACACAC; 59 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 60 | color: black; 61 | } 62 | .headerButton a:hover { 63 | color: white; 64 | background-color: #787878; 65 | 66 | } 67 | 68 | li#toc_button { 69 | text-align:left; 70 | } 71 | 72 | li#toc_button .headerButton a { 73 | width:198px; 74 | padding-top: 4px; 75 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 76 | color: black; 77 | float: left; 78 | padding-left:15px; 79 | border-right:1px solid #ACACAC; 80 | background:transparent url(triangle_open.png) no-repeat scroll 4px 6px; 81 | } 82 | 83 | li#toc_button .headerButton a:hover { 84 | background-color: #787878; 85 | color: white; 86 | } 87 | 88 | li#page_buttons { 89 | position:absolute; 90 | right:0; 91 | } 92 | 93 | #breadcrumbs { 94 | color: black; 95 | background-image:url(breadcrumb_background.png); 96 | border-top:1px solid #2B334F; 97 | bottom:0; 98 | font-size:10px; 99 | height:15px; 100 | left:0; 101 | overflow:hidden; 102 | padding:3px 10px 0; 103 | position:absolute; 104 | right:0; 105 | white-space:nowrap; 106 | z-index:901; 107 | } 108 | #breadcrumbs a { 109 | color: black; 110 | text-decoration: none; 111 | } 112 | #breadcrumbs a:hover { 113 | text-decoration: underline; 114 | } 115 | #breadcrumbs img { 116 | padding-left: 3px; 117 | } 118 | /* -- sidebar --------------------------------------------------------------- */ 119 | #sphinxsidebar { 120 | position: absolute; 121 | top: 84px; 122 | bottom: 19px; 123 | left: 0px; 124 | width: 229px; 125 | background-color: #E4EBF7; 126 | border-right: 1px solid #ACACAC; 127 | border-top: 1px solid #2B334F; 128 | overflow-x: hidden; 129 | overflow-y: auto; 130 | padding: 0px 0px 0px 0px; 131 | font-size:11px; 132 | } 133 | 134 | div.sphinxsidebarwrapper { 135 | padding: 10px 5px 0 10px; 136 | } 137 | 138 | #sphinxsidebar li { 139 | margin: 0px; 140 | padding: 0px; 141 | font-weight: normal; 142 | margin: 0px 0px 7px 0px; 143 | overflow: hidden; 144 | text-overflow: ellipsis; 145 | font-size: 11px; 146 | } 147 | 148 | #sphinxsidebar ul { 149 | list-style: none; 150 | margin: 0px 0px 0px 0px; 151 | padding: 0px 5px 0px 5px; 152 | } 153 | 154 | #sphinxsidebar ul ul, 155 | #sphinxsidebar ul.want-points { 156 | list-style: square; 157 | } 158 | 159 | #sphinxsidebar ul ul { 160 | margin-top: 0; 161 | margin-bottom: 0; 162 | } 163 | 164 | #sphinxsidebar form { 165 | margin-top: 10px; 166 | } 167 | 168 | #sphinxsidebar input { 169 | border: 1px solid #787878; 170 | font-family: sans-serif; 171 | font-size: 1em; 172 | } 173 | 174 | img { 175 | border: 0; 176 | } 177 | 178 | #sphinxsidebar li.toctree-l1 a { 179 | font-weight: bold; 180 | color: #000; 181 | text-decoration: none; 182 | } 183 | 184 | #sphinxsidebar li.toctree-l2 a { 185 | font-weight: bold; 186 | color: #4f4f4f; 187 | text-decoration: none; 188 | } 189 | 190 | /* -- search page ----------------------------------------------------------- */ 191 | 192 | ul.search { 193 | margin: 10px 0 0 20px; 194 | padding: 0; 195 | } 196 | 197 | ul.search li { 198 | padding: 5px 0 5px 20px; 199 | background-image: url(file.png); 200 | background-repeat: no-repeat; 201 | background-position: 0 7px; 202 | } 203 | 204 | ul.search li a { 205 | font-weight: bold; 206 | } 207 | 208 | ul.search li div.context { 209 | color: #888; 210 | margin: 2px 0 0 30px; 211 | text-align: left; 212 | } 213 | 214 | ul.keywordmatches li.goodmatch a { 215 | font-weight: bold; 216 | } 217 | #sphinxsidebar input.prettysearch {border:none;} 218 | input.searchbutton { 219 | float: right; 220 | } 221 | .search-wrapper {width: 100%; height: 25px;} 222 | .search-wrapper input.prettysearch { border: none; width:200px; height: 16px; background: url(searchfield_repeat.png) center top repeat-x; border: 0px; margin: 0; padding: 3px 0 0 0; font: 11px "Lucida Grande", "Lucida Sans Unicode", Arial, sans-serif; } 223 | .search-wrapper input.prettysearch { width: 184px; margin-left: 20px; *margin-top:-1px; *margin-right:-2px; *margin-left:10px; } 224 | .search-wrapper .search-left { display: block; position: absolute; width: 20px; height: 19px; background: url(searchfield_leftcap.png) left top no-repeat; } 225 | .search-wrapper .search-right { display: block; position: relative; left: 204px; top: -19px; width: 10px; height: 19px; background: url(searchfield_rightcap.png) right top no-repeat; } 226 | 227 | /* -- index page ------------------------------------------------------------ */ 228 | 229 | table.contentstable { 230 | width: 90%; 231 | } 232 | 233 | table.contentstable p.biglink { 234 | line-height: 150%; 235 | } 236 | 237 | a.biglink { 238 | font-size: 1.3em; 239 | } 240 | 241 | span.linkdescr { 242 | font-style: italic; 243 | padding-top: 5px; 244 | font-size: 90%; 245 | } 246 | 247 | /* -- general index --------------------------------------------------------- */ 248 | 249 | table.indextable td { 250 | text-align: left; 251 | vertical-align: top; 252 | } 253 | 254 | table.indextable dl, table.indextable dd { 255 | margin-top: 0; 256 | margin-bottom: 0; 257 | } 258 | 259 | table.indextable tr.pcap { 260 | height: 10px; 261 | } 262 | 263 | table.indextable tr.cap { 264 | margin-top: 10px; 265 | background-color: #f2f2f2; 266 | } 267 | 268 | img.toggler { 269 | margin-right: 3px; 270 | margin-top: 3px; 271 | cursor: pointer; 272 | } 273 | 274 | /* -- general body styles --------------------------------------------------- */ 275 | .document { 276 | border-top:1px solid #2B334F; 277 | overflow:auto; 278 | padding-left:2em; 279 | padding-right:2em; 280 | position:absolute; 281 | z-index:1; 282 | top:84px; 283 | bottom:19px; 284 | right:0; 285 | left:230px; 286 | } 287 | 288 | a.headerlink { 289 | visibility: hidden; 290 | } 291 | 292 | h1:hover > a.headerlink, 293 | h2:hover > a.headerlink, 294 | h3:hover > a.headerlink, 295 | h4:hover > a.headerlink, 296 | h5:hover > a.headerlink, 297 | h6:hover > a.headerlink, 298 | dt:hover > a.headerlink { 299 | visibility: visible; 300 | } 301 | 302 | div.body p.caption { 303 | text-align: inherit; 304 | } 305 | 306 | div.body td { 307 | text-align: left; 308 | } 309 | 310 | .field-list ul { 311 | padding-left: 1em; 312 | } 313 | 314 | .first { 315 | margin-top: 0 !important; 316 | } 317 | 318 | p.rubric { 319 | margin-top: 30px; 320 | font-weight: bold; 321 | } 322 | 323 | /* -- sidebars -------------------------------------------------------------- */ 324 | 325 | /*div.sidebar { 326 | margin: 0 0 0.5em 1em; 327 | border: 1px solid #ddb; 328 | padding: 7px 7px 0 7px; 329 | background-color: #ffe; 330 | width: 40%; 331 | float: right; 332 | } 333 | 334 | p.sidebar-title { 335 | font-weight: bold; 336 | } 337 | */ 338 | /* -- topics ---------------------------------------------------------------- */ 339 | 340 | div.topic { 341 | border: 1px solid #ccc; 342 | padding: 7px 7px 0 7px; 343 | margin: 10px 0 10px 0; 344 | } 345 | 346 | p.topic-title { 347 | font-size: 1.1em; 348 | font-weight: bold; 349 | margin-top: 10px; 350 | } 351 | 352 | /* -- admonitions ----------------------------------------------------------- */ 353 | .admonition { 354 | border: 1px solid #a1a5a9; 355 | background-color: #f7f7f7; 356 | margin: 20px; 357 | padding: 0px 8px 7px 9px; 358 | text-align: left; 359 | } 360 | .warning { 361 | background-color:#E8E8E8; 362 | border:1px solid #111111; 363 | margin:30px; 364 | } 365 | .admonition p { 366 | font: 12px 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; 367 | margin-top: 7px; 368 | margin-bottom: 0px; 369 | } 370 | 371 | div.admonition dt { 372 | font-weight: bold; 373 | } 374 | 375 | div.admonition dl { 376 | margin-bottom: 0; 377 | } 378 | 379 | p.admonition-title { 380 | margin: 0px 10px 5px 0px; 381 | font-weight: bold; 382 | padding-top: 3px; 383 | } 384 | 385 | div.body p.centered { 386 | text-align: center; 387 | margin-top: 25px; 388 | } 389 | 390 | /* -- tables ---------------------------------------------------------------- */ 391 | 392 | table.docutils { 393 | border-collapse: collapse; 394 | border-top: 1px solid #919699; 395 | border-left: 1px solid #919699; 396 | border-right: 1px solid #919699; 397 | font-size:12px; 398 | padding:8px; 399 | text-align:left; 400 | vertical-align:top; 401 | } 402 | 403 | table.docutils td, table.docutils th { 404 | padding: 8px; 405 | font-size: 12px; 406 | text-align: left; 407 | vertical-align: top; 408 | border-bottom: 1px solid #919699; 409 | } 410 | 411 | table.docutils th { 412 | font-weight: bold; 413 | } 414 | /* This alternates colors in up to six table rows (light blue for odd, white for even)*/ 415 | .docutils tr { 416 | background: #F0F5F9; 417 | } 418 | 419 | .docutils tr + tr { 420 | background: #FFFFFF; 421 | } 422 | 423 | .docutils tr + tr + tr { 424 | background: #F0F5F9; 425 | } 426 | 427 | .docutils tr + tr + tr + tr { 428 | background: #FFFFFF; 429 | } 430 | 431 | .docutils tr + tr + tr +tr + tr { 432 | background: #F0F5F9; 433 | } 434 | 435 | .docutils tr + tr + tr + tr + tr + tr { 436 | background: #FFFFFF; 437 | } 438 | 439 | .docutils tr + tr + tr + tr + tr + tr + tr { 440 | background: #F0F5F9; 441 | } 442 | 443 | table.footnote td, table.footnote th { 444 | border: 0 !important; 445 | } 446 | 447 | th { 448 | text-align: left; 449 | padding-right: 5px; 450 | } 451 | 452 | /* -- other body styles ----------------------------------------------------- */ 453 | 454 | dl { 455 | margin-bottom: 15px; 456 | } 457 | 458 | dd p { 459 | margin-top: 0px; 460 | font-size: 12px; 461 | } 462 | 463 | dd ul, dd table { 464 | margin-bottom: 10px; 465 | } 466 | 467 | dd { 468 | margin-top: 3px; 469 | margin-bottom: 10px; 470 | margin-left: 30px; 471 | font-size: 12px; 472 | } 473 | 474 | dt:target, .highlight { 475 | background-color: #fbe54e; 476 | } 477 | 478 | dl.glossary dt { 479 | font-weight: bold; 480 | font-size: 0.8em; 481 | } 482 | 483 | dl.glossary dd { 484 | font-size:12px; 485 | } 486 | .field-list ul { 487 | vertical-align: top; 488 | margin: 0; 489 | padding-bottom: 0; 490 | list-style: none inside; 491 | } 492 | 493 | .field-list ul li { 494 | margin-top: 0; 495 | } 496 | 497 | .field-list p { 498 | margin: 0; 499 | } 500 | 501 | .refcount { 502 | color: #060; 503 | } 504 | 505 | .optional { 506 | font-size: 1.3em; 507 | } 508 | 509 | .versionmodified { 510 | font-style: italic; 511 | } 512 | 513 | .system-message { 514 | background-color: #fda; 515 | padding: 5px; 516 | border: 3px solid red; 517 | } 518 | 519 | .footnote:target { 520 | background-color: #ffa 521 | } 522 | 523 | /* -- code displays --------------------------------------------------------- */ 524 | 525 | pre { 526 | overflow: auto; 527 | background-color:#F1F5F9; 528 | border:1px solid #C9D1D7; 529 | border-spacing:0; 530 | font-family:"Bitstream Vera Sans Mono",Monaco,"Lucida Console",Courier,Consolas,monospace; 531 | font-size:11px; 532 | padding: 10px; 533 | } 534 | 535 | td.linenos pre { 536 | padding: 5px 0px; 537 | border: 0; 538 | background-color: transparent; 539 | color: #aaa; 540 | } 541 | 542 | table.highlighttable { 543 | margin-left: 0.5em; 544 | } 545 | 546 | table.highlighttable td { 547 | padding: 0 0.5em 0 0.5em; 548 | } 549 | 550 | tt { 551 | font-family:"Bitstream Vera Sans Mono",Monaco,"Lucida Console",Courier,Consolas,monospace; 552 | 553 | } 554 | 555 | tt.descname { 556 | background-color: transparent; 557 | font-weight: bold; 558 | font-size: 1em; 559 | } 560 | 561 | tt.descclassname { 562 | background-color: transparent; 563 | } 564 | 565 | tt.xref, a tt { 566 | background-color: transparent; 567 | font-weight: bold; 568 | } 569 | 570 | h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { 571 | background-color: transparent; 572 | } 573 | 574 | /* -- math display ---------------------------------------------------------- */ 575 | 576 | img.math { 577 | vertical-align: middle; 578 | } 579 | 580 | div.body div.math p { 581 | text-align: center; 582 | } 583 | 584 | span.eqno { 585 | float: right; 586 | } 587 | 588 | /* -- printout stylesheet --------------------------------------------------- */ 589 | 590 | @media print { 591 | div.document, 592 | div.documentwrapper, 593 | div.bodywrapper { 594 | margin: 0; 595 | width: 100%; 596 | } 597 | 598 | div.sphinxsidebar, 599 | div.related, 600 | div.footer, 601 | #top-link { 602 | display: none; 603 | } 604 | } 605 | 606 | body { 607 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 608 | } 609 | 610 | dl.class dt { 611 | padding: 3px; 612 | /* border-top: 2px solid #999;*/ 613 | } 614 | 615 | em.property { 616 | font-style: normal; 617 | } 618 | 619 | dl.class dd p { 620 | margin-top: 6px; 621 | } 622 | 623 | dl.class dd dl.exception dt { 624 | padding: 3px; 625 | background-color: #FFD6D6; 626 | border-top: none; 627 | } 628 | 629 | dl.class dd dl.method dt { 630 | padding: 3px; 631 | background-color: #e9e9e9; 632 | border-top: none; 633 | 634 | } 635 | 636 | dl.function dt { 637 | padding: 3px; 638 | border-top: 2px solid #999; 639 | } 640 | 641 | ul { 642 | list-style-image:none; 643 | list-style-position:outside; 644 | list-style-type:square; 645 | margin:0 0 0 30px; 646 | padding:0 0 12px 6px; 647 | } 648 | #docstitle { 649 | height: 36px; 650 | background-image: url(header_sm_mid.png); 651 | left: 0; 652 | top: 0; 653 | position: absolute; 654 | width: 100%; 655 | } 656 | #docstitle p { 657 | padding:7px 0 0 45px; 658 | margin: 0; 659 | color: white; 660 | text-shadow:0 1px 0 #787878; 661 | background: transparent url(documentation.png) no-repeat scroll 10px 3px; 662 | height: 36px; 663 | font-size: 15px; 664 | } 665 | #header { 666 | height:45px; 667 | left:0; 668 | position:absolute; 669 | right:0; 670 | top:36px; 671 | z-index:900; 672 | } 673 | 674 | #header h1 { 675 | font-size:10pt; 676 | margin:0; 677 | padding:5px 0 0 10px; 678 | text-shadow:0 1px 0 #D5D5D5; 679 | white-space:nowrap; 680 | } 681 | 682 | h1 { 683 | -x-system-font:none; 684 | color:#000000; 685 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 686 | font-size:30px; 687 | font-size-adjust:none; 688 | font-stretch:normal; 689 | font-style:normal; 690 | font-variant:normal; 691 | font-weight:bold; 692 | line-height:normal; 693 | margin-bottom:25px; 694 | margin-top:1em; 695 | } 696 | 697 | .footer { 698 | border-top:1px solid #DDDDDD; 699 | clear:both; 700 | padding-top:9px; 701 | width:100%; 702 | font-size:10px; 703 | } 704 | 705 | p { 706 | -x-system-font:none; 707 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 708 | font-size:12px; 709 | font-size-adjust:none; 710 | font-stretch:normal; 711 | font-style:normal; 712 | font-variant:normal; 713 | font-weight:normal; 714 | line-height:normal; 715 | margin-bottom:10px; 716 | margin-top:0; 717 | } 718 | 719 | h2 { 720 | border-bottom:1px solid #919699; 721 | color:#000000; 722 | font-size:24px; 723 | margin-top:2.5em; 724 | padding-bottom:2px; 725 | } 726 | 727 | a:link:hover { 728 | color:#093D92; 729 | text-decoration:underline; 730 | } 731 | 732 | a:link { 733 | color:#093D92; 734 | text-decoration:none; 735 | } 736 | 737 | 738 | ol { 739 | list-style-position:outside; 740 | list-style-type:decimal; 741 | margin:0 0 0 30px; 742 | padding:0 0 12px 6px; 743 | } 744 | li { 745 | margin-top:7px; 746 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 747 | font-size:12px; 748 | font-size-adjust:none; 749 | font-stretch:normal; 750 | font-style:normal; 751 | font-variant:normal; 752 | font-weight:normal; 753 | line-height:normal; 754 | } 755 | li > p { 756 | display:inline; 757 | } 758 | li p { 759 | margin-top:8px; 760 | } -------------------------------------------------------------------------------- /docs/_static/default.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Sphinx stylesheet -- basic theme 3 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | */ 5 | h3 { 6 | color:#000000; 7 | font-size: 17px; 8 | margin-bottom:0.5em; 9 | margin-top:2em; 10 | } 11 | /* -- main layout ----------------------------------------------------------- */ 12 | 13 | div.clearer { 14 | clear: both; 15 | } 16 | 17 | /* -- header ---------------------------------------------------------------- */ 18 | 19 | #header #title { 20 | background:#29334F url(title_background.png) repeat-x scroll 0 0; 21 | border-bottom:1px solid #B6B6B6; 22 | height:25px; 23 | overflow:hidden; 24 | } 25 | #headerButtons { 26 | position: absolute; 27 | list-style: none outside; 28 | top: 26px; 29 | left: 0px; 30 | right: 0px; 31 | margin: 0px; 32 | padding: 0px; 33 | border-top: 1px solid #2B334F; 34 | border-bottom: 1px solid #EDEDED; 35 | height: 20px; 36 | font-size: 8pt; 37 | overflow: hidden; 38 | background-color: #D8D8D8; 39 | } 40 | 41 | #headerButtons li { 42 | background-repeat:no-repeat; 43 | display:inline; 44 | margin-top:0; 45 | padding:0; 46 | } 47 | 48 | .headerButton { 49 | display: inline; 50 | height:20px; 51 | } 52 | 53 | .headerButton a { 54 | text-decoration: none; 55 | float: right; 56 | height: 20px; 57 | padding: 4px 15px; 58 | border-left: 1px solid #ACACAC; 59 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 60 | color: black; 61 | } 62 | .headerButton a:hover { 63 | color: white; 64 | background-color: #787878; 65 | 66 | } 67 | 68 | li#toc_button { 69 | text-align:left; 70 | } 71 | 72 | li#toc_button .headerButton a { 73 | width:198px; 74 | padding-top: 4px; 75 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 76 | color: black; 77 | float: left; 78 | padding-left:15px; 79 | border-right:1px solid #ACACAC; 80 | background:transparent url(triangle_open.png) no-repeat scroll 4px 6px; 81 | } 82 | 83 | li#toc_button .headerButton a:hover { 84 | background-color: #787878; 85 | color: white; 86 | } 87 | 88 | li#page_buttons { 89 | position:absolute; 90 | right:0; 91 | } 92 | 93 | #breadcrumbs { 94 | color: black; 95 | background-image:url(breadcrumb_background.png); 96 | border-top:1px solid #2B334F; 97 | bottom:0; 98 | font-size:10px; 99 | height:15px; 100 | left:0; 101 | overflow:hidden; 102 | padding:3px 10px 0; 103 | position:absolute; 104 | right:0; 105 | white-space:nowrap; 106 | z-index:901; 107 | } 108 | #breadcrumbs a { 109 | color: black; 110 | text-decoration: none; 111 | } 112 | #breadcrumbs a:hover { 113 | text-decoration: underline; 114 | } 115 | #breadcrumbs img { 116 | padding-left: 3px; 117 | } 118 | /* -- sidebar --------------------------------------------------------------- */ 119 | #sphinxsidebar { 120 | position: absolute; 121 | top: 84px; 122 | bottom: 19px; 123 | left: 0px; 124 | width: 229px; 125 | background-color: #E4EBF7; 126 | border-right: 1px solid #ACACAC; 127 | border-top: 1px solid #2B334F; 128 | overflow-x: hidden; 129 | overflow-y: auto; 130 | padding: 0px 0px 0px 0px; 131 | font-size:11px; 132 | } 133 | 134 | div.sphinxsidebarwrapper { 135 | padding: 10px 5px 0 10px; 136 | } 137 | 138 | #sphinxsidebar li { 139 | margin: 0px; 140 | padding: 0px; 141 | font-weight: normal; 142 | margin: 0px 0px 7px 0px; 143 | overflow: hidden; 144 | text-overflow: ellipsis; 145 | font-size: 11px; 146 | } 147 | 148 | #sphinxsidebar ul { 149 | list-style: none; 150 | margin: 0px 0px 0px 0px; 151 | padding: 0px 5px 0px 5px; 152 | } 153 | 154 | #sphinxsidebar ul ul, 155 | #sphinxsidebar ul.want-points { 156 | list-style: square; 157 | } 158 | 159 | #sphinxsidebar ul ul { 160 | margin-top: 0; 161 | margin-bottom: 0; 162 | } 163 | 164 | #sphinxsidebar form { 165 | margin-top: 10px; 166 | } 167 | 168 | #sphinxsidebar input { 169 | border: 1px solid #787878; 170 | font-family: sans-serif; 171 | font-size: 1em; 172 | } 173 | 174 | img { 175 | border: 0; 176 | } 177 | 178 | #sphinxsidebar li.toctree-l1 a { 179 | font-weight: bold; 180 | color: #000; 181 | text-decoration: none; 182 | } 183 | 184 | #sphinxsidebar li.toctree-l2 a { 185 | font-weight: bold; 186 | color: #4f4f4f; 187 | text-decoration: none; 188 | } 189 | 190 | /* -- search page ----------------------------------------------------------- */ 191 | 192 | ul.search { 193 | margin: 10px 0 0 20px; 194 | padding: 0; 195 | } 196 | 197 | ul.search li { 198 | padding: 5px 0 5px 20px; 199 | background-image: url(file.png); 200 | background-repeat: no-repeat; 201 | background-position: 0 7px; 202 | } 203 | 204 | ul.search li a { 205 | font-weight: bold; 206 | } 207 | 208 | ul.search li div.context { 209 | color: #888; 210 | margin: 2px 0 0 30px; 211 | text-align: left; 212 | } 213 | 214 | ul.keywordmatches li.goodmatch a { 215 | font-weight: bold; 216 | } 217 | #sphinxsidebar input.prettysearch {border:none;} 218 | input.searchbutton { 219 | float: right; 220 | } 221 | .search-wrapper {width: 100%; height: 25px;} 222 | .search-wrapper input.prettysearch { border: none; width:200px; height: 16px; background: url(searchfield_repeat.png) center top repeat-x; border: 0px; margin: 0; padding: 3px 0 0 0; font: 11px "Lucida Grande", "Lucida Sans Unicode", Arial, sans-serif; } 223 | .search-wrapper input.prettysearch { width: 184px; margin-left: 20px; *margin-top:-1px; *margin-right:-2px; *margin-left:10px; } 224 | .search-wrapper .search-left { display: block; position: absolute; width: 20px; height: 19px; background: url(searchfield_leftcap.png) left top no-repeat; } 225 | .search-wrapper .search-right { display: block; position: relative; left: 204px; top: -19px; width: 10px; height: 19px; background: url(searchfield_rightcap.png) right top no-repeat; } 226 | 227 | /* -- index page ------------------------------------------------------------ */ 228 | 229 | table.contentstable { 230 | width: 90%; 231 | } 232 | 233 | table.contentstable p.biglink { 234 | line-height: 150%; 235 | } 236 | 237 | a.biglink { 238 | font-size: 1.3em; 239 | } 240 | 241 | span.linkdescr { 242 | font-style: italic; 243 | padding-top: 5px; 244 | font-size: 90%; 245 | } 246 | 247 | /* -- general index --------------------------------------------------------- */ 248 | 249 | table.indextable td { 250 | text-align: left; 251 | vertical-align: top; 252 | } 253 | 254 | table.indextable dl, table.indextable dd { 255 | margin-top: 0; 256 | margin-bottom: 0; 257 | } 258 | 259 | table.indextable tr.pcap { 260 | height: 10px; 261 | } 262 | 263 | table.indextable tr.cap { 264 | margin-top: 10px; 265 | background-color: #f2f2f2; 266 | } 267 | 268 | img.toggler { 269 | margin-right: 3px; 270 | margin-top: 3px; 271 | cursor: pointer; 272 | } 273 | 274 | /* -- general body styles --------------------------------------------------- */ 275 | .document { 276 | border-top:1px solid #2B334F; 277 | overflow:auto; 278 | padding-left:2em; 279 | padding-right:2em; 280 | position:absolute; 281 | z-index:1; 282 | top:84px; 283 | bottom:19px; 284 | right:0; 285 | left:230px; 286 | } 287 | 288 | a.headerlink { 289 | visibility: hidden; 290 | } 291 | 292 | h1:hover > a.headerlink, 293 | h2:hover > a.headerlink, 294 | h3:hover > a.headerlink, 295 | h4:hover > a.headerlink, 296 | h5:hover > a.headerlink, 297 | h6:hover > a.headerlink, 298 | dt:hover > a.headerlink { 299 | visibility: visible; 300 | } 301 | 302 | div.body p.caption { 303 | text-align: inherit; 304 | } 305 | 306 | div.body td { 307 | text-align: left; 308 | } 309 | 310 | .field-list ul { 311 | padding-left: 1em; 312 | } 313 | 314 | .first { 315 | margin-top: 0 !important; 316 | } 317 | 318 | p.rubric { 319 | margin-top: 30px; 320 | font-weight: bold; 321 | } 322 | 323 | /* -- sidebars -------------------------------------------------------------- */ 324 | 325 | /*div.sidebar { 326 | margin: 0 0 0.5em 1em; 327 | border: 1px solid #ddb; 328 | padding: 7px 7px 0 7px; 329 | background-color: #ffe; 330 | width: 40%; 331 | float: right; 332 | } 333 | 334 | p.sidebar-title { 335 | font-weight: bold; 336 | } 337 | */ 338 | /* -- topics ---------------------------------------------------------------- */ 339 | 340 | div.topic { 341 | border: 1px solid #ccc; 342 | padding: 7px 7px 0 7px; 343 | margin: 10px 0 10px 0; 344 | } 345 | 346 | p.topic-title { 347 | font-size: 1.1em; 348 | font-weight: bold; 349 | margin-top: 10px; 350 | } 351 | 352 | /* -- admonitions ----------------------------------------------------------- */ 353 | .admonition { 354 | border: 1px solid #a1a5a9; 355 | background-color: #f7f7f7; 356 | margin: 20px; 357 | padding: 0px 8px 7px 9px; 358 | text-align: left; 359 | } 360 | .warning { 361 | background-color:#E8E8E8; 362 | border:1px solid #111111; 363 | margin:30px; 364 | } 365 | .admonition p { 366 | font: 12px 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; 367 | margin-top: 7px; 368 | margin-bottom: 0px; 369 | } 370 | 371 | div.admonition dt { 372 | font-weight: bold; 373 | } 374 | 375 | div.admonition dl { 376 | margin-bottom: 0; 377 | } 378 | 379 | p.admonition-title { 380 | margin: 0px 10px 5px 0px; 381 | font-weight: bold; 382 | padding-top: 3px; 383 | } 384 | 385 | div.body p.centered { 386 | text-align: center; 387 | margin-top: 25px; 388 | } 389 | 390 | /* -- tables ---------------------------------------------------------------- */ 391 | 392 | table.docutils { 393 | border-collapse: collapse; 394 | border-top: 1px solid #919699; 395 | border-left: 1px solid #919699; 396 | border-right: 1px solid #919699; 397 | font-size:12px; 398 | padding:8px; 399 | text-align:left; 400 | vertical-align:top; 401 | } 402 | 403 | table.docutils td, table.docutils th { 404 | padding: 8px; 405 | font-size: 12px; 406 | text-align: left; 407 | vertical-align: top; 408 | border-bottom: 1px solid #919699; 409 | } 410 | 411 | table.docutils th { 412 | font-weight: bold; 413 | } 414 | /* This alternates colors in up to six table rows (light blue for odd, white for even)*/ 415 | .docutils tr { 416 | background: #F0F5F9; 417 | } 418 | 419 | .docutils tr + tr { 420 | background: #FFFFFF; 421 | } 422 | 423 | .docutils tr + tr + tr { 424 | background: #F0F5F9; 425 | } 426 | 427 | .docutils tr + tr + tr + tr { 428 | background: #FFFFFF; 429 | } 430 | 431 | .docutils tr + tr + tr +tr + tr { 432 | background: #F0F5F9; 433 | } 434 | 435 | .docutils tr + tr + tr + tr + tr + tr { 436 | background: #FFFFFF; 437 | } 438 | 439 | .docutils tr + tr + tr + tr + tr + tr + tr { 440 | background: #F0F5F9; 441 | } 442 | 443 | table.footnote td, table.footnote th { 444 | border: 0 !important; 445 | } 446 | 447 | th { 448 | text-align: left; 449 | padding-right: 5px; 450 | } 451 | 452 | /* -- other body styles ----------------------------------------------------- */ 453 | 454 | dl { 455 | margin-bottom: 15px; 456 | } 457 | 458 | dd p { 459 | margin-top: 0px; 460 | font-size: 12px; 461 | } 462 | 463 | dd ul, dd table { 464 | margin-bottom: 10px; 465 | } 466 | 467 | dd { 468 | margin-top: 3px; 469 | margin-bottom: 10px; 470 | margin-left: 30px; 471 | font-size: 12px; 472 | } 473 | 474 | dt:target, .highlight { 475 | background-color: #fbe54e; 476 | } 477 | 478 | dl.glossary dt { 479 | font-weight: bold; 480 | font-size: 0.8em; 481 | } 482 | 483 | dl.glossary dd { 484 | font-size:12px; 485 | } 486 | .field-list ul { 487 | vertical-align: top; 488 | margin: 0; 489 | padding-bottom: 0; 490 | list-style: none inside; 491 | } 492 | 493 | .field-list ul li { 494 | margin-top: 0; 495 | } 496 | 497 | .field-list p { 498 | margin: 0; 499 | } 500 | 501 | .refcount { 502 | color: #060; 503 | } 504 | 505 | .optional { 506 | font-size: 1.3em; 507 | } 508 | 509 | .versionmodified { 510 | font-style: italic; 511 | } 512 | 513 | .system-message { 514 | background-color: #fda; 515 | padding: 5px; 516 | border: 3px solid red; 517 | } 518 | 519 | .footnote:target { 520 | background-color: #ffa 521 | } 522 | 523 | /* -- code displays --------------------------------------------------------- */ 524 | 525 | pre { 526 | overflow: auto; 527 | background-color:#F1F5F9; 528 | border:1px solid #C9D1D7; 529 | border-spacing:0; 530 | font-family:"Bitstream Vera Sans Mono",Monaco,"Lucida Console",Courier,Consolas,monospace; 531 | font-size:11px; 532 | padding: 10px; 533 | } 534 | 535 | td.linenos pre { 536 | padding: 5px 0px; 537 | border: 0; 538 | background-color: transparent; 539 | color: #aaa; 540 | } 541 | 542 | table.highlighttable { 543 | margin-left: 0.5em; 544 | } 545 | 546 | table.highlighttable td { 547 | padding: 0 0.5em 0 0.5em; 548 | } 549 | 550 | tt { 551 | font-family:"Bitstream Vera Sans Mono",Monaco,"Lucida Console",Courier,Consolas,monospace; 552 | 553 | } 554 | 555 | tt.descname { 556 | background-color: transparent; 557 | font-weight: bold; 558 | font-size: 1em; 559 | } 560 | 561 | tt.descclassname { 562 | background-color: transparent; 563 | } 564 | 565 | tt.xref, a tt { 566 | background-color: transparent; 567 | font-weight: bold; 568 | } 569 | 570 | h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { 571 | background-color: transparent; 572 | } 573 | 574 | /* -- math display ---------------------------------------------------------- */ 575 | 576 | img.math { 577 | vertical-align: middle; 578 | } 579 | 580 | div.body div.math p { 581 | text-align: center; 582 | } 583 | 584 | span.eqno { 585 | float: right; 586 | } 587 | 588 | /* -- printout stylesheet --------------------------------------------------- */ 589 | 590 | @media print { 591 | div.document, 592 | div.documentwrapper, 593 | div.bodywrapper { 594 | margin: 0; 595 | width: 100%; 596 | } 597 | 598 | div.sphinxsidebar, 599 | div.related, 600 | div.footer, 601 | #top-link { 602 | display: none; 603 | } 604 | } 605 | 606 | body { 607 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 608 | } 609 | 610 | dl.class dt { 611 | padding: 3px; 612 | /* border-top: 2px solid #999;*/ 613 | } 614 | 615 | em.property { 616 | font-style: normal; 617 | } 618 | 619 | dl.class dd p { 620 | margin-top: 6px; 621 | } 622 | 623 | dl.class dd dl.exception dt { 624 | padding: 3px; 625 | background-color: #FFD6D6; 626 | border-top: none; 627 | } 628 | 629 | dl.class dd dl.method dt { 630 | padding: 3px; 631 | background-color: #e9e9e9; 632 | border-top: none; 633 | 634 | } 635 | 636 | dl.function dt { 637 | padding: 3px; 638 | border-top: 2px solid #999; 639 | } 640 | 641 | ul { 642 | list-style-image:none; 643 | list-style-position:outside; 644 | list-style-type:square; 645 | margin:0 0 0 30px; 646 | padding:0 0 12px 6px; 647 | } 648 | #docstitle { 649 | height: 36px; 650 | background-image: url(header_sm_mid.png); 651 | left: 0; 652 | top: 0; 653 | position: absolute; 654 | width: 100%; 655 | } 656 | #docstitle p { 657 | padding:7px 0 0 45px; 658 | margin: 0; 659 | color: white; 660 | text-shadow:0 1px 0 #787878; 661 | background: transparent url(documentation.png) no-repeat scroll 10px 3px; 662 | height: 36px; 663 | font-size: 15px; 664 | } 665 | #header { 666 | height:45px; 667 | left:0; 668 | position:absolute; 669 | right:0; 670 | top:36px; 671 | z-index:900; 672 | } 673 | 674 | #header h1 { 675 | font-size:10pt; 676 | margin:0; 677 | padding:5px 0 0 10px; 678 | text-shadow:0 1px 0 #D5D5D5; 679 | white-space:nowrap; 680 | } 681 | 682 | h1 { 683 | -x-system-font:none; 684 | color:#000000; 685 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 686 | font-size:30px; 687 | font-size-adjust:none; 688 | font-stretch:normal; 689 | font-style:normal; 690 | font-variant:normal; 691 | font-weight:bold; 692 | line-height:normal; 693 | margin-bottom:25px; 694 | margin-top:1em; 695 | } 696 | 697 | .footer { 698 | border-top:1px solid #DDDDDD; 699 | clear:both; 700 | padding-top:9px; 701 | width:100%; 702 | font-size:10px; 703 | } 704 | 705 | p { 706 | -x-system-font:none; 707 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 708 | font-size:12px; 709 | font-size-adjust:none; 710 | font-stretch:normal; 711 | font-style:normal; 712 | font-variant:normal; 713 | font-weight:normal; 714 | line-height:normal; 715 | margin-bottom:10px; 716 | margin-top:0; 717 | } 718 | 719 | h2 { 720 | border-bottom:1px solid #919699; 721 | color:#000000; 722 | font-size:24px; 723 | margin-top:2.5em; 724 | padding-bottom:2px; 725 | } 726 | 727 | a:link:hover { 728 | color:#093D92; 729 | text-decoration:underline; 730 | } 731 | 732 | a:link { 733 | color:#093D92; 734 | text-decoration:none; 735 | } 736 | 737 | 738 | ol { 739 | list-style-position:outside; 740 | list-style-type:decimal; 741 | margin:0 0 0 30px; 742 | padding:0 0 12px 6px; 743 | } 744 | li { 745 | margin-top:7px; 746 | font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 747 | font-size:12px; 748 | font-size-adjust:none; 749 | font-stretch:normal; 750 | font-style:normal; 751 | font-variant:normal; 752 | font-weight:normal; 753 | line-height:normal; 754 | } 755 | li > p { 756 | display:inline; 757 | } 758 | li p { 759 | margin-top:8px; 760 | } --------------------------------------------------------------------------------