diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..73efe61
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,95 @@
+version: 2.0
+
+common: &common
+ working_directory: ~/repo
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v2-deps-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }}
+ - run:
+ name: install dependencies
+ command: pip install --user tox
+ - run:
+ name: run tox
+ command: ~/.local/bin/tox
+ - run:
+ name: upload coverage report
+ command: |
+ if [[ "$UPLOAD_COVERAGE" != 0 ]]; then
+ PATH=$HOME/.local/bin:$PATH
+ pip install --user codecov
+ coverage xml
+ ~/.local/bin/codecov --required -X search gcov pycov -f coverage.xml --flags $CIRCLE_JOB
+ fi
+ - save_cache:
+ paths:
+ - .tox
+ - ~/.cache/pip
+ - ~/.local
+ - ./eggs
+ key: v2-deps-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.py" }}-{{ checksum "tox.ini" }}
+
+jobs:
+ lint:
+ <<: *common
+ docker:
+ - image: circleci/python:3.6
+ environment:
+ - TOXENV=checkqa
+ - UPLOAD_COVERAGE=0
+ py27dj111:
+ <<: *common
+ docker:
+ - image: circleci/python:2.7
+ environment:
+ TOXENV=py27-dj111
+ py34dj111:
+ <<: *common
+ docker:
+ - image: circleci/python:3.4
+ environment:
+ TOXENV=py34-dj111
+ py34dj20:
+ <<: *common
+ docker:
+ - image: circleci/python:3.4
+ environment:
+ TOXENV=py34-dj20
+ py35dj111:
+ <<: *common
+ docker:
+ - image: circleci/python:3.5
+ environment:
+ TOXENV=py35-dj111
+ py35dj20:
+ <<: *common
+ docker:
+ - image: circleci/python:3.5
+ environment:
+ TOXENV=py35-dj20
+ py36dj111:
+ <<: *common
+ docker:
+ - image: circleci/python:3.6
+ environment:
+ TOXENV=py36-dj111
+ py36dj20:
+ <<: *common
+ docker:
+ - image: circleci/python:3.6
+ environment:
+ TOXENV=py36-dj20
+
+workflows:
+ version: 2
+ test:
+ jobs:
+ - lint
+ - py27dj111
+ - py34dj111
+ - py34dj20
+ - py35dj111
+ - py35dj20
+ - py36dj111
+ - py36dj20
\ No newline at end of file
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 7b361ef..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,7 +0,0 @@
-[run]
-source = formly
-omit = formly/tests/*,formly/admin.py
-branch = 1
-
-[report]
-omit = formly/tests/*,formly/admin.py
diff --git a/.gitignore b/.gitignore
index d405653..c6037eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,45 @@
-docs/_build
-formly.egg-info
-dist
-*.pyc
-.coverage
-.tox
+MANIFEST
+.DS_Store
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+docs/_build/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+*.eggs
.python-version
-.eggs/
+# Pipfile
+Pipfile
+Pipfile.lock
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# IDEs
+.idea/
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 002bd17..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-sudo: false
-language: python
-python:
- - "2.7"
- - "3.4"
- - "3.5"
-env:
- - DJANGO=1.8
- - DJANGO=1.9
- - DJANGO=1.10
-matrix:
- exclude:
-install:
- - pip install tox coveralls
-script:
- - tox -e py${TRAVIS_PYTHON_VERSION//[.]/}-$DJANGO
-after_success:
- - coveralls
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9b7b778
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Patrick Altman
+Luke Hatcher
+Ben Bliss
+James Tauber
+Jake Wegner
+Ronan Amicel
+Graham Ullrich
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 3dff49d..5aff318 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,10 @@
-all: init docs test
+all: init test
init:
python setup.py develop
- pip install detox coverage mkdocs
+ pip install detox coverage
test:
coverage erase
detox
coverage html
-
-docs:
- mkdocs build
-
-.PHONY: docs
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..17ed219
--- /dev/null
+++ b/README.md
@@ -0,0 +1,553 @@
+
+# Formly
+
+[![](https://img.shields.io/pypi/v/formly.svg)](https://pypi.python.org/pypi/formly/)
+
+[![CircleCi](https://img.shields.io/circleci/project/github/eldarion/formly.svg)](https://circleci.com/gh/eldarion/formly)
+[![Codecov](https://img.shields.io/codecov/c/github/eldarion/formly.svg)](https://codecov.io/gh/eldarion/formly)
+[![](https://img.shields.io/github/contributors/eldarion/formly.svg)](https://github.com/eldarion/formly/graphs/contributors)
+[![](https://img.shields.io/github/issues-pr/eldarion/formly.svg)](https://github.com/eldarion/formly/pulls)
+[![](https://img.shields.io/github/issues-pr-closed/eldarion/formly.svg)](https://github.com/eldarion/formly/pulls?q=is%3Apr+is%3Aclosed)
+
+[![](https://img.shields.io/badge/license-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
+
+
+## Table of Contents
+
+* [Overview](#overview)
+ * [Supported Django and Python versions](#supported-django-and-python-versions)
+* [Documentation](#documentation)
+ * [Installation](#installation)
+ * [Optional Requirements](#optional-requirements)
+ * [Usage](#usage)
+ * [Fields](#fields)
+ * [Templates](#templates)
+ * [Authorization](#authorization)
+ * [Callbacks](#callbacks)
+* [Change Log](#change-log)
+* [License](#license)
+
+
+## formly
+
+### Overview
+
+``formly`` is a forms/survey generator for dynamically constructed multi-page surveys with the ability to be non-linear.
+
+`formly` is an app that provides an out of the box solution to building adhoc
+forms to collect data from end users. It is multi-faceted in that it provides
+interfaces for building multi-page forms, interfaces for executing the survey,
+as well as views for reviewing results.
+
+Also, it is non-linear, meaning that you can route users taking the survey to
+different pages based on what they answered on certain questions. This allows
+you to create very rich surveys that dive deep on detail you care about while
+not wasting the time of users who would otherwise have to go through questions
+that do not apply to them.
+
+Development sponsored by [Midwest Communications](http://mwcradio.com/) and [Massachusetts General Hospital](http://www.massgeneral.org/).
+
+
+#### Supported Django and Python versions
+
+Django \ Python | 2.7 | 3.4 | 3.5 | 3.6
+--------------- | --- | --- | --- | ---
+1.11 | * | * | * | *
+2.0 | | * | * | *
+
+
+## Documentation
+
+### Installation
+
+To install formly:
+
+```shell
+ $ pip install formly
+```
+
+Add `formly` to your ``INSTALLED_APPS`` setting:
+
+```python
+ INSTALLED_APPS = [
+ # other apps
+ "formly",
+ ]
+```
+
+Next, add `formly.urls` to your project urlpatterns:
+
+```python
+ urlpatterns = [
+ # other urls
+ url(r"^surveys/", include("formly.urls", namespace="formly")),
+ ]
+```
+
+Finally, if you want to use formly's permission authentications,
+add `formly.auth_backend.AuthenticationBackend` to your settings
+AUTHENTICATION_BACKENDS:
+
+```python
+ AUTHENTICATION_BACKENDS = [
+ # other authentication backends
+ "formly.auth_backend.AuthenticationBackend",
+ ]
+```
+
+### Optional Requirements
+
+In order to use built-in templates, add the following dependencies to your project:
+
+* pinax-theme-bootstrap (not required if you use different block names)
+* django-bootstrap-form (required for form rendering in templates)
+
+```python
+ INSTALLED_APPS = [
+ # other apps
+ "bootstrapform",
+ ]
+```
+
+
+### Usage
+
+`formly` is designed to be plug-and-play, in that after you install `formly`
+using it should be as simple as creating and publishing surveys through
+the web interface.
+
+After installation, browse to wherever you mounted the urls for `formly` and
+you'll see an interface to be able to create a new survey. From here you can
+create a survey and begin editing pages. Pages in `formly` represent each
+step of the survey. The user will be guided through each page of the survey
+in the appropriate order saving each page at a time.
+
+For each page, you can give a title and add as many fields as you desire. If
+the field is a type that requires choices, then you will have the option
+on the fields detail/edit form to add choices. An optional value that can
+be supplied for a choice answer is the page to redirect the user to if they
+select that answer.
+
+If you have multiple choice fields in a single page with conflicting page
+routing, `formly` resolves to the first page it encounters. For example, if
+you had a question that had choice B route to page 3 and another question
+later in the form that had choice C route to page 5 and the user answered
+both questions with choice B and choice C, the user would go to page 3
+next. Keep this in mind when building surveys.
+
+
+### Fields
+
+`formly` enables you to create questions with a multitude of field types that
+will control the dynamic rendering and processing of form input on each page of
+your survey.
+
+All field types tie directly to a specific `field` and `widget`
+configuration as found in `django.forms`. The other attributes that can be
+passed into every field, `label`, `help_text`, and `required` can be
+set and managed at design time.
+
+For the field types that accept choices, there is the ability to set key/value
+pairs for each field that are used to populate the choices attribute for the
+field to be used for both display as well as form validation upon execution.
+
+#### Boolean (True/False)
+
+Renders and processes input using `django.forms.BooleanField`.
+
+#### Checkbox (Multiple Choice, Multiple Answers)
+
+A field generated from a `django.forms.MultipleChoiceField` with a
+`django.forms.CheckboxInput` widget, populated with choices specified
+at design time. This field allows for multiple selections.
+
+#### Date
+
+Provides a way to constrain input to dates only. It is
+generated from a `django.forms.DateField`.
+
+#### Likert Scale
+
+A `django.forms.ChoiceField` populated with choices specified at design time.
+The field template `formly/templates/bootstrapform/field.html` emits:
+
+```html
+
+```
+
+for hooking in CSS design. The following sample CSS presents a Likert field
+in familiar horizontal layout. You should add this (or similar) CSS to your
+project to get Likert-scale presentation.
+
+```css
+ form .likert-question {
+ list-style:none;
+ width:100%;
+ margin:0;
+ padding:0 0 35px;
+ display:block;
+ border-bottom:2px solid #efefef;
+ }
+ form .likert-question:last-of-type {
+ border-bottom:0;
+ }
+ form .likert-question:before {
+ content: '';
+ position:relative;
+ top:13px;
+ left:13%;
+ display:block;
+ background-color:#dfdfdf;
+ height:4px;
+ width:75%;
+ }
+ form .likert-question li {
+ display:inline-block;
+ width:19%;
+ text-align:center;
+ vertical-align: top;
+ }
+ form .likert-question li input[type=radio] {
+ display:block;
+ position:relative;
+ top:0;
+ left:50%;
+ margin-left:-6px;
+ }
+ form .likert-question li label {
+ width:100%;
+ }
+```
+
+#### Media (File Upload)
+
+Enables users to upload content as a response using `django.forms.FileField`.
+
+#### Multiple Text (Multiple Free Responses - Single Lines)
+
+Presents a number of single line fields.
+The number of fields is specified at design time.
+
+#### Radio (Multiple Choice, Pick One)
+
+A `django.forms.ChoiceField` with a `django.forms.RadioSelect` widget,
+populated with choices specified at design time.
+
+#### Rating Scale
+
+A `django.forms.ChoiceField` populated with choices specified at design time.
+The field template `formly/templates/bootstrapform/field.html` emits:
+
+```html
+
+```
+
+#### Select (Multiple Choice, Pick One - Dropdown)
+
+A select field generated from a `django.forms.ChoiceField` with a
+`django.forms.Select` widget, populated with choices specified at design time.
+
+#### Text (Free Response, One Line)
+
+A field for open ended text input and is interpreted as `django.forms.CharField`.
+
+#### TextArea (Free Response, Box)
+
+A `django.forms.CharField` with a `django.forms.Textarea` widget used
+to collect longer form text input.
+
+
+### Templates
+
+`formly` ships with some stock templates that are based on
+`pinax-theme-bootstrap` and `django-forms-bootstrap`. You are not required
+to use these of course and in case you are rolling your own templates, here
+is what the views in `formly` expect.
+
+#### `formly/design/choice_form.html`
+
+**Context:** `form`, `choice`, `page`
+
+**Extends:** `formly/design/survey_edit_base.html`
+
+Provides the ability to update the values for a particular choice for a choice field.
+
+#### `formly/design/field_confirm_delete.html`
+
+**Context:** `form`, `field`
+
+**Extends:** `site_base.html`
+
+Rendered to supply a delete confirmation form for field deletion.
+
+#### `formly/design/field_form.html`
+
+**Context:** `form`, `field`, `page`, `field_choice_form`
+
+**Extends:** `formly/design/survey_edit_base.html`
+
+Rendered for a user interface to update a field.
+
+#### `formly/design/fieldchoice_confirm_delete.html`
+
+**Context:** `form`, `fieldchoice`
+
+**Extends:** `site_base.html`
+
+Rendered to supply a delete confirmation form for field choice deletion.
+
+#### `formly/design/page_confirm_delete.html`
+
+**Context:** `form`, `page`
+
+**Extends:** `site_base.html`
+
+Rendered to supply a delete confirmation form for page deletion.
+
+#### `formly/design/page_form.html`
+
+**Context:** `form`, `page`, `field_form`
+
+**Extends:** `formly/design/survey_edit_base.html`
+
+Displays the user interface for updating a page object.
+
+#### `formly/design/survey_confirm_delete.html`
+
+**Context:** `form`, `survey`
+
+**Extends:** `site_base.html`
+
+Rendered to supply a delete confirmation form for survey deletion.
+
+#### `formly/design/survey_detail.html`
+
+**Context:** `survey`
+
+**Extends:** `site_base.html`
+
+Displays the detail for a survey.
+
+#### `formly/design/survey_edit_base.html`
+
+**Context:** `page`
+
+**Extends:** `subnav_base.html`
+
+**Extended By:** `formly/design/choice_form.html`, `formly/design/field_form.html`, `formly/design/page_form.html`
+
+A base template to provide common sub-navigation.
+
+#### `formly/design/survey_form.html`
+
+**Context:** `form`
+
+**Extends:** `site_base.html`
+
+Contains the creation form for creating a new survey object.
+
+#### `formly/design/survey_list.html`
+
+**Context:** `unpublished_surveys`, `published_surveys`
+
+**Extends:** `site_base.html`
+
+This template receives all surveys in the system split between two context objects,
+one for published surveys and the other for unpublished surveys.
+
+#### `formly/results/home.html`
+
+**Context:** `survey`
+
+**Extends:** `site_base.html`
+
+Displays the results of a given survey.
+
+#### `formly/run/page.html`
+
+**Context:** `form`, `page`
+
+**Extends:** `site_base.html`
+
+Rendered for the end user to complete a particular survey. Always
+rendered with the appropriate page for the user.
+
+#### `formly/bootstrapform/field.html`
+
+**Context:** `field`
+
+This modified `django-bootstrap-form` template renders the various field types,
+including special handling for Likert and Rating fields.
+
+
+### Authorization
+
+`formly` ships with an auth backend that by default, when added to
+your `AUTHENTICATION_BACKENDS` setting will segment the create,
+edit, delete and results viewing based on `request.user` being
+the `Survey.creator`.
+
+You can override this by writing your own auth backend and using in
+it's place.
+
+The permission labels used are as follows:
+
+
+#### `formly.view_survey_list`
+
+User can see the list of published and unpublished surveys.
+
+#### `formly.create_survey`
+
+User can create a survey.
+
+#### `formly.view_survey_detail`
+
+User can view the survey's detail. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.change_survey_name`
+
+User can change the survey's name. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.publish_survey`
+
+User can publish the survey. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.duplicate_survey`
+
+User can duplicate the survey. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.edit_survey`
+
+User can edit the survey. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.view_results`
+
+User can view the survey's results. The survey object in question is
+passed to the `has_perm` method of the auth backend.
+
+#### `formly.delete_object`
+
+User can delete the object in question. The object will be either a
+`Survey`, `Page`, `Field`, or a `FieldChoice`.
+
+
+### Callbacks
+
+Callbacks are a way to provide functionality to `formly` that requires some
+runtime decision making instead of just a setting. They are callables
+defined in settings.py and ship some sane defaults.
+
+
+#### `FORMLY_COMPLETE_REDIRECT_CALLBACK`
+
+**Default:** `formly.callbacks.survey_complete_redirect`
+
+**Arguments:** `survey`
+
+**Expected Return:** a url that will be passed to `redirect()`
+
+
+## Change Log
+
+### 1.0.0
+
+* Add Django v1.11, 2.0 support
+* Drop Django 1.8, 1.9, 1.10, and Python 3.3 support
+* Add URL namespacing (i.e. urlname "formly_survey_results" is now "formly:survey_results") **Backwards Incompatible**
+* Rename URL names, removing "dt_" and "rt_" prefixes **Backwards incompatible**
+* Move documentation into README and standardize layout
+* Convert CI and coverage to CircleCi and CodeCov
+* Add PyPi-compatible long description
+* Add migration checking to test suite
+
+### 0.15.0
+
+* fix bug where widget instances were passed instead of widget classes (#34)
+
+### 0.14.0
+
+* add hookset to support customizing available field type choices when designing a survey
+
+### 0.13.0
+
+* fix field mapping bug (#30)
+* improve output of MultipleTextField widget (#20)
+
+### 0.12.0
+
+* fix broken migrations from 0.11.0
+
+### 0.11.0
+
+* add support for Rating field
+
+### 0.10.2
+
+* fix app to work with a custom user module
+* add missing migration for formly.Field
+
+### 0.10.1
+
+* fix Field.form_field() bug when Likert field has no choices
+
+### 0.10
+
+* add Likert-style field widget and presentation
+
+
+### 0.9
+
+* make label and help_text textfields
+
+### 0.6
+
+* changed field label descriptions to be more suitable for less technical audiences
+* made compatible with Django > 1.5
+* drop unique constraint on field label
+
+### 0.5
+
+* made urls Django 1.5 compatible
+* add maximum_choices field
+* drop unique constraint on field label
+
+### 0.4.2
+
+* fixed multiple choice field
+* added survey to context
+
+### 0.4.1
+
+* fixed serialization bug, note this is a backwards incompatible change
+ if you have previously stored results
+
+### 0.4
+
+* added authorization checks for all the views
+
+### 0.3
+
+* added ability to control redirection at the end of a survey
+
+### 0.2
+
+* added ability to change the ordering of fields on a page
+
+### 0.1
+
+* initial release
+
+
+## License
+
+Copyright (c) 2012-2018 Patrick Altman and contributors under the [BSD license](https://opensource.org/licenses/BSD-3-Clause).
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 0a2e291..0000000
--- a/README.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-formly
-======
-
-
-.. image:: https://img.shields.io/travis/eldarion/formly.svg
- :target: https://travis-ci.org/eldarion/formly
-
-.. image:: https://img.shields.io/coveralls/eldarion/formly.svg
- :target: https://coveralls.io/r/eldarion/formly
-
-.. image:: https://img.shields.io/pypi/dm/formly.svg
- :target: https://pypi.python.org/pypi/formly/
-
-.. image:: https://img.shields.io/pypi/v/formly.svg
- :target: https://pypi.python.org/pypi/formly/
-
-.. image:: https://img.shields.io/badge/license-BSD-blue.svg
- :target: https://pypi.python.org/pypi/formly/
-
-
-A forms/survey generator for dynamically constructor multi-page surveys that have the ability to be non-linear.
-
-
-development sponsored by `Midwest Communications`_ and `Massachusetts General Hospital`_.
-
-.. _Midwest Communications: http://mwcradio.com/
-.. _Massachusetts General Hospital: http://www.massgeneral.org/
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index ad80acd..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,131 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-PROJECT = formly
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
-
-help:
- @echo "Please use \`make ' where is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(PROJECT).qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/$(PROJECT).qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/$(PROJECT)"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/$(PROJECT)"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/authorization.rst b/docs/authorization.rst
deleted file mode 100644
index 1a55352..0000000
--- a/docs/authorization.rst
+++ /dev/null
@@ -1,76 +0,0 @@
-.. _authorization:
-
-
-Authorization
-=============
-
-``formly`` ships with an auth backend that by default, when added to
-your ``AUTHENTICATION_BACKENDS`` setting will segment the create,
-edit, delete and results viewing based on the ``reauest.user`` being
-the ``Survey.creator``.
-
-You can override this by writing your own auth backend and using in
-it's place.
-
-The permission labels used are as follows:
-
-
-formly.view_survey_list
------------------------
-
-Can the user see the list of published and unpublished surveys
-
-
-formly.create_survey
---------------------
-
-Can the user create a survey
-
-
-formly.view_survey_detail
--------------------------
-
-Can the user view the survey's detail. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.change_survey_name
--------------------------
-
-Can the user change the survey's name. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.publish_survey
----------------------
-
-Can the user publish the survey. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.duplicate_survey
------------------------
-
-Can the user duplicate the survey. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.edit_survey
-------------------
-
-Can the user edit the survey. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.view_results
--------------------
-
-Can the user view the survey's results. The survey object in question is
-passed to the ``has_perm`` method of the auth backend.
-
-
-formly.delete_object
---------------------
-
-Can the user delete the object in question. The object will be either a
-``Survey``, ``Page``, ``Field``, or a ``FieldChoice``.
diff --git a/docs/callbacks.rst b/docs/callbacks.rst
deleted file mode 100644
index e2548c8..0000000
--- a/docs/callbacks.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-.. _callbacks:
-
-
-Callbacks
-=========
-
-Callbacks are a way to provide functionality to formly that requires some
-runtime decision making instead of just a setting. They are callables
-that are defined in settings and ship some sane defaults.
-
-
-``FORMLY_COMPLETE_REDIRECT_CALLBACK``
--------------------------------------
-
-:Default: ``formly.callbacks.survey_complete_redirect``
-:Arguments: ``survey``
-:Expected Return: a url that will be passed to ``redirect()``
diff --git a/docs/changelog.rst b/docs/changelog.rst
deleted file mode 100644
index b90d905..0000000
--- a/docs/changelog.rst
+++ /dev/null
@@ -1,100 +0,0 @@
-.. _changelog:
-
-ChangeLog
-=========
-
-0.15.0
-------
-- fix bug where widget instances were passed instead of widget classes (#34)
-
-0.14.0
-------
-- add hookset to support customizing available field type choices when designing a survey
-
-0.13.0
-------
-- fix field mapping bug (#30)
-- improve output of MultipleTextField widget (#20)
-
-0.12.0
-------
-
-- fix broken migrations from 0.11.0
-
-0.11.0
-------
-
-- add support for Rating field
-
-0.10.2
-------
-
-- fix app to work with a custom user module
-- add missing migration for formly.Field
-
-0.10.1
-------
-
-- fix Field.form_field() bug when Likert field has no choices
-
-0.10
------
-
-- add Likert-style field widget and presentation
-
-
-0.9
----
-
-- make label and help_text textfields
-
-
-0.6
----
-
-- changed field label descriptions to be more suitable for less technical audiences
-- made compatible with Django > 1.5
-- drop unique constraint on field label
-
-
-0.5
----
-
-- made urls Django 1.5 compatible
-- add maximum_choices field
-- drop unique constraint on field label
-
-0.4.2
------
-
-- fixed multiple choice field
-- added survey to context
-
-0.4.1
------
-
-- fixed serialization bug, note this is a backwards incompatible change
- if you have previously stored results
-
-0.4
----
-
-- added authorization checks for all the views
-
-
-0.3
----
-
-- added ability to control redirection at the end of a survey
-
-
-0.2
----
-
-- added ability to change the ordering of fields on a page
-
-
-0.1
----
-
-- initial release
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index 38e4fbd..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import os
-import sys
-
-extensions = []
-templates_path = []
-source_suffix = '.rst'
-master_doc = 'index'
-project = u'formly'
-copyright_holder = 'Eldarion'
-copyright = u'2012, %s' % copyright_holder
-exclude_patterns = ['_build']
-pygments_style = 'sphinx'
-html_theme = 'default'
-htmlhelp_basename = '%sdoc' % project
-latex_documents = [
- ('index', '%s.tex' % project, u'%s Documentation' % project,
- copyright_holder, 'manual'),
-]
-man_pages = [
- ('index', project, u'%s Documentation' % project,
- [copyright_holder], 1)
-]
-
-sys.path.insert(0, os.pardir)
-m = __import__(project)
-
-version = m.__version__
-release = version
diff --git a/docs/fields.rst b/docs/fields.rst
deleted file mode 100644
index ac8fc99..0000000
--- a/docs/fields.rst
+++ /dev/null
@@ -1,150 +0,0 @@
-.. _fields:
-
-Fields
-======
-
-``formly`` enables you to create questions with a multitude of field types that
-will control the dynamic rendering and processing of form input on each page of
-your survey.
-
-All field types tie directly to a specific ``field`` and ``widget``
-configuration as found in ``django.forms``. The other attributes that can be
-passed into every field, ``label``, ``help_text``, and ``required`` can be
-set and managed at design time.
-
-For the field types that accept choices, there is the ability to set key/value
-pairs for each field that are used to populate the choices attribute for the
-field to be used for both display as well as form validation upon execution.
-
-
-text field
-----------
-
-The ``text field`` is a field for open ended text input and is interpreted as
-``django.forms.CharField``.
-
-
-textarea
---------
-
-The ``textarea`` field type is a ``django.forms.CharField`` with a
-``django.forms.Textarea`` widget to be used to collect longer form text input.
-
-
-radio choices
--------------
-
-The ``radio choices`` field type is a ``django.forms.ChoiceField`` with a
-``django.forms.RadioSelect`` widget, populated with choices specified at
-design time.
-
-
-dropdown field
---------------
-
-The ``dropdown field`` is a select field generated from a
-``django.forms.ChoiceField`` with a ``django.forms.Select`` widget, populated
-with choices specified at design time.
-
-
-checkbox field
---------------
-
-The ``checkbox field`` is a field generated from a
-``django.forms.MultipleChoiceField`` with a ``django.forms.CheckboxInput`` widget,
-populated with choices specified at design time. This field allows for
-multiple selections.
-
-
-date field
-----------
-
-The ``date field`` provides a way to constrain input to dates only. It is
-generated from a ``django.forms.DateField``.
-
-
-media upload field
-------------------
-
-The ``media upload field`` enables users to upload content as a response. It
-is uses ``django.forms.FileField``.
-
-
-boolean field
--------------
-
-The ``boolean field`` renders and processes input using
-``django.forms.BooleanField``.
-
-
-multiple text field
--------------------
-
-The ``multiple text`` field type presents a number of single line fields.
-The number of fields is specified at design time.
-
-
-likert scale field
-------------------
-
-The ``likert scale`` field type is a ``django.forms.ChoiceField``,
-populated with choices specified at design time. The field template
-``formly/templates/bootstrapform/field.html`` emits:
-
-
-
-for hooking in CSS design. The following sample CSS presents a Likert field
-in familiar horizontal layout. You should add this (or similar)
-CSS to your project to get Likert-scale presentation.
-
- form .likert-question {
- list-style:none;
- width:100%;
- margin:0;
- padding:0 0 35px;
- display:block;
- border-bottom:2px solid #efefef;
- }
- form .likert-question:last-of-type {
- border-bottom:0;
- }
- form .likert-question:before {
- content: '';
- position:relative;
- top:13px;
- left:13%;
- display:block;
- background-color:#dfdfdf;
- height:4px;
- width:75%;
- }
- form .likert-question li {
- display:inline-block;
- width:19%;
- text-align:center;
- vertical-align: top;
- }
- form .likert-question li input[type=radio] {
- display:block;
- position:relative;
- top:0;
- left:50%;
- margin-left:-6px;
- }
- form .likert-question li label {
- width:100%;
- }
-
-
-rating scale field
-------------------
-
-The ``rating scale`` field type is a ``django.forms.ChoiceField``,
-populated with choices specified at design time. The field template
-``formly/templates/bootstrapform/field.html`` emits:
-
-
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index a49524c..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,37 +0,0 @@
-======
-formly
-======
-
-`formly` is an app that provides an out of the box solution to building adhoc
-forms to collect data from end users. It is multi-faceted in that it provides
-interfaces for building multi-page forms, interfaces for executing the survey,
-as well as views for reviewing results.
-
-Also, it is non-linear, meaning that you can route users taking the survey to
-different pages based on what they answered on certain questions. This allows
-you to create very rich surveys that dive deep on detail you care about while
-not wasting the time of users who would otherwise have to go through questions
-that do not apply to them.
-
-This project is brought to you by Midwest Communications.
-
-
-Development
------------
-
-The source repository can be found at https://github.com/eldarion/formly/
-
-
-Contents
-========
-
-.. toctree::
- :maxdepth: 1
-
- changelog
- installation
- usage
- fields
- templates
- authorization
- callbacks
diff --git a/docs/installation.rst b/docs/installation.rst
deleted file mode 100644
index 5f5e716..0000000
--- a/docs/installation.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-.. _installation:
-
-Installation
-============
-
-* Requirements:
- * django-jsonfield==0.9.13
-
-* Optional Requirements (to use the built in templates):
- * pinax-theme-bootstrap (not required if you use different block names)
- * django-forms-bootstrap (required for form rendering in templates)
-
-* To install::
-
- pip install formly
-
-* Add ``'formly'`` to your ``INSTALLED_APPS`` setting::
-
- INSTALLED_APPS = (
- # other apps
- "formly",
- )
-
-* Mount the ``formly.urls`` somewhere::
-
- urlpatterns = patterns("",
- ...
- url(r"^surveys/", include("formly.urls")),
- ...
- )
diff --git a/docs/templates.rst b/docs/templates.rst
deleted file mode 100644
index 2d4261f..0000000
--- a/docs/templates.rst
+++ /dev/null
@@ -1,144 +0,0 @@
-.. _templates:
-
-Templates
-=========
-
-``formly`` ships with some stock templates that are based on
-``pinax-theme-bootstrap`` and ``django-forms-bootstrap``. You are not required
-to use these of course and in case you are rolling your own templates, here
-is what the views in ``formly`` expect.
-
-
-``formly/design/choice_form.html``
-----------------------------------
-
-:Context: ``form``, ``choice``, ``page``
-:Extends: ``formly/design/survey_edit_base.html``
-
-This is the template that provides the ability to update the values for a
-particular choice for a choice field.
-
-
-``formly/design/field_confirm_delete.html``
--------------------------------------------
-
-:Context: ``form``, ``field``
-:Extends: ``site_base.html``
-
-This is the template is rendered to supply a delete confirmation form for
-field deletion.
-
-
-``formly/design/field_form.html``
-----------------------------------
-
-:Context: ``form``, ``field``, ``page``, ``field_choice_form``
-:Extends: ``formly/design/survey_edit_base.html``
-
-This is the template is rendered for a user interface to update a field.
-
-
-``formly/design/fieldchoice_confirm_delete.html``
--------------------------------------------------
-
-:Context: ``form``, ``fieldchoice``
-:Extends: ``site_base.html``
-
-This is the template is rendered to supply a delete confirmation form for
-field choice deletion.
-
-
-``formly/design/page_confirm_delete.html``
-------------------------------------------
-
-:Context: ``form``, ``page``
-:Extends: ``site_base.html``
-
-This is the template is rendered to supply a delete confirmation form for
-page deletion.
-
-
-``formly/design/page_form.html``
---------------------------------
-
-:Context: ``form``, ``page``, ``field_form``
-:Extends: ``formly/design/survey_edit_base.html``
-
-This is the template is that displays the user interface for updating a
-page object.
-
-
-``formly/design/survey_confirm_delete.html``
---------------------------------------------
-
-:Context: ``form``, ``survey``
-:Extends: ``site_base.html``
-
-This is the template is rendered to supply a delete confirmation form for
-survey deletion.
-
-
-``formly/design/survey_detail.html``
-------------------------------------
-
-:Context: ``survey``
-:Extends: ``site_base.html``
-
-This template displays the detail for a survey.
-
-
-``formly/design/survey_edit_base.html``
----------------------------------------
-
-:Context: ``page``
-:Extends: ``subnav_base.html``
-:Extended By: ``formly/design/choice_form.html``, ``formly/design/field_form.html``, ``formly/design/page_form.html``
-
-This a base template to provide some common subnav.
-
-
-``formly/design/survey_form.html``
-----------------------------------
-
-:Context: ``form``
-:Extends: ``site_base.html``
-
-This template hosts the creation form for creating a new survey object.
-
-
-``formly/design/survey_list.html``
-----------------------------------
-
-:Context: ``unpublished_surveys``, ``published_surveys``
-:Extends: ``site_base.html``
-
-This template receives all surveys in the system split between two context objects,
-one for published surveys and the other for unpublished surveys.
-
-
-``formly/results/home.html``
-----------------------------
-
-:Context: ``survey``
-:Extends: ``site_base.html``
-
-A template for displaying the results of a given survey.
-
-
-``formly/run/page.html``
-------------------------
-
-:Context: ``form``, ``page``
-:Extends: ``site_base.html``
-
-This template is rendered for the end user to complete a particular survey, it is always
-rendered with the appropriate page for the user.
-
-
-``formly/bootstrapform/field.html``
-------------------------
-
-:Context: ``field``
-
-This modified ``django-bootstrap-form`` template renders the various field types,
-including special handling for Likert and Rating fields.
diff --git a/docs/usage.rst b/docs/usage.rst
deleted file mode 100644
index 5154833..0000000
--- a/docs/usage.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-.. _usage:
-
-Usage
-=====
-
-``formly`` is designed to be pretty plug-and-play, in that after you install
-it, using it should be as simple as creating and publishing surveys through
-the web interface.
-
-After installation, browse to whereever you mounted the urls for `formly` and
-you'll see an interface to be able to create a new survey. From here you can
-create a survey and begin editing pages. Pages in `formly` represent each
-step of the survey. The user will be guided through each page of the survey
-in the appropriate order saving each page at a time.
-
-For each page, you can give a title and add as many fields as you desire. If
-the field is a type that requires choices, then you will have the option
-on the fields detail/edit form to add choices. An optional value that can
-be supplied for a choice answer is the page to redirect the user to if they
-select that answer.
-
-If you have multiple choice fields in a single page with conflicting page
-routing, `formly` resolves to the first page it encounters. For example, if
-you had a question that had choice B route to page 3 and another question
-later in the form that had choice C route to page 5 and the user answered
-both questions with choice B and choice C, the user would go to page 3
-next. Keep this in mind when building surveys.
diff --git a/formly/auth_backend.py b/formly/auth_backend.py
index 00f7935..934c7dc 100644
--- a/formly/auth_backend.py
+++ b/formly/auth_backend.py
@@ -38,7 +38,7 @@ def has_perm(self, user, perm, obj=None):
"formly.view_results"
]
if perm in permissions:
- return user.is_authenticated()
+ return user.is_authenticated
if perm in survey_permissions:
return obj and user == obj.creator
if perm == "formly.delete_object":
diff --git a/formly/callbacks.py b/formly/callbacks.py
index 7bf5675..a7c8452 100644
--- a/formly/callbacks.py
+++ b/formly/callbacks.py
@@ -1,4 +1,4 @@
-from django.core.urlresolvers import reverse
+from django.urls import reverse
def survey_complete_redirect(survey):
diff --git a/formly/fields.py b/formly/fields.py
index 328304e..5f66812 100644
--- a/formly/fields.py
+++ b/formly/fields.py
@@ -10,7 +10,7 @@ def __init__(self, *args, **kwargs):
self.maximum_choices = kwargs.pop("maximum_choices")
self.default_error_messages.update({
- 'maximum_choices': _('You may select at most %(maximum)d choices (%(selected)d selected)')
+ "maximum_choices": _("You may select at most %(maximum)d choices (%(selected)d selected)")
})
super(LimitedMultipleChoiceField, self).__init__(*args, **kwargs)
@@ -21,9 +21,9 @@ def validate(self, value):
selected_count = len(value)
if self.maximum_choices and selected_count > self.maximum_choices:
raise ValidationError(
- self.error_messages['maximum_choices'],
- code='maximum_choices',
- params={'maximum': self.maximum_choices, 'selected': selected_count},
+ self.error_messages["maximum_choices"],
+ code="maximum_choices",
+ params={"maximum": self.maximum_choices, "selected": selected_count},
)
diff --git a/formly/forms/design.py b/formly/forms/design.py
index fbd7279..a3f57a9 100644
--- a/formly/forms/design.py
+++ b/formly/forms/design.py
@@ -1,7 +1,7 @@
from django import forms
from ..hooks import hookset
-from ..models import Survey, Page, Field, FieldChoice, OrdinalScale
+from ..models import Field, FieldChoice, OrdinalScale, Page, Survey
class SurveyCreateForm(forms.ModelForm):
diff --git a/formly/forms/run.py b/formly/forms/run.py
index 1366954..f0fcfb1 100644
--- a/formly/forms/run.py
+++ b/formly/forms/run.py
@@ -1,6 +1,6 @@
from django import forms
-from formly.models import SurveyResult, Field, FieldResult
+from formly.models import Field, FieldResult, SurveyResult
class FieldResultMixin(object):
diff --git a/formly/forms/widgets.py b/formly/forms/widgets.py
index c1e55e8..a36a97f 100644
--- a/formly/forms/widgets.py
+++ b/formly/forms/widgets.py
@@ -17,7 +17,9 @@ def decompress(self, value):
def format_output(self, rendered_widgets):
return render_to_string(
"formly/run/_multiple_input.html",
- {"inputs": rendered_widgets}
+ context={
+ "inputs": rendered_widgets
+ }
)
diff --git a/formly/migrations/0001_initial.py b/formly/migrations/0001_initial.py
index 47bc778..b21e830 100644
--- a/formly/migrations/0001_initial.py
+++ b/formly/migrations/0001_initial.py
@@ -34,8 +34,8 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('label', models.CharField(max_length=100)),
- ('field', models.ForeignKey(related_name='choices', to='formly.Field')),
- ('target', models.ForeignKey(related_name='target_choices', blank=True, to='formly.Field', null=True)),
+ ('field', models.ForeignKey(related_name='choices', to='formly.Field', on_delete=models.CASCADE)),
+ ('target', models.ForeignKey(related_name='target_choices', blank=True, to='formly.Field', null=True, on_delete=models.SET_NULL)),
],
),
migrations.CreateModel(
@@ -68,7 +68,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=django.utils.timezone.now)),
('updated', models.DateTimeField(default=django.utils.timezone.now)),
('published', models.DateTimeField(null=True, blank=True)),
- ('creator', models.ForeignKey(related_name='surveys', to=settings.AUTH_USER_MODEL)),
+ ('creator', models.ForeignKey(related_name='surveys', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
migrations.CreateModel(
@@ -76,49 +76,49 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('date_submitted', models.DateTimeField(default=django.utils.timezone.now)),
- ('survey', models.ForeignKey(related_name='survey_results', to='formly.Survey')),
- ('user', models.ForeignKey(related_name='survey_results', to=settings.AUTH_USER_MODEL)),
+ ('survey', models.ForeignKey(related_name='survey_results', to='formly.Survey', on_delete=models.CASCADE)),
+ ('user', models.ForeignKey(related_name='survey_results', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
migrations.AddField(
model_name='page',
name='survey',
- field=models.ForeignKey(related_name='pages', to='formly.Survey'),
+ field=models.ForeignKey(related_name='pages', to='formly.Survey', on_delete=models.CASCADE),
),
migrations.AddField(
model_name='page',
name='target',
- field=models.ForeignKey(blank=True, to='formly.Page', null=True),
+ field=models.ForeignKey(blank=True, to='formly.Page', null=True, on_delete=models.SET_NULL),
),
migrations.AddField(
model_name='fieldresult',
name='page',
- field=models.ForeignKey(related_name='results', to='formly.Page'),
+ field=models.ForeignKey(related_name='results', to='formly.Page', on_delete=models.CASCADE),
),
migrations.AddField(
model_name='fieldresult',
name='question',
- field=models.ForeignKey(related_name='results', to='formly.Field'),
+ field=models.ForeignKey(related_name='results', to='formly.Field', on_delete=models.CASCADE),
),
migrations.AddField(
model_name='fieldresult',
name='result',
- field=models.ForeignKey(related_name='results', to='formly.SurveyResult'),
+ field=models.ForeignKey(related_name='results', to='formly.SurveyResult', on_delete=models.CASCADE),
),
migrations.AddField(
model_name='fieldresult',
name='survey',
- field=models.ForeignKey(related_name='results', to='formly.Survey'),
+ field=models.ForeignKey(related_name='results', to='formly.Survey', on_delete=models.CASCADE),
),
migrations.AddField(
model_name='field',
name='page',
- field=models.ForeignKey(related_name='fields', blank=True, to='formly.Page', null=True),
+ field=models.ForeignKey(related_name='fields', blank=True, to='formly.Page', null=True, on_delete=models.SET_NULL),
),
migrations.AddField(
model_name='field',
name='survey',
- field=models.ForeignKey(related_name='fields', to='formly.Survey'),
+ field=models.ForeignKey(related_name='fields', to='formly.Survey', on_delete=models.CASCADE),
),
migrations.AlterUniqueTogether(
name='page',
diff --git a/formly/migrations/0005_field_scale.py b/formly/migrations/0005_field_scale.py
index 59325e8..19979b9 100644
--- a/formly/migrations/0005_field_scale.py
+++ b/formly/migrations/0005_field_scale.py
@@ -16,6 +16,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='field',
name='scale',
- field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='formly.LikertScale'),
+ field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fields', to='formly.LikertScale'),
),
]
diff --git a/formly/migrations/0006_auto_20161206_1415.py b/formly/migrations/0006_auto_20161206_1415.py
index 99cc3c3..f3b96d4 100644
--- a/formly/migrations/0006_auto_20161206_1415.py
+++ b/formly/migrations/0006_auto_20161206_1415.py
@@ -16,6 +16,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='field',
name='scale',
- field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='formly.LikertScale'),
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fields', to='formly.LikertScale'),
),
]
diff --git a/formly/migrations/0008_auto_20170609_0800.py b/formly/migrations/0008_auto_20170609_0800.py
index 3700550..8623954 100644
--- a/formly/migrations/0008_auto_20170609_0800.py
+++ b/formly/migrations/0008_auto_20170609_0800.py
@@ -10,6 +10,7 @@ class Migration(migrations.Migration):
dependencies = [
('formly', '0007_help_text_and_label_to_textfield'),
]
+ atomic = False
operations = [
migrations.RenameModel(
diff --git a/formly/models.py b/formly/models.py
index bc894af..1550a95 100644
--- a/formly/models.py
+++ b/formly/models.py
@@ -3,10 +3,10 @@
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
-from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import Max
from django.template.defaultfilters import slugify
+from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
@@ -34,12 +34,12 @@ def __str__(self):
@python_2_unicode_compatible
class OrdinalChoice(models.Model):
- scale = models.ForeignKey(OrdinalScale, related_name="choices")
+ scale = models.ForeignKey(OrdinalScale, related_name="choices", on_delete=models.CASCADE)
label = models.CharField(max_length=100)
score = models.IntegerField()
def __str__(self):
- return "{} ({})".format(self.label, self.score)
+ return "{} ({})".format(self.label, self.score) # pragma: no cover
class Meta:
unique_together = [("scale", "score"), ("scale", "label")]
@@ -48,7 +48,7 @@ class Meta:
@python_2_unicode_compatible
class Survey(models.Model):
name = models.CharField(max_length=255)
- creator = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="surveys")
+ creator = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="surveys", on_delete=models.CASCADE)
created = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(default=timezone.now)
published = models.DateTimeField(null=True, blank=True)
@@ -59,10 +59,13 @@ def save(self, *args, **kwargs):
return super(Survey, self).save(*args, **kwargs)
def __str__(self):
- return self.name
+ return self.name # pragma: no cover
def get_absolute_url(self):
- return reverse("formly_dt_survey_detail", kwargs={"pk": self.pk})
+ return reverse("formly:survey_detail", kwargs={"pk": self.pk})
+
+ def get_run_url(self):
+ return reverse("formly:take_survey", kwargs={"pk": self.pk})
def duplicate(self): # @@@ This could like use with some refactoring
survey = Survey.objects.get(pk=self.pk)
@@ -137,11 +140,11 @@ def publish(self):
@python_2_unicode_compatible
class Page(models.Model):
- survey = models.ForeignKey(Survey, related_name="pages")
+ survey = models.ForeignKey(Survey, related_name="pages", on_delete=models.CASCADE)
page_num = models.PositiveIntegerField(null=True, blank=True)
subtitle = models.CharField(max_length=255, blank=True)
# Should be null when a FieldChoice on it's last field has a target.
- target = models.ForeignKey("self", null=True, blank=True)
+ target = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL)
class Meta:
unique_together = [
@@ -156,7 +159,7 @@ def save(self, *args, **kwargs):
return super(Page, self).save(*args, **kwargs)
def __str__(self):
- return self.label()
+ return self.label() # pragma: no cover
def label(self):
if self.subtitle:
@@ -165,35 +168,7 @@ def label(self):
return "Page %d" % self.page_num
def get_absolute_url(self):
- return reverse("formly_dt_page_detail", kwargs={"pk": self.pk})
-
- def move_up(self):
- try:
- other_field = self.survey.pages.order_by("-page_num").filter(
- page_num__lt=self.page_num
- )[0]
- existing = self.page_num
- other = other_field.page_num
- self.page_num = other
- other_field.page_num = existing
- other_field.save()
- self.save()
- except IndexError:
- return
-
- def move_down(self):
- try:
- other_field = self.page.fields.order_by("page_num").filter(
- page_num__gt=self.page_num
- )[0]
- existing = self.page_num
- other = other_field.page_num
- self.page_num = other
- other_field.page_num = existing
- other_field.save()
- self.save()
- except IndexError:
- return
+ return reverse("formly:page_detail", kwargs={"pk": self.pk})
def next_page(self, user):
target = self
@@ -249,11 +224,11 @@ class Field(models.Model):
(RATING_FIELD, "Rating Scale")
]
- survey = models.ForeignKey(Survey, related_name="fields") # Denorm
- page = models.ForeignKey(Page, null=True, blank=True, related_name="fields")
+ survey = models.ForeignKey(Survey, related_name="fields", on_delete=models.CASCADE) # Denorm
+ page = models.ForeignKey(Page, null=True, blank=True, related_name="fields", on_delete=models.SET_NULL)
label = models.TextField()
field_type = models.IntegerField(choices=FIELD_TYPE_CHOICES)
- scale = models.ForeignKey(OrdinalScale, default=None, null=True, blank=True, related_name="fields")
+ scale = models.ForeignKey(OrdinalScale, default=None, null=True, blank=True, related_name="fields", on_delete=models.SET_NULL)
help_text = models.TextField(blank=True)
ordinal = models.IntegerField()
maximum_choices = models.IntegerField(null=True, blank=True)
@@ -271,6 +246,9 @@ class Field(models.Model):
# )
def save(self, *args, **kwargs):
+ if not self.ordinal:
+ # Set ordinal, since full_clean() will fail if not set
+ self.ordinal = 1
self.full_clean()
if not self.pk and self.page is not None:
self.ordinal = (self.page.fields.aggregate(
@@ -315,7 +293,7 @@ def __str__(self):
)
def get_absolute_url(self):
- return reverse("formly_dt_field_update", kwargs={"pk": self.pk})
+ return reverse("formly:field_update", kwargs={"pk": self.pk})
@property
def needs_choices(self):
@@ -430,9 +408,9 @@ def _get_field_class(self, choices):
@python_2_unicode_compatible
class FieldChoice(models.Model):
- field = models.ForeignKey(Field, related_name="choices")
+ field = models.ForeignKey(Field, related_name="choices", on_delete=models.CASCADE)
label = models.CharField(max_length=100)
- target = models.ForeignKey(Field, null=True, blank=True, related_name="target_choices")
+ target = models.ForeignKey(Field, null=True, blank=True, related_name="target_choices", on_delete=models.SET_NULL)
def clean(self):
super(FieldChoice, self).clean()
@@ -452,8 +430,8 @@ def __str__(self):
@python_2_unicode_compatible
class SurveyResult(models.Model):
- survey = models.ForeignKey(Survey, related_name="survey_results")
- user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="survey_results")
+ survey = models.ForeignKey(Survey, related_name="survey_results", on_delete=models.CASCADE)
+ user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="survey_results", on_delete=models.CASCADE)
date_submitted = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
@@ -465,10 +443,10 @@ def __str__(self):
@python_2_unicode_compatible
class FieldResult(models.Model):
- survey = models.ForeignKey(Survey, related_name="results") # Denorm
- page = models.ForeignKey(Page, related_name="results") # Denorm
- result = models.ForeignKey(SurveyResult, related_name="results")
- question = models.ForeignKey(Field, related_name="results")
+ survey = models.ForeignKey(Survey, related_name="results", on_delete=models.CASCADE) # Denorm
+ page = models.ForeignKey(Page, related_name="results", on_delete=models.CASCADE) # Denorm
+ result = models.ForeignKey(SurveyResult, related_name="results", on_delete=models.CASCADE)
+ question = models.ForeignKey(Field, related_name="results", on_delete=models.CASCADE)
upload = models.FileField(upload_to="formly/", blank=True)
answer = JSONField(blank=True) # @@@ I think this should be something different than a string
diff --git a/formly/receivers.py b/formly/receivers.py
index 894605c..d42877d 100644
--- a/formly/receivers.py
+++ b/formly/receivers.py
@@ -1,5 +1,5 @@
-from django.dispatch import receiver
from django.db.models import signals
+from django.dispatch import receiver
from formly.models import Survey
diff --git a/formly/templates/formly/design/_field_edit.html b/formly/templates/formly/design/_field_edit.html
index a28ed7c..4d8a9c9 100644
--- a/formly/templates/formly/design/_field_edit.html
+++ b/formly/templates/formly/design/_field_edit.html
@@ -2,19 +2,19 @@
{% if field_form %}
Edit Field
-
{% if selected_field.needs_choices %}
Choices
-