├── .github └── workflows │ └── install.yml ├── .gitignore ├── .hgignore ├── CHANGELOG.rst ├── COPYRIGHT.txt ├── MANIFEST.in ├── README.txt ├── RELEASE_PROCESS.txt ├── bootstrap.py ├── buildout-py3k.cfg ├── buildout.cfg ├── docs ├── FormAlchemy.pdf ├── Makefile ├── __init__.py ├── conf.py ├── config.txt ├── customisation.txt ├── doc.txt ├── example-grid.png ├── example.png ├── ext │ ├── admin-listing.png │ ├── admin-models.png │ ├── admin-new.png │ ├── couchdb.txt │ ├── fsblob.txt │ ├── pylons.txt │ ├── rdf.txt │ └── zope.txt ├── fields.txt ├── formalchemy.txt ├── forms.txt ├── index.txt ├── internationalisation.txt ├── models.txt ├── pylons_sample.txt ├── tables.txt ├── templates.txt └── validators.txt ├── formalchemy ├── __init__.py ├── base.py ├── config.py ├── exceptions.py ├── ext │ ├── __init__.py │ ├── couchdb.py │ ├── fsblob.py │ ├── pylons │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── admin_edit.mako │ │ ├── admin_index.mako │ │ ├── admin_list.mako │ │ ├── base.mako │ │ ├── controller.py │ │ ├── maps.py │ │ ├── pastertemplate.py │ │ └── resources │ │ │ ├── add.png │ │ │ ├── admin.css │ │ │ ├── delete.png │ │ │ └── edit.png │ ├── rdf.py │ └── zope │ │ └── __init__.py ├── fatypes.py ├── fields.py ├── fieldset.tmpl ├── fieldset_readonly.tmpl ├── forms.py ├── grid.tmpl ├── grid_readonly.tmpl ├── helpers.py ├── i18n.py ├── i18n_resources │ ├── de │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── en │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── es │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── fa │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── formalchemy.pot │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── hu │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── ja │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ └── formalchemy.po │ └── ru │ │ └── LC_MESSAGES │ │ └── formalchemy.po ├── msgfmt.py ├── multidict.py ├── paster_templates │ └── pylons_fa │ │ └── +package+ │ │ ├── config │ │ └── routing.py_tmpl │ │ ├── controllers │ │ └── admin.py_tmpl │ │ ├── forms │ │ └── __init__.py_tmpl │ │ └── templates │ │ └── forms │ │ ├── fieldset.mako_tmpl │ │ ├── fieldset_readonly.mako_tmpl │ │ ├── grid.mako_tmpl │ │ ├── grid_readonly.mako_tmpl │ │ └── restfieldset.mako_tmpl ├── tables.py ├── templates.py ├── tests │ ├── __init__.py │ ├── data │ │ ├── genshi │ │ │ └── fieldset.html │ │ └── mako │ │ │ └── fieldset.mako │ ├── fa_cgi.py │ ├── fake_module.py │ ├── test_aliases.py │ ├── test_binary.py │ ├── test_column.py │ ├── test_dates.py │ ├── test_fieldset.py │ ├── test_fieldset_api.py │ ├── test_fsblob.py │ ├── test_html5.py │ ├── test_json.py │ ├── test_manual.py │ ├── test_misc.py │ ├── test_multiple_keys.py │ ├── test_options.py │ ├── test_readonly.py │ ├── test_request.py │ ├── test_tables.py │ ├── test_unicode.py │ ├── test_validate.py │ └── test_validators.py └── validators.py ├── pylonsapp ├── MANIFEST.in ├── README.txt ├── development.ini ├── docs │ └── index.txt ├── ez_setup.py ├── performance_test.py ├── pylonsapp │ ├── __init__.py │ ├── _design │ │ ├── person │ │ │ └── views │ │ │ │ └── all │ │ │ │ └── map.js │ │ └── pet │ │ │ └── views │ │ │ └── all │ │ │ └── map.js │ ├── config │ │ ├── __init__.py │ │ ├── deployment.ini_tmpl │ │ ├── environment.py │ │ ├── middleware.py │ │ └── routing.py │ ├── controllers │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── basic.py │ │ ├── couchdb.py │ │ ├── error.py │ │ ├── fsblob.py │ │ └── owners.py │ ├── forms │ │ ├── __init__.py │ │ └── fsblob.py │ ├── lib │ │ ├── __init__.py │ │ ├── app_globals.py │ │ ├── base.py │ │ └── helpers.py │ ├── model │ │ ├── __init__.py │ │ └── meta.py │ ├── public │ │ ├── bg.png │ │ ├── favicon.ico │ │ ├── index.html │ │ └── pylons-logo.gif │ ├── templates │ │ ├── form.mako │ │ └── forms │ │ │ ├── fieldset.mako │ │ │ ├── fieldset_readonly.mako │ │ │ ├── grid.mako │ │ │ ├── grid_readonly.mako │ │ │ └── restfieldset.mako │ ├── tests │ │ ├── __init__.py │ │ ├── functional │ │ │ ├── __init__.py │ │ │ ├── test_admin.py │ │ │ ├── test_basic.py │ │ │ ├── test_couchdb.py │ │ │ ├── test_fsblob.py │ │ │ └── test_owners.py │ │ └── test_models.py │ └── websetup.py ├── setup.cfg ├── setup.py └── test.ini ├── setup.cfg ├── setup.py ├── sphinx.ini └── tempitification.sh /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Install package 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | python-version: [ 15 | "2.7", 16 | "3.6", 17 | "3.7", 18 | "3.8", 19 | "3.9", 20 | ] 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Get pip cache dir 30 | id: pip-cache 31 | run: | 32 | echo "::set-output name=dir::$(pip cache dir)" 33 | - name: Cache 34 | uses: actions/cache@v2 35 | with: 36 | path: ${{ steps.pip-cache.outputs.dir }} 37 | key: 38 | ${{ matrix.os }}-${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }} 39 | restore-keys: | 40 | ${{ matrix.os }}-${{ matrix.python-version }}-v1- 41 | - name: Install dependencies 42 | run: | 43 | python -m pip install -U pip 44 | python -m pip install -e . 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.log 3 | *.mo 4 | *.pt.py 5 | *.pyc 6 | *.pyo 7 | *.swp 8 | .coverage 9 | .eggs 10 | .installed.cfg 11 | Paste*.egg 12 | bin 13 | build 14 | develop-eggs 15 | dist 16 | docs/_build 17 | eggs 18 | parts 19 | pylonsapp/data/ 20 | pylonsapp/development.db 21 | pylonsapp/pylonsapp/public/*/*/*/*.* 22 | pylonsapp/pylonsapp/public/*/*/*/*.txt 23 | pyramid/ 24 | pyramidapp/*.db 25 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | bin 4 | dist 5 | build 6 | eggs 7 | parts 8 | develop-eggs 9 | Paste*.egg 10 | docs/_build 11 | pyramid/ 12 | pyramidapp/*.db 13 | pylonsapp/data/ 14 | pylonsapp/development.db 15 | pylonsapp/pylonsapp/public/*/*/*/*.txt 16 | .installed.cfg 17 | .coverage 18 | *.pt.py 19 | *.egg-info 20 | *.swp 21 | *.pyc 22 | *.pyo 23 | *.log 24 | 25 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2007 Alexandre Conrad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | exclude *.cfg 2 | exclude *.ini 3 | exclude RELEASE_PROCESS.txt 4 | exclude bootstrap.py 5 | exclude docs/*.py 6 | exclude docs/Makefile 7 | include *.sh 8 | include *.txt 9 | include *.rst 10 | include docs/*.txt 11 | prune pylonsapp 12 | recursive-include docs *.pdf 13 | recursive-include docs *.png 14 | recursive-include docs *.txt 15 | recursive-include formalchemy *.py 16 | recursive-include formalchemy *css 17 | recursive-include formalchemy *mako 18 | recursive-include formalchemy *png 19 | recursive-include formalchemy *pt 20 | recursive-include formalchemy *tmpl 21 | recursive-include formalchemy/i18n_resources * 22 | recursive-include formalchemy/tests/data * 23 | 24 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | FormAlchemy eliminates boilerplate by autogenerating HTML input fields from a 2 | given model. FormAlchemy will try to figure out what kind of HTML code should 3 | be returned by introspecting the model's properties and generate ready-to-use 4 | HTML code that will fit the developer's application. 5 | 6 | Of course, FormAlchemy can't figure out everything, i.e, the developer might 7 | want to display only a few columns from the given model. Thus, FormAlchemy is 8 | also highly customizable. 9 | 10 | Please visit the FormAlchemy's homepage for documentation and more information: 11 | 12 | http://docs.formalchemy.org/ 13 | 14 | You can subscribe to FormAlchemy's mailing list: 15 | 16 | http://groups.google.com/group/formalchemy 17 | -------------------------------------------------------------------------------- /RELEASE_PROCESS.txt: -------------------------------------------------------------------------------- 1 | How to release FormAlchemy 2 | ========================== 3 | 4 | This assumes that the maintainance branch is 1.5 and we want to release 5 | version 1.5.3. 6 | 7 | Check out the release branch: 8 | 9 | $ git checkout v1.5 10 | 11 | Run tests:: 12 | 13 | $ nosetests 14 | $ nosetests3 15 | 16 | Make sure you have a clean working directory: 17 | 18 | $ git status 19 | 20 | Update version numbers: 21 | 22 | $ : remove the -dev suffix 23 | $ vi CHANGELOG.txt formalchemy/__init__.py 24 | $ git commit -a 25 | 26 | Tag the release: 27 | 28 | $ : use the tag to briefly describe this release's highlights 29 | $ git tag -s v1.5.3 30 | 31 | Compile .mo files, create the tarball, and push to PyPI:: 32 | 33 | $ python setup.py make_dist register upload 34 | 35 | Prepare for the next cycle: 36 | 37 | $ : increment version numbers. Add a -dev suffix! 38 | $ vi CHANGELOG.txt formalchemy/__init__.py 39 | $ git commit -a 40 | 41 | Merge the release into the master branch: 42 | 43 | $ git checkout master 44 | $ : use the release tag, NOT the release branch 45 | $ git merge v1.5.3 46 | $ : you will have conflicts in these files. Fix them. 47 | $ vi CHANGELOG.txt formalchemy/__init__.py 48 | $ git commit -a 49 | 50 | make sure that the master branch still works: 51 | 52 | $ nosetests 53 | $ nosetests3 54 | 55 | Finally, update git: 56 | 57 | $ git push --tags --all 58 | 59 | 60 | -------------------------------------------------------------------------------- /buildout-py3k.cfg: -------------------------------------------------------------------------------- 1 | [buildout] 2 | newest=false 3 | parts = test 4 | develop = . 5 | 6 | [test] 7 | recipe = zc.recipe.egg 8 | eggs = 9 | nose>=0.10.4 10 | coverage 11 | Babel 12 | Mako>=0.2.4 13 | SQLAlchemy>=0.6 14 | FormAlchemy 15 | lxml 16 | interpreter = python 17 | initialization = from formalchemy import tests 18 | scripts = 19 | nosetests=test 20 | ipython=ipython 21 | 22 | -------------------------------------------------------------------------------- /buildout.cfg: -------------------------------------------------------------------------------- 1 | [buildout] 2 | newest=false 3 | parts = test pylonsapp pylons_test sphinx 4 | develop = . pylonsapp 5 | versions = versions 6 | 7 | [versions] 8 | Pylons=1.0 9 | 10 | [test] 11 | recipe = zc.recipe.egg 12 | eggs = 13 | WebOb>1.1.9 14 | WebTest 15 | ipython<6.0 16 | couchdbkit 17 | zope.schema 18 | httplib2 19 | simplejson 20 | nose>=0.10.4 21 | coverage 22 | Babel 23 | Mako>=0.2.4 24 | genshi 25 | pysqlite>=2.5.5 26 | SQLAlchemy>=0.7 27 | # RDFAlchemy 28 | FormAlchemy 29 | BeautifulSoup 30 | traitlets<5.0 31 | Sphinx<2.0 32 | pygments<2.6 33 | interpreter = python 34 | initialization = from formalchemy import tests 35 | scripts = 36 | nosetests=test 37 | ipython=ipython 38 | 39 | [pylonsapp] 40 | recipe = zc.recipe.egg 41 | eggs = 42 | ${test:eggs} 43 | PasteScript 44 | Pylons 45 | repoze.profile 46 | interpreter = python_perf 47 | scripts = 48 | paster=paster_pylons 49 | 50 | [pylons_test] 51 | recipe = zc.recipe.egg 52 | initialization = import os; os.chdir('${buildout:directory}/pylonsapp/') 53 | eggs = 54 | ${pylonsapp:eggs} 55 | scripts = 56 | nosetests=test_pylons 57 | 58 | [sphinx] 59 | recipe = zc.recipe.egg 60 | eggs= 61 | ${pylons_test:eggs} 62 | PasteScript 63 | Sphinx<2.0 64 | scripts = 65 | sphinx-build 66 | sphinx-quickstart 67 | -------------------------------------------------------------------------------- /docs/FormAlchemy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/FormAlchemy.pdf -------------------------------------------------------------------------------- /docs/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 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 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 " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | 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 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyramid_formalchemy.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyramid_formalchemy.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pyramid_formalchemy" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyramid_formalchemy" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/__init__.py -------------------------------------------------------------------------------- /docs/config.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.config` -- Global configuration 2 | ================================================= 3 | 4 | .. automodule:: formalchemy.config 5 | -------------------------------------------------------------------------------- /docs/customisation.txt: -------------------------------------------------------------------------------- 1 | Other customizations 2 | ==================== 3 | 4 | Customization: CSS 5 | ------------------ 6 | 7 | `FormAlchemy` uses the following CSS classes: 8 | 9 | - `fieldset_error`: class for a div containing a "global" error 10 | 11 | - `field_error`: class for a span containing an error from a single `Field` 12 | 13 | - `field_req`: class for a label for a required field 14 | 15 | - `field_opt`: class for a label for an optional field 16 | 17 | - `field_readonly`: class for the td of the 'label' for a field in a readonly 18 | `FieldSet` table 19 | 20 | - `grid_error`: class for a span containing an error from a single `Field` in a 21 | `Grid` 22 | 23 | Here is some basic CSS for aligning your forms nicely:: 24 | 25 | label { 26 | float: left; 27 | text-align: right; 28 | margin-right: 1em; 29 | width: 10em; 30 | } 31 | 32 | form div { 33 | margin: 0.5em; 34 | float: left; 35 | width: 100%; 36 | } 37 | 38 | form input[type="submit"] { 39 | margin-top: 1em; 40 | margin-left: 9em; 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/doc.txt: -------------------------------------------------------------------------------- 1 | Modules contents 2 | ================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :glob: 7 | 8 | formalchemy 9 | models 10 | fields 11 | forms 12 | tables 13 | validators 14 | internationalisation 15 | config 16 | templates 17 | customisation 18 | pylons_sample 19 | ext/* 20 | 21 | 22 | Indices and tables 23 | ================== 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | 29 | -------------------------------------------------------------------------------- /docs/example-grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/example-grid.png -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/example.png -------------------------------------------------------------------------------- /docs/ext/admin-listing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/ext/admin-listing.png -------------------------------------------------------------------------------- /docs/ext/admin-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/ext/admin-models.png -------------------------------------------------------------------------------- /docs/ext/admin-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/docs/ext/admin-new.png -------------------------------------------------------------------------------- /docs/ext/couchdb.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.ext.couchdb` -- CouchDB support 2 | ************************************************** 3 | 4 | This module provides a subclass of :class:`~formalchemy.forms.FieldSet` to support couchdbkit_'s schema. 5 | 6 | .. _couchdbkit: http://couchdbkit.org/ 7 | 8 | Usage 9 | ===== 10 | 11 | .. automodule:: formalchemy.ext.couchdb 12 | 13 | Classes definitions 14 | =================== 15 | 16 | FieldSet 17 | -------- 18 | 19 | .. autoclass:: FieldSet 20 | 21 | Grid 22 | -------- 23 | 24 | .. autoclass:: Grid 25 | 26 | Session 27 | -------- 28 | 29 | .. autoclass:: Session 30 | :members: 31 | 32 | Query 33 | -------- 34 | 35 | .. autoclass:: Query 36 | :members: 37 | 38 | -------------------------------------------------------------------------------- /docs/ext/fsblob.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.ext.fsblob` -- File system renderer 2 | ===================================================== 3 | 4 | This extension is used to store binary files/images on filesystem and only 5 | store the file path in the database. 6 | 7 | This page present a Pylons integration but it should work on most framework. 8 | 9 | .. automodule:: formalchemy.ext.fsblob 10 | 11 | Renderers 12 | --------- 13 | 14 | .. autoclass:: FileFieldRenderer 15 | :members: 16 | 17 | .. autoclass:: ImageFieldRenderer 18 | :members: 19 | 20 | 21 | Usage 22 | ----- 23 | 24 | You must override the `storage_path` attribute: 25 | 26 | .. literalinclude:: ../../pylonsapp/pylonsapp/forms/fsblob.py 27 | 28 | As you can see, you can use it like all :mod:`~formalchemy.fields` in the 29 | `.configure` method. 30 | 31 | Validators 32 | ---------- 33 | 34 | Work like all :mod:`~formalchemy.validators`. 35 | 36 | .. autofunction:: file_extension 37 | 38 | >>> from formalchemy.ext.fsblob import file_extension 39 | >>> validator = file_extension(extensions=['txt']) 40 | >>> validator('my image.txt') 41 | 42 | >>> validator('my image.png') # doctest: +ELLIPSIS 43 | Traceback (most recent call last): 44 | ... 45 | ValidationError: Invalid file extension. Must be txt 46 | 47 | 48 | .. autofunction:: image_extension 49 | 50 | >>> from formalchemy.ext.fsblob import image_extension 51 | >>> validator = image_extension() 52 | >>> validator('my image.png') 53 | 54 | >>> validator('my image.txt') # doctest: +ELLIPSIS 55 | Traceback (most recent call last): 56 | ... 57 | ValidationError: Invalid image file. Must be jpeg, jpg, gif, png 58 | 59 | Sample app 60 | ---------- 61 | 62 | You can have a look at the complete `source 63 | `_ of 64 | the application used for FA's testing. 65 | 66 | -------------------------------------------------------------------------------- /docs/ext/pylons.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.ext.pylons` -- Pylons extensions 2 | ================================================== 3 | 4 | .. automodule:: formalchemy.ext.pylons.controller 5 | 6 | Administration interface 7 | ************************ 8 | 9 | Purpose 10 | ------- 11 | 12 | The Pylons administration interface provides a simple way to enable 13 | CRUD (create, retrieve, update, delete) operations on your SQLAlchemy 14 | models, with a high degree of customizability. 15 | 16 | Sample model listing: 17 | 18 | .. image:: admin-models.png 19 | 20 | Sample model overview page: 21 | 22 | .. image:: admin-listing.png 23 | 24 | Sample model creation page: 25 | 26 | .. image:: admin-new.png 27 | 28 | 29 | Setup 30 | ----- 31 | 32 | First, generate a controller in your application:: 33 | 34 | $ paster controller admin 35 | 36 | Next, edit your ``controllers/admin.py``, replacing ``pylonsapp`` with your 37 | application name: 38 | 39 | .. literalinclude:: ../../pylonsapp/pylonsapp/controllers/admin.py 40 | 41 | Now you need to configure your routing. As an example here is the 42 | ``routing.py`` used for testing the UI. Check ``fa_static`` and the ``/admin`` 43 | part: 44 | 45 | .. literalinclude:: ../../pylonsapp/pylonsapp/config/routing.py 46 | 47 | All done! Now you can go to the ``/admin/`` url. 48 | 49 | 50 | Customization 51 | ------------- 52 | 53 | :class:`~formalchemy.ext.pylons.controller.ModelsController` creates a new 54 | class having `AdminControllerBase` and the internal FA models controller 55 | (:class:`~formalchemy.ext.pylons.controller._ModelsController`) as its parent 56 | classes, in that order. 57 | 58 | So, you can do simple customization just by overriding the 59 | :class:`~formalchemy.ext.pylons.controller._ModelsController` methods in 60 | `AdminControllerBase`, e.g.,:: 61 | 62 | class AdminControllerBase(BaseController): 63 | ... 64 | 65 | @auth_required 66 | def edit(self, *args, **kwargs): 67 | return super(AdminControllerBase, self).edit(*args, **kwargs) 68 | 69 | To customize the forms used to list and edit your objects, 70 | create a module `yourapp.forms` and specify that 71 | as the forms module in AdminController. In this module, create 72 | :class:`~formalchemy.forms.FieldSet` (for create and edit forms) 73 | and :class:`~formalchemy.tables.Grid` (for object lists) 74 | instances for the models you wish to customize. (The `Grids` will 75 | automatically get edit and delete links added, and be made readonly.) 76 | 77 | See :mod:`~formalchemy.forms` for details on form configuration. 78 | 79 | API 80 | --- 81 | 82 | .. autofunction:: ModelsController 83 | 84 | .. autoclass:: _ModelsController 85 | 86 | See also :class:`~formalchemy.ext.pylons.controller._RESTController` 87 | 88 | **model** 89 | Models module or list of models 90 | 91 | **forms** 92 | Forms module or None 93 | 94 | **FieldSet** 95 | A FieldSet class. Default to :class:`~formalchemy.forms.FieldSet` 96 | 97 | **Grid** 98 | A Grid class. Default to :class:`~formalchemy.tables.Grid`` 99 | 100 | Troubleshooting 101 | --------------- 102 | 103 | If you don't see all your models on the top-level admin page, you'll 104 | need to import them into your model module, or tell `FormAlchemy` the 105 | correct module to look in (the "model = " line in the controller class 106 | you created). In particular, `FormAlchemy` does not recursively scan 107 | for models, so if you have models in e.g., `model/foo.py`, you will want 108 | to add `from foo import *` in `model/__init__.py`. 109 | 110 | Sample app 111 | ---------- 112 | 113 | You can have a look at the complete `source 114 | `_ of 115 | the application used for FA's testing. 116 | 117 | .. _restfulcontroller: 118 | 119 | RESTful controller 120 | ****************** 121 | 122 | This module provide a RESTful controller for formalchemy's FieldSets. 123 | 124 | You can use your fieldset as a REST resource. And yes, it also works with JSON. 125 | 126 | Usage 127 | ------ 128 | 129 | Use the FieldSetController to wrap your Pylons controller: 130 | 131 | .. autofunction:: formalchemy.ext.pylons.controller.RESTController 132 | 133 | .. literalinclude:: ../../pylonsapp/pylonsapp/controllers/owners.py 134 | 135 | Add this to your ``config/routing.py``: 136 | 137 | .. sourcecode:: py 138 | 139 | map.resource('owner', 'owners') 140 | 141 | Customisation 142 | -------------- 143 | 144 | You can override the following methods: 145 | 146 | .. autoclass:: formalchemy.ext.pylons.controller._RESTController 147 | :members: Session, get_model, get, get_fieldset, get_add_fieldset, get_grid, get_page, sync 148 | 149 | Here is a customisation sample to use CouchDB as backend using the :class:`~formalchemy.ext.pylons.controller.ModelsController` (~= API): 150 | 151 | .. literalinclude:: ../../pylonsapp/pylonsapp/controllers/couchdb.py 152 | 153 | Helpers 154 | ------- 155 | 156 | .. autofunction:: model_url 157 | 158 | .. autoclass:: Session 159 | :members: 160 | 161 | 162 | -------------------------------------------------------------------------------- /docs/ext/rdf.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.ext.rdf` -- rdfalchemy support 2 | ************************************************** 3 | 4 | .. automodule:: formalchemy.ext.rdf 5 | 6 | 7 | Classes definitions 8 | =================== 9 | 10 | 11 | Field 12 | -------- 13 | 14 | .. autoclass:: Field 15 | 16 | FieldSet 17 | -------- 18 | 19 | .. autoclass:: FieldSet 20 | 21 | Grid 22 | -------- 23 | 24 | .. autoclass:: Grid 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/ext/zope.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.ext.zope` -- zope.schema support 2 | ************************************************** 3 | 4 | .. automodule:: formalchemy.ext.zope 5 | 6 | 7 | Classes definitions 8 | =================== 9 | 10 | 11 | Field 12 | -------- 13 | 14 | .. autoclass:: Field 15 | 16 | FieldSet 17 | -------- 18 | 19 | .. autoclass:: FieldSet 20 | 21 | Grid 22 | -------- 23 | 24 | .. autoclass:: Grid 25 | 26 | Utilities 27 | ---------- 28 | 29 | .. autoclass:: Pk 30 | 31 | .. autoclass:: FlexibleModel 32 | 33 | .. autoclass:: FlexibleDict 34 | 35 | .. autofunction:: gen_model 36 | 37 | -------------------------------------------------------------------------------- /docs/formalchemy.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy` -- Imports 2 | ============================= 3 | 4 | .. automodule:: formalchemy 5 | 6 | All `FormAlchemy`'s objects live under the `formalchemy` package 7 | 8 | :mod:`~formalchemy.forms` related classes:: 9 | 10 | >>> from formalchemy import FieldSet, Field 11 | 12 | :mod:`~formalchemy.validators`:: 13 | 14 | >>> from formalchemy import validators, ValidationError 15 | 16 | For manual Field definition:: 17 | 18 | >>> from formalchemy import types 19 | 20 | :mod:`~formalchemy.tables` for collection rendering:: 21 | 22 | >>> from formalchemy import Grid 23 | 24 | Advanced :mod:`~formalchemy.fields` customization:: 25 | 26 | >>> from formalchemy import FieldRenderer 27 | 28 | The above imports are equivalent to:: 29 | 30 | >>> from formalchemy import * 31 | 32 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | Welcome to FormAlchemy's documentation! 2 | ======================================= 3 | 4 | .. seealso:: 5 | 6 | If you use the trunk you may look at a more up to date version 7 | of the documentation at `http://docs.formalchemy.org/current/ 8 | `_. 9 | 10 | 11 | .. toctree:: 12 | :maxdepth: 3 13 | 14 | doc 15 | 16 | 17 | Changes 18 | ======= 19 | 20 | .. include:: ../CHANGELOG.txt 21 | 22 | Copyright and License 23 | ===================== 24 | 25 | .. include:: ../COPYRIGHT.txt 26 | -------------------------------------------------------------------------------- /docs/internationalisation.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.i18n` -- Internationalisation 2 | ================================================ 3 | 4 | .. automodule:: formalchemy.i18n 5 | 6 | .. Commented imports 7 | 8 | >>> from library import fs 9 | 10 | `FormAlchemy` is able to render error messages in your own language. You just 11 | need to provide a `lang` attribute to the render method:: 12 | 13 | >>> html_fr = fs.render(lang='fr') 14 | 15 | If you use `Pylons` the language is retrieved from `pylons.i18n.get_lang()` so 16 | the `lang` attribute become optional. 17 | 18 | At the moment only the french translation is available. 19 | 20 | You have to checkout the source 21 | (http://code.google.com/p/formalchemy/source/checkout) and install 22 | `FormAlchemy` in develop mode to add a new translation:: 23 | 24 | $ cd FormAlchemy && python setup.py develop 25 | 26 | Then install Babel with easy_install:: 27 | 28 | $ easy_install Babel 29 | 30 | You are now able to initialize a new catalog 31 | 32 | $ python setup.py init_catalog -l 33 | 34 | Where `` is your language code. This will generate a new file named 35 | `formalchemy/i18n//LC_MESSAGES/formalchemy.po` 36 | 37 | Replace all the `msgstr` in the new `.po` file with your translated messages 38 | and compile the catalogs:: 39 | 40 | $ python setup.py compile_catalog 41 | 42 | Now the new language is avalaible. Last step, send your `.po` to the [http://groups.google.com/group/formalchemy project list] ! 43 | 44 | -------------------------------------------------------------------------------- /docs/models.txt: -------------------------------------------------------------------------------- 1 | Models API 2 | ================== 3 | 4 | FormAlchemy is aware of the ``__unicode__`` and ``__html__`` methods: 5 | 6 | .. sourcecode:: python 7 | 8 | class User(Base): 9 | """A User model""" 10 | __tablename__ = 'users' 11 | id = Column(Integer, primary_key=True) 12 | email = Column(Unicode(40), unique=True, nullable=False) 13 | password = Column(Unicode(20), nullable=False) 14 | name = Column(Unicode(30)) 15 | def __unicode__(self): 16 | """This is used to render the model in a relation field. Must return an 17 | unicode string.""" 18 | return self.name 19 | def __html__(self): 20 | """This is used to render the model in relation field (readonly mode). 21 | You need need to clean up the html yourself. Use it at your own 22 | risk.""" 23 | return '%s' % (self.email, self.name) 24 | def __repr__(self): 25 | return '' % self.name 26 | 27 | You can also use the :func:`formalchemy.Column` wrapper to set some extra options: 28 | 29 | .. autofunction:: formalchemy.Column 30 | 31 | -------------------------------------------------------------------------------- /docs/pylons_sample.txt: -------------------------------------------------------------------------------- 1 | Pylons integration 2 | ================== 3 | 4 | Bootstrap your project 5 | ---------------------- 6 | 7 | `FormAlchemy` come with a subclass of the Pylons_ template. 8 | If you have Pylons_ and `FormAlchemy` installed you should see that:: 9 | 10 | $ paster create --list-templates 11 | Available templates: 12 | basic_package: A basic setuptools-enabled package 13 | paste_deploy: A web application deployed through paste.deploy 14 | pylons: Pylons application template 15 | pylons_fa: Pylons application template with formalchemy support 16 | pylons_minimal: Pylons minimal application template 17 | 18 | To bootstrap a new Pylons_ project with `FormAlchemy` support enable just run:: 19 | 20 | $ paster create -t pylons_fa pylonsapp 21 | 22 | Using forms in controllers 23 | -------------------------- 24 | 25 | Imagine you have a `Foo` model in your `model/__init__.py` then your controller 26 | can look like this: 27 | 28 | .. literalinclude:: ../pylonsapp/pylonsapp/controllers/basic.py 29 | 30 | If you have a lot of fieldset and configuration stuff and want to use them in 31 | different controller, then you can use the `forms/` module to put your 32 | fieldsets. This is a standard and allow you to use the 33 | :mod:`formalchemy.ext.pylons` extension 34 | 35 | .. _Pylons: http://pylonshq.com/ 36 | 37 | You can also have a look at the :ref:`RESTful Controller ` 38 | -------------------------------------------------------------------------------- /docs/tables.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.tables` -- `Grid`: Rendering collections 2 | ========================================================== 3 | 4 | .. automodule:: formalchemy.tables 5 | 6 | Besides :class:`~formalchemy.forms.FieldSet`, `FormAlchemy` provides `Grid` for editing and 7 | rendering multiple instances at once. Most of what you know about 8 | :class:`~formalchemy.forms.FieldSet` applies to `Grid`, with the following differences to 9 | accomodate being bound to multiple objects: 10 | 11 | The Grid class 12 | -------------- 13 | 14 | .. autoclass:: Grid 15 | :members: 16 | 17 | Creating 18 | -------- 19 | 20 | The `Grid` constructor takes parameters (`cls`, `instances=[]`, `session=None`, `data=None`). 21 | A significant difference from :class:`~formalchemy.forms.FieldSet` is that the first argument 22 | must _always_ be a mapped class, e.g., `User`. `instances` is the objects to render, 23 | which must all be of the given type. The other parameters are the same as in 24 | :class:`~formalchemy.forms.FieldSet`. 25 | 26 | Binding 27 | ------- 28 | 29 | `Grid` `bind` and `rebind` methods are similar to those methods in 30 | :class:`~formalchemy.forms.FieldSet`, except they take an iterable `instances` 31 | instead of an instance `model`. Thus, the full signature is 32 | (`instances`, `session=None`, `data=None`). 33 | 34 | Configuration 35 | ------------- 36 | 37 | The `Grid` `configure` method takes the same arguments as :class:`~formalchemy.forms.FieldSet` 38 | (`pk`, `exclude`, `include`, `options`, `readonly`), except there is 39 | no `focus` argument. 40 | 41 | Validation and Sync 42 | ------------------- 43 | 44 | These are the same as in :class:`~formalchemy.forms.FieldSet`, except that you can also call 45 | `sync_one(instance)` to sync a single one of the instances that are 46 | bound to the `Grid`. 47 | 48 | The `Grid` `errors` attribute is a dictionary keyed by bound instance, 49 | whose value is similar to the `errors` from a :class:`~formalchemy.forms.FieldSet`, that is, a 50 | dictionary whose keys are `Field`s, and whose values are 51 | `ValidationError` instances. 52 | 53 | Customizing Grid 54 | ---------------- 55 | 56 | Overriding `Grid` rendering is similar to :class:`~formalchemy.forms.FieldSet`. The differences are: 57 | 58 | * The default templates take a `collection` parameter instead of `fieldset`, which is the instance of `Grid` to render 59 | * The instances given to the collection are available by iterating the collection (`for row in collection`). Alternately, you can call `_set_active(row)`. 60 | 61 | The default templates are `formalchemy.tables.template_grid_readonly` and `formalchemy.tables.template_grid`. 62 | 63 | Usage 64 | ----- 65 | 66 | You need some imports:: 67 | 68 | >>> from formalchemy.tables import * 69 | 70 | .. Hidden code needed for testing 71 | 72 | >>> from formalchemy.tests import * 73 | 74 | Then you can initialize a `Grid` and bind it to a list of row instance:: 75 | 76 | >>> tc = Grid(User, [bill]) 77 | >>> tc.configure(readonly=True) 78 | 79 | This will render instances as an html table:: 80 | 81 | >>> print(tc.render()) 82 | 83 | 84 | 85 | Email 86 | 87 | 88 | Password 89 | 90 | 91 | Name 92 | 93 | 94 | Orders 95 | 96 | 97 | 98 | 99 | 100 | 101 | bill@example.com 102 | 103 | 104 | 1234 105 | 106 | 107 | Bill 108 | 109 | 110 | Quantity: 10 111 | 112 | 113 | 114 | 115 | You can also add a field to the `Grid` manually:: 116 | 117 | >>> from formalchemy.helpers import literal 118 | >>> tc2 = Grid(User, [bill, john]) 119 | >>> tc2.append(Field('link', type=types.String, value=lambda item: literal('link') % item.id)) 120 | >>> tc2.configure(readonly=True) 121 | >>> print(tc2.render()) 122 | 123 | 124 | 125 | Email 126 | 127 | 128 | Password 129 | 130 | 131 | Name 132 | 133 | 134 | Orders 135 | 136 | 137 | Link 138 | 139 | 140 | 141 | 142 | 143 | 144 | bill@example.com 145 | 146 | 147 | 1234 148 | 149 | 150 | Bill 151 | 152 | 153 | Quantity: 10 154 | 155 | 156 | 157 | link 158 | 159 | 160 | 161 | 162 | 163 | john@example.com 164 | 165 | 166 | 5678 167 | 168 | 169 | John 170 | 171 | 172 | Quantity: 5, Quantity: 6 173 | 174 | 175 | 176 | link 177 | 178 | 179 | 180 | 181 | 182 | You can provide a dict as new values:: 183 | 184 | >>> g = Grid(User, [bill, john], data={'User-1-email': 'bill_@example.com', 'User-1-password': '1234_', 'User-1-name': 'Bill_', 'User-1-orders': '1', 'User-2-email': 'john_@example.com', 'User-2-password': '5678_', 'User-2-name': 'John_', 'User-2-orders': ['2', '3'], }) 185 | 186 | Validation work like `Fieldset`:: 187 | 188 | >>> g.validate() 189 | True 190 | 191 | Sync too: 192 | 193 | >>> g.sync() 194 | >>> session.flush() 195 | >>> session.refresh(john) 196 | >>> john.email == 'john_@example.com' 197 | True 198 | >>> session.rollback() 199 | -------------------------------------------------------------------------------- /docs/templates.txt: -------------------------------------------------------------------------------- 1 | :mod:`formalchemy.templates` -- Template engines 2 | ================================================= 3 | 4 | .. automodule:: formalchemy.templates 5 | 6 | .. Commented imports 7 | 8 | >>> from formalchemy.tests import User 9 | >>> from formalchemy.forms import FieldSet 10 | >>> from formalchemy.templates import TemplateEngine 11 | >>> from formalchemy import config 12 | >>> old_engine = config.engine 13 | 14 | Available engines 15 | ----------------- 16 | 17 | .. autoclass:: MakoEngine 18 | :members: 19 | 20 | .. autoclass:: GenshiEngine 21 | :members: 22 | 23 | .. autoclass:: TempitaEngine 24 | :members: 25 | 26 | Base class 27 | ---------- 28 | 29 | .. autoclass:: TemplateEngine 30 | :members: 31 | 32 | Customize templates 33 | ------------------- 34 | 35 | You can override the default template by adding a directory for your project which will contain the templates. 36 | The engine will scan the directory and try to load templates from it. If he can't, the default templates are used. 37 | 38 | .. set templates dirs 39 | 40 | >>> import os 41 | >>> import formalchemy.tests 42 | >>> from formalchemy. tests import ls, cat 43 | >>> mako_templates_dir = os.path.join(os.path.dirname(formalchemy.tests.__file__), 'data', 'mako') 44 | >>> genshi_templates_dir = os.path.join(os.path.dirname(formalchemy.tests.__file__), 'data', 'genshi') 45 | 46 | Here is an example:: 47 | 48 | >>> ls(mako_templates_dir) 49 | - fieldset.mako 50 | 51 | >>> cat(mako_templates_dir, 'fieldset.mako') 52 |
    53 | %for field in fieldset.render_fields.values(): 54 |
  • ${field.name}
  • 55 | %endfor 56 |
57 | 58 | 59 | Then you can override the default mako templates:: 60 | 61 | >>> from formalchemy import config 62 | >>> from formalchemy import templates 63 | >>> config.engine = templates.MakoEngine( 64 | ... directories=[mako_templates_dir], 65 | ... input_encoding='utf-8', output_encoding='utf-8') 66 | 67 | And see the result:: 68 | 69 | >>> print(FieldSet(User).render()) 70 |
    71 |
  • email
  • 72 |
  • password
  • 73 |
  • name
  • 74 |
  • orders
  • 75 |
76 | 77 | 78 | Same with genshi except that :mod:`formalchemy` don't provide default templates:: 79 | 80 | >>> cat(genshi_templates_dir, 'fieldset.html') 81 |
    82 |
  • ${field.name}
  • 83 |
84 | 85 | 86 | >>> config.engine = templates.GenshiEngine(directories=[genshi_templates_dir]) 87 | 88 | And same the result of course:: 89 | 90 | >>> print(FieldSet(User).render()) 91 |
    92 |
  • email
  • password
  • name
  • orders
  • 93 |
94 | 95 | Write your own engine 96 | ---------------------- 97 | 98 | You need to subclass the :class:`~formalchemy.templates.TemplateEngine`:: 99 | 100 | >>> class MyEngine(TemplateEngine): 101 | ... def render(self, template_name, **kw): 102 | ... return 'It works !' 103 | 104 | You can use it for a specific :class:`~formalchemy.forms.FieldSet`:: 105 | 106 | >>> fs = FieldSet(User) 107 | >>> fs.engine = MyEngine() 108 | >>> print(fs.render()) 109 | It works ! 110 | 111 | You can also override the engine in a subclass:: 112 | 113 | >>> class MyFieldSet(FieldSet): 114 | ... engine = MyEngine() 115 | 116 | Or set it as the global engine with :mod:`formalchemy`'s :mod:`~formalchemy.config`:: 117 | 118 | >>> from formalchemy import config 119 | >>> config.engine = MyEngine() 120 | 121 | It should be available for all :class:`~formalchemy.forms.FieldSet`:: 122 | 123 | >>> print(FieldSet(User).render()) 124 | It works ! 125 | 126 | .. restore config 127 | 128 | >>> config.engine = old_engine 129 | -------------------------------------------------------------------------------- /formalchemy/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007 Alexandre Conrad, alexandre (dot) conrad (at) gmail (dot) com 2 | # 3 | # This module is part of FormAlchemy and is released under 4 | # the MIT License: http://www.opensource.org/licenses/mit-license.php 5 | 6 | from sqlalchemy import Column as SAColumn 7 | from formalchemy import templates 8 | from formalchemy import config 9 | from formalchemy.tables import Grid 10 | from formalchemy.forms import FieldSet, SimpleMultiDict 11 | from formalchemy.fields import Field, FieldRenderer 12 | from formalchemy.validators import ValidationError 13 | import formalchemy.validators as validators 14 | import formalchemy.fatypes as types 15 | 16 | column_options = Field._valide_options[:] 17 | column_options.extend(['backref_%s' % v for v in Field._valide_options]) 18 | 19 | def Column(*args, **kwargs): 20 | """Wrap the standard Column to allow to add some FormAlchemy options to a 21 | model field. Basically label and renderer but all the values are passed to 22 | :meth:`~formalchemy.fields.AbstractField.set`:: 23 | 24 | >>> from sqlalchemy import Integer 25 | >>> from sqlalchemy.ext.declarative import declarative_base 26 | >>> from formalchemy import Column 27 | >>> Base = declarative_base() 28 | >>> class MyArticle(Base): 29 | ... __tablename__ = 'myarticles' 30 | ... id = Column(Integer, primary_key=True, label='My id') 31 | >>> MyArticle.__table__.c.id.info 32 | {'label': 'My id'} 33 | 34 | """ 35 | info = kwargs.get('info', {}) 36 | drop = set() 37 | for k, v in kwargs.items(): 38 | if k in column_options: 39 | info[k] = v 40 | drop.add(k) 41 | for k in drop: 42 | del kwargs[k] 43 | if info: 44 | kwargs['info'] = info 45 | return SAColumn(*args, **kwargs) 46 | 47 | 48 | __all__ = ["FieldSet", "Field", "FieldRenderer", "Grid", "ValidationError", "validators", "SimpleMultiDict", "types"] 49 | __version__ = '1.5.7.dev0' 50 | 51 | -------------------------------------------------------------------------------- /formalchemy/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007 Alexandre Conrad, alexandre (dot) conrad (at) gmail (dot) com 2 | # 3 | # This module is part of FormAlchemy and is released under 4 | # the MIT License: http://www.opensource.org/licenses/mit-license.php 5 | 6 | -------------------------------------------------------------------------------- /formalchemy/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from formalchemy import templates 4 | 5 | __doc__ = """ 6 | There is two configuration settings available in a global config object. 7 | 8 | - encoding: the global encoding used by FormAlchemy to deal with unicode. Default: utf-8 9 | 10 | - engine: A valide :class:`~formalchemy.templates.TemplateEngine` 11 | 12 | - date_format: Used to format date fields. Default to %Y-%d-%m 13 | 14 | - date_edit_format: Used to retrieve field order. Default to m-d-y 15 | 16 | Here is a simple example:: 17 | 18 | >>> from formalchemy import config 19 | >>> config.encoding = 'iso-8859-1' 20 | >>> config.encoding 21 | 'iso-8859-1' 22 | 23 | >>> from formalchemy import templates 24 | >>> config.engine = templates.TempitaEngine 25 | 26 | There is also a convenience method to set the configuration from a config file:: 27 | 28 | >>> config.from_config({'formalchemy.encoding':'utf-8', 29 | ... 'formalchemy.engine':'mako', 30 | ... 'formalchemy.engine.options.input_encoding':'utf-8', 31 | ... 'formalchemy.engine.options.output_encoding':'utf-8', 32 | ... }) 33 | >>> config.from_config({'formalchemy.encoding':'utf-8'}) 34 | >>> config.encoding 35 | 'utf-8' 36 | >>> isinstance(config.engine, templates.MakoEngine) 37 | True 38 | 39 | """ 40 | 41 | class Config(object): 42 | __doc__ = __doc__ 43 | __name__ = 'formalchemy.config' 44 | __file__ = __file__ 45 | __data = dict( 46 | encoding='utf-8', 47 | date_format='%Y-%m-%d', 48 | date_edit_format='m-d-y', 49 | engine = templates.default_engine, 50 | ) 51 | 52 | def __getattr__(self, attr): 53 | if attr in self.__data: 54 | return self.__data[attr] 55 | else: 56 | raise AttributeError('Configuration has no attribute %s' % attr) 57 | 58 | def __setattr__(self, attr, value): 59 | meth = getattr(self, '__set_%s' % attr, None) 60 | if callable(meth): 61 | meth(value) 62 | else: 63 | self.__data[attr] = value 64 | 65 | def __set_engine(self, value): 66 | if isinstance(value, templates.TemplateEngine): 67 | self.__data['engine'] = value 68 | else: 69 | raise ValueError('%s is not a template engine') 70 | 71 | def _get_config(self, config, prefix): 72 | values = {} 73 | config_keys = config.keys() 74 | for k in list(config_keys): 75 | if k.startswith(prefix): 76 | v = config.pop(k) 77 | k = k[len(prefix):] 78 | values[k] = v 79 | return values 80 | 81 | def from_config(self, config, prefix='formalchemy.'): 82 | from formalchemy import templates 83 | engine_config = self._get_config(config, '%s.engine.options.' % prefix) 84 | for k, v in self._get_config(config, prefix).items(): 85 | if k == 'engine': 86 | engine = templates.__dict__.get('%sEngine' % v.title(), None) 87 | if engine is not None: 88 | v = engine(**engine_config) 89 | else: 90 | raise ValueError('%sEngine does not exist' % v.title()) 91 | self.__setattr__(k, v) 92 | 93 | def __repr__(self): 94 | return "" % (self.__file__, self.__data) 95 | 96 | sys.modules['formalchemy.config'] = Config() 97 | 98 | -------------------------------------------------------------------------------- /formalchemy/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class PkError(Exception): 4 | """An exception raised when a primary key conflict occur""" 5 | 6 | class ValidationError(Exception): 7 | """an exception raised when the validation failed 8 | """ 9 | @property 10 | def message(self): 11 | return self.args[0] 12 | def __repr__(self): 13 | return 'ValidationError(%r,)' % self.message 14 | 15 | class FieldNotFoundError(ValidationError): 16 | """an exception raise when the field is not found in request data""" 17 | -------------------------------------------------------------------------------- /formalchemy/ext/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/admin_edit.mako: -------------------------------------------------------------------------------- 1 | <%inherit file="base.mako"/>\ 2 | <% 3 | from pylons import url 4 | %>\ 5 | <%def name="title()"> 6 | ${F_(action)} ${modelname} ${id and id or ''} 7 | 8 |
9 | 10 | ${F_(action)} ${modelname} ${id and id or ''} 11 | 12 |
13 | ${c.fs.render()} 14 |
15 | %if id: 16 | 17 | %else: 18 | 19 | %endif 20 | 21 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/admin_index.mako: -------------------------------------------------------------------------------- 1 | <%inherit file="base.mako"/>\ 2 | <% 3 | from pylons import url 4 | %>\ 5 | <%def name="title()"> 6 | ${F_('Models')} 7 | 8 | 9 | %for i, modelname in enumerate(modelnames): 10 | 11 | %endfor 12 |
${modelname}
13 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/admin_list.mako: -------------------------------------------------------------------------------- 1 | <%inherit file="base.mako"/>\ 2 | <%! 3 | from pylons import url 4 | %>\ 5 | <%def name="title()"> 6 | ${modelname} 7 | 8 | 9 | <%def name="sidebar()"> 10 | %if clsnames: 11 | 21 | %endif 22 | 23 | 24 |
25 |

26 | ${F_('Existing objects')} 27 |

28 |
29 | ${page.pager()} 30 |
31 | 32 | ${grid.render()} 33 |
34 | 35 | ${F_('New object')} 36 | 37 |
38 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/base.mako: -------------------------------------------------------------------------------- 1 | <% 2 | from pylons import url, request 3 | %>\ 4 | 5 | 6 | ${self.title()} 7 | 8 | 23 | ${custom_css} 24 | ${custom_js} 25 | 26 | 27 | 42 | 43 | <%def name="sidebar()"> 44 | 45 | ${self.sidebar()} 46 | 47 |
48 | <% 49 | from pylons import session 50 | flashes = session.get('_admin_flashes', []) 51 | if flashes: 52 | session['_admin_flashes'] = [] 53 | session.save() 54 | %> 55 | %for flash in flashes: 56 |
${flash}
57 | %endfor 58 | ${self.body()} 59 |
60 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/maps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pylons 3 | import logging 4 | log = logging.getLogger(__name__) 5 | 6 | try: 7 | version = pylons.__version__.split('.') 8 | except AttributeError: 9 | version = ['0', '6'] 10 | 11 | def format(environ, result): 12 | if environ.get('HTTP_ACCEPT', '') == 'application/json': 13 | result['format'] = 'json' 14 | return True 15 | elif 'format' not in result: 16 | result['format'] = 'html' 17 | return True 18 | 19 | def admin_map(map, controller, url='%s'): 20 | """connect the admin controller `cls` under the given `url`""" 21 | log.info('connecting %s to %s' % (url, controller)) 22 | map.connect('static_contents', '%s/static_contents/{id}' % url, controller=controller, action='static') 23 | 24 | map.connect('admin', '%s' % url, 25 | controller=controller, action='index') 26 | 27 | map.connect('formatted_admin', '%s.{format}' % url, 28 | controller=controller, action='index') 29 | 30 | map.connect("models", "%s/{modelname}" % url, 31 | controller=controller, action="edit", id=None, format='html', 32 | conditions=dict(method=["POST"], function=format)) 33 | 34 | map.connect("models", "%s/{modelname}" % url, 35 | controller=controller, action="list", 36 | conditions=dict(method=["GET"], function=format)) 37 | 38 | map.connect("formatted_models", "%s/{modelname}.{format}" % url, 39 | controller=controller, action="list", 40 | conditions=dict(method=["GET"])) 41 | 42 | map.connect("new_model", "%s/{modelname}/new" % url, 43 | controller=controller, action="edit", id=None, 44 | conditions=dict(method=["GET"])) 45 | 46 | map.connect("formatted_new_model", "%s/{modelname}/new.{format}" % url, 47 | controller=controller, action="edit", id=None, 48 | conditions=dict(method=["GET"])) 49 | 50 | map.connect("%s/{modelname}/{id}" % url, 51 | controller=controller, action="edit", 52 | conditions=dict(method=["PUT"], function=format)) 53 | 54 | map.connect("%s/{modelname}/{id}" % url, 55 | controller=controller, action="delete", 56 | conditions=dict(method=["DELETE"])) 57 | 58 | map.connect("edit_model", "%s/{modelname}/{id}/edit" % url, 59 | controller=controller, action="edit", 60 | conditions=dict(method=["GET"])) 61 | 62 | map.connect("formatted_edit_model", "%s/{modelname}/{id}.{format}/edit" % url, 63 | controller=controller, action="edit", 64 | conditions=dict(method=["GET"])) 65 | 66 | map.connect("view_model", "%s/{modelname}/{id}" % url, 67 | controller=controller, action="edit", 68 | conditions=dict(method=["GET"], function=format)) 69 | 70 | map.connect("formatted_view_model", "%s/{modelname}/{id}.{format}" % url, 71 | controller=controller, action="edit", 72 | conditions=dict(method=["GET"])) 73 | 74 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/pastertemplate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | try: 3 | from tempita import paste_script_template_renderer 4 | from paste.script.templates import Template, var 5 | except ImportError: 6 | class PylonsTemplate(object): 7 | pass 8 | else: 9 | class PylonsTemplate(Template): 10 | _template_dir = ('formalchemy', 'paster_templates/pylons_fa') 11 | summary = 'Pylons application template with formalchemy support' 12 | required_templates = ['pylons'] 13 | template_renderer = staticmethod(paste_script_template_renderer) 14 | vars = [ 15 | var('admin_controller', 'Add formalchemy\'s admin controller', 16 | default=False), 17 | ] 18 | 19 | -------------------------------------------------------------------------------- /formalchemy/ext/pylons/resources/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/formalchemy/ext/pylons/resources/add.png -------------------------------------------------------------------------------- /formalchemy/ext/pylons/resources/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/formalchemy/ext/pylons/resources/delete.png -------------------------------------------------------------------------------- /formalchemy/ext/pylons/resources/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/formalchemy/ext/pylons/resources/edit.png -------------------------------------------------------------------------------- /formalchemy/fatypes.py: -------------------------------------------------------------------------------- 1 | # This module is part of FormAlchemy and is released under 2 | # the MIT License: http://www.opensource.org/licenses/mit-license.php 3 | 4 | from sqlalchemy.types import TypeEngine, Integer, Float, String, Unicode, Text, Boolean, Date, DateTime, Time, Numeric, Interval 5 | 6 | try: 7 | from sqlalchemy.types import LargeBinary 8 | except ImportError: 9 | # SA < 0.6 10 | from sqlalchemy.types import Binary as LargeBinary 11 | 12 | sa_types = set([Integer, Float, String, Unicode, Text, LargeBinary, Boolean, Date, DateTime, Time, Numeric, Interval]) 13 | 14 | class HTML5Email(String): 15 | """HTML5 email field""" 16 | 17 | class HTML5Url(String): 18 | """HTML5 url field""" 19 | 20 | class HTML5Number(Integer): 21 | """HTML5 number field""" 22 | 23 | class HTML5Color(String): 24 | """HTML5 color field""" 25 | 26 | class HTML5DateTime(DateTime): 27 | """HTML5 datetime field""" 28 | 29 | class HTML5Date(Date): 30 | """HTML5 date field""" 31 | 32 | class HTML5Time(Time): 33 | """HTML5 time field""" 34 | 35 | class List(TypeEngine): 36 | def get_dbapi_type(self): 37 | raise NotImplementedError() 38 | 39 | class Set(TypeEngine): 40 | def get_dbapi_type(self): 41 | raise NotImplementedError() 42 | 43 | -------------------------------------------------------------------------------- /formalchemy/fieldset.tmpl: -------------------------------------------------------------------------------- 1 | {{py:_focus_rendered = False}} 2 | {{py:_ = F_}} 3 | 4 | {{for error in fieldset.errors.get(None, [])}} 5 |
6 | {{_(error)}} 7 |
8 | {{endfor}} 9 | 10 | {{for field in fieldset.render_fields.values()}} 11 | {{if field.requires_label}} 12 |
13 | {{field.label_tag()}} 14 | {{field.render()}} 15 | {{if 'instructions' in field.metadata}} 16 | {{field.metadata['instructions']}} 17 | {{endif}} 18 | {{for error in field.errors}} 19 | {{_(error)}} 20 | {{endfor}} 21 |
22 | 23 | {{if (fieldset.focus == field or fieldset.focus is True) and not _focus_rendered}} 24 | {{if not field.is_readonly()}} 25 | 30 | {{py:_focus_rendered = True}} 31 | {{endif}} 32 | {{endif}} 33 | {{else}} 34 | {{field.render()}} 35 | {{endif}} 36 | {{endfor}} 37 | -------------------------------------------------------------------------------- /formalchemy/fieldset_readonly.tmpl: -------------------------------------------------------------------------------- 1 | 2 | {{for field in fieldset.render_fields.values()}} 3 | {{if field.requires_label}} 4 | 5 | {{field.label()}}: 6 | {{field.render_readonly()}} 7 | 8 | {{endif}} 9 | {{endfor}} 10 | 11 | -------------------------------------------------------------------------------- /formalchemy/grid.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{for field in collection.render_fields.values()}} 4 | {{field.label()}} 5 | {{endfor}} 6 | 7 | 8 | 9 | 10 | {{for i, row in enumerate(collection):}} 11 | {{py:row_errors = collection.get_errors(row)}} 12 | 13 | {{for field in collection.render_fields.values()}} 14 | 15 | {{field.render()}} 16 | {{for error in row_errors.get(field, []):}} 17 | {{error}} 18 | {{endfor}} 19 | 20 | {{endfor}} 21 | 22 | {{endfor}} 23 | 24 | -------------------------------------------------------------------------------- /formalchemy/grid_readonly.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{for field in collection.render_fields.values()}} 4 | {{field.label()}} 5 | {{endfor}} 6 | 7 | 8 | 9 | 10 | {{for i, row in enumerate(collection):}} 11 | 12 | {{for field in collection.render_fields.values()}} 13 | {{field.render_readonly()}} 14 | {{endfor}} 15 | 16 | {{endfor}} 17 | 18 | -------------------------------------------------------------------------------- /formalchemy/i18n.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007 Alexandre Conrad, alexandre (dot) conrad (at) gmail (dot) com 2 | # 3 | # This module is part of FormAlchemy and is released under 4 | # the MIT License: http://www.opensource.org/licenses/mit-license.php 5 | 6 | import os 7 | from gettext import GNUTranslations 8 | 9 | i18n_path = os.path.join(os.path.dirname(__file__), 'i18n_resources') 10 | 11 | try: 12 | from pyramid.i18n import get_localizer 13 | from pyramid.i18n import TranslationStringFactory 14 | HAS_PYRAMID = True 15 | except ImportError: 16 | HAS_PYRAMID = False 17 | 18 | try: 19 | from pylons.i18n import get_lang 20 | HAS_PYLONS = True 21 | except: 22 | HAS_PYLONS = False 23 | 24 | if not HAS_PYLONS: 25 | def get_lang(): return [] 26 | 27 | class _Translator(object): 28 | """dummy translator""" 29 | def gettext(self, value): 30 | return value 31 | _translator = _Translator() 32 | 33 | def get_translator(lang=None, request=None): 34 | """ 35 | return a GNUTranslations instance for `lang`:: 36 | 37 | >>> translate = get_translator('fr') 38 | >>> assert translate('Remove') == 'Supprimer' 39 | >>> assert translate('month_01') == 'Janvier' 40 | >>> translate = get_translator('en') 41 | >>> assert translate('Remove') == 'Remove' 42 | >>> assert translate('month_01') == 'January' 43 | 44 | The correct gettext method is stored in request if possible:: 45 | 46 | >>> from webob import Request 47 | >>> req = Request.blank('/') 48 | >>> translate = get_translator('fr', request=req) 49 | >>> assert translate('Remove') == 'Supprimer' 50 | >>> translate = get_translator('en', request=req) 51 | >>> assert translate('Remove') == 'Supprimer' 52 | 53 | """ 54 | if request is not None: 55 | translate = request.environ.get('fa.translate') 56 | if translate: 57 | return translate 58 | 59 | if HAS_PYRAMID: 60 | translate = get_localizer(request).translate 61 | request.environ['fa.translate'] = translate 62 | return translate 63 | 64 | # get possible fallback languages 65 | try: 66 | langs = get_lang() or [] 67 | except TypeError: 68 | # this occurs when Pylons is available and we are not in a valid thread 69 | langs = [] 70 | 71 | # insert lang if provided 72 | if lang and lang not in langs: 73 | langs.insert(0, lang) 74 | 75 | if not langs: 76 | langs = ['en'] 77 | 78 | # get the first available catalog 79 | for lang in langs: 80 | filename = os.path.join(i18n_path, lang, 'LC_MESSAGES','formalchemy.mo') 81 | if os.path.isfile(filename): 82 | translations_path = os.path.join(i18n_path, lang, 'LC_MESSAGES','formalchemy.mo') 83 | tr = GNUTranslations(open(translations_path, 'rb')).gettext 84 | def translate(value): 85 | value = tr(value) 86 | return value 87 | if request is not None: 88 | request.environ['fa.translate'] = translate 89 | return translate 90 | 91 | # dummy translator 92 | if request is not None: 93 | request.environ['fa.translate'] = _translator.gettext 94 | return _translator.gettext 95 | 96 | if HAS_PYRAMID: 97 | _ = TranslationStringFactory('formalchemy') 98 | else: 99 | def _(value): 100 | """dummy 'translator' to mark translation strings in python code""" 101 | return value 102 | 103 | # month translation 104 | _('Year') 105 | _('Month') 106 | _('Day') 107 | _('month_01') 108 | _('month_02') 109 | _('month_03') 110 | _('month_04') 111 | _('month_05') 112 | _('month_06') 113 | _('month_07') 114 | _('month_08') 115 | _('month_09') 116 | _('month_10') 117 | _('month_11') 118 | _('month_12') 119 | 120 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/en/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # English translations for FormAlchemy. 2 | # Copyright (C) 2009 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2009. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.1\n" 10 | "Report-Msgid-Bugs-To: formalchemy@googlegroups.com\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2011-06-21 16:13+0200\n" 13 | "Last-Translator: gael@gawel.org\n" 14 | "Language-Team: fr \n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 0.9.6\n" 20 | 21 | #: formalchemy/fields.py:474 22 | msgid "Remove" 23 | msgstr "" 24 | 25 | #: formalchemy/i18n.py:85 26 | msgid "Year" 27 | msgstr "" 28 | 29 | #: formalchemy/i18n.py:86 30 | msgid "Month" 31 | msgstr "" 32 | 33 | #: formalchemy/i18n.py:87 34 | msgid "Day" 35 | msgstr "" 36 | 37 | #: formalchemy/i18n.py:88 38 | msgid "month_01" 39 | msgstr "January" 40 | 41 | #: formalchemy/i18n.py:89 42 | msgid "month_02" 43 | msgstr "February" 44 | 45 | #: formalchemy/i18n.py:90 46 | msgid "month_03" 47 | msgstr "March" 48 | 49 | #: formalchemy/i18n.py:91 50 | msgid "month_04" 51 | msgstr "April" 52 | 53 | #: formalchemy/i18n.py:92 54 | msgid "month_05" 55 | msgstr "May" 56 | 57 | #: formalchemy/i18n.py:93 58 | msgid "month_06" 59 | msgstr "June" 60 | 61 | #: formalchemy/i18n.py:94 62 | msgid "month_07" 63 | msgstr "July" 64 | 65 | #: formalchemy/i18n.py:95 66 | msgid "month_08" 67 | msgstr "August" 68 | 69 | #: formalchemy/i18n.py:96 70 | msgid "month_09" 71 | msgstr "September" 72 | 73 | #: formalchemy/i18n.py:97 74 | msgid "month_10" 75 | msgstr "October" 76 | 77 | #: formalchemy/i18n.py:98 78 | msgid "month_11" 79 | msgstr "November" 80 | 81 | #: formalchemy/i18n.py:99 82 | msgid "month_12" 83 | msgstr "December" 84 | 85 | #: formalchemy/validators.py:32 86 | msgid "Please select a value" 87 | msgstr "" 88 | 89 | #: formalchemy/validators.py:32 90 | msgid "Please enter a value" 91 | msgstr "" 92 | 93 | #: formalchemy/validators.py:49 94 | msgid "Value is not an integer" 95 | msgstr "" 96 | 97 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 98 | msgid "Value is not a number" 99 | msgstr "" 100 | 101 | #: formalchemy/validators.py:95 102 | msgid "Missing @ sign" 103 | msgstr "" 104 | 105 | #: formalchemy/validators.py:98 106 | msgid "Control characters present" 107 | msgstr "" 108 | 109 | #: formalchemy/validators.py:100 110 | msgid "Non-ASCII characters present" 111 | msgstr "" 112 | 113 | #: formalchemy/validators.py:104 114 | msgid "Recipient must be non-empty" 115 | msgstr "" 116 | 117 | #: formalchemy/validators.py:106 118 | msgid "Recipient must not end with '.'" 119 | msgstr "" 120 | 121 | #: formalchemy/validators.py:121 122 | msgid "Unterminated quoted section in recipient" 123 | msgstr "" 124 | 125 | #: formalchemy/validators.py:124 126 | msgid "Quoted section must be followed by '@' or '.'" 127 | msgstr "" 128 | 129 | #: formalchemy/validators.py:127 130 | msgid "Reserved character present in recipient" 131 | msgstr "" 132 | 133 | #: formalchemy/validators.py:132 134 | msgid "Domain must be non-empty" 135 | msgstr "" 136 | 137 | #: formalchemy/validators.py:134 138 | msgid "Domain must not end with '.'" 139 | msgstr "" 140 | 141 | #: formalchemy/validators.py:136 142 | msgid "Domain must not contain '..'" 143 | msgstr "" 144 | 145 | #: formalchemy/validators.py:138 146 | msgid "Reserved character present in domain" 147 | msgstr "" 148 | 149 | #: formalchemy/validators.py:148 150 | #, python-format 151 | msgid "Value must be at least %d characters long" 152 | msgstr "" 153 | 154 | #: formalchemy/validators.py:150 155 | #, python-format 156 | msgid "Value must be no more than %d characters long" 157 | msgstr "" 158 | 159 | #: formalchemy/validators.py:165 160 | msgid "Invalid input" 161 | msgstr "" 162 | 163 | #: formalchemy/ext/fsblob.py:26 164 | #, python-format 165 | msgid "Invalid file extension. Must be %s, " 166 | msgstr "" 167 | 168 | #: formalchemy/ext/fsblob.py:33 169 | #, python-format 170 | msgid "Invalid image file. Must be %s, " 171 | msgstr "" 172 | 173 | #: formalchemy/ext/pylons/admin.py:25 174 | msgid "Add" 175 | msgstr "" 176 | 177 | #: formalchemy/ext/pylons/admin.py:26 178 | msgid "Edit" 179 | msgstr "" 180 | 181 | #: formalchemy/ext/pylons/admin.py:27 182 | msgid "New" 183 | msgstr "" 184 | 185 | #: formalchemy/ext/pylons/admin.py:28 186 | msgid "Save" 187 | msgstr "" 188 | 189 | #: formalchemy/ext/pylons/admin.py:29 190 | msgid "Delete" 191 | msgstr "" 192 | 193 | #: formalchemy/ext/pylons/admin.py:30 194 | msgid "Cancel" 195 | msgstr "" 196 | 197 | #: formalchemy/ext/pylons/admin.py:31 198 | msgid "Models" 199 | msgstr "" 200 | 201 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 202 | msgid "Existing objects" 203 | msgstr "" 204 | 205 | #: formalchemy/ext/pylons/admin.py:33 206 | msgid "New object" 207 | msgstr "" 208 | 209 | #: formalchemy/ext/pylons/admin.py:34 210 | msgid "Related types" 211 | msgstr "" 212 | 213 | #: formalchemy/ext/pylons/admin.py:36 214 | msgid "Create form" 215 | msgstr "" 216 | 217 | #: formalchemy/ext/pylons/admin.py:81 218 | msgid "edit" 219 | msgstr "" 220 | 221 | #: formalchemy/ext/pylons/admin.py:90 222 | msgid "delete" 223 | msgstr "" 224 | 225 | #: formalchemy/ext/pylons/admin.py:201 226 | #, python-format 227 | msgid "Created %s %s" 228 | msgstr "" 229 | 230 | #: formalchemy/ext/pylons/admin.py:204 231 | #, python-format 232 | msgid "Modified %s %s" 233 | msgstr "" 234 | 235 | #: formalchemy/ext/pylons/admin.py:236 236 | #, python-format 237 | msgid "Deleted %s %s" 238 | msgstr "" 239 | 240 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/es/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Spanish translations for FormAlchemy. 2 | # Copyright (C) 2008 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2008. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 0.5.1\n" 10 | "Report-Msgid-Bugs-To: robarago@gmail.com\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2011-06-21 16:13+0200\n" 13 | "Last-Translator: Roberto Aragón \n" 14 | "Language-Team: es \n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 0.9.6\n" 20 | 21 | #: formalchemy/fields.py:474 22 | msgid "Remove" 23 | msgstr "Eliminar" 24 | 25 | #: formalchemy/i18n.py:85 26 | msgid "Year" 27 | msgstr "Año" 28 | 29 | #: formalchemy/i18n.py:86 30 | msgid "Month" 31 | msgstr "Mes" 32 | 33 | #: formalchemy/i18n.py:87 34 | msgid "Day" 35 | msgstr "Día" 36 | 37 | #: formalchemy/i18n.py:88 38 | msgid "month_01" 39 | msgstr "Enero" 40 | 41 | #: formalchemy/i18n.py:89 42 | msgid "month_02" 43 | msgstr "Febrero" 44 | 45 | #: formalchemy/i18n.py:90 46 | msgid "month_03" 47 | msgstr "Marzo" 48 | 49 | #: formalchemy/i18n.py:91 50 | msgid "month_04" 51 | msgstr "Abril" 52 | 53 | #: formalchemy/i18n.py:92 54 | msgid "month_05" 55 | msgstr "Mayo" 56 | 57 | #: formalchemy/i18n.py:93 58 | msgid "month_06" 59 | msgstr "Junio" 60 | 61 | #: formalchemy/i18n.py:94 62 | msgid "month_07" 63 | msgstr "Julio" 64 | 65 | #: formalchemy/i18n.py:95 66 | msgid "month_08" 67 | msgstr "Agosto" 68 | 69 | #: formalchemy/i18n.py:96 70 | msgid "month_09" 71 | msgstr "Septiembre" 72 | 73 | #: formalchemy/i18n.py:97 74 | msgid "month_10" 75 | msgstr "Octubre" 76 | 77 | #: formalchemy/i18n.py:98 78 | msgid "month_11" 79 | msgstr "Noviembre" 80 | 81 | #: formalchemy/i18n.py:99 82 | msgid "month_12" 83 | msgstr "Diciembre" 84 | 85 | #: formalchemy/validators.py:32 86 | msgid "Please select a value" 87 | msgstr "Por favor, elija un valor" 88 | 89 | #: formalchemy/validators.py:32 90 | msgid "Please enter a value" 91 | msgstr "Por favor, introduzca un valor" 92 | 93 | #: formalchemy/validators.py:49 94 | msgid "Value is not an integer" 95 | msgstr "El valor no es un entero" 96 | 97 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 98 | msgid "Value is not a number" 99 | msgstr "El valor no es un número" 100 | 101 | #: formalchemy/validators.py:95 102 | msgid "Missing @ sign" 103 | msgstr "Falta el símbolo @" 104 | 105 | #: formalchemy/validators.py:98 106 | msgid "Control characters present" 107 | msgstr "Hay caracteres de control" 108 | 109 | #: formalchemy/validators.py:100 110 | msgid "Non-ASCII characters present" 111 | msgstr "Hay caracteres no ASCII" 112 | 113 | #: formalchemy/validators.py:104 114 | msgid "Recipient must be non-empty" 115 | msgstr "El destinatario no puede estar vacío" 116 | 117 | #: formalchemy/validators.py:106 118 | msgid "Recipient must not end with '.'" 119 | msgstr "El destinatario no puede terminar con '.'" 120 | 121 | #: formalchemy/validators.py:121 122 | msgid "Unterminated quoted section in recipient" 123 | msgstr "Sección citada no terminada en el destinatario" 124 | 125 | #: formalchemy/validators.py:124 126 | msgid "Quoted section must be followed by '@' or '.'" 127 | msgstr "La sección citada debe estar seguida de '@' o '.'" 128 | 129 | #: formalchemy/validators.py:127 130 | msgid "Reserved character present in recipient" 131 | msgstr "Hay un carácter reservado en el destinatario" 132 | 133 | #: formalchemy/validators.py:132 134 | msgid "Domain must be non-empty" 135 | msgstr "El dominio no puede estar vacío" 136 | 137 | #: formalchemy/validators.py:134 138 | msgid "Domain must not end with '.'" 139 | msgstr "El dominio no puede terminar con '.'" 140 | 141 | #: formalchemy/validators.py:136 142 | msgid "Domain must not contain '..'" 143 | msgstr "El dominio no puede contener '..'" 144 | 145 | #: formalchemy/validators.py:138 146 | msgid "Reserved character present in domain" 147 | msgstr "Hay un carácter reservado en el dominio" 148 | 149 | #: formalchemy/validators.py:148 150 | #, python-format 151 | msgid "Value must be at least %d characters long" 152 | msgstr "El valor debe tener al menos %d caracteres" 153 | 154 | #: formalchemy/validators.py:150 155 | #, python-format 156 | msgid "Value must be no more than %d characters long" 157 | msgstr "El valor no puede tener más de %d caracteres" 158 | 159 | #: formalchemy/validators.py:165 160 | msgid "Invalid input" 161 | msgstr "Entrada no válida" 162 | 163 | #: formalchemy/ext/fsblob.py:26 164 | #, python-format 165 | msgid "Invalid file extension. Must be %s, " 166 | msgstr "Extensión de fichero no válida. Debe ser %s, " 167 | 168 | #: formalchemy/ext/fsblob.py:33 169 | #, python-format 170 | msgid "Invalid image file. Must be %s, " 171 | msgstr "Fichero de imagen no válido. Debe ser %s, " 172 | 173 | #: formalchemy/ext/pylons/admin.py:25 174 | msgid "Add" 175 | msgstr "Agrerar" 176 | 177 | #: formalchemy/ext/pylons/admin.py:26 178 | msgid "Edit" 179 | msgstr "Editar" 180 | 181 | #: formalchemy/ext/pylons/admin.py:27 182 | msgid "New" 183 | msgstr "Nuevo" 184 | 185 | #: formalchemy/ext/pylons/admin.py:28 186 | msgid "Save" 187 | msgstr "Salvar" 188 | 189 | #: formalchemy/ext/pylons/admin.py:29 190 | msgid "Delete" 191 | msgstr "Eliminar" 192 | 193 | #: formalchemy/ext/pylons/admin.py:30 194 | msgid "Cancel" 195 | msgstr "Cancelar" 196 | 197 | #: formalchemy/ext/pylons/admin.py:31 198 | msgid "Models" 199 | msgstr "Modelos" 200 | 201 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 202 | msgid "Existing objects" 203 | msgstr "Objetos existentes" 204 | 205 | #: formalchemy/ext/pylons/admin.py:33 206 | msgid "New object" 207 | msgstr "Nuevo objeto" 208 | 209 | #: formalchemy/ext/pylons/admin.py:34 210 | msgid "Related types" 211 | msgstr "Tipos relacionados" 212 | 213 | #: formalchemy/ext/pylons/admin.py:36 214 | msgid "Create form" 215 | msgstr "Crear formulario" 216 | 217 | #: formalchemy/ext/pylons/admin.py:81 218 | msgid "edit" 219 | msgstr "editar" 220 | 221 | #: formalchemy/ext/pylons/admin.py:90 222 | msgid "delete" 223 | msgstr "eliminar" 224 | 225 | #: formalchemy/ext/pylons/admin.py:201 226 | #, python-format 227 | msgid "Created %s %s" 228 | msgstr "Creado %s %s" 229 | 230 | #: formalchemy/ext/pylons/admin.py:204 231 | #, python-format 232 | msgid "Modified %s %s" 233 | msgstr "Modificado %s %s" 234 | 235 | #: formalchemy/ext/pylons/admin.py:236 236 | #, python-format 237 | msgid "Deleted %s %s" 238 | msgstr "Eliminado %s %s" 239 | 240 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/fa/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Persian translations for FormAlchemy. 2 | # Copyright (C) 2011 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2011. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.3.9\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2014-04-22 18:57+0430\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: fa \n" 15 | "Plural-Forms: nplurals=1; plural=0\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 1.3\n" 20 | 21 | #: formalchemy/fields.py:474 22 | msgid "Remove" 23 | msgstr "حذف" 24 | 25 | #: formalchemy/i18n.py:85 26 | msgid "Year" 27 | msgstr "سال" 28 | 29 | #: formalchemy/i18n.py:86 30 | msgid "Month" 31 | msgstr "ماه" 32 | 33 | #: formalchemy/i18n.py:87 34 | msgid "Day" 35 | msgstr "روز" 36 | 37 | #: formalchemy/i18n.py:88 38 | msgid "month_01" 39 | msgstr "" 40 | 41 | #: formalchemy/i18n.py:89 42 | msgid "month_02" 43 | msgstr "" 44 | 45 | #: formalchemy/i18n.py:90 46 | msgid "month_03" 47 | msgstr "" 48 | 49 | #: formalchemy/i18n.py:91 50 | msgid "month_04" 51 | msgstr "" 52 | 53 | #: formalchemy/i18n.py:92 54 | msgid "month_05" 55 | msgstr "" 56 | 57 | #: formalchemy/i18n.py:93 58 | msgid "month_06" 59 | msgstr "" 60 | 61 | #: formalchemy/i18n.py:94 62 | msgid "month_07" 63 | msgstr "" 64 | 65 | #: formalchemy/i18n.py:95 66 | msgid "month_08" 67 | msgstr "" 68 | 69 | #: formalchemy/i18n.py:96 70 | msgid "month_09" 71 | msgstr "" 72 | 73 | #: formalchemy/i18n.py:97 74 | msgid "month_10" 75 | msgstr "" 76 | 77 | #: formalchemy/i18n.py:98 78 | msgid "month_11" 79 | msgstr "" 80 | 81 | #: formalchemy/i18n.py:99 82 | msgid "month_12" 83 | msgstr "" 84 | 85 | #: formalchemy/validators.py:32 86 | msgid "Please select a value" 87 | msgstr "لطفا گزینه‌ای را انتخاب کنید" 88 | 89 | #: formalchemy/validators.py:32 90 | msgid "Please enter a value" 91 | msgstr "لطفا مقداری را وارد کنید" 92 | 93 | #: formalchemy/validators.py:49 94 | msgid "Value is not an integer" 95 | msgstr "مقدار وارد شده عدد صحیح نیست" 96 | 97 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 98 | msgid "Value is not a number" 99 | msgstr "مقدار وارد شده عدد معتبر نیست" 100 | 101 | #: formalchemy/validators.py:95 102 | msgid "Missing @ sign" 103 | msgstr "پست الکترونیک وارد شده معتبر نیست" 104 | 105 | #: formalchemy/validators.py:98 106 | msgid "Control characters present" 107 | msgstr "پست الکترونیک وارد شده معتبر نیست" 108 | 109 | #: formalchemy/validators.py:100 110 | msgid "Non-ASCII characters present" 111 | msgstr "پست الکترونیک وارد شده معتبر نیست" 112 | 113 | #: formalchemy/validators.py:104 114 | msgid "Recipient must be non-empty" 115 | msgstr "پست الکترونیک وارد شده معتبر نیست" 116 | 117 | #: formalchemy/validators.py:106 118 | msgid "Recipient must not end with '.'" 119 | msgstr "پست الکترونیک وارد شده معتبر نیست" 120 | 121 | #: formalchemy/validators.py:121 122 | msgid "Unterminated quoted section in recipient" 123 | msgstr "پست الکترونیک وارد شده معتبر نیست" 124 | 125 | #: formalchemy/validators.py:124 126 | msgid "Quoted section must be followed by '@' or '.'" 127 | msgstr "پست الکترونیک وارد شده معتبر نیست" 128 | 129 | #: formalchemy/validators.py:127 130 | msgid "Reserved character present in recipient" 131 | msgstr "پست الکترونیک وارد شده معتبر نیست" 132 | 133 | #: formalchemy/validators.py:132 134 | msgid "Domain must be non-empty" 135 | msgstr "پست الکترونیک وارد شده معتبر نیست" 136 | 137 | #: formalchemy/validators.py:134 138 | msgid "Domain must not end with '.'" 139 | msgstr "پست الکترونیک وارد شده معتبر نیست" 140 | 141 | #: formalchemy/validators.py:136 142 | msgid "Domain must not contain '..'" 143 | msgstr "پست الکترونیک وارد شده معتبر نیست" 144 | 145 | #: formalchemy/validators.py:138 146 | msgid "Reserved character present in domain" 147 | msgstr "پست الکترونیک وارد شده معتبر نیست" 148 | 149 | #: formalchemy/validators.py:148 150 | #, python-format 151 | msgid "Value must be at least %d characters long" 152 | msgstr "مقدار وارد شده باید حداقل %d حرف باشد" 153 | 154 | #: formalchemy/validators.py:150 155 | #, python-format 156 | msgid "Value must be no more than %d characters long" 157 | msgstr "مقدار وارد شده باید حداکثر %d حرف باشد" 158 | 159 | #: formalchemy/validators.py:165 160 | msgid "Invalid input" 161 | msgstr "مقدار وارد شده نامعتبر است" 162 | 163 | #: formalchemy/ext/fsblob.py:26 164 | #, python-format 165 | msgid "Invalid file extension. Must be %s, " 166 | msgstr "پسوند فایل باید %s باشد" 167 | 168 | #: formalchemy/ext/fsblob.py:33 169 | #, python-format 170 | msgid "Invalid image file. Must be %s, " 171 | msgstr "نوع تصویر نامعتبر است و باید %s باشد." 172 | 173 | #: formalchemy/ext/pylons/admin.py:25 174 | msgid "Add" 175 | msgstr "اضافه کردن" 176 | 177 | #: formalchemy/ext/pylons/admin.py:26 178 | msgid "Edit" 179 | msgstr "ویرایش" 180 | 181 | #: formalchemy/ext/pylons/admin.py:27 182 | msgid "New" 183 | msgstr "جدید" 184 | 185 | #: formalchemy/ext/pylons/admin.py:28 186 | msgid "Save" 187 | msgstr "ذخیره" 188 | 189 | #: formalchemy/ext/pylons/admin.py:29 190 | msgid "Delete" 191 | msgstr "حذف" 192 | 193 | #: formalchemy/ext/pylons/admin.py:30 194 | msgid "Cancel" 195 | msgstr "لغو" 196 | 197 | #: formalchemy/ext/pylons/admin.py:31 198 | msgid "Models" 199 | msgstr "مدل‌ها" 200 | 201 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 202 | msgid "Existing objects" 203 | msgstr "اشیاء موجود" 204 | 205 | #: formalchemy/ext/pylons/admin.py:33 206 | msgid "New object" 207 | msgstr "شیء جدید" 208 | 209 | #: formalchemy/ext/pylons/admin.py:34 210 | msgid "Related types" 211 | msgstr "نوع‌های مربوط" 212 | 213 | #: formalchemy/ext/pylons/admin.py:36 214 | msgid "Create form" 215 | msgstr "فرم جدید" 216 | 217 | #: formalchemy/ext/pylons/admin.py:81 218 | msgid "edit" 219 | msgstr "ویرایش" 220 | 221 | #: formalchemy/ext/pylons/admin.py:90 222 | msgid "delete" 223 | msgstr "حذف" 224 | 225 | #: formalchemy/ext/pylons/admin.py:201 226 | #, python-format 227 | msgid "Created %s %s" 228 | msgstr "%s %s ایجاد شده" 229 | 230 | #: formalchemy/ext/pylons/admin.py:204 231 | #, python-format 232 | msgid "Modified %s %s" 233 | msgstr "%s %s تغییر کرده" 234 | 235 | #: formalchemy/ext/pylons/admin.py:236 236 | #, python-format 237 | msgid "Deleted %s %s" 238 | msgstr "%s %s حذف شده" 239 | 240 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/formalchemy.pot: -------------------------------------------------------------------------------- 1 | # Translations template for FormAlchemy. 2 | # Copyright (C) 2011 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy project. 4 | # FIRST AUTHOR , 2011. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.3.9\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 0.9.5\n" 19 | 20 | #: formalchemy/fields.py:474 21 | msgid "Remove" 22 | msgstr "" 23 | 24 | #: formalchemy/i18n.py:85 25 | msgid "Year" 26 | msgstr "" 27 | 28 | #: formalchemy/i18n.py:86 29 | msgid "Month" 30 | msgstr "" 31 | 32 | #: formalchemy/i18n.py:87 33 | msgid "Day" 34 | msgstr "" 35 | 36 | #: formalchemy/i18n.py:88 37 | msgid "month_01" 38 | msgstr "" 39 | 40 | #: formalchemy/i18n.py:89 41 | msgid "month_02" 42 | msgstr "" 43 | 44 | #: formalchemy/i18n.py:90 45 | msgid "month_03" 46 | msgstr "" 47 | 48 | #: formalchemy/i18n.py:91 49 | msgid "month_04" 50 | msgstr "" 51 | 52 | #: formalchemy/i18n.py:92 53 | msgid "month_05" 54 | msgstr "" 55 | 56 | #: formalchemy/i18n.py:93 57 | msgid "month_06" 58 | msgstr "" 59 | 60 | #: formalchemy/i18n.py:94 61 | msgid "month_07" 62 | msgstr "" 63 | 64 | #: formalchemy/i18n.py:95 65 | msgid "month_08" 66 | msgstr "" 67 | 68 | #: formalchemy/i18n.py:96 69 | msgid "month_09" 70 | msgstr "" 71 | 72 | #: formalchemy/i18n.py:97 73 | msgid "month_10" 74 | msgstr "" 75 | 76 | #: formalchemy/i18n.py:98 77 | msgid "month_11" 78 | msgstr "" 79 | 80 | #: formalchemy/i18n.py:99 81 | msgid "month_12" 82 | msgstr "" 83 | 84 | #: formalchemy/validators.py:32 85 | msgid "Please select a value" 86 | msgstr "" 87 | 88 | #: formalchemy/validators.py:32 89 | msgid "Please enter a value" 90 | msgstr "" 91 | 92 | #: formalchemy/validators.py:49 93 | msgid "Value is not an integer" 94 | msgstr "" 95 | 96 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 97 | msgid "Value is not a number" 98 | msgstr "" 99 | 100 | #: formalchemy/validators.py:95 101 | msgid "Missing @ sign" 102 | msgstr "" 103 | 104 | #: formalchemy/validators.py:98 105 | msgid "Control characters present" 106 | msgstr "" 107 | 108 | #: formalchemy/validators.py:100 109 | msgid "Non-ASCII characters present" 110 | msgstr "" 111 | 112 | #: formalchemy/validators.py:104 113 | msgid "Recipient must be non-empty" 114 | msgstr "" 115 | 116 | #: formalchemy/validators.py:106 117 | msgid "Recipient must not end with '.'" 118 | msgstr "" 119 | 120 | #: formalchemy/validators.py:121 121 | msgid "Unterminated quoted section in recipient" 122 | msgstr "" 123 | 124 | #: formalchemy/validators.py:124 125 | msgid "Quoted section must be followed by '@' or '.'" 126 | msgstr "" 127 | 128 | #: formalchemy/validators.py:127 129 | msgid "Reserved character present in recipient" 130 | msgstr "" 131 | 132 | #: formalchemy/validators.py:132 133 | msgid "Domain must be non-empty" 134 | msgstr "" 135 | 136 | #: formalchemy/validators.py:134 137 | msgid "Domain must not end with '.'" 138 | msgstr "" 139 | 140 | #: formalchemy/validators.py:136 141 | msgid "Domain must not contain '..'" 142 | msgstr "" 143 | 144 | #: formalchemy/validators.py:138 145 | msgid "Reserved character present in domain" 146 | msgstr "" 147 | 148 | #: formalchemy/validators.py:148 149 | #, python-format 150 | msgid "Value must be at least %d characters long" 151 | msgstr "" 152 | 153 | #: formalchemy/validators.py:150 154 | #, python-format 155 | msgid "Value must be no more than %d characters long" 156 | msgstr "" 157 | 158 | #: formalchemy/validators.py:165 159 | msgid "Invalid input" 160 | msgstr "" 161 | 162 | #: formalchemy/ext/fsblob.py:26 163 | #, python-format 164 | msgid "Invalid file extension. Must be %s, " 165 | msgstr "" 166 | 167 | #: formalchemy/ext/fsblob.py:33 168 | #, python-format 169 | msgid "Invalid image file. Must be %s, " 170 | msgstr "" 171 | 172 | #: formalchemy/ext/pylons/admin.py:25 173 | msgid "Add" 174 | msgstr "" 175 | 176 | #: formalchemy/ext/pylons/admin.py:26 177 | msgid "Edit" 178 | msgstr "" 179 | 180 | #: formalchemy/ext/pylons/admin.py:27 181 | msgid "New" 182 | msgstr "" 183 | 184 | #: formalchemy/ext/pylons/admin.py:28 185 | msgid "Save" 186 | msgstr "" 187 | 188 | #: formalchemy/ext/pylons/admin.py:29 189 | msgid "Delete" 190 | msgstr "" 191 | 192 | #: formalchemy/ext/pylons/admin.py:30 193 | msgid "Cancel" 194 | msgstr "" 195 | 196 | #: formalchemy/ext/pylons/admin.py:31 197 | msgid "Models" 198 | msgstr "" 199 | 200 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 201 | msgid "Existing objects" 202 | msgstr "" 203 | 204 | #: formalchemy/ext/pylons/admin.py:33 205 | msgid "New object" 206 | msgstr "" 207 | 208 | #: formalchemy/ext/pylons/admin.py:34 209 | msgid "Related types" 210 | msgstr "" 211 | 212 | #: formalchemy/ext/pylons/admin.py:36 213 | msgid "Create form" 214 | msgstr "" 215 | 216 | #: formalchemy/ext/pylons/admin.py:81 217 | msgid "edit" 218 | msgstr "" 219 | 220 | #: formalchemy/ext/pylons/admin.py:90 221 | msgid "delete" 222 | msgstr "" 223 | 224 | #: formalchemy/ext/pylons/admin.py:201 225 | #, python-format 226 | msgid "Created %s %s" 227 | msgstr "" 228 | 229 | #: formalchemy/ext/pylons/admin.py:204 230 | #, python-format 231 | msgid "Modified %s %s" 232 | msgstr "" 233 | 234 | #: formalchemy/ext/pylons/admin.py:236 235 | #, python-format 236 | msgid "Deleted %s %s" 237 | msgstr "" 238 | 239 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/hu/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Hungarian translations for FormAlchemy. 2 | # Copyright (C) 2009 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2009. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: FormAlchemy 1.2\n" 11 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 12 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 13 | "PO-Revision-Date: 2011-06-21 16:13+0200\n" 14 | "Last-Translator: Bajusz Tamás \n" 15 | "Language-Team: Hungarian \n" 16 | "Plural-Forms: nplurals=2; plural=(n!=1)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 0.9.6\n" 21 | 22 | #: formalchemy/fields.py:474 23 | msgid "Remove" 24 | msgstr "Törlés" 25 | 26 | #: formalchemy/i18n.py:85 27 | msgid "Year" 28 | msgstr "Év" 29 | 30 | #: formalchemy/i18n.py:86 31 | msgid "Month" 32 | msgstr "Hó" 33 | 34 | #: formalchemy/i18n.py:87 35 | msgid "Day" 36 | msgstr "Nap" 37 | 38 | #: formalchemy/i18n.py:88 39 | msgid "month_01" 40 | msgstr "Január" 41 | 42 | #: formalchemy/i18n.py:89 43 | msgid "month_02" 44 | msgstr "Február" 45 | 46 | #: formalchemy/i18n.py:90 47 | msgid "month_03" 48 | msgstr "Március" 49 | 50 | #: formalchemy/i18n.py:91 51 | msgid "month_04" 52 | msgstr "Április" 53 | 54 | #: formalchemy/i18n.py:92 55 | msgid "month_05" 56 | msgstr "Május" 57 | 58 | #: formalchemy/i18n.py:93 59 | msgid "month_06" 60 | msgstr "Június" 61 | 62 | #: formalchemy/i18n.py:94 63 | msgid "month_07" 64 | msgstr "Július" 65 | 66 | #: formalchemy/i18n.py:95 67 | msgid "month_08" 68 | msgstr "Augusztus" 69 | 70 | #: formalchemy/i18n.py:96 71 | msgid "month_09" 72 | msgstr "Szeptember" 73 | 74 | #: formalchemy/i18n.py:97 75 | msgid "month_10" 76 | msgstr "Október" 77 | 78 | #: formalchemy/i18n.py:98 79 | msgid "month_11" 80 | msgstr "November" 81 | 82 | #: formalchemy/i18n.py:99 83 | msgid "month_12" 84 | msgstr "December" 85 | 86 | #: formalchemy/validators.py:32 87 | msgid "Please select a value" 88 | msgstr "Válassz egy értéket" 89 | 90 | #: formalchemy/validators.py:32 91 | msgid "Please enter a value" 92 | msgstr "Rögzíts egy értéket" 93 | 94 | #: formalchemy/validators.py:49 95 | msgid "Value is not an integer" 96 | msgstr "Az érték nem egész szám" 97 | 98 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 99 | msgid "Value is not a number" 100 | msgstr "Az érték nem szám" 101 | 102 | #: formalchemy/validators.py:95 103 | msgid "Missing @ sign" 104 | msgstr "Hiányzó @ jel" 105 | 106 | #: formalchemy/validators.py:98 107 | msgid "Control characters present" 108 | msgstr "Kontrol karakterek is vannak" 109 | 110 | #: formalchemy/validators.py:100 111 | msgid "Non-ASCII characters present" 112 | msgstr "Nem ASCII karakterek is vannak" 113 | 114 | #: formalchemy/validators.py:104 115 | msgid "Recipient must be non-empty" 116 | msgstr "A címzett nem lehet üres" 117 | 118 | #: formalchemy/validators.py:106 119 | msgid "Recipient must not end with '.'" 120 | msgstr "A címzett nem végződhet '.'-ra" 121 | 122 | #: formalchemy/validators.py:121 123 | msgid "Unterminated quoted section in recipient" 124 | msgstr "Meghatározhatatlan első rész a címzettben" 125 | 126 | #: formalchemy/validators.py:124 127 | msgid "Quoted section must be followed by '@' or '.'" 128 | msgstr "Az első rész után '@' vagy '.' karakter kell" 129 | 130 | #: formalchemy/validators.py:127 131 | msgid "Reserved character present in recipient" 132 | msgstr "Foglalt karakterek a címzettben" 133 | 134 | #: formalchemy/validators.py:132 135 | msgid "Domain must be non-empty" 136 | msgstr "A domain nem lehet üres" 137 | 138 | #: formalchemy/validators.py:134 139 | msgid "Domain must not end with '.'" 140 | msgstr "A domain nem végződhet '.'-ra" 141 | 142 | #: formalchemy/validators.py:136 143 | msgid "Domain must not contain '..'" 144 | msgstr "A domain nem tartalmazhat '..'-ot" 145 | 146 | #: formalchemy/validators.py:138 147 | msgid "Reserved character present in domain" 148 | msgstr "Foglalt karakter a domain-ben" 149 | 150 | #: formalchemy/validators.py:148 151 | #, python-format 152 | msgid "Value must be at least %d characters long" 153 | msgstr "Az értéknek legalább %d hosszúnak kell lennie" 154 | 155 | #: formalchemy/validators.py:150 156 | #, python-format 157 | msgid "Value must be no more than %d characters long" 158 | msgstr "Az érték nem lehet hosszabb %d karakternél" 159 | 160 | #: formalchemy/validators.py:165 161 | msgid "Invalid input" 162 | msgstr "Hibás érték" 163 | 164 | #: formalchemy/ext/fsblob.py:26 165 | #, python-format 166 | msgid "Invalid file extension. Must be %s, " 167 | msgstr "Hibás kiterjesztés. %s kell legyen, " 168 | 169 | #: formalchemy/ext/fsblob.py:33 170 | #, python-format 171 | msgid "Invalid image file. Must be %s, " 172 | msgstr "Hibás kép fájl. %s kell legyen, " 173 | 174 | #: formalchemy/ext/pylons/admin.py:25 175 | msgid "Add" 176 | msgstr "Hozzáadás" 177 | 178 | #: formalchemy/ext/pylons/admin.py:26 179 | msgid "Edit" 180 | msgstr "Módosítás" 181 | 182 | #: formalchemy/ext/pylons/admin.py:27 183 | msgid "New" 184 | msgstr "Új" 185 | 186 | #: formalchemy/ext/pylons/admin.py:28 187 | msgid "Save" 188 | msgstr "Mentés" 189 | 190 | #: formalchemy/ext/pylons/admin.py:29 191 | msgid "Delete" 192 | msgstr "Törlés" 193 | 194 | #: formalchemy/ext/pylons/admin.py:30 195 | msgid "Cancel" 196 | msgstr "Mégse" 197 | 198 | #: formalchemy/ext/pylons/admin.py:31 199 | msgid "Models" 200 | msgstr "Modellek" 201 | 202 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 203 | msgid "Existing objects" 204 | msgstr "Objektumok" 205 | 206 | #: formalchemy/ext/pylons/admin.py:33 207 | msgid "New object" 208 | msgstr "Új objektum" 209 | 210 | #: formalchemy/ext/pylons/admin.py:34 211 | msgid "Related types" 212 | msgstr "Összetartozó típusok" 213 | 214 | #: formalchemy/ext/pylons/admin.py:36 215 | msgid "Create form" 216 | msgstr "Űrlap létrehozása" 217 | 218 | #: formalchemy/ext/pylons/admin.py:81 219 | msgid "edit" 220 | msgstr "módosítás" 221 | 222 | #: formalchemy/ext/pylons/admin.py:90 223 | msgid "delete" 224 | msgstr "törlés" 225 | 226 | #: formalchemy/ext/pylons/admin.py:201 227 | #, python-format 228 | msgid "Created %s %s" 229 | msgstr "Készült %s %s" 230 | 231 | #: formalchemy/ext/pylons/admin.py:204 232 | #, python-format 233 | msgid "Modified %s %s" 234 | msgstr "Módosítva %s %s" 235 | 236 | #: formalchemy/ext/pylons/admin.py:236 237 | #, python-format 238 | msgid "Deleted %s %s" 239 | msgstr "Törölve %s %s" 240 | 241 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/ja/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Japanese translations for FormAlchemy. 2 | # Copyright (C) 2011 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # Atsushi Odagiri , 2011. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.3.9\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2012-01-03 08:11-0800\n" 13 | "Last-Translator: Atsushi Odagiri \n" 14 | "Language-Team: Japanese\n" 15 | "Plural-Forms: nplurals=1; plural=0\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 0.9.6\n" 20 | 21 | #: formalchemy/fields.py:474 22 | msgid "Remove" 23 | msgstr "削除" 24 | 25 | #: formalchemy/i18n.py:85 26 | msgid "Year" 27 | msgstr "年" 28 | 29 | #: formalchemy/i18n.py:86 30 | msgid "Month" 31 | msgstr "月" 32 | 33 | #: formalchemy/i18n.py:87 34 | msgid "Day" 35 | msgstr "日" 36 | 37 | #: formalchemy/i18n.py:88 38 | msgid "month_01" 39 | msgstr "1月" 40 | 41 | #: formalchemy/i18n.py:89 42 | msgid "month_02" 43 | msgstr "2月" 44 | 45 | #: formalchemy/i18n.py:90 46 | msgid "month_03" 47 | msgstr "3月" 48 | 49 | #: formalchemy/i18n.py:91 50 | msgid "month_04" 51 | msgstr "4月" 52 | 53 | #: formalchemy/i18n.py:92 54 | msgid "month_05" 55 | msgstr "5月" 56 | 57 | #: formalchemy/i18n.py:93 58 | msgid "month_06" 59 | msgstr "6月" 60 | 61 | #: formalchemy/i18n.py:94 62 | msgid "month_07" 63 | msgstr "7月" 64 | 65 | #: formalchemy/i18n.py:95 66 | msgid "month_08" 67 | msgstr "8月" 68 | 69 | #: formalchemy/i18n.py:96 70 | msgid "month_09" 71 | msgstr "9月" 72 | 73 | #: formalchemy/i18n.py:97 74 | msgid "month_10" 75 | msgstr "10月" 76 | 77 | #: formalchemy/i18n.py:98 78 | msgid "month_11" 79 | msgstr "11月" 80 | 81 | #: formalchemy/i18n.py:99 82 | msgid "month_12" 83 | msgstr "12月" 84 | 85 | #: formalchemy/validators.py:32 86 | msgid "Please select a value" 87 | msgstr "値を選択してください" 88 | 89 | #: formalchemy/validators.py:32 90 | msgid "Please enter a value" 91 | msgstr "値を入力してください" 92 | 93 | #: formalchemy/validators.py:49 94 | msgid "Value is not an integer" 95 | msgstr "値が整数値でありません" 96 | 97 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 98 | msgid "Value is not a number" 99 | msgstr "値が数値でありません" 100 | 101 | #: formalchemy/validators.py:95 102 | msgid "Missing @ sign" 103 | msgstr "@がありません" 104 | 105 | #: formalchemy/validators.py:98 106 | msgid "Control characters present" 107 | msgstr "制御文字列が含まれています" 108 | 109 | #: formalchemy/validators.py:100 110 | msgid "Non-ASCII characters present" 111 | msgstr "アルファベットや数値以外の文字が含まれています" 112 | 113 | #: formalchemy/validators.py:104 114 | msgid "Recipient must be non-empty" 115 | msgstr "メールアカウントがありません" 116 | 117 | #: formalchemy/validators.py:106 118 | msgid "Recipient must not end with '.'" 119 | msgstr "メールアカウントは '.' で終了してはいけません" 120 | 121 | #: formalchemy/validators.py:121 122 | msgid "Unterminated quoted section in recipient" 123 | msgstr "メールアカウントの引用符が終了していません" 124 | 125 | #: formalchemy/validators.py:124 126 | msgid "Quoted section must be followed by '@' or '.'" 127 | msgstr "メールアカウントの引用符で囲まれた部分は、 '@' か '.' が続かなければなりません" 128 | 129 | #: formalchemy/validators.py:127 130 | msgid "Reserved character present in recipient" 131 | msgstr "予約語がメールアカウントに含まれています" 132 | 133 | #: formalchemy/validators.py:132 134 | msgid "Domain must be non-empty" 135 | msgstr "ドメインが入力されていません" 136 | 137 | #: formalchemy/validators.py:134 138 | msgid "Domain must not end with '.'" 139 | msgstr "ドメインは '.' で終了してはいけません" 140 | 141 | #: formalchemy/validators.py:136 142 | msgid "Domain must not contain '..'" 143 | msgstr "ドメインは '..' を含んではいけません" 144 | 145 | #: formalchemy/validators.py:138 146 | msgid "Reserved character present in domain" 147 | msgstr "ドメインに予約文字が含まれています" 148 | 149 | #: formalchemy/validators.py:148 150 | #, python-format 151 | msgid "Value must be at least %d characters long" 152 | msgstr "少なくとも %d 文字以上を入力してください" 153 | 154 | #: formalchemy/validators.py:150 155 | #, python-format 156 | msgid "Value must be no more than %d characters long" 157 | msgstr "%d 文字以下の文字数で入力してください" 158 | 159 | #: formalchemy/validators.py:165 160 | msgid "Invalid input" 161 | msgstr "内容に間違いがあります" 162 | 163 | #: formalchemy/ext/fsblob.py:26 164 | #, python-format 165 | msgid "Invalid file extension. Must be %s, " 166 | msgstr "ファイルの拡張子が間違っています。 %s にしてください" 167 | 168 | #: formalchemy/ext/fsblob.py:33 169 | #, python-format 170 | msgid "Invalid image file. Must be %s, " 171 | msgstr "画像ファイルではありません。 %s にしてください" 172 | 173 | #: formalchemy/ext/pylons/admin.py:25 174 | msgid "Add" 175 | msgstr "追加" 176 | 177 | #: formalchemy/ext/pylons/admin.py:26 178 | msgid "Edit" 179 | msgstr "編集" 180 | 181 | #: formalchemy/ext/pylons/admin.py:27 182 | msgid "New" 183 | msgstr "新規" 184 | 185 | #: formalchemy/ext/pylons/admin.py:28 186 | msgid "Save" 187 | msgstr "保存" 188 | 189 | #: formalchemy/ext/pylons/admin.py:29 190 | msgid "Delete" 191 | msgstr "削除" 192 | 193 | #: formalchemy/ext/pylons/admin.py:30 194 | msgid "Cancel" 195 | msgstr "キャンセル" 196 | 197 | #: formalchemy/ext/pylons/admin.py:31 198 | msgid "Models" 199 | msgstr "モデル" 200 | 201 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 202 | msgid "Existing objects" 203 | msgstr "既存のオブジェクト" 204 | 205 | #: formalchemy/ext/pylons/admin.py:33 206 | msgid "New object" 207 | msgstr "新規オブジェクト" 208 | 209 | #: formalchemy/ext/pylons/admin.py:34 210 | msgid "Related types" 211 | msgstr "" 212 | 213 | #: formalchemy/ext/pylons/admin.py:36 214 | msgid "Create form" 215 | msgstr "新規追加フォーム" 216 | 217 | #: formalchemy/ext/pylons/admin.py:81 218 | msgid "edit" 219 | msgstr "編集" 220 | 221 | #: formalchemy/ext/pylons/admin.py:90 222 | msgid "delete" 223 | msgstr "削除" 224 | 225 | #: formalchemy/ext/pylons/admin.py:201 226 | #, python-format 227 | msgid "Created %s %s" 228 | msgstr "%s %s が作成されました" 229 | 230 | #: formalchemy/ext/pylons/admin.py:204 231 | #, python-format 232 | msgid "Modified %s %s" 233 | msgstr "%s %s が変更されました" 234 | 235 | #: formalchemy/ext/pylons/admin.py:236 236 | #, python-format 237 | msgid "Deleted %s %s" 238 | msgstr "%s %s が削除されました" 239 | 240 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/pl/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Polish translations for FormAlchemy. 2 | # Copyright (C) 2009 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2009. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.3.1\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2011-06-21 16:13+0200\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: pl \n" 15 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " 16 | "(n%100<10 || n%100>=20) ? 1 : 2)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=utf-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Generated-By: Babel 0.9.6\n" 21 | 22 | #: formalchemy/fields.py:474 23 | msgid "Remove" 24 | msgstr "Usuń" 25 | 26 | #: formalchemy/i18n.py:85 27 | msgid "Year" 28 | msgstr "Rok" 29 | 30 | #: formalchemy/i18n.py:86 31 | msgid "Month" 32 | msgstr "Miesiąc" 33 | 34 | #: formalchemy/i18n.py:87 35 | msgid "Day" 36 | msgstr "Dzień" 37 | 38 | #: formalchemy/i18n.py:88 39 | msgid "month_01" 40 | msgstr "styczeń" 41 | 42 | #: formalchemy/i18n.py:89 43 | msgid "month_02" 44 | msgstr "luty" 45 | 46 | #: formalchemy/i18n.py:90 47 | msgid "month_03" 48 | msgstr "marzec" 49 | 50 | #: formalchemy/i18n.py:91 51 | msgid "month_04" 52 | msgstr "kwiecień" 53 | 54 | #: formalchemy/i18n.py:92 55 | msgid "month_05" 56 | msgstr "maj" 57 | 58 | #: formalchemy/i18n.py:93 59 | msgid "month_06" 60 | msgstr "czerwiec" 61 | 62 | #: formalchemy/i18n.py:94 63 | msgid "month_07" 64 | msgstr "lipiec" 65 | 66 | #: formalchemy/i18n.py:95 67 | msgid "month_08" 68 | msgstr "sierpień" 69 | 70 | #: formalchemy/i18n.py:96 71 | msgid "month_09" 72 | msgstr "wrzesień" 73 | 74 | #: formalchemy/i18n.py:97 75 | msgid "month_10" 76 | msgstr "październik" 77 | 78 | #: formalchemy/i18n.py:98 79 | msgid "month_11" 80 | msgstr "listopad" 81 | 82 | #: formalchemy/i18n.py:99 83 | msgid "month_12" 84 | msgstr "grudzień" 85 | 86 | #: formalchemy/validators.py:32 87 | msgid "Please select a value" 88 | msgstr "Proszę wybrać wartość" 89 | 90 | #: formalchemy/validators.py:32 91 | msgid "Please enter a value" 92 | msgstr "Proszę wprowadzić wartość" 93 | 94 | #: formalchemy/validators.py:49 95 | msgid "Value is not an integer" 96 | msgstr "Wartość musi być liczbą" 97 | 98 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 99 | msgid "Value is not a number" 100 | msgstr "Wartość musi być liczbą" 101 | 102 | #: formalchemy/validators.py:95 103 | msgid "Missing @ sign" 104 | msgstr "Brak znaku @" 105 | 106 | #: formalchemy/validators.py:98 107 | msgid "Control characters present" 108 | msgstr "Niepoprawne znaki" 109 | 110 | #: formalchemy/validators.py:100 111 | msgid "Non-ASCII characters present" 112 | msgstr "Niepoprawne znaki" 113 | 114 | #: formalchemy/validators.py:104 115 | msgid "Recipient must be non-empty" 116 | msgstr "Nie podano odbiorcy" 117 | 118 | #: formalchemy/validators.py:106 119 | msgid "Recipient must not end with '.'" 120 | msgstr "Odbiorca nie może kończyć się na '.'" 121 | 122 | #: formalchemy/validators.py:121 123 | msgid "Unterminated quoted section in recipient" 124 | msgstr "Niezamknięty cudzysłów w odbiorcy" 125 | 126 | #: formalchemy/validators.py:124 127 | msgid "Quoted section must be followed by '@' or '.'" 128 | msgstr "Za cudzysłowem musi być znak '@' lub '.' " 129 | 130 | #: formalchemy/validators.py:127 131 | msgid "Reserved character present in recipient" 132 | msgstr "Zarezerwowany znak w odbiorcy" 133 | 134 | #: formalchemy/validators.py:132 135 | msgid "Domain must be non-empty" 136 | msgstr "Podaj domenę" 137 | 138 | #: formalchemy/validators.py:134 139 | msgid "Domain must not end with '.'" 140 | msgstr "Domena nie może kończyć się na '.'" 141 | 142 | #: formalchemy/validators.py:136 143 | msgid "Domain must not contain '..'" 144 | msgstr "Domena nie może zawierać '..'" 145 | 146 | #: formalchemy/validators.py:138 147 | msgid "Reserved character present in domain" 148 | msgstr "Zarezerwowany znak w domenie" 149 | 150 | #: formalchemy/validators.py:148 151 | #, python-format 152 | msgid "Value must be at least %d characters long" 153 | msgstr "Wartość nie może być krótsza niż %d znaków" 154 | 155 | #: formalchemy/validators.py:150 156 | #, python-format 157 | msgid "Value must be no more than %d characters long" 158 | msgstr "Wartość nie może być dłuższa niż %d znaków" 159 | 160 | #: formalchemy/validators.py:165 161 | msgid "Invalid input" 162 | msgstr "Niepoprawna wartość" 163 | 164 | #: formalchemy/ext/fsblob.py:26 165 | #, python-format 166 | msgid "Invalid file extension. Must be %s, " 167 | msgstr "Niepoprawne rozszerzenie pliku. Powinno być %s, " 168 | 169 | #: formalchemy/ext/fsblob.py:33 170 | #, python-format 171 | msgid "Invalid image file. Must be %s, " 172 | msgstr "Niepoprawny plik z obrazem. Powinno być %s, " 173 | 174 | #: formalchemy/ext/pylons/admin.py:25 175 | msgid "Add" 176 | msgstr "Dodaj" 177 | 178 | #: formalchemy/ext/pylons/admin.py:26 179 | msgid "Edit" 180 | msgstr "Edytuj" 181 | 182 | #: formalchemy/ext/pylons/admin.py:27 183 | msgid "New" 184 | msgstr "Nowy" 185 | 186 | #: formalchemy/ext/pylons/admin.py:28 187 | msgid "Save" 188 | msgstr "Zapisz" 189 | 190 | #: formalchemy/ext/pylons/admin.py:29 191 | msgid "Delete" 192 | msgstr "Usuń" 193 | 194 | #: formalchemy/ext/pylons/admin.py:30 195 | msgid "Cancel" 196 | msgstr "Anuluj" 197 | 198 | #: formalchemy/ext/pylons/admin.py:31 199 | msgid "Models" 200 | msgstr "Modele" 201 | 202 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 203 | msgid "Existing objects" 204 | msgstr "Istniejące obiekty" 205 | 206 | #: formalchemy/ext/pylons/admin.py:33 207 | msgid "New object" 208 | msgstr "Nowy obiekt" 209 | 210 | #: formalchemy/ext/pylons/admin.py:34 211 | msgid "Related types" 212 | msgstr "Powiązane typy" 213 | 214 | #: formalchemy/ext/pylons/admin.py:36 215 | msgid "Create form" 216 | msgstr "Stwórz formularz" 217 | 218 | #: formalchemy/ext/pylons/admin.py:81 219 | msgid "edit" 220 | msgstr "edytuj" 221 | 222 | #: formalchemy/ext/pylons/admin.py:90 223 | msgid "delete" 224 | msgstr "usuń" 225 | 226 | #: formalchemy/ext/pylons/admin.py:201 227 | #, python-format 228 | msgid "Created %s %s" 229 | msgstr "Stworzono %s %s" 230 | 231 | #: formalchemy/ext/pylons/admin.py:204 232 | #, python-format 233 | msgid "Modified %s %s" 234 | msgstr "Zmodyfikowano %s %s" 235 | 236 | #: formalchemy/ext/pylons/admin.py:236 237 | #, python-format 238 | msgid "Deleted %s %s" 239 | msgstr "Usunięto %s %s" 240 | 241 | -------------------------------------------------------------------------------- /formalchemy/i18n_resources/pt_BR/LC_MESSAGES/formalchemy.po: -------------------------------------------------------------------------------- 1 | # Portuguese (Brazil) translations for FormAlchemy. 2 | # Copyright (C) 2011 ORGANIZATION 3 | # This file is distributed under the same license as the FormAlchemy 4 | # project. 5 | # FIRST AUTHOR , 2011. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: FormAlchemy 1.3.9\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2011-06-21 16:13+0200\n" 12 | "PO-Revision-Date: 2011-08-01 20:45-0300\n" 13 | "Last-Translator: Rodrigo Ferreira de Souza \n" 14 | "Language-Team: pt_BR \n" 15 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 0.9.6\n" 20 | 21 | #: formalchemy/fields.py:474 22 | msgid "Remove" 23 | msgstr "Remover" 24 | 25 | #: formalchemy/i18n.py:85 26 | msgid "Year" 27 | msgstr "Ano" 28 | 29 | #: formalchemy/i18n.py:86 30 | msgid "Month" 31 | msgstr "Mês" 32 | 33 | #: formalchemy/i18n.py:87 34 | msgid "Day" 35 | msgstr "Dia" 36 | 37 | #: formalchemy/i18n.py:88 38 | msgid "month_01" 39 | msgstr "mês_01" 40 | 41 | #: formalchemy/i18n.py:89 42 | msgid "month_02" 43 | msgstr "mês_02" 44 | 45 | #: formalchemy/i18n.py:90 46 | msgid "month_03" 47 | msgstr "mês_03" 48 | 49 | #: formalchemy/i18n.py:91 50 | msgid "month_04" 51 | msgstr "mês_04" 52 | 53 | #: formalchemy/i18n.py:92 54 | msgid "month_05" 55 | msgstr "mês_05" 56 | 57 | #: formalchemy/i18n.py:93 58 | msgid "month_06" 59 | msgstr "mês_06" 60 | 61 | #: formalchemy/i18n.py:94 62 | msgid "month_07" 63 | msgstr "mês_07" 64 | 65 | #: formalchemy/i18n.py:95 66 | msgid "month_08" 67 | msgstr "mês_09" 68 | 69 | #: formalchemy/i18n.py:96 70 | msgid "month_09" 71 | msgstr "mês_09" 72 | 73 | #: formalchemy/i18n.py:97 74 | msgid "month_10" 75 | msgstr "mês_10" 76 | 77 | #: formalchemy/i18n.py:98 78 | msgid "month_11" 79 | msgstr "mês_11" 80 | 81 | #: formalchemy/i18n.py:99 82 | msgid "month_12" 83 | msgstr "mês_12" 84 | 85 | #: formalchemy/validators.py:32 86 | msgid "Please select a value" 87 | msgstr "Por favor selecione um valor" 88 | 89 | #: formalchemy/validators.py:32 90 | msgid "Please enter a value" 91 | msgstr "Por favor digite um valor" 92 | 93 | #: formalchemy/validators.py:49 94 | msgid "Value is not an integer" 95 | msgstr "Valor não é um inteiro" 96 | 97 | #: formalchemy/validators.py:60 formalchemy/validators.py:72 98 | msgid "Value is not a number" 99 | msgstr "Valor não é um número" 100 | 101 | #: formalchemy/validators.py:95 102 | msgid "Missing @ sign" 103 | msgstr "Faltando o símbolo @" 104 | 105 | #: formalchemy/validators.py:98 106 | msgid "Control characters present" 107 | msgstr "Caracteres de controle encontrados" 108 | 109 | #: formalchemy/validators.py:100 110 | msgid "Non-ASCII characters present" 111 | msgstr "Encontrados caracteres não-ASCII" 112 | 113 | #: formalchemy/validators.py:104 114 | msgid "Recipient must be non-empty" 115 | msgstr "Campo deve ser preenchido" 116 | 117 | #: formalchemy/validators.py:106 118 | msgid "Recipient must not end with '.'" 119 | msgstr "Campo não pode terminar com '.'" 120 | 121 | #: formalchemy/validators.py:121 122 | msgid "Unterminated quoted section in recipient" 123 | msgstr "Campo com com aspas não fechadas" 124 | 125 | #: formalchemy/validators.py:124 126 | msgid "Quoted section must be followed by '@' or '.'" 127 | msgstr "Campo deve ser seguido por '@' ou '.'" 128 | 129 | #: formalchemy/validators.py:127 130 | msgid "Reserved character present in recipient" 131 | msgstr "Caractere proibido encontrado no campo" 132 | 133 | #: formalchemy/validators.py:132 134 | msgid "Domain must be non-empty" 135 | msgstr "Campo deve ser preenchido" 136 | 137 | #: formalchemy/validators.py:134 138 | msgid "Domain must not end with '.'" 139 | msgstr "Campo não deve terminar com '.'" 140 | 141 | #: formalchemy/validators.py:136 142 | msgid "Domain must not contain '..'" 143 | msgstr "Campo não pode conter '..'" 144 | 145 | #: formalchemy/validators.py:138 146 | msgid "Reserved character present in domain" 147 | msgstr "Caractere proibido encontrado no campo" 148 | 149 | #: formalchemy/validators.py:148 150 | #, python-format 151 | msgid "Value must be at least %d characters long" 152 | msgstr "Valor deve ter pelo menos %d caracteres" 153 | 154 | #: formalchemy/validators.py:150 155 | #, python-format 156 | msgid "Value must be no more than %d characters long" 157 | msgstr "Valor deve ser menor que %d caracteres" 158 | 159 | #: formalchemy/validators.py:165 160 | msgid "Invalid input" 161 | msgstr "Entrada inválida" 162 | 163 | #: formalchemy/ext/fsblob.py:26 164 | #, python-format 165 | msgid "Invalid file extension. Must be %s, " 166 | msgstr "Extensão de arquivo inválida. Deve ser %s," 167 | 168 | #: formalchemy/ext/fsblob.py:33 169 | #, python-format 170 | msgid "Invalid image file. Must be %s, " 171 | msgstr "Arquivo de imagem inválido. Deve ser %s." 172 | 173 | #: formalchemy/ext/pylons/admin.py:25 174 | msgid "Add" 175 | msgstr "Adicionar" 176 | 177 | #: formalchemy/ext/pylons/admin.py:26 178 | msgid "Edit" 179 | msgstr "Editar" 180 | 181 | #: formalchemy/ext/pylons/admin.py:27 182 | msgid "New" 183 | msgstr "Novo" 184 | 185 | #: formalchemy/ext/pylons/admin.py:28 186 | msgid "Save" 187 | msgstr "Salvar" 188 | 189 | #: formalchemy/ext/pylons/admin.py:29 190 | msgid "Delete" 191 | msgstr "Apagar" 192 | 193 | #: formalchemy/ext/pylons/admin.py:30 194 | msgid "Cancel" 195 | msgstr "Cancelar" 196 | 197 | #: formalchemy/ext/pylons/admin.py:31 198 | msgid "Models" 199 | msgstr "Modelos" 200 | 201 | #: formalchemy/ext/pylons/admin.py:32 formalchemy/ext/pylons/admin.py:35 202 | msgid "Existing objects" 203 | msgstr "Objetos existentes" 204 | 205 | #: formalchemy/ext/pylons/admin.py:33 206 | msgid "New object" 207 | msgstr "Novo objeto" 208 | 209 | #: formalchemy/ext/pylons/admin.py:34 210 | msgid "Related types" 211 | msgstr "Tipos relacionados" 212 | 213 | #: formalchemy/ext/pylons/admin.py:36 214 | msgid "Create form" 215 | msgstr "Criar formulário" 216 | 217 | #: formalchemy/ext/pylons/admin.py:81 218 | msgid "edit" 219 | msgstr "editar" 220 | 221 | #: formalchemy/ext/pylons/admin.py:90 222 | msgid "delete" 223 | msgstr "apagar" 224 | 225 | #: formalchemy/ext/pylons/admin.py:201 226 | #, python-format 227 | msgid "Created %s %s" 228 | msgstr "Criado %s %s" 229 | 230 | #: formalchemy/ext/pylons/admin.py:204 231 | #, python-format 232 | msgid "Modified %s %s" 233 | msgstr "Modificado %s %s" 234 | 235 | #: formalchemy/ext/pylons/admin.py:236 236 | #, python-format 237 | msgid "Deleted %s %s" 238 | msgstr "Apagado %s %s" 239 | 240 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/config/routing.py_tmpl: -------------------------------------------------------------------------------- 1 | """Routes configuration 2 | 3 | The more specific and detailed routes should be defined first so they 4 | may take precedent over the more generic routes. For more information 5 | refer to the routes manual at http://routes.groovie.org/docs/ 6 | """ 7 | from routes import Mapper 8 | 9 | def make_map(config): 10 | """Create, configure and return the routes Mapper""" 11 | map = Mapper(directory=config['pylons.paths']['controllers'], 12 | always_scan=config['debug']) 13 | map.minimization = False 14 | 15 | # The ErrorController route (handles 404/500 error pages); it should 16 | # likely stay at the top, ensuring it can always be resolved 17 | map.connect('/error/{action}', controller='error') 18 | map.connect('/error/{action}/{id}', controller='error') 19 | 20 | # CUSTOM ROUTES HERE 21 | 22 | {{if admin_controller}} 23 | # Map the /admin url to FA's AdminController 24 | # Map static files 25 | map.connect('fa_static', '/admin/_static/{path_info:.*}', controller='admin', action='static') 26 | # Index page 27 | map.connect('admin', '/admin', controller='admin', action='models') 28 | map.connect('formatted_admin', '/admin.json', controller='admin', action='models', format='json') 29 | # Models 30 | map.resource('model', 'models', path_prefix='/admin/{model_name}', controller='admin') 31 | {{endif}} 32 | 33 | {{if package == 'pylonsapp'}} 34 | # serve Owner Model as resource 35 | map.resource('owner', 'owners') 36 | {{endif}} 37 | 38 | map.connect('/{controller}/{action}') 39 | map.connect('/{controller}/{action}/{id}') 40 | 41 | return map 42 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/controllers/admin.py_tmpl: -------------------------------------------------------------------------------- 1 | {{if admin_controller}} 2 | import logging 3 | from formalchemy.ext.pylons.controller import ModelsController 4 | from webhelpers.paginate import Page 5 | from {{package}}.lib.base import BaseController, render 6 | from {{package}} import model 7 | from {{package}} import forms 8 | from {{package}}.model import meta 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | class AdminControllerBase(BaseController): 13 | model = model # where your SQLAlchemy mappers are 14 | forms = forms # module containing FormAlchemy fieldsets definitions 15 | def Session(self): # Session factory 16 | return meta.Session 17 | 18 | ## customize the query for a model listing 19 | # def get_page(self): 20 | # if self.model_name == 'Foo': 21 | # return Page(meta.Session.query(model.Foo).order_by(model.Foo.bar) 22 | # return super(AdminControllerBase, self).get_page() 23 | 24 | AdminController = ModelsController(AdminControllerBase, 25 | prefix_name='admin', 26 | member_name='model', 27 | collection_name='models', 28 | ) 29 | 30 | {{endif}} 31 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/forms/__init__.py_tmpl: -------------------------------------------------------------------------------- 1 | from pylons import config 2 | from {{package}} import model 3 | from {{package}}.lib.base import render 4 | from formalchemy import config as fa_config 5 | from formalchemy import templates 6 | from formalchemy import validators 7 | from formalchemy import fields 8 | from formalchemy import forms 9 | from formalchemy import tables 10 | from formalchemy.ext.fsblob import FileFieldRenderer 11 | from formalchemy.ext.fsblob import ImageFieldRenderer 12 | 13 | fa_config.encoding = 'utf-8' 14 | 15 | 16 | {{if template_engine == 'mako'}} 17 | class TemplateEngine(templates.TemplateEngine): 18 | def render(self, name, **kwargs): 19 | return render('/forms/%s.mako' % name, extra_vars=kwargs) 20 | fa_config.engine = TemplateEngine() 21 | {{else}} 22 | ## You can use this class to override the default template engine 23 | #class TemplateEngine(templates.TemplateEngine): 24 | # def render(self, name, **kwargs): 25 | # return render('/forms/%s.mako' % name, extra_vars=kwargs) 26 | #fa_config.engine = TemplateEngine() 27 | {{endif}} 28 | 29 | class FieldSet(forms.FieldSet): 30 | pass 31 | 32 | class Grid(tables.Grid): 33 | pass 34 | 35 | ## Initialize fieldsets 36 | 37 | #Foo = FieldSet(model.Foo) 38 | #Reflected = FieldSet(Reflected) 39 | 40 | ## Initialize grids 41 | 42 | #FooGrid = Grid(model.Foo) 43 | #ReflectedGrid = Grid(Reflected) 44 | 45 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/templates/forms/fieldset.mako_tmpl: -------------------------------------------------------------------------------- 1 | {{if template_engine == 'mako'}} 2 | # -*- coding: utf-8 -*- 3 | <% 4 | _ = F_ 5 | _focus_rendered = False 6 | %>\ 7 | 8 | % for error in fieldset.errors.get(None, []): 9 |
10 | ${_(error)} 11 |
12 | % endfor 13 | 14 | % for field in fieldset.render_fields.values(): 15 | % if field.requires_label: 16 |
17 | ${field.label_tag()|n} 18 | ${field.render()|n} 19 | % if 'instructions' in field.metadata: 20 | ${field.metadata['instructions']} 21 | % endif 22 | % for error in field.errors: 23 | ${_(error)} 24 | % endfor 25 |
26 | 27 | % if (fieldset.focus == field or fieldset.focus is True) and not _focus_rendered: 28 | % if not field.is_readonly(): 29 | 34 | <% _focus_rendered = True %>\ 35 | % endif 36 | % endif 37 | % else: 38 | ${field.render()|n} 39 | % endif 40 | % endfor 41 | {{endif}} 42 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/templates/forms/fieldset_readonly.mako_tmpl: -------------------------------------------------------------------------------- 1 | {{if template_engine == 'mako'}} 2 | # -*- coding: utf-8 -*- 3 | 4 | %for field in fieldset.render_fields.values(): 5 | %if field.requires_label: 6 | 7 | ${field.label()|h}: 8 | ${field.render_readonly()|n} 9 | 10 | %endif 11 | %endfor 12 | 13 | {{endif}} 14 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/templates/forms/grid.mako_tmpl: -------------------------------------------------------------------------------- 1 | {{if template_engine == 'mako'}} 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | %for field in collection.render_fields.values(): 6 | ${field.label()|h} 7 | %endfor 8 | 9 | 10 | 11 | 12 | %for i, row in enumerate(collection): 13 | <% row_errors = collection.get_errors(row) %> 14 | 15 | %for field in collection.render_fields.values(): 16 | 17 | ${field.render()|n} 18 | %for error in row_errors.get(field, []): 19 | ${error} 20 | %endfor 21 | 22 | %endfor 23 | 24 | %endfor 25 | 26 | {{endif}} 27 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/templates/forms/grid_readonly.mako_tmpl: -------------------------------------------------------------------------------- 1 | {{if template_engine == 'mako'}} 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | %for field in collection.render_fields.values(): 6 | ${field.label()|h} 7 | %endfor 8 | 9 | 10 | 11 | 12 | %for i, row in enumerate(collection): 13 | 14 | %for field in collection.render_fields.values(): 15 | ${field.render_readonly()|n} 16 | %endfor 17 | 18 | %endfor 19 | 20 | {{endif}} 21 | -------------------------------------------------------------------------------- /formalchemy/paster_templates/pylons_fa/+package+/templates/forms/restfieldset.mako_tmpl: -------------------------------------------------------------------------------- 1 | {{if template_engine == 'mako'}} 2 | # -*- coding: utf-8 -*- 3 | <%! 4 | from formalchemy.ext.pylons.controller import model_url 5 | from pylons import url 6 | %> 7 | <%def name="h1(title, href=None)"> 8 |

9 | %if breadcrumb: 10 | 13 | %endif 14 | %if href: 15 | ${title.title()} 16 | %else: 17 | ${title.title()} 18 | %endif 19 |

20 | 21 | <%def name="buttons()"> 22 |

23 | 24 | 25 | 26 | 27 | 28 | ${F_('Cancel')} 29 | 30 |

31 | 32 | 33 | 34 | 35 | ${collection_name.title()} 36 | 37 | 38 | 39 | 40 |
41 | %if isinstance(models, dict): 42 |

${F_('Models')}

43 | %for name in sorted(models): 44 |

45 | ${name} 46 |

47 | %endfor 48 | %elif is_grid: 49 | ${h1(model_name)} 50 |
51 | ${pager|n} 52 |
53 | 54 | ${fs.render()|n} 55 |
56 |

57 | 58 | 59 | ${F_('New')} ${model_name} 60 | 61 |

62 | %else: 63 | ${h1(model_name, href=model_url(collection_name))} 64 | %if action == 'show': 65 | 66 | ${fs.render()|n} 67 |
68 |

69 | 70 | 71 | ${F_('Edit')} 72 | 73 |

74 | %elif action == 'edit': 75 |
76 | ${fs.render()|n} 77 | 78 | ${buttons()} 79 |
80 | %else: 81 |
82 | ${fs.render()|n} 83 | ${buttons()} 84 |
85 | %endif 86 | %endif 87 |
88 | 94 | 95 | {{endif}} 96 | -------------------------------------------------------------------------------- /formalchemy/templates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | 5 | from formalchemy.i18n import get_translator 6 | from formalchemy import helpers 7 | from formalchemy.helpers import literal 8 | 9 | from tempita import Template as _TempitaTemplate 10 | class TempitaTemplate(_TempitaTemplate): 11 | default_encoding = None 12 | try: 13 | from mako.lookup import TemplateLookup 14 | from mako.template import Template as MakoTemplate 15 | from mako.exceptions import TopLevelLookupException 16 | HAS_MAKO = True 17 | except ImportError: 18 | HAS_MAKO = False 19 | try: 20 | from genshi.template import TemplateLoader as GenshiTemplateLoader 21 | HAS_GENSHI = True 22 | except ImportError: 23 | HAS_GENSHI = False 24 | 25 | MAKO_TEMPLATES = os.path.join( 26 | os.path.dirname(__file__), 27 | 'paster_templates','pylons_fa','+package+','templates', 'forms') 28 | 29 | class TemplateEngine(object): 30 | """Base class for templates engines 31 | """ 32 | directories = [] 33 | extension = None 34 | _templates = ['fieldset', 'fieldset_readonly', 35 | 'grid', 'grid_readonly'] 36 | def __init__(self, **kw): 37 | self.templates = {} 38 | if 'extension' in kw: 39 | self.extension = kw.pop('extension') 40 | if 'directories' in kw: 41 | self.directories = list(kw.pop('directories')) 42 | for name in self._templates: 43 | self.templates[name] = self.get_template(name, **kw) 44 | 45 | def get_template(self, name, **kw): 46 | """return the template object for `name`. Likely to be overridden by engines""" 47 | return None 48 | 49 | def get_filename(self, name): 50 | """return the filename for template `name`""" 51 | for dirname in self.directories + [os.path.dirname(__file__)]: 52 | filename = os.path.join(dirname, '%s.%s' % (name, self.extension)) 53 | if os.path.isfile(filename): 54 | return filename 55 | 56 | def render(self, template_name, **kwargs): 57 | """render the template. Must be overridden by engines""" 58 | raise NotImplementedError("You need to implement %s.render." % self.__class__.__name__) 59 | 60 | def _update_args(cls, kw): 61 | kw['F_'] = get_translator(lang=kw.get('lang', None), 62 | request=kw.get('request', None)) 63 | kw['html'] = helpers 64 | return kw 65 | _update_args = classmethod(_update_args) 66 | 67 | def __call__(self, template_name, **kw): 68 | """update kw to extend the namespace with some FA's utils then call `render`""" 69 | self._update_args(kw) 70 | return self.render(template_name, **kw) 71 | 72 | class TempitaEngine(TemplateEngine): 73 | """Template engine for tempita. File extension is `.tmpl`. 74 | """ 75 | extension = 'tmpl' 76 | def get_template(self, name, **kw): 77 | filename = self.get_filename(name) 78 | kw['encoding'] = 'utf-8' 79 | if filename: 80 | return TempitaTemplate.from_filename(filename, **kw) 81 | 82 | def render(self, template_name, **kwargs): 83 | template = self.templates.get(template_name, None) 84 | return literal(template.substitute(**kwargs)) 85 | 86 | class MakoEngine(TemplateEngine): 87 | """Template engine for mako. File extension is `.mako`. 88 | """ 89 | extension = 'mako' 90 | _lookup = None 91 | def get_template(self, name, **kw): 92 | if self._lookup is None: 93 | self._lookup = TemplateLookup(directories=self.directories, **kw) 94 | try: 95 | return self._lookup.get_template('%s.%s' % (name, self.extension)) 96 | except TopLevelLookupException: 97 | filename = os.path.join(MAKO_TEMPLATES, '%s.mako_tmpl' % name) 98 | if os.path.isfile(filename): 99 | template = TempitaTemplate.from_filename(filename, encoding="utf-8") 100 | return MakoTemplate(template.substitute(template_engine='mako'), **kw) 101 | 102 | def render(self, template_name, **kwargs): 103 | template = self.templates.get(template_name, None) 104 | return literal(template.render_unicode(**kwargs)) 105 | 106 | class GenshiEngine(TemplateEngine): 107 | """Template engine for genshi. File extension is `.html`. 108 | """ 109 | extension = 'html' 110 | def get_template(self, name, **kw): 111 | filename = self.get_filename(name) 112 | if filename: 113 | loader = GenshiTemplateLoader(os.path.dirname(filename), **kw) 114 | return loader.load(os.path.basename(filename)) 115 | 116 | def render(self, template_name, **kwargs): 117 | template = self.templates.get(template_name, None) 118 | return literal(template.generate(**kwargs).render('html', doctype=None)) 119 | 120 | 121 | if HAS_MAKO: 122 | default_engine = MakoEngine(input_encoding='utf-8', output_encoding='utf-8') 123 | engines = dict(mako=default_engine, tempita=TempitaEngine()) 124 | else: 125 | default_engine = TempitaEngine() 126 | engines = dict(tempita=TempitaEngine()) 127 | -------------------------------------------------------------------------------- /formalchemy/tests/data/genshi/fieldset.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • ${field.name}
  • 3 |
4 | -------------------------------------------------------------------------------- /formalchemy/tests/data/mako/fieldset.mako: -------------------------------------------------------------------------------- 1 |
    2 | %for field in fieldset.render_fields.values(): 3 |
  • ${field.name}
  • 4 | %endfor 5 |
6 | -------------------------------------------------------------------------------- /formalchemy/tests/fake_module.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #used to simulate the library module used in doctests 3 | 4 | -------------------------------------------------------------------------------- /formalchemy/tests/test_aliases.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | 5 | def test_aliases(): 6 | fs = FieldSet(Aliases) 7 | fs.bind(Aliases) 8 | assert fs.id.name == 'id' 9 | 10 | def test_render_aliases(): 11 | """ 12 | >>> alias = session.query(Aliases).first() 13 | >>> alias 14 | >>> fs = FieldSet(Aliases) 15 | >>> print(fs.render()) 16 |
17 | 20 | 21 |
22 | 27 | """ 28 | 29 | -------------------------------------------------------------------------------- /formalchemy/tests/test_column.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | class Label(Base): 5 | __tablename__ = 'label' 6 | id = Column(Integer, primary_key=True) 7 | label = Column(String, label='My label') 8 | 9 | def test_label(): 10 | """ 11 | >>> Label.__table__.c.label.info 12 | {'label': 'My label'} 13 | >>> fs = FieldSet(Label) 14 | >>> print(fs.label.label_text) 15 | My label 16 | >>> print(fs.label.label()) 17 | My label 18 | """ 19 | 20 | def test_fk_label(self): 21 | """ 22 | >>> fs = FieldSet(Order) 23 | >>> print(fs.user.label_text) 24 | User 25 | >>> print(fs.user.label()) 26 | User 27 | """ 28 | 29 | -------------------------------------------------------------------------------- /formalchemy/tests/test_fsblob.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cgi 3 | import shutil 4 | import tempfile 5 | from io import StringIO 6 | from nose import with_setup 7 | 8 | from formalchemy.tests import * 9 | from formalchemy.tests.test_binary import * 10 | from formalchemy.ext.fsblob import FileFieldRenderer as BaseFile 11 | from formalchemy.ext.fsblob import ImageFieldRenderer as BaseImage 12 | from formalchemy.ext.fsblob import file_extension 13 | 14 | TEMPDIR = tempfile.mkdtemp() 15 | 16 | class FileFieldRenderer(BaseFile): 17 | storage_path = TEMPDIR 18 | 19 | class ImageFieldRenderer(BaseImage): 20 | storage_path = TEMPDIR 21 | 22 | def setup_tempdir(): 23 | if not os.path.isdir(TEMPDIR): 24 | os.makedirs(TEMPDIR) 25 | 26 | def teardown_tempdir(): 27 | if os.path.isdir(TEMPDIR): 28 | shutil.rmtree(TEMPDIR) 29 | 30 | @with_setup(setup_tempdir, teardown_tempdir) 31 | def test_file_storage(): 32 | fs = FieldSet(Binaries) 33 | record = fs.model 34 | fs.configure(include=[fs.file.with_renderer(FileFieldRenderer)]) 35 | 36 | assert 'test.js' not in fs.render() 37 | 38 | data = get_fields(TEST_DATA) 39 | fs.rebind(data=data) 40 | assert fs.validate() is True 41 | assert fs.file.value.endswith('/test.js') 42 | fs.sync() 43 | filepath = os.path.join(TEMPDIR, fs.file.value) 44 | assert os.path.isfile(filepath), filepath 45 | 46 | view = fs.file.render_readonly() 47 | value = 'test.js (1 KB)' % fs.file.value 48 | assert value in view, '%s != %s' % (value, view) 49 | 50 | assert value in fs.file.render(), fs.render() 51 | 52 | @with_setup(setup_tempdir, teardown_tempdir) 53 | def test_image_storage(): 54 | fs = FieldSet(Binaries) 55 | record = fs.model 56 | fs.configure(include=[fs.file.with_renderer(ImageFieldRenderer)]) 57 | 58 | assert 'test.js' not in fs.render() 59 | 60 | data = get_fields(TEST_DATA) 61 | fs.rebind(data=data) 62 | assert fs.validate() is True 63 | fs.sync() 64 | assert fs.file.value.endswith('/test.js') 65 | filepath = os.path.join(TEMPDIR, fs.file.value) 66 | assert os.path.isfile(filepath), filepath 67 | 68 | view = fs.file.render_readonly() 69 | v = fs.file.value 70 | value = 'test.js (1 KB)' % (v, v) 71 | assert value in view, '%s != %s' % (value, view) 72 | 73 | assert value in fs.file.render(), fs.render() 74 | 75 | @with_setup(setup_tempdir, teardown_tempdir) 76 | def test_file_validation(): 77 | fs = FieldSet(Binaries) 78 | record = fs.model 79 | fs.configure(include=[ 80 | fs.file.with_renderer( 81 | FileFieldRenderer 82 | ).validate(file_extension(['js']))]) 83 | data = get_fields(TEST_DATA) 84 | fs.rebind(data=data) 85 | assert fs.validate() is True 86 | 87 | fs.configure(include=[ 88 | fs.file.with_renderer( 89 | FileFieldRenderer 90 | ).validate(file_extension(['txt']))]) 91 | data = get_fields(TEST_DATA) 92 | fs.rebind(data=data) 93 | assert fs.validate() is False 94 | 95 | -------------------------------------------------------------------------------- /formalchemy/tests/test_html5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | from formalchemy.fatypes import * 4 | from formalchemy import tests 5 | try: 6 | from webtest import SeleniumApp, selenium 7 | except ImportError: 8 | from unittest.case import SkipTest 9 | raise SkipTest("Selenium is not available") 10 | 11 | 12 | def test_render(): 13 | """ 14 | >>> html5_test_fieldset = FieldSet(Three) 15 | >>> print(html5_test_fieldset.foo.url().render()) 16 | 17 | 18 | >>> print(html5_test_fieldset.foo.email().render()) 19 | 20 | 21 | >>> print(html5_test_fieldset.foo.range(min_=2, max_=10, step=5).render()) 22 | 23 | 24 | >>> print(html5_test_fieldset.foo.number(min_=2, max_=10, step=5).render()) 25 | 26 | 27 | >>> print(html5_test_fieldset.foo.time().render()) 28 | 29 | 30 | >>> print(html5_test_fieldset.foo.date().render()) 31 | 32 | 33 | >>> print(html5_test_fieldset.foo.datetime().render()) 34 | 35 | 36 | >>> print(html5_test_fieldset.foo.datetime_local().render()) 37 | 38 | 39 | >>> print(html5_test_fieldset.foo.week().render()) 40 | 41 | 42 | >>> print(html5_test_fieldset.foo.month().render()) 43 | 44 | 45 | >>> print(html5_test_fieldset.foo.color().render()) 46 | 47 | """ 48 | 49 | class HTML5(Base): 50 | __tablename__ = 'html5' 51 | id = Column('id', Integer, primary_key=True) 52 | date = Column(HTML5Date, nullable=True) 53 | time = Column(HTML5Time, nullable=True) 54 | datetime = Column(HTML5DateTime, nullable=True) 55 | color = Column(HTML5Color, nullable=True) 56 | 57 | @selenium 58 | class TestDateTime(unittest.TestCase): 59 | 60 | def setUp(self): 61 | self.app = SeleniumApp(application(HTML5)) 62 | 63 | def test_render(self): 64 | resp = self.app.get('/') 65 | form = resp.form 66 | form['HTML5--date'] = '2011-01-1' 67 | form['HTML5--time'] = '12:10' 68 | form['HTML5--datetime'] = '2011-01-1T10:11Z' 69 | form['HTML5--color'] = '#fff' 70 | resp = form.submit() 71 | resp.mustcontain('OK') 72 | resp.mustcontain('2011-01-01 10:11:00') 73 | 74 | def tearDown(self): 75 | self.app.close() 76 | 77 | -------------------------------------------------------------------------------- /formalchemy/tests/test_json.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | from formalchemy.fields import PasswordFieldRenderer 4 | try: 5 | import json 6 | except ImportError: 7 | import simplejson as json 8 | 9 | def to_dict(): 10 | """ 11 | >>> fs = FieldSet(User, session=session) 12 | >>> _ = fs.password.set(renderer=PasswordFieldRenderer) 13 | 14 | >>> sorted(str("%s=%s"%(k,v)) for k,v in fs.to_dict().items()) 15 | ['User--email=None', 'User--id=None', 'User--name=None', 'User--orders=[]'] 16 | 17 | >>> fs = FieldSet(bill) 18 | >>> _ = fs.password.set(renderer=PasswordFieldRenderer) 19 | 20 | >>> sorted(str("%s=%s"%(k,v)) for k,v in fs.to_dict().items()) 21 | ['User-1-email=bill@example.com', 'User-1-id=1', 'User-1-name=Bill', 'User-1-orders=[1]'] 22 | 23 | >>> sorted(str("%s=%s"%(k,v)) for k,v in fs.to_dict(with_prefix=False).items()) 24 | ['email=bill@example.com', 'id=1', 'name=Bill', 'orders=[1]'] 25 | 26 | Yes, this is convoluted; the order of keys in json.dumps() is undefined. 27 | 28 | >>> d = json.loads(json.dumps(fs.to_dict(with_prefix=False, as_string=True))) 29 | >>> sorted(str("%s=%s"%(k,v)) for k,v in d.items()) 30 | ['email=bill@example.com', 'id=1', 'name=Bill', 'orders=Quantity: 10', 'password=******'] 31 | """ 32 | 33 | def bind_without_prefix(): 34 | """ 35 | >>> data = {u'password': u'1', u'id': 1, u'orders': [1], u'email': u'bill@example.com', u'name': u'Bill'} 36 | 37 | >>> fs = FieldSet(User) 38 | >>> fs = fs.bind(data=data, session=session, with_prefix=False) 39 | >>> fs.validate() 40 | True 41 | 42 | >>> fs.rebind(bill, data=data, with_prefix=False) 43 | >>> fs.validate() 44 | True 45 | >>> fs.password.value 46 | '1' 47 | 48 | >>> data = {u'password': u'2', u'id': 1, u'orders': [1], u'email': u'bill@example.com', u'name': u'Bill'} 49 | >>> fs = fs.bind(bill, data=data, with_prefix=False) 50 | >>> fs.validate() 51 | True 52 | >>> fs.password.value 53 | '2' 54 | 55 | """ 56 | -------------------------------------------------------------------------------- /formalchemy/tests/test_manual.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import FieldSet, Field, EscapingReadonlyRenderer, types, configure_and_render, pretty_html 3 | 4 | class Manual(object): 5 | a = Field() 6 | b = Field(type=types.Integer).dropdown([('one', 1), ('two', 2)], multiple=True) 7 | d = Field().textarea((80, 10)) 8 | 9 | class ReportByUserForm(object): 10 | user_id = Field(type=types.Integer) 11 | from_date = Field(type=types.Date).required() 12 | to_date = Field(type=types.Date).required() 13 | 14 | 15 | def test_manual(self): 16 | """ 17 | >>> fs = FieldSet(Manual) 18 | >>> print(configure_and_render(fs, focus=None)) 19 |
20 | 23 | 24 |
25 |
26 | 29 | 37 |
38 |
39 | 42 | 44 |
45 | >>> fs.rebind(data={'Manual--a': 'asdf'}) 46 | >>> print(pretty_html(fs.a.render())) 47 | 48 | 49 | >>> t = FieldSet(Manual) 50 | >>> t.configure(include=[t.a, t.b], readonly=True) 51 | >>> t.model.b = [1, 2] 52 | >>> print(t.render()) 53 | 54 | 55 | 56 | A: 57 | 58 | 59 | 60 | 61 | 62 | 63 | B: 64 | 65 | 66 | one, two 67 | 68 | 69 | 70 | >>> t.model.a = 'test' 71 | >>> print(t.a.render_readonly()) 72 | test 73 | >>> t.configure(readonly=True, options=[t.a.with_renderer(EscapingReadonlyRenderer)]) 74 | >>> t.model.a = '' 75 | >>> print(t.a.render_readonly()) 76 | <test> 77 | 78 | """ 79 | 80 | def test_manual2(): 81 | """ 82 | >>> fs = FieldSet(ReportByUserForm) 83 | >>> print(fs.render()) #doctest: +ELLIPSIS 84 |
85 | 88 | ... 89 |
90 | 93 | 94 |
95 | """ 96 | -------------------------------------------------------------------------------- /formalchemy/tests/test_misc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | from formalchemy.tests import * 4 | from formalchemy.fields import AbstractField, FieldRenderer 5 | from formalchemy.fields import _htmlify, deserialize_once 6 | 7 | class TestAbstractField(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fs = FieldSet(User) 11 | self.f = AbstractField(self.fs, name="field", type=types.String) 12 | self.f.set(renderer=FieldRenderer) 13 | self.fs.append(self.f) 14 | 15 | def test_not_implemented(self): 16 | f = self.f 17 | self.assertRaises(NotImplementedError, lambda: f.model_value) 18 | self.assertRaises(NotImplementedError, lambda: f.raw_value) 19 | self.assertRaises(NotImplementedError, f.render) 20 | 21 | def test_errors(self): 22 | f = self.f 23 | self.assertEqual(f.errors, []) 24 | 25 | class TestUtils(unittest.TestCase): 26 | 27 | def test_htmlify(self): 28 | class H(object): 29 | __html__ = '' 30 | def __repr__(self): return '-' 31 | self.assertEqual(_htmlify(H()), '-') 32 | 33 | class H(object): 34 | def __html__(self): return 'html' 35 | def __repr__(self): return '-' 36 | self.assertEqual(_htmlify(H()), 'html') 37 | 38 | def test_deserialize_once(self): 39 | class H(object): 40 | value = 'foo' 41 | @deserialize_once 42 | def deserialize(self): 43 | return self.value 44 | h = H() 45 | self.assertEqual(h.deserialize(), 'foo') 46 | h.value = 'bar' 47 | self.assertEqual(h.deserialize(), 'foo') 48 | -------------------------------------------------------------------------------- /formalchemy/tests/test_multiple_keys.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | def test_renderer_names(): 5 | """ 6 | Check that the input name take care of multiple primary keys:: 7 | 8 | >>> fs = FieldSet(primary1) 9 | >>> print(fs.field.render()) 10 | 11 | 12 | >>> fs = FieldSet(primary2) 13 | >>> print(fs.field.render()) 14 | 15 | 16 | Check form rendering with keys:: 17 | 18 | >>> fs = FieldSet(primary2) 19 | >>> fs.configure(pk=True) 20 | >>> print(fs.render()) 21 |
22 | 25 | 26 |
27 | 32 |
33 | 36 | 37 |
38 |
39 | 42 | 43 |
44 | """ 45 | 46 | def test_foreign_keys(): 47 | """ 48 | Assume that we can have more than one ForeignKey as primary key:: 49 | 50 | >>> fs = FieldSet(orderuser2) 51 | >>> fs.configure(pk=True) 52 | 53 | >>> print(pretty_html(fs.user.render())) 54 | 62 | 63 | >>> print(pretty_html(fs.order.render())) 64 | 75 | """ 76 | 77 | 78 | def test_deserialize(): 79 | """ 80 | Assume that we can deserialize a value 81 | """ 82 | fs = FieldSet(primary1, data={'PrimaryKeys-1_22-field':'new_value'}) 83 | assert fs.validate() is True 84 | assert fs.field.value == 'new_value' 85 | fs.sync() 86 | session.rollback() 87 | 88 | def test_deserialize_new_record(): 89 | """ 90 | Assume that we can deserialize a value 91 | """ 92 | fs = FieldSet(PrimaryKeys(), data={'PrimaryKeys-_-id':'8', 93 | 'PrimaryKeys-_-id2':'9'}) 94 | fs.configure(include=[fs.id, fs.id2]) 95 | assert fs.validate() is True 96 | fs.sync() 97 | assert fs.model.id == 8, fs.model.id 98 | assert fs.model.id2 == '9', fs.model.id2 99 | session.rollback() 100 | 101 | 102 | -------------------------------------------------------------------------------- /formalchemy/tests/test_options.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | def test_dropdown(): 5 | """ 6 | >>> fs = FieldSet(bill) 7 | >>> print(pretty_html(fs.orders.render())) 8 | 19 | """ 20 | 21 | def test_lazy_filtered_dropdown(): 22 | """ 23 | >>> fs = FieldSet(bill) 24 | >>> def available_orders(fs_): 25 | ... return fs_.session.query(Order).filter_by(quantity=10) 26 | >>> fs.configure(include=[fs.orders.dropdown(options=available_orders)]) 27 | >>> print(pretty_html(fs.orders.render())) 28 | 33 | """ 34 | 35 | def test_lazy_record(): 36 | """ 37 | >>> fs = FieldSet(bill) 38 | >>> r = engine.execute('select quantity, user_id from orders').fetchall() 39 | >>> r = engine.execute("select 'Swedish' as name, 'sv_SE' as iso_code union all select 'English', 'en_US'").fetchall() 40 | >>> fs.configure(include=[fs.orders.dropdown(options=r)]) 41 | >>> print(pretty_html(fs.orders.render())) 42 | 50 | """ 51 | 52 | def test_manual_options(): 53 | """ 54 | >>> fs = FieldSet(bill) 55 | >>> fs.append(Field(name="cb").checkbox(options=[('one', 1), ('two', 2)])) 56 | >>> print(fs.render()) #doctest: +ELLIPSIS 57 |
... 58 | 61 | 62 | 65 |
66 | 67 | 70 |
71 | """ 72 | -------------------------------------------------------------------------------- /formalchemy/tests/test_readonly.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | 5 | def test_readonly_mode(): 6 | """ 7 | Assume that the field value is render in readonly mode:: 8 | 9 | >>> fs = FieldSet(Two) 10 | >>> fs.configure(options=[fs.foo.readonly()]) 11 | >>> print(fs.render()) 12 |
13 | 16 | 133 17 |
18 | """ 19 | 20 | def test_focus_with_readonly_mode(): 21 | """ 22 | Assume that the field value is render in readonly mode and that the focus 23 | is set to the correct field:: 24 | 25 | >>> fs = FieldSet(Three) 26 | >>> fs.configure(options=[fs.foo.readonly()]) 27 | >>> print(fs.render()) 28 |
29 | 32 |
33 |
34 | 37 | 38 |
39 | 44 | 45 | """ 46 | 47 | def test_ignore_request_in_readonly(): 48 | fs = FieldSet(bill) 49 | 50 | value = bill.name 51 | 52 | assert fs.name.value == value, '%s != %s' % (fs.name.value, value) 53 | 54 | fs.configure(options=[fs.name.readonly()]) 55 | 56 | assert value in fs.render(), fs.render() 57 | 58 | data = {'User-1-password':bill.password, 59 | 'User-1-email': bill.email, 60 | 'User-1-name': 'new name', 61 | 'User-1-orders': [o.id for o in bill.orders]} 62 | 63 | fs.rebind(bill, data=data) 64 | fs.configure(options=[fs.name.readonly()]) 65 | 66 | assert fs.name.value == value, '%s != %s' % (fs.name.value, value) 67 | 68 | assert fs.name.is_readonly() 69 | 70 | fs.sync() 71 | 72 | assert bill.name == value, '%s != %s' % (bill.name, value) 73 | 74 | bill.name = value 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /formalchemy/tests/test_request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | from webob import Request 4 | 5 | def test_get(): 6 | """ 7 | >>> fs = FieldSet(User) 8 | >>> request = Request.blank('/') 9 | >>> fs = fs.bind(User, request=request) 10 | >>> fs.id.renderer.request is request 11 | True 12 | """ 13 | 14 | def test_post(): 15 | """ 16 | >>> fs = FieldSet(User) 17 | >>> request = Request.blank('/') 18 | >>> request.method = 'POST' 19 | >>> request.POST['User--id'] = '1' 20 | >>> request.POST['User--name'] = 'bill' 21 | >>> request.POST['User--email'] = 'a@a.com' 22 | >>> request.POST['User--password'] = 'xx' 23 | >>> fs = fs.bind(request=request) 24 | >>> fs.id.renderer.request is request 25 | True 26 | >>> fs.validate() 27 | True 28 | """ 29 | 30 | def test_post_on_fieldset(): 31 | """ 32 | >>> request = Request.blank('/') 33 | >>> request.method = 'POST' 34 | >>> request.POST['User--id'] = '1' 35 | >>> request.POST['User--name'] = 'bill' 36 | >>> request.POST['User--email'] = 'a@a.com' 37 | >>> request.POST['User--password'] = 'xx' 38 | >>> fs = FieldSet(User, request=request) 39 | >>> fs.id.renderer.request is request 40 | True 41 | >>> fs.validate() 42 | True 43 | """ 44 | 45 | def test_post_on_grid(): 46 | """ 47 | >>> request = Request.blank('/') 48 | >>> request.method = 'POST' 49 | >>> request.POST['User-1-id'] = '1' 50 | >>> request.POST['User-1-name'] = 'bill' 51 | >>> request.POST['User-1-email'] = 'a@a.com' 52 | >>> request.POST['User-1-password'] = 'xx' 53 | >>> g = Grid(User, [bill], request=request) 54 | >>> g.id.renderer.request is request 55 | True 56 | >>> g.validate() 57 | True 58 | >>> print(g.render()) #doctest: +ELLIPSIS 59 | ...... 60 | """ 61 | 62 | 63 | -------------------------------------------------------------------------------- /formalchemy/tests/test_unicode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | from formalchemy.tests import * 4 | from formalchemy.multidict import UnicodeMultiDict 5 | from formalchemy.multidict import MultiDict 6 | 7 | def test_unicode(): 8 | """ 9 | >>> jose = User(email='jose@example.com', 10 | ... password='6565', 11 | ... name=u'Jos\xe9') 12 | >>> order4 = Order(user=jose, quantity=4) 13 | >>> session.add(jose) 14 | >>> session.add(order4) 15 | >>> session.flush() 16 | >>> FieldSet.default_renderers = original_renderers.copy() 17 | >>> fs = FieldSet(jose) 18 | >>> print(fs.render()) #doctest: +ELLIPSIS 19 |
20 | ...... 21 | 22 | >>> fs.readonly = True 23 | >>> print(fs.render()) #doctest: +ELLIPSIS 24 | ...José... 25 | 26 | >>> fs = FieldSet(order4) 27 | >>> print(fs.render()) #doctest: +ELLIPSIS 28 |
29 | ...José... 30 | 31 | >>> fs.readonly = True 32 | >>> print(fs.render()) #doctest: +ELLIPSIS 33 | ...José... 34 | 35 | >>> session.rollback() 36 | """ 37 | 38 | def test_unicode_data(self): 39 | """ 40 | >>> fs = FieldSet(User, session=session) 41 | >>> data = UnicodeMultiDict(MultiDict({'User--name': u'José', 'User--email': 'j@jose.com', 'User--password': 'pwd'}), encoding='utf-8') 42 | >>> str(data.encoding) 43 | 'utf-8' 44 | >>> fs.rebind(data=data) 45 | >>> fs.data is data 46 | True 47 | >>> print(fs.render()) # doctest: +ELLIPSIS 48 |
......
49 | 50 | >>> data = UnicodeMultiDict(MultiDict({'name': 'José', 'email': 'j@jose.com', 'password': 'pwd'}), encoding='utf-8') 51 | >>> fs.rebind(data=data, with_prefix=False) 52 | >>> print(fs.render()) # doctest: +ELLIPSIS 53 |
......
54 | 55 | >>> fs.rebind(data={'User--name': 'José', 'User--email': 'j@jose.com', 'User--password': 'pwd'}) 56 | >>> isinstance(fs.data, UnicodeMultiDict) 57 | True 58 | >>> print(fs.render()) # doctest: +ELLIPSIS 59 |
......
60 | """ 61 | -------------------------------------------------------------------------------- /formalchemy/tests/test_validate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | 4 | def validate_empty(): 5 | """ 6 | >>> fs = FieldSet(bill) 7 | >>> fs.validate() #doctest: +ELLIPSIS,+IGNORE_EXCEPTION_DETAIL 8 | Traceback (most recent call last): 9 | ... 10 | ValidationError: Cannot validate without binding data 11 | >>> fs.render() #doctest: +ELLIPSIS 12 | '
...
' 13 | """ 14 | 15 | def validate_no_field_in_data(): 16 | """ 17 | >>> fs = FieldSet(bill) 18 | >>> fs.rebind(data={}) 19 | >>> fs.validate() 20 | False 21 | >>> fs.render() #doctest: +ELLIPSIS 22 | '
...
' 23 | """ 24 | -------------------------------------------------------------------------------- /formalchemy/tests/test_validators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from formalchemy.tests import * 3 | from formalchemy import validators 4 | 5 | def validator1(value, field): 6 | if not value: 7 | raise ValidationError('Must have a value') 8 | 9 | @validators.accepts_none 10 | def validator2(value, field): 11 | if not value: 12 | raise ValidationError('Must have a value') 13 | 14 | 15 | def accepts_none(): 16 | """ 17 | >>> fs = FieldSet(bill) 18 | >>> fs.configure(include=[fs.name.validate(validator1)]) 19 | >>> fs = fs.bind(data={'User-1-name':''}) 20 | >>> fs.validate() 21 | True 22 | 23 | >>> fs = FieldSet(bill) 24 | >>> fs.configure(include=[fs.name.validate(validator2)]) 25 | >>> fs = fs.bind(data={'User-1-name':''}) 26 | >>> fs.validate() 27 | False 28 | """ 29 | 30 | -------------------------------------------------------------------------------- /pylonsapp/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pylonsapp/config/deployment.ini_tmpl 2 | recursive-include pylonsapp/public * 3 | recursive-include pylonsapp/templates * 4 | -------------------------------------------------------------------------------- /pylonsapp/README.txt: -------------------------------------------------------------------------------- 1 | This file is for you to describe the pylonsapp application. Typically 2 | you would include information such as the information below: 3 | 4 | Installation and Setup 5 | ====================== 6 | 7 | Install ``pylonsapp`` using easy_install:: 8 | 9 | easy_install pylonsapp 10 | 11 | Make a config file as follows:: 12 | 13 | paster make-config pylonsapp config.ini 14 | 15 | Tweak the config file as appropriate and then setup the application:: 16 | 17 | paster setup-app config.ini 18 | 19 | Then you are ready to go. 20 | -------------------------------------------------------------------------------- /pylonsapp/development.ini: -------------------------------------------------------------------------------- 1 | # 2 | # pylonsapp - Pylons development environment configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | [DEFAULT] 7 | debug = true 8 | # Uncomment and replace with the address which should receive any error reports 9 | #email_to = you@yourdomain.com 10 | smtp_server = localhost 11 | error_email_from = paste@localhost 12 | 13 | [server:main] 14 | use = egg:Paste#http 15 | host = 127.0.0.1 16 | port = 5000 17 | 18 | [app:main] 19 | use = egg:pylonsapp 20 | full_stack = true 21 | static_files = true 22 | 23 | cache_dir = %(here)s/data 24 | beaker.session.key = pylonsapp 25 | beaker.session.secret = somesecret 26 | 27 | # If you'd like to fine-tune the individual locations of the cache data dirs 28 | # for the Cache data, or the Session saves, un-comment the desired settings 29 | # here: 30 | #beaker.cache.data_dir = %(here)s/data/cache 31 | #beaker.session.data_dir = %(here)s/data/sessions 32 | 33 | # SQLAlchemy database URL 34 | sqlalchemy.url = sqlite:///%(here)s/development.db 35 | 36 | # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* 37 | # Debug mode will enable the interactive debugging tool, allowing ANYONE to 38 | # execute malicious code after an exception is raised. 39 | #set debug = false 40 | 41 | storage_path = %(here)s/pylonsapp/public 42 | 43 | # Logging configuration 44 | [loggers] 45 | keys = root, routes, pylonsapp, sqlalchemy 46 | 47 | [handlers] 48 | keys = console 49 | 50 | [formatters] 51 | keys = generic 52 | 53 | [logger_root] 54 | level = INFO 55 | handlers = console 56 | 57 | [logger_routes] 58 | level = INFO 59 | handlers = 60 | qualname = routes.middleware 61 | # "level = DEBUG" logs the route matched and routing variables. 62 | 63 | [logger_pylonsapp] 64 | level = DEBUG 65 | handlers = 66 | qualname = pylonsapp 67 | 68 | [logger_sqlalchemy] 69 | level = INFO 70 | handlers = 71 | qualname = sqlalchemy.engine 72 | # "level = INFO" logs SQL queries. 73 | # "level = DEBUG" logs SQL queries and results. 74 | # "level = WARN" logs neither. (Recommended for production systems.) 75 | 76 | [handler_console] 77 | class = StreamHandler 78 | args = (sys.stderr,) 79 | level = NOTSET 80 | formatter = generic 81 | 82 | [formatter_generic] 83 | format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s 84 | datefmt = %H:%M:%S 85 | -------------------------------------------------------------------------------- /pylonsapp/docs/index.txt: -------------------------------------------------------------------------------- 1 | pylonsapp 2 | +++++++++ 3 | 4 | This is the main index page of your documentation. It should be written in 5 | `reStructuredText format `_. 6 | 7 | You can generate your documentation in HTML format by running this command:: 8 | 9 | setup.py pudge 10 | 11 | For this to work you will need to download and install `buildutils`_, 12 | `pudge`_, and `pygments`_. The ``pudge`` command is disabled by 13 | default; to ativate it in your project, run:: 14 | 15 | setup.py addcommand -p buildutils.pudge_command 16 | 17 | .. _buildutils: http://pypi.python.org/pypi/buildutils 18 | .. _pudge: http://pudge.lesscode.org/ 19 | .. _pygments: http://pygments.org/ 20 | -------------------------------------------------------------------------------- /pylonsapp/performance_test.py: -------------------------------------------------------------------------------- 1 | #!bin/python 2 | import webob 3 | import formalchemy 4 | import sqlalchemy as sa 5 | from sqlalchemy.orm import relation, backref 6 | from sqlalchemy.ext.declarative import declarative_base 7 | 8 | from repoze.profile.profiler import AccumulatingProfileMiddleware 9 | 10 | def make_middleware(app): 11 | return AccumulatingProfileMiddleware( 12 | app, 13 | log_filename='/tmp/profile.log', 14 | discard_first_request=True, 15 | flush_at_shutdown=True, 16 | path='/__profile__') 17 | 18 | Base = declarative_base() 19 | 20 | class User(Base): 21 | __tablename__ = 'users' 22 | id = sa.Column(sa.Integer, primary_key=True) 23 | name = sa.Column(sa.Unicode(12)) 24 | fullname = sa.Column(sa.Unicode(40)) 25 | password = sa.Column(sa.Unicode(20)) 26 | 27 | def simple_app(environ, start_response): 28 | resp = webob.Response() 29 | fs = formalchemy.FieldSet(User) 30 | body = fs.bind(User()).render() 31 | body += fs.bind(User()).render() 32 | fs.rebind(User()) 33 | body += fs.render() 34 | resp.body = body 35 | return resp(environ, start_response) 36 | 37 | if __name__ == '__main__': 38 | import sys 39 | import os 40 | import signal 41 | from paste.httpserver import serve 42 | print 'Now do:' 43 | print 'ab -n 100 http://127.0.0.1:8080/' 44 | print 'wget -O - http://127.0.0.1:8080/__profile__' 45 | serve(make_middleware(simple_app)) 46 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/__init__.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/_design/person/views/all/map.js: -------------------------------------------------------------------------------- 1 | function(doc) { 2 | if (doc.doc_type == "Person") 3 | emit(doc._id, doc); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/_design/pet/views/all/map.js: -------------------------------------------------------------------------------- 1 | function(doc) { 2 | if (doc.doc_type == "Pet") 3 | emit(doc._id, doc); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/config/__init__.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/config/deployment.ini_tmpl: -------------------------------------------------------------------------------- 1 | # 2 | # pylonsapp - Pylons configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | [DEFAULT] 7 | debug = true 8 | email_to = you@yourdomain.com 9 | smtp_server = localhost 10 | error_email_from = paste@localhost 11 | 12 | [server:main] 13 | use = egg:Paste#http 14 | host = 0.0.0.0 15 | port = 5000 16 | 17 | [app:main] 18 | use = egg:pylonsapp 19 | full_stack = true 20 | static_files = true 21 | 22 | cache_dir = %(here)s/data 23 | beaker.session.key = pylonsapp 24 | beaker.session.secret = ${app_instance_secret} 25 | app_instance_uuid = ${app_instance_uuid} 26 | 27 | # If you'd like to fine-tune the individual locations of the cache data dirs 28 | # for the Cache data, or the Session saves, un-comment the desired settings 29 | # here: 30 | #beaker.cache.data_dir = %(here)s/data/cache 31 | #beaker.session.data_dir = %(here)s/data/sessions 32 | 33 | # SQLAlchemy database URL 34 | sqlalchemy.url = sqlite:///production.db 35 | 36 | # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* 37 | # Debug mode will enable the interactive debugging tool, allowing ANYONE to 38 | # execute malicious code after an exception is raised. 39 | set debug = false 40 | 41 | 42 | # Logging configuration 43 | [loggers] 44 | keys = root 45 | 46 | [handlers] 47 | keys = console 48 | 49 | [formatters] 50 | keys = generic 51 | 52 | [logger_root] 53 | level = INFO 54 | handlers = console 55 | 56 | [handler_console] 57 | class = StreamHandler 58 | args = (sys.stderr,) 59 | level = NOTSET 60 | formatter = generic 61 | 62 | [formatter_generic] 63 | format = %(asctime)s %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s 64 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/config/environment.py: -------------------------------------------------------------------------------- 1 | """Pylons environment configuration""" 2 | import os 3 | 4 | from mako.lookup import TemplateLookup 5 | from pylons.configuration import PylonsConfig 6 | from pylons.error import handle_mako_error 7 | from sqlalchemy import engine_from_config 8 | 9 | import pylonsapp.lib.app_globals as app_globals 10 | import pylonsapp.lib.helpers 11 | from pylonsapp.config.routing import make_map 12 | from pylonsapp.model import init_model 13 | 14 | def load_environment(global_conf, app_conf): 15 | """Configure the Pylons environment via the ``pylons.config`` 16 | object 17 | """ 18 | config = PylonsConfig() 19 | 20 | # Pylons paths 21 | root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 22 | paths = dict(root=root, 23 | controllers=os.path.join(root, 'controllers'), 24 | static_files=os.path.join(root, 'public'), 25 | templates=[os.path.join(root, 'templates')]) 26 | 27 | # Initialize config with the basic options 28 | config.init_app(global_conf, app_conf, package='pylonsapp', paths=paths) 29 | 30 | config['routes.map'] = make_map(config) 31 | config['pylons.app_globals'] = app_globals.Globals(config) 32 | config['pylons.h'] = pylonsapp.lib.helpers 33 | 34 | # Setup cache object as early as possible 35 | import pylons 36 | pylons.cache._push_object(config['pylons.app_globals'].cache) 37 | 38 | 39 | # Create the Mako TemplateLookup, with the default auto-escaping 40 | config['pylons.app_globals'].mako_lookup = TemplateLookup( 41 | directories=paths['templates'], 42 | error_handler=handle_mako_error, 43 | module_directory=os.path.join(app_conf['cache_dir'], 'templates'), 44 | input_encoding='utf-8', default_filters=['escape'], 45 | imports=['from webhelpers.html import escape']) 46 | 47 | # Setup the SQLAlchemy database engine 48 | engine = engine_from_config(config, 'sqlalchemy.') 49 | init_model(engine) 50 | 51 | # CONFIGURATION OPTIONS HERE (note: all config options will override 52 | # any Pylons config options) 53 | 54 | return config 55 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/config/middleware.py: -------------------------------------------------------------------------------- 1 | """Pylons middleware initialization""" 2 | from beaker.middleware import SessionMiddleware 3 | from paste.cascade import Cascade 4 | from paste.registry import RegistryManager 5 | from paste.urlparser import StaticURLParser 6 | from paste.deploy.converters import asbool 7 | from pylons.middleware import ErrorHandler, StatusCodeRedirect 8 | from pylons.wsgiapp import PylonsApp 9 | from routes.middleware import RoutesMiddleware 10 | 11 | from pylonsapp.config.environment import load_environment 12 | 13 | def make_app(global_conf, full_stack=True, static_files=True, **app_conf): 14 | """Create a Pylons WSGI application and return it 15 | 16 | ``global_conf`` 17 | The inherited configuration for this application. Normally from 18 | the [DEFAULT] section of the Paste ini file. 19 | 20 | ``full_stack`` 21 | Whether this application provides a full WSGI stack (by default, 22 | meaning it handles its own exceptions and errors). Disable 23 | full_stack when this application is "managed" by another WSGI 24 | middleware. 25 | 26 | ``static_files`` 27 | Whether this application serves its own static files; disable 28 | when another web server is responsible for serving them. 29 | 30 | ``app_conf`` 31 | The application's local configuration. Normally specified in 32 | the [app:] section of the Paste ini file (where 33 | defaults to main). 34 | 35 | """ 36 | # Configure the Pylons environment 37 | config = load_environment(global_conf, app_conf) 38 | 39 | # The Pylons WSGI app 40 | app = PylonsApp(config=config) 41 | 42 | # Routing/Session/Cache Middleware 43 | app = RoutesMiddleware(app, config['routes.map']) 44 | app = SessionMiddleware(app, config) 45 | 46 | # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) 47 | 48 | if asbool(full_stack): 49 | # Handle Python exceptions 50 | app = ErrorHandler(app, global_conf, **config['pylons.errorware']) 51 | 52 | # Display error documents for 401, 403, 404 status codes (and 53 | # 500 when debug is disabled) 54 | if asbool(config['debug']): 55 | app = StatusCodeRedirect(app) 56 | else: 57 | app = StatusCodeRedirect(app, [400, 401, 403, 404, 500]) 58 | 59 | # Establish the Registry for this application 60 | app = RegistryManager(app) 61 | 62 | if asbool(static_files): 63 | # Serve static files 64 | static_app = StaticURLParser(config['pylons.paths']['static_files']) 65 | app = Cascade([static_app, app]) 66 | app.config = config 67 | return app 68 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/config/routing.py: -------------------------------------------------------------------------------- 1 | """Routes configuration 2 | 3 | The more specific and detailed routes should be defined first so they 4 | may take precedent over the more generic routes. For more information 5 | refer to the routes manual at http://routes.groovie.org/docs/ 6 | """ 7 | from routes import Mapper 8 | 9 | def make_map(config): 10 | """Create, configure and return the routes Mapper""" 11 | map = Mapper(directory=config['pylons.paths']['controllers'], 12 | always_scan=config['debug']) 13 | map.minimization = False 14 | 15 | # The ErrorController route (handles 404/500 error pages); it should 16 | # likely stay at the top, ensuring it can always be resolved 17 | map.connect('/error/{action}', controller='error') 18 | map.connect('/error/{action}/{id}', controller='error') 19 | 20 | # CUSTOM ROUTES HERE 21 | # Map the /admin url to FA's AdminController 22 | # Map static files 23 | map.connect('fa_static', '/admin/_static/{path_info:.*}', controller='admin', action='static') 24 | # Index page 25 | map.connect('admin', '/admin', controller='admin', action='models') 26 | map.connect('formatted_admin', '/admin.json', controller='admin', action='models', format='json') 27 | # Models 28 | map.resource('model', 'models', path_prefix='/admin/{model_name}', controller='admin') 29 | 30 | # serve couchdb's Pets as resource 31 | # Index page 32 | map.connect('couchdb', '/couchdb', controller='couchdb', action='models') 33 | # Model resources 34 | map.resource('node', 'nodes', path_prefix='/couchdb/{model_name}', controller='couchdb') 35 | 36 | 37 | # serve Owner Model as resource 38 | map.resource('owner', 'owners') 39 | 40 | map.connect('/{controller}/{action}') 41 | map.connect('/{controller}/{action}/{id}') 42 | 43 | return map 44 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/controllers/__init__.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/admin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from formalchemy.ext.pylons.controller import ModelsController 3 | from webhelpers.paginate import Page 4 | from pylonsapp.lib.base import BaseController, render 5 | from pylonsapp import model 6 | from pylonsapp import forms 7 | from pylonsapp.model import meta 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | class AdminControllerBase(BaseController): 12 | model = model # where your SQLAlchemy mappers are 13 | forms = forms # module containing FormAlchemy fieldsets definitions 14 | def Session(self): # Session factory 15 | return meta.Session 16 | 17 | ## customize the query for a model listing 18 | # def get_page(self): 19 | # if self.model_name == 'Foo': 20 | # return Page(meta.Session.query(model.Foo).order_by(model.Foo.bar) 21 | # return super(AdminControllerBase, self).get_page() 22 | 23 | AdminController = ModelsController(AdminControllerBase, 24 | prefix_name='admin', 25 | member_name='model', 26 | collection_name='models', 27 | ) 28 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/basic.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pylons import request, response, session, url, tmpl_context as c 3 | from pylons.controllers.util import abort, redirect 4 | from pylonsapp.lib.base import BaseController, render 5 | from pylonsapp.model import meta 6 | from pylonsapp import model 7 | from pylonsapp.forms import FieldSet 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | Foo = FieldSet(model.Foo) 12 | Foo.configure(options=[Foo.bar.label('This is the bar field')]) 13 | 14 | class BasicController(BaseController): 15 | 16 | def index(self, id=None): 17 | if id: 18 | record = meta.Session.query(model.Foo).filter_by(id=id).first() 19 | else: 20 | record = model.Foo() 21 | assert record is not None, repr(id) 22 | c.fs = Foo.bind(record, data=request.POST or None) 23 | if request.POST and c.fs.validate(): 24 | c.fs.sync() 25 | if id: 26 | meta.Session.update(record) 27 | else: 28 | meta.Session.add(record) 29 | meta.Session.commit() 30 | redirect(url.current(id=record.id)) 31 | return render('/form.mako') 32 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/couchdb.py: -------------------------------------------------------------------------------- 1 | __doc__ = """This is an example on ow to setup a CRUD UI with couchdb as 2 | backend""" 3 | import os 4 | import logging 5 | import pylonsapp 6 | from couchdbkit import * 7 | from webhelpers.paginate import Page 8 | from pylonsapp.lib.base import BaseController, render 9 | from couchdbkit.loaders import FileSystemDocsLoader 10 | from formalchemy.ext import couchdb 11 | from formalchemy.ext.pylons.controller import ModelsController 12 | 13 | log = logging.getLogger(__name__) 14 | 15 | class Person(couchdb.Document): 16 | """A Person node""" 17 | name = StringProperty(required=True) 18 | def __unicode__(self): 19 | return self.name or u'' 20 | 21 | class Pet(couchdb.Document): 22 | """A Pet node""" 23 | name = StringProperty(required=True) 24 | type = StringProperty(required=True) 25 | birthdate = DateProperty(auto_now=True) 26 | weight_in_pounds = IntegerProperty(default=0) 27 | spayed_or_neutered = BooleanProperty() 28 | owner = SchemaListProperty(Person) 29 | def __unicode__(self): 30 | return self.name or u'' 31 | 32 | # You don't need a try/except. This is just to allow to run FA's tests without 33 | # couchdb installed. Btw this have to be in another place in your app. eg: you 34 | # don't need to sync views each time the controller is loaded. 35 | try: 36 | server = Server() 37 | if server: pass 38 | except: 39 | server = None 40 | else: 41 | db = server.get_or_create_db('formalchemy_test') 42 | 43 | design_docs = os.path.join(os.path.dirname(pylonsapp.__file__), '_design') 44 | loader = FileSystemDocsLoader(design_docs) 45 | loader.sync(db, verbose=True) 46 | 47 | contain(db, Pet, Person) 48 | 49 | class CouchdbController(BaseController): 50 | 51 | # override default classes to use couchdb fieldsets 52 | FieldSet = couchdb.FieldSet 53 | Grid = couchdb.Grid 54 | model = [Person, Pet] 55 | 56 | def Session(self): 57 | """return a formalchemy.ext.couchdb.Session""" 58 | return couchdb.Session(db) 59 | 60 | CouchdbController = ModelsController(CouchdbController, prefix_name='couchdb', member_name='node', collection_name='nodes') 61 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/error.py: -------------------------------------------------------------------------------- 1 | import cgi 2 | 3 | from paste.urlparser import PkgResourcesParser 4 | from pylons.middleware import error_document_template 5 | from webhelpers.html.builder import literal 6 | 7 | from pylonsapp.lib.base import BaseController 8 | 9 | class ErrorController(BaseController): 10 | """Generates error documents as and when they are required. 11 | 12 | The ErrorDocuments middleware forwards to ErrorController when error 13 | related status codes are returned from the application. 14 | 15 | This behaviour can be altered by changing the parameters to the 16 | ErrorDocuments middleware in your config/middleware.py file. 17 | 18 | """ 19 | def document(self): 20 | """Render the error document""" 21 | request = self._py_object.request 22 | resp = request.environ.get('pylons.original_response') 23 | content = literal(resp.body) or cgi.escape(request.GET.get('message', '')) 24 | page = error_document_template % \ 25 | dict(prefix=request.environ.get('SCRIPT_NAME', ''), 26 | code=cgi.escape(request.GET.get('code', str(resp.status_int))), 27 | message=content) 28 | return page 29 | 30 | def img(self, id): 31 | """Serve Pylons' stock images""" 32 | return self._serve_file('/'.join(['media/img', id])) 33 | 34 | def style(self, id): 35 | """Serve Pylons' stock stylesheets""" 36 | return self._serve_file('/'.join(['media/style', id])) 37 | 38 | def _serve_file(self, path): 39 | """Call Paste's FileApp (a WSGI application) to serve the file 40 | at the specified path 41 | """ 42 | request = self._py_object.request 43 | request.environ['PATH_INFO'] = '/%s' % path 44 | return PkgResourcesParser('pylons', 'pylons')(request.environ, self.start_response) 45 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/fsblob.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pylons import request, response, session, url, tmpl_context as c 3 | from pylons.controllers.util import abort, redirect 4 | from pylonsapp.lib.base import BaseController, render 5 | from pylonsapp.model import meta 6 | from pylonsapp import model 7 | from pylonsapp.forms.fsblob import Files 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | class FsblobController(BaseController): 12 | 13 | def index(self, id=None): 14 | if id: 15 | record = meta.Session.query(model.Files).filter_by(id=id).first() 16 | else: 17 | record = model.Files() 18 | assert record is not None, repr(id) 19 | c.fs = Files.bind(record, data=request.POST or None) 20 | if request.POST and c.fs.validate(): 21 | c.fs.sync() 22 | if id: 23 | meta.Session.merge(record) 24 | else: 25 | meta.Session.add(record) 26 | meta.Session.commit() 27 | redirect(url.current(id=record.id)) 28 | return render('/form.mako') 29 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/controllers/owners.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from pylons import request, response, session, url, tmpl_context as c 4 | from pylons.controllers.util import abort, redirect 5 | 6 | from pylonsapp.lib.base import BaseController, render 7 | from pylonsapp import model 8 | from pylonsapp.model import meta 9 | 10 | from formalchemy.ext.pylons.controller import RESTController 11 | 12 | log = logging.getLogger(__name__) 13 | 14 | class OwnersController(BaseController): 15 | 16 | def Session(self): 17 | return meta.Session 18 | 19 | def get_model(self): 20 | return model.Owner 21 | 22 | OwnersController = RESTController(OwnersController, 'owner', 'owners') 23 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/forms/__init__.py: -------------------------------------------------------------------------------- 1 | from pylons import config 2 | from pylonsapp import model 3 | from pylonsapp.lib.base import render 4 | from formalchemy import config as fa_config 5 | from formalchemy import templates 6 | from formalchemy import validators 7 | from formalchemy import fields 8 | from formalchemy import forms 9 | from formalchemy import tables 10 | from formalchemy.ext.fsblob import FileFieldRenderer 11 | from formalchemy.ext.fsblob import ImageFieldRenderer 12 | 13 | #if 'storage_path' in config['app_conf']: 14 | # # set the storage_path if we can find an options in app_conf 15 | # FileFieldRenderer.storage_path = config['app_conf']['storage_path'] 16 | # ImageFieldRenderer.storage_path = config['app_conf']['storage_path'] 17 | 18 | fa_config.encoding = 'utf-8' 19 | 20 | class TemplateEngine(templates.TemplateEngine): 21 | def render(self, name, **kwargs): 22 | return render('/forms/%s.mako' % name, extra_vars=kwargs) 23 | fa_config.engine = TemplateEngine() 24 | 25 | class FieldSet(forms.FieldSet): 26 | pass 27 | 28 | class Grid(tables.Grid): 29 | pass 30 | 31 | ## Initialize fieldsets 32 | 33 | #Foo = FieldSet(model.Foo) 34 | #Reflected = FieldSet(Reflected) 35 | 36 | ## Initialize grids 37 | 38 | #FooGrid = Grid(model.Foo) 39 | #ReflectedGrid = Grid(Reflected) 40 | 41 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/forms/fsblob.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pylons import config 3 | from pylonsapp import model 4 | from pylonsapp.forms import FieldSet 5 | from formalchemy.ext.fsblob import FileFieldRenderer 6 | 7 | Files = FieldSet(model.Files) 8 | Files.configure(options=[Files.path.with_renderer( 9 | FileFieldRenderer.new( 10 | storage_path=config['app_conf']['storage_path'], 11 | url_prefix='/'))]) 12 | 13 | 14 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/lib/__init__.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/lib/app_globals.py: -------------------------------------------------------------------------------- 1 | """The application's Globals object""" 2 | 3 | from beaker.cache import CacheManager 4 | from beaker.util import parse_cache_config_options 5 | 6 | class Globals(object): 7 | """Globals acts as a container for objects available throughout the 8 | life of the application 9 | 10 | """ 11 | 12 | def __init__(self, config): 13 | """One instance of Globals is created during application 14 | initialization and is available during requests via the 15 | 'app_globals' variable 16 | 17 | """ 18 | self.cache = CacheManager(**parse_cache_config_options(config)) 19 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/lib/base.py: -------------------------------------------------------------------------------- 1 | """The base Controller API 2 | 3 | Provides the BaseController class for subclassing. 4 | """ 5 | from pylons.controllers import WSGIController 6 | from pylons.templating import render_mako as render 7 | 8 | from pylonsapp.model.meta import Session 9 | 10 | class BaseController(WSGIController): 11 | 12 | def __call__(self, environ, start_response): 13 | """Invoke the Controller""" 14 | # WSGIController.__call__ dispatches to the Controller method 15 | # the request is routed to. This routing information is 16 | # available in environ['pylons.routes_dict'] 17 | try: 18 | return WSGIController.__call__(self, environ, start_response) 19 | finally: 20 | Session.remove() 21 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/lib/helpers.py: -------------------------------------------------------------------------------- 1 | """Helper functions 2 | 3 | Consists of functions to typically be used within templates, but also 4 | available to Controllers. This module is available to templates as 'h'. 5 | """ 6 | # Import helpers as desired, or define your own, ie: 7 | #from webhelpers.html.tags import checkbox, password 8 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/model/__init__.py: -------------------------------------------------------------------------------- 1 | """The application's model objects""" 2 | import sqlalchemy as sa 3 | from sqlalchemy import orm 4 | from pylonsapp.model.meta import Session, metadata 5 | 6 | 7 | def init_model(engine): 8 | """Call me before using any of the tables or classes in the model""" 9 | Session.configure(bind=engine) 10 | 11 | foo_table = sa.Table("Foo", meta.metadata, 12 | sa.Column("id", sa.types.Integer, primary_key=True), 13 | sa.Column("bar", sa.types.String(255), nullable=False), 14 | ) 15 | 16 | class Foo(object): 17 | pass 18 | 19 | orm.mapper(Foo, foo_table) 20 | 21 | files_table = sa.Table("Files", meta.metadata, 22 | sa.Column("id", sa.types.Integer, primary_key=True), 23 | sa.Column("path", sa.types.String(255), nullable=False), 24 | ) 25 | 26 | class Files(object): 27 | pass 28 | 29 | orm.mapper(Files, files_table) 30 | 31 | animals_table = sa.Table("Animals", meta.metadata, 32 | sa.Column("id", sa.types.Integer, primary_key=True), 33 | sa.Column("name", sa.types.String(255), nullable=False), 34 | sa.Column("owner_id", sa.ForeignKey('Owners.id'), nullable=False), 35 | ) 36 | 37 | class Animal(object): 38 | def __unicode__(self): 39 | return self.name 40 | 41 | orm.mapper(Animal, animals_table) 42 | 43 | owners_table = sa.Table("Owners", meta.metadata, 44 | sa.Column("id", sa.types.Integer, primary_key=True), 45 | sa.Column("name", sa.types.String(255), nullable=False), 46 | ) 47 | 48 | class Owner(object): 49 | def __unicode__(self): 50 | return self.name 51 | 52 | orm.mapper(Owner, owners_table, properties=dict( 53 | animals=orm.relation(Animal, 54 | backref=orm.backref('owner', uselist=False)) 55 | )) 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/model/meta.py: -------------------------------------------------------------------------------- 1 | """SQLAlchemy Metadata and Session object""" 2 | from sqlalchemy import MetaData 3 | from sqlalchemy.orm import scoped_session, sessionmaker 4 | 5 | __all__ = ['Session', 'metadata'] 6 | 7 | # SQLAlchemy session manager. Updated by model.init_model() 8 | Session = scoped_session(sessionmaker()) 9 | 10 | # Global metadata. If you have multiple databases with overlapping table 11 | # names, you'll need a metadata for each database 12 | metadata = MetaData() 13 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/public/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/public/bg.png -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/public/favicon.ico -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Welcome to Pylons! 8 | 96 | 97 | 98 |
99 |

Welcome to Logo displaying the word Pylons 101 |

102 |
103 |
104 |

Let's begin!

105 |

If you haven't used Pylons before, start with the beginners' tutorial.

107 |
108 | 131 | 135 |
136 | 137 | 138 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/public/pylons-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/public/pylons-logo.gif -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/form.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 |
5 | ${c.fs.render()|n} 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/forms/fieldset.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | <% 3 | _ = F_ 4 | _focus_rendered = False 5 | %>\ 6 | 7 | % for error in fieldset.errors.get(None, []): 8 |
9 | ${_(error)} 10 |
11 | % endfor 12 | 13 | % for field in fieldset.render_fields.itervalues(): 14 | % if field.requires_label: 15 |
16 | 17 | ${field.render()|n} 18 | % if 'instructions' in field.metadata: 19 | ${field.metadata['instructions']} 20 | % endif 21 | % for error in field.errors: 22 | ${_(error)} 23 | % endfor 24 |
25 | 26 | % if (fieldset.focus == field or fieldset.focus is True) and not _focus_rendered: 27 | % if not field.is_readonly(): 28 | 33 | <% _focus_rendered = True %>\ 34 | % endif 35 | % endif 36 | % else: 37 | ${field.render()|n} 38 | % endif 39 | % endfor 40 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/forms/fieldset_readonly.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | %for field in fieldset.render_fields.itervalues(): 4 | 5 | ${[field.label_text, fieldset.prettify(field.key)][int(field.label_text is None)]|h}: 6 | ${field.render_readonly()|n} 7 | 8 | %endfor 9 | 10 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/forms/grid.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | %for field in collection.render_fields.itervalues(): 5 | ${F_(field.label_text or collection.prettify(field.key))|h} 6 | %endfor 7 | 8 | 9 | 10 | 11 | %for i, row in enumerate(collection): 12 | <% row_errors = collection.get_errors(row) %> 13 | 14 | %for field in collection.render_fields.itervalues(): 15 | 16 | ${field.render()|n} 17 | %for error in row_errors.get(field, []): 18 | ${error} 19 | %endfor 20 | 21 | %endfor 22 | 23 | %endfor 24 | 25 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/forms/grid_readonly.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | %for field in collection.render_fields.itervalues(): 5 | ${F_(field.label_text or collection.prettify(field.key))|h} 6 | %endfor 7 | 8 | 9 | 10 | 11 | %for i, row in enumerate(collection): 12 | 13 | %for field in collection.render_fields.itervalues(): 14 | ${field.render_readonly()|n} 15 | %endfor 16 | 17 | %endfor 18 | 19 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/templates/forms/restfieldset.mako: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | <%! 3 | from formalchemy.ext.pylons.controller import model_url 4 | from pylons import url 5 | %> 6 | <%def name="h1(title, href=None)"> 7 |

8 | %if breadcrumb: 9 | 12 | %endif 13 | %if href: 14 | ${title.title()} 15 | %else: 16 | ${title.title()} 17 | %endif 18 |

19 | 20 | <%def name="buttons()"> 21 |

22 | 23 | 24 | 25 | 26 | 27 | ${F_('Cancel')} 28 | 29 |

30 | 31 | 32 | 33 | 34 | ${collection_name.title()} 35 | 36 | 37 | 38 | 39 |
40 | %if isinstance(models, dict): 41 |

${F_('Models')}

42 | %for name in sorted(models): 43 |

44 | ${name} 45 |

46 | %endfor 47 | %elif is_grid: 48 | ${h1(model_name)} 49 |
50 | ${pager|n} 51 |
52 | 53 | ${fs.render()|n} 54 |
55 |

56 | 57 | 58 | ${F_('New')} ${model_name} 59 | 60 |

61 | %else: 62 | ${h1(model_name, href=model_url(collection_name))} 63 | %if action == 'show': 64 | 65 | ${fs.render()|n} 66 |
67 |

68 | 69 | 70 | ${F_('Edit')} 71 | 72 |

73 | %elif action == 'edit': 74 |
75 | ${fs.render()|n} 76 | 77 | ${buttons()} 78 |
79 | %else: 80 |
81 | ${fs.render()|n} 82 | ${buttons()} 83 |
84 | %endif 85 | %endif 86 |
87 | 93 | 94 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Pylons application test package 2 | 3 | This package assumes the Pylons environment is already loaded, such as 4 | when this script is imported from the `nosetests --with-pylons=test.ini` 5 | command. 6 | 7 | This module initializes the application via ``websetup`` (`paster 8 | setup-app`) and provides the base testing objects. 9 | """ 10 | from unittest import TestCase 11 | 12 | from paste.deploy import loadapp 13 | from paste.script.appinstall import SetupCommand 14 | from pylons import url 15 | from routes.util import URLGenerator 16 | from webtest import TestApp 17 | 18 | import pylons.test 19 | 20 | __all__ = ['environ', 'url', 'TestController'] 21 | 22 | # Invoke websetup with the current config file 23 | SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']]) 24 | 25 | environ = {} 26 | 27 | class TestController(TestCase): 28 | 29 | def __init__(self, *args, **kwargs): 30 | wsgiapp = pylons.test.pylonsapp 31 | config = wsgiapp.config 32 | self.app = TestApp(wsgiapp) 33 | url._push_object(URLGenerator(config['routes.map'], environ)) 34 | TestCase.__init__(self, *args, **kwargs) 35 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/tests/functional/__init__.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/test_admin.py: -------------------------------------------------------------------------------- 1 | from pylonsapp.tests import * 2 | from pylonsapp import model 3 | from pylonsapp.model import meta 4 | import simplejson as json 5 | 6 | class TestAdminController(TestController): 7 | 8 | def setUp(self): 9 | TestController.setUp(self) 10 | meta.Session.bind.execute(model.foo_table.delete()) 11 | 12 | def test_index(self): 13 | # index 14 | response = self.app.get(url('admin')) 15 | response.mustcontain('/admin/Foo/models') 16 | response = response.click('Foo') 17 | 18 | ## Simple model 19 | 20 | # add page 21 | response.mustcontain('/admin/Foo/models/new') 22 | response = response.click('New Foo') 23 | form = response.forms[0] 24 | form['Foo--bar'] = 'value' 25 | response = form.submit() 26 | assert response.headers['location'] == 'http://localhost/admin/Foo/models' 27 | 28 | # model index 29 | response = response.follow() 30 | response.mustcontain('value') 31 | 32 | # edit page 33 | form = response.forms[0] 34 | response = form.submit() 35 | form = response.forms[0] 36 | form['Foo-1-bar'] = 'new value' 37 | form['_method'] = 'PUT' 38 | response = form.submit() 39 | response = response.follow() 40 | 41 | # model index 42 | response.mustcontain('new value') 43 | 44 | # delete 45 | response = self.app.get(url('models', model_name='Foo')) 46 | response.mustcontain('new value') 47 | response = response.forms[1].submit() 48 | response = response.follow() 49 | 50 | assert 'new value' not in response, response 51 | 52 | def test_fk(self): 53 | response = self.app.get(url('admin')) 54 | response.mustcontain('/admin/Animal/models') 55 | 56 | ## Animals / FK 57 | response = response.click('Animal') 58 | 59 | # add page 60 | response.mustcontain('/admin/Animal/models/new', 'New Animal') 61 | response = response.click('New Animal') 62 | response.mustcontain('') 63 | form = response.forms[0] 64 | form['Animal--name'] = 'dewey' 65 | form['Animal--owner_id'] = '1' 66 | response = form.submit() 67 | assert response.headers['location'] == 'http://localhost/admin/Animal/models' 68 | 69 | def test_json(self): 70 | # index 71 | response = self.app.get(url('formatted_admin', format='json')) 72 | response.mustcontain('{"models": {"Files": "/admin/Files/models",') 73 | 74 | ## Simple model 75 | 76 | # add page 77 | response = self.app.post(url('formatted_models', model_name='Foo', format='json'), 78 | {'Foo--bar': 'value'}) 79 | 80 | data = json.loads(response.body) 81 | id = data['item_url'].split('/')[-1] 82 | 83 | response.mustcontain('"Foo-%s-bar": "value"' % id) 84 | 85 | 86 | # get data 87 | response = self.app.get('%s.json' % data['item_url']) 88 | response.mustcontain('"Foo-%s-bar": "value"' % id) 89 | 90 | # edit page 91 | response = self.app.put('%s.json' % data['item_url'], '{"Foo-%s-bar": "new value"}' % id) 92 | response.mustcontain('"Foo-%s-bar": "new value"' % id) 93 | 94 | # delete 95 | response = self.app.delete('%s.json' % data['item_url']) 96 | 97 | 98 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/test_basic.py: -------------------------------------------------------------------------------- 1 | from pylonsapp.tests import * 2 | 3 | class TestBasicController(TestController): 4 | 5 | def test_index(self): 6 | response = self.app.get(url(controller='basic', action='index')) 7 | response.mustcontain('This is the bar field') 8 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/test_couchdb.py: -------------------------------------------------------------------------------- 1 | from pylonsapp.tests import * 2 | from couchdbkit import Server 3 | 4 | try: 5 | server = Server() 6 | if server: pass 7 | except: 8 | server = None 9 | else: 10 | try: 11 | server.delete_db('formalchemy_test') 12 | except: 13 | pass 14 | db = server.get_or_create_db('formalchemy_test') 15 | 16 | def couchdb_runing(func): 17 | if server: 18 | return func 19 | else: 20 | def f(self): pass 21 | return f 22 | 23 | class TestCouchdbController(TestController): 24 | 25 | @couchdb_runing 26 | def test_index(self): 27 | response = self.app.get('/couchdb') 28 | response.mustcontain('/couchdb/Pet/nodes') 29 | response = response.click('Pet') 30 | 31 | response.mustcontain('/couchdb/Pet/nodes/new') 32 | response = response.click('New Pet') 33 | form = response.forms[0] 34 | form['Pet--name'] = 'value' 35 | form['Pet--type'] = 'cat' 36 | response = form.submit() 37 | assert response.headers['location'] == 'http://localhost/couchdb/Pet/nodes' 38 | 39 | # model index 40 | response = response.follow() 41 | response.mustcontain('value') 42 | 43 | # edit page 44 | form = response.forms[0] 45 | response = form.submit() 46 | form = response.forms[0] 47 | for k in form.fields.keys(): 48 | if k and k.endswith('name'): 49 | form[k] = 'new value' 50 | node_id = k.split('-')[1] 51 | form['_method'] = 'PUT' 52 | response = form.submit() 53 | response = response.follow() 54 | 55 | # model index 56 | response.mustcontain('new value') 57 | 58 | # json response 59 | response = self.app.get('%s.json' % url('node', model_name='Pet', id=node_id)) 60 | response.mustcontain('"fields": {"doc_type": "Pet", "name": "new value",') 61 | 62 | # delete 63 | response = self.app.get(url('nodes', model_name='Pet')) 64 | response.mustcontain('new value') 65 | response = response.forms[1].submit() 66 | response = response.follow() 67 | 68 | assert 'new value' not in response, response 69 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/test_fsblob.py: -------------------------------------------------------------------------------- 1 | from pylonsapp.tests import * 2 | from pylonsapp import model 3 | from pylonsapp.model import meta 4 | from pylons import config 5 | import os 6 | 7 | class TestFsblobController(TestController): 8 | 9 | def setUp(self): 10 | TestController.setUp(self) 11 | meta.Session.bind.execute(model.files_table.delete()) 12 | 13 | def test_index(self): 14 | # form 15 | response = self.app.get(url(controller='fsblob', action='index')) 16 | response.mustcontain('Files--path') 17 | 18 | # test post file 19 | response = self.app.post(url(controller='fsblob', action='index'), 20 | upload_files=[('Files--path', 'test.txt', 'My test\n')]) 21 | response = response.follow() 22 | response.mustcontain('Remove') 23 | 24 | # get file with http 25 | fresponse = response.click('test.txt') 26 | assert fresponse.headers['content-type'] == 'text/plain' 27 | fresponse.mustcontain('My test') 28 | 29 | # assume storage 30 | #fpath = os.path.join(config['app_conf']['storage_path'], 31 | # fresponse.request.path_info[1:]) 32 | 33 | #assert os.path.isfile(fpath), fpath 34 | 35 | # remove 36 | form = response.form 37 | form['Files-1-path--remove'] = True 38 | 39 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/functional/test_owners.py: -------------------------------------------------------------------------------- 1 | from pylonsapp.tests import * 2 | 3 | class TestOwnersController(TestController): 4 | 5 | def test_index(self): 6 | resp = self.app.get(url('owners')) 7 | resp.mustcontain('gawel') 8 | 9 | resp = self.app.get(url('formatted_owners', format='json')) 10 | resp.mustcontain('"rows": [{"item_url": "/owners') 11 | 12 | def test_add(self): 13 | resp = self.app.post(url('/owners.json'), {"Owner--animals": '1', "Owner--name": "gawel"}) 14 | resp.mustcontain('"gawel"', '/owners/') 15 | 16 | def test_view(self): 17 | resp = self.app.get(url('/owners/1')) 18 | resp.mustcontain('gawel') 19 | 20 | resp = self.app.get(url('/owners/1.json')) 21 | resp.mustcontain('"gawel"') 22 | 23 | def test_edit(self): 24 | resp = self.app.get(url('/owners/1/edit')) 25 | resp.mustcontain('
', 26 | 'gawel') 27 | 28 | def test_update(self): 29 | resp = self.app.put(url('/owners/31.json'), '{"Owner-31-animals": [1], "Owner-31-name": "gawel"}') 30 | resp.mustcontain('"gawel"', '/owners/31') 31 | 32 | -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/tests/test_models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormAlchemy/formalchemy/dd848678444541278e1fbefffc7445b19dfab6dc/pylonsapp/pylonsapp/tests/test_models.py -------------------------------------------------------------------------------- /pylonsapp/pylonsapp/websetup.py: -------------------------------------------------------------------------------- 1 | """Setup the pylonsapp application""" 2 | import logging 3 | 4 | import pylons.test 5 | 6 | from pylonsapp.config.environment import load_environment 7 | from pylonsapp.model.meta import Session, metadata 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | def setup_app(command, conf, vars): 12 | """Place any commands to setup pylonsapp here""" 13 | # Don't reload the app if it was loaded under the testing environment 14 | if not pylons.test.pylonsapp: 15 | load_environment(conf.global_conf, conf.local_conf) 16 | 17 | # Create the tables if they aren't there already 18 | metadata.create_all(bind=Session.bind) 19 | 20 | from pylonsapp import model 21 | 22 | o = model.Owner() 23 | o.name = 'gawel' 24 | Session.add(o) 25 | for i in range(50): 26 | o = model.Owner() 27 | o.name = 'owner%i' % i 28 | Session.add(o) 29 | Session.commit() 30 | 31 | -------------------------------------------------------------------------------- /pylonsapp/setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_svn_revision = true 4 | 5 | [easy_install] 6 | find_links = http://www.pylonshq.com/download/ 7 | 8 | [nosetests] 9 | with-pylons = test.ini 10 | 11 | # Babel configuration 12 | [compile_catalog] 13 | domain = pylonsapp 14 | directory = pylonsapp/i18n 15 | statistics = true 16 | 17 | [extract_messages] 18 | add_comments = TRANSLATORS: 19 | output_file = pylonsapp/i18n/pylonsapp.pot 20 | width = 80 21 | 22 | [init_catalog] 23 | domain = pylonsapp 24 | input_file = pylonsapp/i18n/pylonsapp.pot 25 | output_dir = pylonsapp/i18n 26 | 27 | [update_catalog] 28 | domain = pylonsapp 29 | input_file = pylonsapp/i18n/pylonsapp.pot 30 | output_dir = pylonsapp/i18n 31 | previous = true 32 | -------------------------------------------------------------------------------- /pylonsapp/setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup, find_packages 3 | except ImportError: 4 | from ez_setup import use_setuptools 5 | use_setuptools() 6 | from setuptools import setup, find_packages 7 | 8 | setup( 9 | name='pylonsapp', 10 | version='0.1', 11 | description='', 12 | author='', 13 | author_email='', 14 | url='', 15 | install_requires=[ 16 | "Pylons", 17 | "SQLAlchemy>=0.5", 18 | ], 19 | setup_requires=["PasteScript>=1.6.3"], 20 | packages=find_packages(exclude=['ez_setup']), 21 | include_package_data=True, 22 | test_suite='nose.collector', 23 | package_data={'pylonsapp': ['i18n/*/LC_MESSAGES/*.mo']}, 24 | #message_extractors={'pylonsapp': [ 25 | # ('**.py', 'python', None), 26 | # ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), 27 | # ('public/**', 'ignore', None)]}, 28 | zip_safe=False, 29 | paster_plugins=['PasteScript', 'Pylons'], 30 | entry_points=""" 31 | [paste.app_factory] 32 | main = pylonsapp.config.middleware:make_app 33 | 34 | [paste.app_install] 35 | main = pylons.util:PylonsInstaller 36 | """, 37 | ) 38 | -------------------------------------------------------------------------------- /pylonsapp/test.ini: -------------------------------------------------------------------------------- 1 | # 2 | # pylonsapp - Pylons testing environment configuration 3 | # 4 | # The %(here)s variable will be replaced with the parent directory of this file 5 | # 6 | [DEFAULT] 7 | debug = true 8 | # Uncomment and replace with the address which should receive any error reports 9 | #email_to = you@yourdomain.com 10 | smtp_server = localhost 11 | error_email_from = paste@localhost 12 | 13 | [server:main] 14 | use = egg:Paste#http 15 | host = 127.0.0.1 16 | port = 5000 17 | 18 | [app:main] 19 | use = config:development.ini 20 | 21 | # Add additional test specific configuration options as necessary. 22 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # nose configuration 2 | [nosetests] 3 | with-doctest=true 4 | doctest-extension=.txt 5 | doctest-tests=true 6 | exclude=(pylons|tempita) 7 | verbosity=1 8 | with-coverage=1 9 | cover-package=formalchemy 10 | 11 | # Babel configuration 12 | [compile_catalog] 13 | domain = formalchemy 14 | directory = formalchemy/i18n_resources 15 | statistics = true 16 | 17 | [extract_messages] 18 | add_comments = TRANSLATORS: 19 | output_file = formalchemy/i18n_resources/formalchemy.pot 20 | width = 80 21 | 22 | [init_catalog] 23 | domain = formalchemy 24 | input_file = formalchemy/i18n_resources/formalchemy.pot 25 | output_dir = formalchemy/i18n_resources 26 | 27 | [update_catalog] 28 | domain = formalchemy 29 | input_file = formalchemy/i18n_resources/formalchemy.pot 30 | output_dir = formalchemy/i18n_resources 31 | previous = true 32 | 33 | [aliases] 34 | make_dist = compile_catalog sdist --formats=zip,gztar 35 | 36 | [zest.releaser] 37 | python-file-with-version = formalchemy/__init__.py 38 | tag-format = v{version} 39 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import setup, find_packages 3 | import xml.sax.saxutils 4 | from os.path import join 5 | import sys 6 | import os 7 | 8 | def get_version(fname='formalchemy/__init__.py'): 9 | with open(fname) as f: 10 | for line in f: 11 | if line.startswith('__version__'): 12 | return eval(line.split('=')[-1]) 13 | 14 | try: 15 | from msgfmt import Msgfmt 16 | except: 17 | sys.path.insert(0, join(os.getcwd(), 'formalchemy')) 18 | 19 | def compile_po(path): 20 | from msgfmt import Msgfmt 21 | for language in os.listdir(path): 22 | lc_path = join(path, language, 'LC_MESSAGES') 23 | if os.path.isdir(lc_path): 24 | for domain_file in os.listdir(lc_path): 25 | if domain_file.endswith('.po'): 26 | file_path = join(lc_path, domain_file) 27 | mo_file = join(lc_path, '%s.mo' % domain_file[:-3]) 28 | mo_content = Msgfmt(file_path, name=file_path).get() 29 | mo = open(mo_file, 'wb') 30 | mo.write(mo_content) 31 | mo.close() 32 | 33 | # We compile .mo files during setup. Don't think, it is a good idea. [sallner] 34 | compile_po(join(os.getcwd(), 'formalchemy', 'i18n_resources')) 35 | 36 | def read(filename): 37 | text = open(filename,'r').read() 38 | return xml.sax.saxutils.escape(text) 39 | 40 | long_description = '.. contents:: :depth: 1\n\n' +\ 41 | 'Description\n' +\ 42 | '===========\n\n' +\ 43 | read('README.txt') +\ 44 | '\n\n' +\ 45 | read('CHANGELOG.rst') 46 | 47 | setup(name='FormAlchemy', 48 | license='MIT License', 49 | version=get_version(), 50 | description='FormAlchemy greatly speeds development with SQLAlchemy mapped classes (models) in a HTML forms environment.', 51 | long_description=long_description, 52 | author='Alexandre Conrad, Jonathan Ellis, Gaël Pasgrimaud, Matthias Urlichs', 53 | author_email='formalchemy@googlegroups.com', 54 | url='https://github.com/FormAlchemy/formalchemy', 55 | install_requires=['SQLAlchemy', 'Tempita', 'WebHelpers2', 'WebOb', 'MarkupSafe', 'six'], 56 | packages=find_packages(exclude=('formalchemy.tests',)), 57 | package_data={'formalchemy': ['*.tmpl', 'i18n_resources/*/LC_MESSAGES/formalchemy.mo', 58 | 'ext/pylons/*.mako', 'ext/pylons/resources/*.css', 'ext/pylons/resources/*.png', 59 | 'tests/data/mako/*.mako', 'tests/data/genshi/*.html', 60 | 'paster_templates/pylons_fa/+package+/*/*_tmpl', 61 | ]}, 62 | include_package_data=True, 63 | classifiers=[ 64 | 'Development Status :: 5 - Production/Stable', 65 | 'Intended Audience :: Developers', 66 | 'License :: OSI Approved :: MIT License', 67 | 'Natural Language :: English', 68 | 'Operating System :: OS Independent', 69 | 'Programming Language :: Python', 70 | 'Topic :: Software Development :: Libraries :: Python Modules', 71 | 'Topic :: Software Development :: User Interfaces', 72 | 'Topic :: Text Processing :: Markup :: HTML', 73 | 'Topic :: Utilities', 74 | ], 75 | message_extractors = {'formalchemy': [ 76 | ('**.py', 'python', None), 77 | ('**.mako', 'mako', None), 78 | ('**.tmpl', 'python', None)]}, 79 | zip_safe=False, 80 | entry_points = """ 81 | [paste.paster_create_template] 82 | pylons_fa = formalchemy.ext.pylons.pastertemplate:PylonsTemplate 83 | """, 84 | ) 85 | 86 | -------------------------------------------------------------------------------- /sphinx.ini: -------------------------------------------------------------------------------- 1 | [app:main] 2 | use=egg:Paste#static 3 | document_root=%(here)s/docs/_build/html 4 | 5 | [server:main] 6 | use=egg:Paste#http 7 | host=0.0.0.0 8 | port=8000 9 | 10 | -------------------------------------------------------------------------------- /tempitification.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | for i in $(ls *.mako); 3 | do 4 | echo "$i""_tmpl" 5 | echo "{{if template_engine == 'mako'}}" > "$i""_tmpl" 6 | cat $i >> "$i""_tmpl" 7 | echo "{{endif}}" >> "$i""_tmpl" 8 | rm "$i" 9 | done 10 | --------------------------------------------------------------------------------