Customisable, modular user- and developer- friendly form builder application for Django
Project description
django-fobi (later on named just fobi) is a customisable, modular, user- and developer- friendly form builder application for Django. With fobi you can build Django forms using an intiutive GUI, save or mail posted form data. API allows you to build your own form elements and form handlers (mechanisms for handling the submitted form data).
Prerequisites
Django 1.5, 1.6, 1.7
Python >= 2.6.8, 2.7.*, 3.3.*
Note, that Django 1.7 is not yet proclaimed to be flawlessly supported!
Key concepts
Each form consists of elements. Form elements are divided into two groups:
form fields (input field, textarea, hidden field, file field, etc.).
content (presentational) elements (text, image, embed video, etc.).
Number of form elements is not limited.
Each form may contain handlers. Handler processes the form data (for example, saves it or mails it). Number of the handlers is not limited.
Both form elements and form handlers are made with Django permission system in mind.
As an addition to form handlers, form callbacks are implemented. Form callbacks are fired on various stages of pre- and post-processing the form data (on POST). Form callbacks do not make use of permission system (unless you intentionally do so in the code of your callback) and are fired for all forms (unlike form handlers, that are executed only if assigned).
Each plugin (form element or form handler) or a callback - is a Django micro-app.
Main features and highlights
User-friendly GUI to quickly build forms.
Large variety of form elements/fields. Most of the Django fields are supported. HTML5 fields are supported as well.
In addition to standard form elements, there are cosmetic (presentational) form elements (for adding a piece of text, image or a embed video) alongside standard form elements.
Data handling in plugins (form handlers). Save the data, mail it to some address or repost it to some other endpoint.
Developer-friendly API, which allows to or build new form fields and handlers without touching the core.
Support for custom user model.
Theming. There are 3 ready to use themes bundled: Bootstrap 3, Foundation 5 and Simple (in style of Django admin).
Implemented integration with FeinCMS (in a form of a FeinCMS Page widget).
Reordering of form elements using drag-n-drop.
Roadmap
Some of the upcoming/in-development features/improvements are:
Django 1.7 support.
Form importers (and as a part of it - MailChimp integration, which would allow to import forms from MailChimp into Fobi using a user-friendly wizard).
Export data for the db_store plugin which would allow to export saved form data as a CSV file.
Fieldsets.
See the TODOS for the full list of planned-, pending- in-development- or to-be-implemented features.
Installation
Install latest stable version from PyPI:
$ pip install django-fobi
Or latest stable version from GitHub:
$ pip install -e git+https://github.com/barseghyanartur/django-fobi@stable#egg=django-fobi
Or latest stable version from BitBucket:
$ pip install -e hg+https://bitbucket.org/barseghyanartur/django-fobi@stable#egg=django-fobi
Add fobi to INSTALLED_APPS of the your projects’ Django settings. Furthermore, all themes and plugins to be used, shall be added to the INSTALLED_APPS as well.
INSTALLED_APPS = (
# ...
# Fobi core
'fobi',
# Fobi themes
'fobi.contrib.themes.bootstrap3', # Bootstrap 3 theme
'fobi.contrib.themes.foundation5', # Foundation 5 theme
'fobi.contrib.themes.simple', # Simple theme
# Fobi form elements - fields
'fobi.contrib.plugins.form_elements.fields.boolean',
'fobi.contrib.plugins.form_elements.fields.date',
'fobi.contrib.plugins.form_elements.fields.datetime',
'fobi.contrib.plugins.form_elements.fields.email',
'fobi.contrib.plugins.form_elements.fields.file',
'fobi.contrib.plugins.form_elements.fields.hidden',
'fobi.contrib.plugins.form_elements.fields.integer',
'fobi.contrib.plugins.form_elements.fields.password',
'fobi.contrib.plugins.form_elements.fields.radio',
'fobi.contrib.plugins.form_elements.fields.select',
'fobi.contrib.plugins.form_elements.fields.select_model_object',
'fobi.contrib.plugins.form_elements.fields.select_multiple',
'fobi.contrib.plugins.form_elements.fields.text',
'fobi.contrib.plugins.form_elements.fields.textarea',
'fobi.contrib.plugins.form_elements.fields.url',
# Fobi form elements - content elements
'fobi.contrib.plugins.form_elements.content.dummy',
'fobi.contrib.plugins.form_elements.content.image',
'fobi.contrib.plugins.form_elements.content.text',
'fobi.contrib.plugins.form_elements.content.video',
# Form handlers
'fobi.contrib.plugins.form_handlers.db_store',
'fobi.contrib.plugins.form_handlers.http_repost',
'fobi.contrib.plugins.form_handlers.mail',
# Other project specific apps
'foo', # Test app
# ...
)
Make appropriate changes to the TEMPLATE_CONTEXT_PROCESSORS of the your projects’ Django settings.
And the following to the context processors.
TEMPLATE_CONTEXT_PROCESSORS = (
# ...
"fobi.context_processors.theme".
# ...
)
Make sure that django.core.context_processors.request is in TEMPLATE_CONTEXT_PROCESSORS too.
Configure URLs
Add the following line to urlpatterns of your urls module.
# View URLs
url(r'^fobi/', include('fobi.urls.view')),
# Edit URLs
url(r'^fobi/', include('fobi.urls.edit')),
View URLs are put separately from edit URLs in order to make it possible to prefix the edit URLs differently. For example, if you’re using the “Simple” theme, you would likely want to prefix the edit URLs with “admin/” so that it looks more like django-admin.
Demo
See the documentation for some screen shots:
PythonHosted http://pythonhosted.org/django-fobi/#screenshots
ReadTheDocs http://django-fobi.readthedocs.org/en/latest/#screenshots
In order to be able to quickly evaluate the Fobi, a demo app (with a quick installer) has been created (works on Ubuntu/Debian, may work on other Linux systems as well, although not guaranteed). Follow the instructions below for having the demo running within a minute.
Grab the latest django_fobi_example_app_installer.sh:
$ wget https://raw.github.com/barseghyanartur/django-fobi/stable/examples/django_fobi_example_app_installer.sh
Assign execute rights to the installer and run the django_fobi_example_app_installer.sh:
$ chmod +x django_fobi_example_app_installer.sh
$ ./django_fobi_example_app_installer.sh
Open your browser and test the app.
Dashboard:
Admin username: test_admin
Admin password: test
Django admin interface:
Admin username: test_admin
Admin password: test
If quick installer doesn’t work for you, see the manual steps on running the example project.
Creating a new form element plugin
Form element plugins represent the elements of which the forms is made: Inputs, checkboxes, textareas, files, hidden fields, as well as pure presentational elements (text or image). Number of form elements in a form is not limited.
Presentational form elements are inherited from fobi.base.FormElementPlugin.
The rest (real form elements, that are supposed to have a value) are inherited from fobi.base.FormFieldPlugin.
You should see a form element plugin as a Django micro app, which could have its’ own models, admin interface, etc.
Fobi comes with several bundled form element plugins. Do check the source code as example.
Let’s say, you want to create a textarea form element plugin.
There are several properties, each textarea should have. They are:
label (string): HTML label of the textarea.
name (string): HTML name of the textarea.
initial (string): Initial value of the textarea.
required (bool): Flag, which tells us whether the field is required or optional.
Let’s name that plugin sample_textarea. The plugin directory should then have the following structure.
path/to/sample_textarea/
├── __init__.py
├── fobi_form_elements.py # Where plugins are defined and registered
├── forms.py # Plugin configuration form
└── widgets.py # Where plugins widgets are defined
Form element plugins should be registered in “fobi_form_elements.py” file. Each plugin module should be put into the INSTALLED_APPS of your Django projects’ settings.
In some cases, you would need plugin specific overridable settings (see fobi.contrib.form_elements.fields.content.image plugin as an example). You are advised to write your settings in such a way, that variables of your Django project settings module would have FOBI_PLUGIN_ prefix.
Define and register the form element plugin
Step by step review of a how to create and register a plugin and plugin widgets. Note, that Fobi autodiscovers your plugins if you place them into a file named fobi_form_elements.py of any Django app listed in INSTALLED_APPS of your Django projects’ settings module.
path/to/sample_textarea/fobi_form_elements.py
A single form element plugin is registered by its’ UID.
Required imports.
from django import forms
from fobi.base import FormFieldPlugin, form_element_plugin_registry
from path.to.sample_textarea.forms import SampleTextareaForm
Defining the Sample textarea plugin.
class SampleTextareaPlugin(FormFieldPlugin):
uid = "sample_textarea"
name = "Sample Textarea"
form = SampleTextareaForm
group = "Samples" # Group to which the plugin belongs to
def get_form_field_instances(self):
kwargs = {
'required': self.data.required,
'label': self.data.label,
'initial': self.data.initial,
'widget': forms.widgets.Textarea(attrs={})
}
return [(self.data.name, forms.CharField, kwargs),]
Registering the SampleTextareaPlugin plugin.
form_element_plugin_registry.register(SampleTextareaPlugin)
Note, that in case you want to define a pure presentational element, make use of fobi.base.FormElementPlugin for subclassing, instead of fobi.base.FormFieldPlugin. See the source of the content plugins (fobi.contrib.plugins.form_elements.content) as a an example.
There might be cases, when you need to do additional handling of the data upon the successful form submittion. In such cases, you will need to define a submit_plugin_form_data method in the plugin, which accepts the following arguments:
form_entry (fobi.models.FormEntry): Form entry, which is being submitted.
request (django.http.HttpRequest): The Django HTTP request.
form (django.forms.Form): Form object (a valid one, which contains the cleaned_data attribute).
Example (taken from fobi.contrib.plugins.form_elements.fields.file):
def submit_plugin_form_data(self, form_entry, request, form):
# Get the file path
file_path = form.cleaned_data.get(self.data.name, None)
if file_path:
# Handle the upload
saved_file = handle_uploaded_file(FILES_UPLOAD_DIR, file_path)
# Overwrite ``cleaned_data`` of the ``form`` with path to moved
# file.
form.cleaned_data[self.data.name] = "{0}{1}".format(
settings.MEDIA_URL, saved_file
)
# It's critically important to return the ``form`` with updated
# ``cleaned_data``
return form
In the example below, the original form is being modified. If you don’t want the original form to be modified, do not return anything.
Check the file form element plugin (fobi.contrib.plugins.form_elements.fields.file) for complete example.
path/to/sample_textarea/forms.py
Why to have another file for defining forms? Just to keep the code clean and less messy, although you could perfectly define all your plugin forms in the module fobi_form_elements.py, it’s recommended to keep it separate.
Take into consideration, that forms.py is not an autodiscovered file pattern. All your form element plugins should be registered in modules named fobi_form_elements.py.
Required imports.
from django import forms
from fobi.base import BasePluginForm
Form for for SampleTextareaPlugin form element plugin.
class SampleTextareaForm(forms.Form, BasePluginForm):
plugin_data_fields = [
("name", ""),
("label", ""),
("initial", ""),
("required", False)
]
name = forms.CharField(label="Name", required=True)
label = forms.CharField(label="Label", required=True)
initial = forms.CharField(label="Initial", required=False)
required = forms.BooleanField(label="Required", required=False)
Note that although it’s not being checked in the code, but for form field plugins the following fields should be present in the plugin form (BasePluginForm) and the form plugin (FormFieldPlugin):
name
In some cases, you might want to do something with the data before it gets saved. For that purpose, save_plugin_data method has been introduced.
See the following example.
def save_plugin_data(self, request=None):
"""
Saving the plugin data and moving the file.
"""
file_path = self.cleaned_data.get('file', None)
if file_path:
saved_image = handle_uploaded_file(IMAGES_UPLOAD_DIR, file_path)
self.cleaned_data['file'] = saved_image
path/to/sample_textarea/widgets.py
Required imports.
from fobi.base import FormElementPluginWidget
Defining the base plugin widget.
class BaseSampleTextareaPluginWidget(FormElementPluginWidget):
# Same as ``uid`` value of the ``SampleTextareaPlugin``.
plugin_uid = "sample_textarea"
path/to/sample_layout/fobi_form_elements.py
Register in the registry (in some module which is for sure to be loaded; it’s handy to do it in the theme module).
Required imports.
from fobi.base import form_element_plugin_widget_registry
from path.to.sample_textarea.widgets import BaseSampleTextareaPluginWidget
Define the theme specific plugin.
class SampleTextareaPluginWidget(BaseSampleTextareaPluginWidget):
theme_uid = 'bootstrap3' # Theme for which the widget is loaded
media_js = ['sample_layout/js/fobi.plugins.form_elements.sample_textarea.js',]
media_css = ['sample_layout/css/fobi.plugins.form_elements.sample_textarea.css',]
Register the widget.
form_element_plugin_widget_registry.register(SampleTextareaPluginWidget)
Form element plugin final steps
Now, that everything is ready, make sure your plugin module is added to INSTALLED_APPS.
INSTALLED_APPS = (
# ...
'path.to.sample_textarea',
# ...
)
Afterwards, go to terminal and type the following command.
$ ./manage.py fobi_sync_plugins
If your HTTP server is running, you would then be able to see the new plugin in the edit form interface.
Dashboard URL: http://127.0.0.1:8000/fobi/
Note, that you have to be logged in, in order to use the dashboard. If your new plugin doesn’t appear, set the FOBI_DEBUG to True in your Django’s local settings module, re-run your code and check console for error notifications.
Creating a new form handler plugin
Form handler plugins handle the form data. Fobi comes with several bundled form handler plugins, among which is the db_store and mail plugins, which are responsible for saving the submitted form data into the database and mailing the data to recipients specified. Number of form handlers in a form is not limited. Certain form handlers are not configurable (for example the db_store form handler isn’t), while others are (mail, http_repost).
You should see a form handler as a Django micro app, which could have its’ own models, admin interface, etc.
As said above, Fobi comes with several bundled form handler plugins. Do check the source code as example.
Define and register the form handler plugin
Let’s name that plugin sample_mail. The plugin directory should then have the following structure.
path/to/sample_mail/
├── __init__.py
├── fobi_form_handlers.py # Where plugins are defined and registered
└── forms.py # Plugin configuration form
Form handler plugins should be registered in “fobi_form_handlers.py” file. Each plugin module should be put into the INSTALLED_APPS of your Django projects’ settings.
path/to/sample_mail/fobi_form_handlers.py
A single form handler plugin is registered by its’ UID.
Required imports.
import json
from django.core.mail import send_mail
from fobi.base import FormHandlerPlugin, form_handler_plugin_registry
from path.to.sample_mail.forms import SampleMailForm
Defining the Sample mail handler plugin.
class SampleMailHandlerPlugin(FormHandlerPlugin):
uid = "sample_mail"
name = _("Sample mail")
form = SampleMailForm
def run(self, form_entry, request, form):
send_mail(
self.data.subject,
json.dumps(form.cleaned_data),
self.data.from_email,
[self.data.to_email],
fail_silently = True
)
Some form handlers are configurable, some others not. In order to have a user friendly way of showing the form handler settings, what’s sometimes needed, a plugin_data_repr method has been introducd. Simplest implementation of it would look as follows:
def plugin_data_repr(self):
"""
Human readable representation of plugin data.
:return string:
"""
return self.data.__dict__
path/to/sample_mail/forms.py
If plugin is configurable, it has configuration data. A single form may have unlimited number of same plugins. Imagine, you want to have different subjects and additional body texts for different user groups. You could then assign two form handler mail plugins to the form. Of course, saving the posted form data many times does not make sense, but it’s up to the user. So, in case if plugin is configurable, it should have a form.
Why to have another file for defining forms? Just to keep the code clean and less messy, although you could perfectly define all your plugin forms in the module fobi_form_handlers.py, it’s recommended to keep it separate.
Take into consideration, that forms.py is not an autodiscovered file pattern. All your form handler plugins should be registered in modules named fobi_form_handlers.py.
Required imports.
from django import forms
from django.utils.translation import ugettext_lazy as _
from fobi.base import BasePluginForm
Defining the form for Sample mail handler plugin.
class MailForm(forms.Form, BasePluginForm):
plugin_data_fields = [
("from_name", ""),
("from_email", ""),
("to_name", ""),
("to_email", ""),
("subject", ""),
("body", ""),
]
from_name = forms.CharField(label=_("From name"), required=True)
from_email = forms.EmailField(label=_("From email"), required=True)
to_name = forms.CharField(label=_("To name"), required=True)
to_email = forms.EmailField(label=_("To email"), required=True)
subject = forms.CharField(label=_("Subject"), required=True)
body = forms.CharField(label=_("Body"), required = False,
widget=forms.widgets.Textarea)
After the plugin has been processed, all its’ data is available in a plugin_instance.data container (for example, plugin_instance.data.subject or plugin_instance.data.from_name).
Prioritise the excecution order
Some form handlers shall be executed prior others. A good example of such, is a combination of “mail” and “db_save” form handlers for the form. In case of large files posted, submittion of form data would fail if “mail” plugin would be executed after “db_save” has been executed. That’s why it’s possible to prioritise that ordering in a FOBI_FORM_HANDLER_PLUGINS_EXECUTION_ORDER setting variable.
If not specified or left empty, form handler plugins would be ran in the order of discovery. All form handler plugins that are not listed in the FORM_HANDLER_PLUGINS_EXECUTION_ORDER, would be ran after the plugins that are mentioned there.
FORM_HANDLER_PLUGINS_EXECUTION_ORDER = (
'http_repost',
'mail',
# The 'db_store' is left out intentionally, since it should
# be the last plugin to be executed.
)
Form handler plugin custom actions
By default, a single form handler plugin has at least a “delete” action. If plugin is configurable, it gets an “edit” action as well.
For some of your plugins, you may want to register a custom action. For example, the “db_store” plugin does have one, for showing a link to a listing page with saved form data for the form given.
For such cases, define a custom_actions method in your form handler plugin. That method shall return a list of triples. In each triple, first value is the URL, second value is the title and the third value is the icon of the URL.
The following example is taken from the “db_store” plugin.
def custom_actions(self):
"""
Adding a link to view the saved form enties.
:return iterable:
"""
return (
(
reverse('fobi.contrib.plugins.form_handlers.db_store.view_saved_form_data_entries'),
_("View entries"),
'glyphicon glyphicon-list'
),
)
Form handler plugin final steps
Do not forget to add the form handler plugin module to INSTALLED_APPS.
INSTALLED_APPS = (
# ...
'path.to.sample_mail',
# ...
)
Afterwards, go to terminal and type the following command.
$ ./manage.py fobi_sync_plugins
If your HTTP server is running, you would then be able to see the new plugin in the edit form interface.
Creating a form callback
Form callbacks are additional hooks, that are executed on various stages of the form submission.
Let’s place the callback in the foo module. The plugin directory should then have the following structure.
path/to/foo/
├── __init__.py
└── fobi_form_callbacks.py # Where callbacks are defined and registered
See the callback example below.
Required imports.
from fobi.constants import (
CALLBACK_BEFORE_FORM_VALIDATION,
CALLBACK_FORM_VALID_BEFORE_SUBMIT_PLUGIN_FORM_DATA,
CALLBACK_FORM_VALID, CALLBACK_FORM_VALID_AFTER_FORM_HANDLERS,
CALLBACK_FORM_INVALID
)
from fobi.base import FormCallback, form_callback_registry
Define and register the callback
class SampleFooCallback(FormCallback):
stage = CALLBACK_FORM_VALID
def callback(self, form_entry, request, form):
print("Great! Your form is valid!")
form_callback_registry.register(SampleFooCallback)
Add the callback module to INSTALLED_APPS.
INSTALLED_APPS = (
# ...
'path.to.foo',
# ...
)
Suggestions
Custom action for the form
Sometimes, you would want to specify a different action for the form. Although it’s possible to define a custom form action (action field in the “Form properties” tab), you’re advised to use the http_repost plugin instead, since then the form would be still validated locally and only then the valid data, as is, would be sent to the desired endpoint.
Take in mind, that if both cases, if CSRF protection is enabled on the endpoint, your post request would result an error.
When you want to customise too many things
Fobi, with its’ flexible form elements, form handlers and form callbacks is very customisable. However, there might be cases when you need to override entire view to fit your needs. Take a look at the FeinCMS integration as a good example of such. You may also want to compare the code from original view fobi.views.view_form_entry with the code from the widget to get a better idea of what could be changed in your case. If need a good advice, just ask me.
Theming
Fobi comes with theming API. While there are several ready-to-use themes:
Bootstrap 3 theme
Foundation 5 theme
Simple theme in style of the Django admin
Have in mind, that creating a brand new theme could be time consuming. Instead, you can create your own theme based on existing ones (just copy the desired theme to your project directory and work it out further).
It’s possible to use different templates for all “view” and “edit” actions (see the source code of the “simple” theme). Both Bootstrap 3 and Foundation 5 themes look great. Although if you can’t use any of those, the “simple” theme is the best start, since it looks just like django-admin.
Create a new theme
Let’s place the theme in the sample_theme module. The theme directory should then have the following structure.
path/to/sample_theme/
├── static
│ ├── css
│ │ └── sample_theme.css
│ └── js
│ └── sample_theme.js
├── templates
│ └── sample_theme
│ ├── _base.html
│ ├── add_form_element_entry.html
│ ├── ...
│ └── view_form_entry_ajax.html
├── __init__.py
├── fobi_form_elements.py
└── fobi_themes.py # Where themes are defined and registered
See the theme example below.
from django.utils.translation import ugettext_lazy as _
from fobi.base import BaseTheme, theme_registry
class SampleTheme(BaseTheme):
"""
Sample theme.
"""
uid = 'sample'
name = _("Sample")
media_css = (
'sample_theme/css/sample_theme.css',
'css/fobi.core.css',
)
media_js = (
'js/jquery-1.10.2.min.js',
'jquery-ui/js/jquery-ui-1.10.3.custom.min.js',
'js/jquery.slugify.js',
'js/fobi.core.js',
'sample_theme/js/sample_theme.js',
)
# Form element specific
form_element_html_class = 'form-control'
form_radio_element_html_class = 'radio'
form_element_checkbox_html_class = 'checkbox'
form_edit_form_entry_option_class = 'glyphicon glyphicon-edit'
form_delete_form_entry_option_class = 'glyphicon glyphicon-remove'
form_list_container_class = 'list-inline'
# Templates
master_base_template = 'sample_theme/_base.html'
base_template = 'sample_theme/base.html'
form_ajax = 'sample_theme/snippets/form_ajax.html'
form_snippet_template_name = 'sample_theme/snippets/form_snippet.html'
form_properties_snippet_template_name = 'sample_theme/snippets/form_properties_snippet.html'
messages_snippet_template_name = 'sample_theme/snippets/messages_snippet.html'
add_form_element_entry_template = 'sample_theme/add_form_element_entry.html'
add_form_element_entry_ajax_template = 'sample_theme/add_form_element_entry_ajax.html'
add_form_handler_entry_template = 'sample_theme/add_form_handler_entry.html'
add_form_handler_entry_ajax_template = 'sample_theme/add_form_handler_entry_ajax.html'
create_form_entry_template = 'sample_theme/create_form_entry.html'
create_form_entry_ajax_template = 'bootstrap3/create_form_entry_ajax.html'
dashboard_template = 'sample_theme/dashboard.html'
edit_form_element_entry_template = 'sample_theme/edit_form_element_entry.html'
edit_form_element_entry_ajax_template = 'sample_theme/edit_form_element_entry_ajax.html'
edit_form_entry_template = 'sample_theme/edit_form_entry.html'
edit_form_entry_ajax_template = 'sample_theme/edit_form_entry_ajax.html'
edit_form_handler_entry_template = 'sample_theme/edit_form_handler_entry.html'
edit_form_handler_entry_ajax_template = 'sample_theme/edit_form_handler_entry_ajax.html'
form_entry_submitted_template = 'sample_theme/form_entry_submitted.html'
form_entry_submitted_ajax_template = 'sample_theme/form_entry_submitted_ajax.html'
view_form_entry_template = 'sample_theme/view_form_entry.html'
view_form_entry_ajax_template = 'sample_theme/view_form_entry_ajax.html'
Registering the SampleTheme plugin.
theme_registry.register(SampleTheme)
Sometimes you would want to attach additional properties to the theme in order to use them later in templates (rememeber, current theme object is always available in templates under name fobi_theme).
For such cases you would need to define a variable in your project’s settings module, called FOBI_CUSTOM_THEME_DATA. See the following code as example:
# Fobi custom theme data for to be displayed in third party apps
# like `django-registraton`.
FOBI_CUSTOM_THEME_DATA = {
'bootstrap3': {
'page_header_html_class': '',
'form_html_class': 'form-horizontal',
'form_button_outer_wrapper_html_class': 'control-group',
'form_button_wrapper_html_class': 'controls',
'form_button_html_class': 'btn',
'form_primary_button_html_class': 'btn-primary pull-right',
},
'foundation5': {
'page_header_html_class': '',
'form_html_class': 'form-horizontal',
'form_button_outer_wrapper_html_class': 'control-group',
'form_button_wrapper_html_class': 'controls',
'form_button_html_class': 'radius button',
'form_primary_button_html_class': 'btn-primary',
},
'simple': {
'page_header_html_class': '',
'form_html_class': 'form-horizontal',
'form_button_outer_wrapper_html_class': 'control-group',
'form_button_wrapper_html_class': 'submit-row',
'form_button_html_class': 'btn',
'form_primary_button_html_class': 'btn-primary',
}
}
You would now be able to access the defined extra properties in templates as shown below.
<div class="{{ fobi_theme.custom_data.form_button_wrapper_html_class }}">
You’re like would want to either remove the footer text or change it. Define a variable in your project’s settings module, called FOBI_THEME_FOOTER_TEXT. See the following code as example:
FOBI_THEME_FOOTER_TEXT = gettext('© django-fobi example site 2014')
Below follow the properties of the theme:
base_edit
base_view
There are generic templates made in order to simplify theming. Some of them you would never need to override. Some others, you would likely want to.
Templates that you likely would want to re-write in your custom theme implemention are marked with three asterics (***):
generic
├── snippets
│ ├── form_ajax.html
│ ├── form_edit_ajax.html
│ ├── *** form_properties_snippet.html
│ ├── *** form_snippet.html
│ ├── --- form_edit_snippet.html (does not exist in generic templates)
│ ├── --- form_view_snippet.html (does not exist in generic templates)
│ ├── form_view_ajax.html
│ └── messages_snippet.html
│
├── _base.html
├── add_form_element_entry.html
├── add_form_element_entry_ajax.html
├── add_form_handler_entry.html
├── add_form_handler_entry_ajax.html
├── base.html
├── create_form_entry.html
├── create_form_entry_ajax.html
├── *** dashboard.html
├── edit_form_element_entry.html
├── edit_form_element_entry_ajax.html
├── edit_form_entry.html
├── *** edit_form_entry_ajax.html
├── edit_form_handler_entry.html
├── edit_form_handler_entry_ajax.html
├── form_entry_submitted.html
├── *** form_entry_submitted_ajax.html
├── *** theme.html
├── view_form_entry.html
└── view_form_entry_ajax.html
From all of the templates listed above, the _base.html template is the most influenced by the Bootstrap 3 theme.
Make changes to an existing theme
As said above, making your own theme from scratch could be costy. Instead, you can override/reuse an existing one and change it to your needs with minimal efforts. See the override simple theme example. In order to see it in action, run the project with settings_override_simple_theme option:
./manage.py runserver --settings=settings_override_simple_theme
Details explained below.
Directory structure
override_simple_theme/
├── static
│ └── override_simple_theme
│ ├── css
│ │ └── override-simple-theme.css
│ └── js
│ └── override-simple-theme.js
│
├── templates
│ └── override_simple_theme
│ ├── snippets
│ │ └── form_ajax.html
│ └── base_view.html
├── __init__.py
└── fobi_themes.py # Where themes are defined and registered
fobi_themes.py
Overriding the “simple” theme.
__all__ = ('MySimpleTheme',)
from fobi.base import theme_registry
from fobi.contrib.themes.simple.fobi_themes import SimpleTheme
class MySimpleTheme(SimpleTheme):
html_classes = ['my-simple-theme',]
base_view_template = 'override_simple_theme/base_view.html'
form_ajax = 'override_simple_theme/snippets/form_ajax.html'
It’s important to set the force argument to True, in order to override the original theme. Force can be applied only once.
theme_registry.register(MySimpleTheme, force=True)
templates/override_simple_theme/base_view.html
{% extends "simple/base_view.html" %}
{% load static future_compat %}
{% block stylesheets %}
<link
href="{% static 'override_simple_theme/css/override-simple-theme.css' %}"
rel="stylesheet" media="all" />
{% endblock stylesheets %}
{% block main-wrapper %}
<div id="sidebar">
<h2>It's easy to override a theme!</h2>
<p>
You have to do as follows in order to do so:
</p>
<ol>
<li>Create an app for holding the overridden theme.</li>
<li>Inherit the theme you want to override.</li>
<li>Register it in the registry with `force` argument
set to True.</li>
<li>Override some of the templates. Take this one as a good
example.</li>
</ol>
<p>
Read the <a href="http://pythonhosted.org//django-fobi/">
documentation</a> for more information.
</p>
</div>
{{ block.super }}
{% endblock main-wrapper %}
templates/override_simple_theme/snippets/form_ajax.html
{% extends "fobi/generic/snippets/form_ajax.html" %}
{% block form_html_class %}basic-grey{% endblock %}
Permissions
Plugin system allows administrators to specify the access rights to every plugin. Fobi permissions are based on Django Users and User Groups. Access rights are managable via Django admin (“/admin/fobi/formelement/”, “/admin/fobi/formhandler/”). If user doesn’t have the rights to access plugin, it doesn’t appear on his form even if has been added to it (imagine, you have once granted the right to use the news plugin to all users, but later on decided to limit it to Staff members group only). Note, that superusers have access to all plugins.
Plugin access rights management interface in Django admin
┌──────────────────────────────┬───────────────────────┬───────────────────────┐
│ `Plugin` │ `Users` │ `Groups` │
├──────────────────────────────┼───────────────────────┼───────────────────────┤
│ Text │ John Doe │ Form builder users │
├──────────────────────────────┼───────────────────────┼───────────────────────┤
│ Textarea │ │ Form builder users │
├──────────────────────────────┼───────────────────────┼───────────────────────┤
│ File │ Oscar, John Doe │ Staff members │
├──────────────────────────────┼───────────────────────┼───────────────────────┤
│ URL │ │ Form builder users │
├──────────────────────────────┼───────────────────────┼───────────────────────┤
│ Hidden │ │ Form builder users │
└──────────────────────────────┴───────────────────────┴───────────────────────┘
Management commands
There are several management commands available.
fobi_find_broken_entries. Find broken form element/handler entries that occur when some plugin which did exist in the system, no longer exists.
fobi_sync_plugins. Should be ran each time a new plugin is being added to the Fobi.
fobi_update_plugin_data. A mechanism to update existing plugin data in case if it had become invalid after a change in a plugin. In order for it to work, each plugin should implement and update method, in which the data update happens.
Tuning
There are number of Dash settings you can override in the settings module of your Django project:
FOBI_RESTRICT_PLUGIN_ACCESS (bool): If set to True, (Django) permission system for dash plugins is enabled. Defaults to True. Setting this to False makes all plugins available for all users.
FOBI_DEFAULT_THEME (str): Active (default) theme UID. Defaults to “bootstrap3”.
FORM_HANDLER_PLUGINS_EXECUTION_ORDER (list of tuples): Order in which the form handlers are executed. See the “Prioritise the excecution order” section for details.
For tuning of specific contrib plugin, see the docs in the plugin directory.
Bundled plugins and themes
Fobi ships with number of bundled form element- and form handler- plugins, as well as themes which are ready to be used as is.
Bundled form element plugins
Below a short overview of the form element plugins. See the README.rst file in directory of each plugin for details.
Fields
Content
Content plugins are presentational plugins, that make your forms look more
Dummy: Mainly for dev purposes.
Content image: Insert an image.
Content text: Add text.
Content video: Add an embed YouTube or Vimeo video.
Bundled form handler plugins
Below a short overview of the form handler plugins. See the README.rst file in directory of each plugin for details.
DB store: Stores form data in a database.
HTTP repost: Repost the POST request to another endpoint.
Mail: Send the form data by email.
Bundled themes
Below a short overview of the themes. See the README.rst file in directory of each theme for details.
Bootstrap 3: Bootstrap 3 theme.
Foundation 5: Foundation 5 theme.
Simple: Basic theme. Form editing is in a style of Django admin.
HTML5 fields
The following HTML5 fields are supported in appropriate bundled plugins:
date
datetime
email
number
url
placeholder
Available translations
Dutch (core and plugins)
Russian (core and plugins)
Debugging
Most of the errors are logged (DEBUG). If you have written a plugin and it somehow doesn’t appear in the list of available plugins, do run the ./manage.py fobi_sync_plugins management command since it not only syncs your plugins into the database, but also is a great way of checking for possible errors.
License
GPL 2.0/LGPL 2.1
Support
For any issues contact me at the e-mail given in the Author section.
Screenshots
Bootstrap3 theme
Dashboard
Dashboard
Create a form
Create a form
View/edit form
Form elements
Edit form - form elements tab active, no elements yet
Edit form - form elements tab active, add a form element menu
Edit form - add a form element (URL plugin)
Edit form - form elements tab active, with form elements
Form handlers
Edit form - form handlers tab active, no handlers yet
Edit form - form handlers tab tactive, add form handler menu
Edit form - add a form handler (Mail plugin)
Edit form - form handlers tab active, with form handlers
Edit form - form properties tab active
View form
View form - form submitted (thanks page)
Edit form - add a form element (Video plugin)
Edit form - add a form element (Boolean plugin)
Edit form
View form
Simple theme
View/edit form
Edit form - form elements tab active, with form elements
Edit form - form elements tab active, add a form element menu
Edit form - add a form element (Hidden plugin)
Edit form - form handlers tab active, with form handlers
Edit form - form properties tab active
View form
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.