123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- import inspect
- import warnings
- from flask_admin.form import BaseForm, rules
- from flask_admin._compat import iteritems
- from wtforms.fields import HiddenField
- from wtforms.fields.core import UnboundField
- from wtforms.validators import InputRequired
- from .widgets import XEditableWidget
- def converts(*args):
- def _inner(func):
- func._converter_for = frozenset(args)
- return func
- return _inner
- def create_editable_list_form(form_base_class, form_class, widget=None):
- """
- Create a form class with all the fields wrapped in a FieldList.
- Wrapping each field in FieldList allows submitting POST requests
- in this format: ('<field_name>-<primary_key>', '<value>')
- Used in the editable list view.
- :param form_base_class:
- WTForms form class, by default `form_base_class` from base.
- :param form_class:
- WTForms form class generated by `form.get_form`.
- :param widget:
- WTForms widget class. Defaults to `XEditableWidget`.
- """
- if widget is None:
- widget = XEditableWidget()
- class ListForm(form_base_class):
- list_form_pk = HiddenField(validators=[InputRequired()])
- # iterate FormMeta to get unbound fields, replace widget, copy to ListForm
- for name, obj in iteritems(form_class.__dict__):
- if isinstance(obj, UnboundField):
- obj.kwargs['widget'] = widget
- setattr(ListForm, name, obj)
- if name == "list_form_pk":
- raise Exception('Form already has a list_form_pk column.')
- return ListForm
- class InlineBaseFormAdmin(object):
- """
- Settings for inline form administration.
- You can use this class to customize displayed form.
- For example::
- class MyUserInfoForm(InlineBaseFormAdmin):
- form_columns = ('name', 'email')
- """
- _defaults = ['form_base_class', 'form_columns', 'form_excluded_columns', 'form_args', 'form_extra_fields']
- def __init__(self, **kwargs):
- """
- Constructor
- :param kwargs:
- Additional options
- """
- for k in self._defaults:
- if not hasattr(self, k):
- setattr(self, k, None)
- for k, v in iteritems(kwargs):
- setattr(self, k, v)
- # Convert form rules
- form_rules = getattr(self, 'form_rules', None)
- if form_rules:
- self._form_rules = rules.RuleSet(self, form_rules)
- else:
- self._form_rules = None
- def get_form(self):
- """
- If you want to use completely custom form for inline field, you can override
- Flask-Admin form generation logic by overriding this method and returning your form.
- """
- return None
- def postprocess_form(self, form_class):
- """
- Post process form. Use this to contribute fields.
- For example::
- class MyInlineForm(InlineFormAdmin):
- def postprocess_form(self, form):
- form.value = StringField('value')
- return form
- class MyAdmin(ModelView):
- inline_models = (MyInlineForm(ValueModel),)
- """
- return form_class
- def on_model_change(self, form, model, is_created):
- """
- Called when inline model is about to be saved.
- :param form:
- Inline form
- :param model:
- Model
- :param is_created:
- Will be set to True if the model is being created, False if edited
- """
- pass
- def _on_model_change(self, form, model, is_created):
- """
- Compatibility helper.
- """
- try:
- self.on_model_change(form, model, is_created)
- except TypeError:
- msg = ('%s.on_model_change() now accepts third ' +
- 'parameter is_created. Please update your code') % self.model
- warnings.warn(msg)
- self.on_model_change(form, model)
- class InlineFormAdmin(InlineBaseFormAdmin):
- """
- Settings for inline form administration. Used by relational backends (SQLAlchemy, Peewee), where model
- class can not be inherited from the parent model definition.
- """
- def __init__(self, model, **kwargs):
- """
- Constructor
- :param model:
- Model class
- """
- self.model = model
- super(InlineFormAdmin, self).__init__(**kwargs)
- class ModelConverterBase(object):
- def __init__(self, converters=None, use_mro=True):
- self.use_mro = use_mro
- if not converters:
- converters = {}
- for name in dir(self):
- obj = getattr(self, name)
- if hasattr(obj, '_converter_for'):
- for classname in obj._converter_for:
- converters[classname] = obj
- self.converters = converters
- def get_converter(self, column):
- if self.use_mro:
- types = inspect.getmro(type(column.type))
- else:
- types = [type(column.type)]
- # Search by module + name
- for col_type in types:
- type_string = '%s.%s' % (col_type.__module__, col_type.__name__)
- if type_string in self.converters:
- return self.converters[type_string]
- # Search by name
- for col_type in types:
- if col_type.__name__ in self.converters:
- return self.converters[col_type.__name__]
- return None
- def get_form(self, model, base_class=BaseForm,
- only=None, exclude=None,
- field_args=None):
- raise NotImplementedError()
- class InlineModelConverterBase(object):
- form_admin_class = InlineFormAdmin
- def __init__(self, view):
- """
- Base constructor
- :param view:
- View class
- """
- self.view = view
- def get_label(self, info, name):
- """
- Get inline model field label
- :param info:
- Inline model info
- :param name:
- Field name
- """
- form_name = getattr(info, 'form_label', None)
- if form_name:
- return form_name
- column_labels = getattr(self.view, 'column_labels', None)
- if column_labels and name in column_labels:
- return column_labels[name]
- return None
- def get_info(self, p):
- """
- Figure out InlineFormAdmin information.
- :param p:
- Inline model. Can be one of:
- - ``tuple``, first value is related model instance,
- second is dictionary with options
- - ``InlineFormAdmin`` instance
- - Model class
- """
- if isinstance(p, tuple):
- return self.form_admin_class(p[0], **p[1])
- elif isinstance(p, self.form_admin_class):
- return p
- return None
- class FieldPlaceholder(object):
- """
- Field placeholder for model convertors.
- """
- def __init__(self, field):
- self.field = field
|