Source code for pyforms_web.widgets.django.modelform
from pyforms_web.basewidget import BaseWidget, no_columns
from pyforms_web.controls.control_textarea import ControlTextArea
from pyforms_web.controls.control_text import ControlText
from pyforms_web.controls.control_integer import ControlInteger
from pyforms_web.controls.control_float import ControlFloat
from pyforms_web.controls.control_decimal import ControlDecimal
from pyforms_web.controls.control_combo import ControlCombo
from pyforms_web.controls.control_autocomplete import ControlAutoComplete
from pyforms_web.controls.control_date import ControlDate
from pyforms_web.controls.control_datetime import ControlDateTime
from pyforms_web.controls.control_button import ControlButton
from pyforms_web.controls.control_emptywidget import ControlEmptyWidget
from pyforms_web.controls.control_fileupload import ControlFileUpload
from pyforms_web.controls.control_checkbox import ControlCheckBox
from pyforms_web.web.middleware import PyFormsMiddleware
from django.core.exceptions import ValidationError, FieldDoesNotExist, NON_FIELD_ERRORS
from .utils import get_fieldsets_strings
import traceback
from django.conf import settings
from django.db import models
import os
from confapp import conf
from pyforms_web.utils import get_lookup_verbose_name
import datetime
from django.utils import timezone
import string
import random
[docs]class ModelFormWidget(BaseWidget):
"""
When a Pyforms application inherit from this class a form for the model ModelFormWidget.MODEL is created.
**Usage example:**
.. code:: python
from funding.models import FundingOpportunity
class EditFundingOpportunitiesApp(ModelFormWidget):
TITLE = "Edit opportunities"
MODEL = FundingOpportunity
FIELDSETS = [
'h2:Opportunity details',
segment([
('subject','fundingopportunity_published','fundingopportunity_rolling'),
('fundingopportunity_name','fundingopportunity_end'),
('_loi','fundingopportunity_loideadline', 'fundingopportunity_fullproposal'),
('fundingopportunity_link','topics'),
]),
'h2:Financing info',
segment([
('financingAgency','currency','paymentfrequency'),
('fundingtype','fundingopportunity_value','fundingopportunity_duration'),
]),
'h2:Description',
segment([
'fundingopportunity_eligibility',
'fundingopportunity_scope',
'fundingopportunity_brifdesc',
])
]
"""
MODEL = None #: class: Model to manage
TITLE = None #: str: Title of the application
INLINES = [] #: list(class): Sub models to show in the interface
FIELDSETS = None #: Formset of the edit form
READ_ONLY = [] #: list(str): List of readonly fields
#: bool: Flag to show or hide the cancel button
HAS_CANCEL_BTN_ON_EDIT = True
#: bool: Flag to show or hide the cancel button
HAS_CANCEL_BTN_ON_ADD = True
#: bool: Close the application on remove
CLOSE_ON_REMOVE = False
#: bool: Close the application on cancel
CLOSE_ON_CANCEL = False
#: bool: Call populate_list function of the parent application when object is saved, updated or deleted
POPULATE_PARENT = True
#: str: Label for the save button
SAVE_BTN_LABEL = '<i class="save icon"></i> Save'
#: str: Label for the create button
CREATE_BTN_LABEL = '<i class="plus icon"></i> Create'
#: str: Label for the cancel button
CANCEL_BTN_LABEL = '<i class="hide icon"></i> Close'
#: str: Label for the delete button
REMOVE_BTN_LABEL = '<i class="trash alternate outline icon"></i> Remove'
#: str: Label for the popup window for the delete confirmation
POPUP_REMOVE_TITLE = 'The next objects are going to be affected or removed'
def __init__(self, *args, **kwargs):
"""
:param str title: Title of the app. By default will assume the value in the class variable TITLE.
:param django.db.models.Model model: Model with the App will represent. By default will assume the value in the class variable MODEL.
:param list(ModelAdminWidget) inlines: Sub models to show in the interface
:param list(str) fieldsets: Organization of the fields
:param int parent_pk: Parent model key
:param django.db.models.Model parent_model: Parent model class
:param int pk: Model register to manage
"""
title = kwargs.get('title') if 'title' in kwargs else self.TITLE
self.object_pk = kwargs.get('pk', None)
self.model = self.MODEL if self.MODEL else kwargs.get('model')
BaseWidget.__init__(self, title, *args, **kwargs )
self.update_permissions_variables()
if self.object_pk:
if not self._has_view_permissions:
self.formset = ['alert:No permissions']
return
else:
if not self._has_add_permissions:
self.formset = ['alert:No permissions']
return
self.fieldsets = self.get_fieldsets(kwargs.get('fieldsets', self.FIELDSETS))
self.readonly = self.get_readonly(kwargs.get('readonly', self.READ_ONLY))
self.has_cancel_btn = kwargs.get('has_cancel_btn', self.HAS_CANCEL_BTN_ON_ADD if self.object_pk is None else self.HAS_CANCEL_BTN_ON_EDIT)
self.inlines = self.INLINES if len(self.INLINES)>0 else kwargs.get('inlines', self.INLINES)
if self.fieldsets is None: self.fieldsets = self.FIELDSETS
self._auto_fields = []
self._callable_fields = []
self.edit_fields = []
self.edit_buttons = []
self.inlines_apps = []
self.inlines_controls_name = []
self.inlines_controls = []
# used to configure the interface to inline
# it will filter the dataset by the foreign key
self.parent_field = None
self.parent_pk = kwargs.get('parent_pk', None)
self.parent_model = kwargs.get('parent_model', None)
if self.parent_model and self.parent_pk:
self.__set_parent(self.parent_model, self.parent_pk)
#######################################################
# Create the edit buttons buttons #####################
if self._has_update_permissions:
self._save_btn = ControlButton(self.SAVE_BTN_LABEL, label_visible=False, default=self.save_btn_event)
self.edit_buttons.append( self._save_btn )
if self._has_add_permissions:
self._create_btn = ControlButton(self.CREATE_BTN_LABEL, label_visible=False, default=self.__create_btn_event)
self.edit_buttons.append( self._create_btn )
if self._has_remove_permissions:
self._remove_btn = ControlButton(self.REMOVE_BTN_LABEL, css='red basic', label_visible=False, default=self.__remove_btn_event)
self.edit_buttons.append( self._remove_btn )
if self.has_cancel_btn:
self._cancel_btn = ControlButton(self.CANCEL_BTN_LABEL, css='gray basic', label_visible=False, default=self.cancel_btn_event)
self.edit_buttons.append( self._cancel_btn )
self.edit_fields += self.edit_buttons
#######################################################
# In case the edition form is being used as inline ####
# set the buttons for tiny size #######################
if self.parent_model:
for btn in self.edit_buttons: btn.css +=' tiny'
#######################################################
self.create_model_formfields()
if self.object_pk:
self.show_edit_form(pk=self.object_pk)
else:
self.show_create_form()
#################################################################################
#### PROPERTIES #################################################################
#################################################################################
@property
def parent_object(self):
if self.parent_pk and self.parent_model:
try:
return self.parent_model.objects.get(pk=self.parent_pk)
except self.parent_object.DoesNotExist:
return None
else:
return None
@property
def model_object(self):
"""
django.db.models.Model object: Return the current object in edition.
"""
if self.object_pk is None:
return None
else:
queryset = self.model.objects.all()
# check if the model has a query_set function
# if so use it to get the data for visualization
if hasattr(self.model, 'get_queryset'):
request = PyFormsMiddleware.get_request()
queryset = self.model.get_queryset(request, queryset)
try:
return queryset.get(pk=self.object_pk)
except self.model.DoesNotExist:
return None
#################################################################################
#### FUNCTIONS ##################################################################
#################################################################################
[docs] def update_permissions_variables(self):
if self.object_pk:
self._has_update_permissions = self.has_update_permissions()
else:
self._has_update_permissions = False
self._has_add_permissions = self.has_add_permissions()
self._has_view_permissions = self.has_view_permissions()
if self.object_pk:
self._has_remove_permissions = self.has_remove_permissions()
else:
self._has_remove_permissions = False
[docs] def get_readonly(self, default):
"""
The function returns the readonly fields to be set in the form.
:param list(str) default: Default readonly configuration.
Returns:
list(str): Read only fields. Check class variable READ_ONLY to know more about it.
"""
return default
[docs] def get_fieldsets(self, default):
"""
The function returns the fieldsets organization to be set in the form.
:param list(str) default: Default fieldsets configuration.
Returns:
list(str): fieldsets. Check class variable FIELDSETS to know more about it.
"""
return default
[docs] def get_buttons_row(self):
"""
This function generate the formset configuration for the save, create,
cancel and remove buttons,
Returns:
list(str): Returns the formset configuration that will be append to
the end of the fieldsets.
"""
buttons = []
if self._has_update_permissions: buttons.append('_save_btn')
if self._has_add_permissions: buttons.append('_create_btn')
if self.has_cancel_btn: buttons.append('_cancel_btn')
if self._has_remove_permissions: buttons.append('_remove_btn')
return [no_columns(*buttons)]
[docs] def hide_form(self):
"""
This functions hides the create and edit form.
"""
if self.parent and hasattr(self.parent, 'hide_form'):
self.parent.hide_form()
else:
for field in self.edit_fields: field.hide()
for field in self.inlines_controls: field.hide()
[docs] def show_form(self):
"""
This shows the create and edit form.
"""
for field in self.edit_fields: field.show()
for field in self.inlines_controls: field.show()
[docs] def cancel_btn_event(self):
"""
Event called when the cancel button is pressed
"""
# for orquestra
try:
if self.LAYOUT_POSITION in [conf.ORQUESTRA_NEW_WINDOW,conf.ORQUESTRA_NEW_TAB]:
self.close()
except:
pass
# close the application on cancel
if self.CLOSE_ON_CANCEL:
self.close()
# the application has a ModelAdmin app as parent. Call parent hide_form
elif hasattr(self.parent, 'hide_form'):
self.parent.hide_form()
[docs] def autocomplete_search(self, queryset, keyword, control):
"""
Function used by a combobox to get the items dynamically
:param django.db.models.query.QuerySet queryset: Queryset from where to filter the results
:param str keyword: Keyword for filter the results
:param pyforms.controls.BaseControl: Control calling the autocomplete
Returns:
django.db.models.query.QuerySet: Queryset used to update the autocomplete control
"""
field = self.model._meta.get_field(control.name)
queryset = self.get_related_field_queryset(field, queryset)
return queryset
[docs] def show_create_form(self):
"""
This function prepares the fields to be shown as create form.
"""
#check if it has permissions to add new registers
if not self._has_add_permissions:
raise Exception('Your user does not have permissions to add')
fields2show = self.get_visible_fields_names()
self.__update_related_fields()
# clear all the fields present in the model
for field_name in fields2show:
if hasattr(self, field_name):
try:
field = self.model._meta.get_field(field_name)
pyforms_field = getattr(self, field_name)
if not hasattr(field, 'default') or field.default==models.fields.NOT_PROVIDED:
pyforms_field.value = None
elif callable(field.default):
pyforms_field.value = field.default()
elif isinstance(field, models.DecimalField) and type(field).__name__ == 'MoneyField':
if field.default:
pyforms_field.value = field.default.amount
else:
pyforms_field.value = field.default
else:
pyforms_field.value = field.default
if field.get_internal_type() in ('CharField', 'TextField') and field.blank:
pyforms_field.value = ''
except FieldDoesNotExist:
pass
for field in self.edit_fields: field.show()
for inline in self.inlines_controls:
inline.hide()
if self._has_update_permissions: self._save_btn.hide()
if self._has_remove_permissions: self._remove_btn.hide()
[docs] def update_callable_fields(self):
"""
Update the callable fields after the form is saved.
"""
if not self._callable_fields: return
obj = self.model_object
if obj is None: return
for field_name in self._callable_fields:
pyforms_field = getattr(self, field_name)
value = getattr(obj, field_name)()
pyforms_field.value = value
[docs] def update_autonumber_fields(self):
"""
Update the auto number fields after the form is saved.
"""
if not self._auto_fields: return
obj = self.model_object
if obj is None: return
for field_name in self._auto_fields:
pyforms_field = getattr(self, field_name)
value = getattr(obj, field_name)
pyforms_field.value = value
[docs] def show_edit_form(self, pk=None):
"""
This function prepares the fields to be shown as edit form.
:param int pk: Primary key of the object to be show in the edit form.
Returns:
:django.db.models.Model object: Returns the object in edition.
"""
if pk: self.object_pk = pk
if not self._has_view_permissions:
raise Exception('Your user does not have permissions to save')
for field in self.edit_fields: field.show()
for field in self.inlines_controls: field.show()
if self._has_add_permissions: self._create_btn.hide()
self.__update_related_fields()
obj = self.model_object
fields2show = self.get_visible_fields_names()
for field_name in fields2show:
if hasattr(self, field_name) and hasattr(obj, field_name):
pyforms_field = getattr(self, field_name)
value = getattr(obj, field_name)
try:
field = self.model._meta.get_field(field_name)
except FieldDoesNotExist:
try:
field = getattr(self.model, field_name)
except AttributeError:
continue
if callable(field) and not isinstance(field, models.Model):
pyforms_field.value = value()
elif field_name in self.readonly:
if isinstance(field, models.ManyToManyField):
pyforms_field.value = ';'.join([str(o) for o in value.all()])
elif isinstance(value, datetime.datetime ):
if not value:
pyforms_field.value = ''
else:
value = timezone.localtime(value)
pyforms_field.value = value.strftime('%Y-%m-%d %H:%M')
elif isinstance(value, datetime.date ):
if not value:
pyforms_field.value = ''
else:
pyforms_field.value = value.strftime('%Y-%m-%d')
else:
if field.choices:
for v, l in field.choices:
if v==value:
pyforms_field.value = l
break
else:
pyforms_field.value = value
elif isinstance(field, models.DecimalField) and type(field).__name__ == 'MoneyField':
# support django-money MoneyField
pyforms_field.value = value.amount
elif isinstance(field, models.AutoField):
pyforms_field.value = value
elif isinstance(field, models.FileField):
pyforms_field.value = value.url if value else None
elif isinstance(field, models.ImageField):
pyforms_field.value = value.url if value else None
elif isinstance(field, models.ForeignKey):
pyforms_field.value = value.pk if value else None
elif isinstance(field, models.ManyToManyField):
pyforms_field.value = [str(o.pk) for o in value.all()]
else:
pyforms_field.value = value
self.inlines_apps = []
for inline in self.inlines:
pyforms_field = getattr(self, inline.__name__)
pyforms_field._name = inline.__name__
app = inline(parent_model=self.model, parent_pk=self.object_pk, parent_win=self)
self.inlines_apps.append(app)
pyforms_field.value = app
pyforms_field.show()
self.update_permissions_variables()
if hasattr(self, '_save_btn'):
if self._has_update_permissions:
self._save_btn.show()
else:
self._save_btn.hide()
if hasattr(self, '_remove_btn'):
if self._has_remove_permissions:
self._remove_btn.show()
else:
self._remove_btn.hide()
return obj
[docs] def delete_event(self):
"""
Function called to delete the current object in edition.
Returns:
:bool: True if the object was deleted with success, False if not.
"""
if self.object_pk:
obj = self.model_object
obj.delete()
self.object_pk = None
if self._has_remove_permissions: self._remove_btn.hide()
if self._has_add_permissions: self._create_btn.show()
if self._has_update_permissions: self._save_btn.hide()
for field in self.inlines_controls: field.hide()
if self.parent: self.parent.populate_list()
return True
else:
return False
[docs] def popup_remove_handler(self, popup=None, button=None):
"""
Function that handles the buttons events of the object delete confirmation popup.
:param BaseWidget popup: Popup application.
:param str button: Label of the pressed button.
"""
if button==self.CANCEL_BTN_LABEL:
popup.close()
elif button==self.REMOVE_BTN_LABEL:
if self.delete_event():
self.success('The object was deleted with success!','Success!')
popup.close()
if self.CLOSE_ON_REMOVE: self.close()
else:
popup.warning('The object was not deleted!','Warning!')
[docs] def create_newobject(self):
"""
Function called to create a new object of the model.
Returns:
:django.db.models.Model object: Created object
"""
return self.model()
[docs] def save_object(self, obj, **kwargs):
"""
Function called to save the object
It validates the form fields values.
:param django.db.models.Model obj: Object to save.
:param dict kwargs: Any named argument passed to this function will be passed to the Model save method. Example: Model.save(**kwargs).
"""
### validate form values
obj.save(**kwargs)
return obj
[docs] def validate_object(self, obj):
"""
Function called the model object
:param django.db.models.Model obj: Object to validate.
Returns:
:django.db.models.Model object: Created object or None if the object was not saved with success.
"""
# Validate the object
try:
obj.full_clean()
except ValidationError as e:
obj = None
# Found errors, the object was not saved
html = '<ul class="list">'
for field_name, messages in e.message_dict.items():
try:
if hasattr(self, field_name):
getattr(self, field_name).error = True
label = get_lookup_verbose_name(self.model, field_name)
html += '<li><b>{0}</b>'.format(label)
field_error = True
elif field_name==NON_FIELD_ERRORS:
field_error = False
else:
html += '<li><b>{0}</b>'.format(field_name)
field_error = True
except FieldDoesNotExist:
field_error = False
if field_error: html += '<ul>'
for msg in messages: html += '<li>{0}</li>'.format(msg)
if field_error: html += '</ul></li>'
html+= '</ul>'
raise Exception(html)
return obj
[docs] def update_object_fields(self, obj):
"""
Update the obj fields values with the form inputs values
:param django.db.models.Model obj: Object to update the values.
Returns:
:django.db.models.Model: Updated object.
"""
# if it is working as an inline edition form #
if self.parent_field:
setattr(obj,
self.parent_field.name,
self.parent_model.objects.get(pk=self.parent_pk)
)
fields2show = self.get_visible_fields_names()
for field in self.model._meta.get_fields():
# ignore fields that are not in the formset
if field.name not in fields2show: continue
# ignore read only fields
if field.name in self.readonly: continue
pyforms_field = getattr(self, field.name)
value = pyforms_field.value
# if AutoField
if isinstance(field, models.AutoField):
continue
# if FileField
elif isinstance(field, models.FileField):
getattr(self, field.name).error = False
# get the temporary path of the file
tmp_filepath = getattr(self, field.name).value
if tmp_filepath:
# get the temporary filename
tmp_filename = os.path.basename(tmp_filepath)
# in case the upload_to is callable get
if callable(field.upload_to):
# in the case the upload_to property it is a function
filepath = field.upload_to(obj, tmp_filename)
filename = os.path.basename(filepath)
dirpath = os.path.dirname(filepath)
else:
dirpath = field.upload_to
filename = tmp_filename
try:
os.makedirs(os.path.join(settings.MEDIA_ROOT, dirpath))
except os.error as e:
pass
paths = [p for p in value.split(os.path.sep) if len(p)>0][1:]
from_path = os.path.join(settings.MEDIA_ROOT,*paths)
if os.path.exists(from_path):
to_path = os.path.join(settings.MEDIA_ROOT, dirpath, filename)
while os.path.exists(to_path):
name, ext = os.path.splitext(filename)
sufix = ''.join([random.choice(string.ascii_uppercase + string.digits) for _ in range(3)])
filename = name+'_'+sufix+ext
to_path = os.path.join(settings.MEDIA_ROOT, dirpath, filename)
os.rename(from_path, to_path)
url = '/'.join([dirpath]+[filename])
if url[0]=='/': url = url[1:]
setattr(obj, field.name, url)
elif field.null:
setattr(obj, field.name, None)
else:
setattr(obj, field.name, '')
# if ForeignKey
elif isinstance(field, models.ForeignKey):
if value is not None:
try:
value = field.related_model.objects.get(pk=value)
except:
self.alert(
'The field [{0}] has an error.'.format(field.verbose_name)
)
pyforms_field.error = True
else:
value = None
setattr(obj, field.name, value)
elif obj.pk and isinstance(field, models.ManyToManyField):
pyforms_field.error = False
getattr(obj, field.name).set([] if value is None else value)
# all other fields except the ManyToManyField
elif not isinstance(field, models.ManyToManyField):
pyforms_field.error = False
if isinstance(field, models.CharField) and value is None and field.null is False:
value = ''
if value == '' and field.null:
value = None
setattr(obj, field.name, value)
return obj
[docs] def save_form_event(self, obj):
"""
Function handling the form save.
This function, updates the obj with the form values,
validate the obj fields, and call the save_event function.
:param django.db.models.Model obj: Model object used for the save.
Returns:
:boolean: It returns True or False if the save was successfully.
"""
user = PyFormsMiddleware.user()
# decides if an object is going to be created or updated
new_object = obj.pk is None
try:
obj = self.update_object_fields(obj)
obj = self.validate_object(obj)
return self.save_event(obj, new_object)
except Exception as e:
traceback.print_exc()
self.alert(str(e))
return False
[docs] def save_event(self, obj, new_object):
"""
Function handling the form save.
This function, updates the obj with the form values,
validate the obj fields, and call the save_event function.
:param django.db.models.Model obj: Model object used for the save.
Returns:
:boolean: It returns True or False if the save was successfully.
"""
obj = self.save_object(obj)
obj = self.save_related_fields(obj)
self.object_pk = obj.pk
self.update_callable_fields()
self.update_autonumber_fields()
# Add object mode
if new_object:
if self.parent and obj:
# it is being use from a ModelAdminWidget
# update the parent list
if self.POPULATE_PARENT: self.parent.populate_list()
self.cancel_btn_event()
self.parent.show_edit_form(obj.pk)
self.parent.success('The object <b>{0}</b> was saved with success!'.format(obj),'Success!')
elif obj:
# it is executing as a single app
if self._has_add_permissions: self._create_btn.hide()
if self._has_update_permissions: self._save_btn.show()
if self._has_remove_permissions: self._remove_btn.show()
self.inlines_apps = []
for inline in self.inlines:
pyforms_field = getattr(self, inline.__name__)
pyforms_field._name = inline.__name__
app = inline(
parent_model=self.model,
parent_pk=self.object_pk
)
self.inlines_apps.append(app)
pyforms_field.value = app
pyforms_field.show()
self.success('The object <b>{0}</b> was saved with success!'.format(obj),'Success!')
# Update object mode
else:
if obj:
self.success('The object <b>{0}</b> was saved with success!'.format(obj),'Success!')
# update the parent list
if self.POPULATE_PARENT and self.parent:
self.parent.populate_list()
for inline in self.inlines_apps:
inline.populate_list()
return True
#################################################################################
#### PRIVATE FUNCTIONS ##########################################################
#################################################################################
def __set_parent(self, parent_model, parent_pk):
"""
Set the form to work as inline
:param django.db.models.Model parent_model: Parent model.
:param int parent_pk: Parent object primary key.
"""
self.parent_pk = parent_pk
self.parent_model = parent_model
for field in self.model._meta.get_fields():
if isinstance(field, models.ForeignKey):
if issubclass(parent_model, field.related_model):
self.parent_field = field
break
[docs] def get_visible_fields_names(self):
"""
Function called to get names of the visible fields.
Returns:
:list(str): List names of the visible fields.
"""
if self.fieldsets:
fields = get_fieldsets_strings(self.fieldsets)
else:
fields = []
for field in self.model._meta.get_fields():
if field.one_to_many: continue
if field.one_to_one and field.name.endswith('_ptr'): continue
fields.append(field.name)
if self.parent_field:
try:
fields.remove(self.parent_field.name)
except ValueError: pass
return [field for field in fields if field is not None]
def __update_related_fields(self):
"""
Update all related fields
"""
fields2show = self.get_visible_fields_names()
formset = []
for field in self.model._meta.get_fields():
if not isinstance(
field,
(models.ForeignKey,models.ManyToManyField)
): continue
if field.name not in fields2show: continue #only update this field if is visible
if field.name in self.readonly: continue
pyforms_field = getattr(self, field.name)
queryset = field.related_model.objects.all()
#limit_choices = field.get_limit_choices_to()
#if limit_choices:
# queryset = queryset.filter(**limit_choices)
queryset = self.get_related_field_queryset(field, queryset)
self.update_related_field(field, pyforms_field, queryset)
[docs] def create_model_formfields(self):
"""
Create the model edition form.
"""
fields2show = self.get_visible_fields_names()
formset = []
for field_name in fields2show:
# if the field already exists then ignore the creation
if hasattr(self, field_name): continue
try:
field = self.model._meta.get_field(field_name)
except FieldDoesNotExist:
try:
field = getattr(self.model, field_name)
except AttributeError:
continue
if hasattr(field, "field"):
# follow relationships,e.g. ManyToManyRel
field = field.field
required = (not field.blank and not field.has_default()) if not callable(field) else False
if not (callable(field) and not isinstance(field, models.Model)):
label = get_lookup_verbose_name(self.model, field_name)
# if it is a function
if callable(field) and not isinstance(field, models.Model):
label = getattr(field, 'short_description') if hasattr(field, 'short_description') else field_name
pyforms_field = ControlText( label, readonly=True)
self._callable_fields.append( field_name )
# if it is read only
elif field.name in self.readonly:
if isinstance(field, models.TextField):
pyforms_field = ControlTextArea( label, readonly=True, required=required, helptext=field.help_text )
else:
pyforms_field = ControlText( label, readonly=True, required=required, helptext=field.help_text )
# if it is AutoField
elif isinstance(field, models.AutoField):
pyforms_field = ControlText( label, readonly=True, required=required, helptext=field.help_text )
self._auto_fields.append( field_name )
elif isinstance(field, models.Field) and field.choices:
pyforms_field = ControlCombo(
label,
items=[
(c[1], c[0])
for c in field.get_choices(include_blank=field.blank)
],
set_blank_to_null = field.null,
default=field.default, required=required, helptext=field.help_text
)
elif isinstance(field, models.BigIntegerField): pyforms_field = ControlInteger( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.BooleanField): pyforms_field = ControlCheckBox( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.DateTimeField): pyforms_field = ControlDateTime( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.DateField): pyforms_field = ControlDate( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.DecimalField): pyforms_field = ControlDecimal( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.FileField): pyforms_field = ControlFileUpload( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.FloatField): pyforms_field = ControlFloat( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.ImageField): pyforms_field = ControlFileUpload( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.IntegerField): pyforms_field = ControlInteger( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.PositiveIntegerField): pyforms_field = ControlInteger(label, default=field.default, required=required, helptext=field.help_text)
elif isinstance(field, models.PositiveSmallIntegerField):
pyforms_field = ControlInteger(label, default=field.default, required=required,
helptext=field.help_text)
elif isinstance(field, models.SmallIntegerField): pyforms_field = ControlInteger(label, default=field.default, required=required, helptext=field.help_text)
elif isinstance(field, models.TextField): pyforms_field = ControlTextArea( label, default=field.default, required=required, helptext=field.help_text )
elif isinstance(field, models.NullBooleanField):
pyforms_field = ControlCombo(
label,
items=[('Unknown', None), ('Yes', True), ('No', False)],
default=field.default, required=required, helptext=field.help_text
)
elif isinstance(field, models.ForeignKey):
query = field.related_model.objects.all()
limit_choices = field.get_limit_choices_to()
if limit_choices: query = query.filter(**limit_choices)
pyforms_field = ControlAutoComplete(
label,
queryset=query,
queryset_filter=self.autocomplete_search,
default=field.default, required=required, helptext=field.help_text
)
elif isinstance(field, models.ManyToManyField):
query = field.related_model.objects.all()
limit_choices = field.get_limit_choices_to()
if limit_choices: query = query.filter(**limit_choices)
pyforms_field = ControlAutoComplete(
label,
queryset=query,
multiple=True,
queryset_filter=self.autocomplete_search, required=required, helptext=field.help_text
)
else:
default = None
if hasattr(field, 'default'): default = field.default
pyforms_field = ControlText(
label,
default=default,
required=required,
helptext=field.help_text,
)
# add the field to the application
if pyforms_field is not None:
setattr(self, field_name, pyforms_field)
formset.append(field_name)
self.edit_fields.append( pyforms_field )
#Create the inlines edition forms.
self.inlines_controls_name = []
self.inlines_controls = []
for inline in self.inlines:
pyforms_field = ControlEmptyWidget()
pyforms_field.name = inline.__name__
pyforms_field._parent = self
setattr(self, inline.__name__, pyforms_field)
self.inlines_controls_name.append(inline.__name__)
self.inlines_controls.append( pyforms_field )
self.formset = self.fieldsets if self.fieldsets else formset
self.formset = self.formset + self.get_buttons_row()
def __create_btn_event(self):
"""
Event called by the create button
"""
if not self._has_add_permissions:
raise Exception('You do not have permissions to add objects.')
obj = self.create_newobject()
self.save_form_event(obj)
[docs] def save_btn_event(self):
"""
Event called by the save button
"""
obj = self.model_object
if obj is None:
# The object is None, call the create event
self.__create_btn_event()
return
else:
if not self._has_update_permissions:
raise Exception('You do not have permissions to update the object.')
self.save_form_event(obj)
def __remove_btn_event(self):
"""
Event called by the remove button
"""
def related_objects(obj):
objects = []
for rel in list(obj.__class__._meta.related_objects):
f = {rel.field.name: obj}
rel_objects = rel.related_model.objects.filter(**f)
for o in rel_objects:
objects.append( (o, related_objects(o) ) )
return objects
def related_objects_html(objects):
html = "<ul>"
for o, objs in objects:
html += "<li>"
html += "{1}: <b>{0}</b>".format(
str(o), o.__class__._meta.verbose_name
)
if len(objs)>0:
html += related_objects_html(objs)
html += "</li>"
html += "</ul>"
return html
if self.object_pk:
obj = self.model_object
if not self._has_remove_permissions:
raise Exception('Your user does not have permissions to remove the object')
objects = obj, related_objects(obj)
html = related_objects_html([objects])
popup = self.warning_popup(html,
self.POPUP_REMOVE_TITLE,
buttons=[self.REMOVE_BTN_LABEL,self.CANCEL_BTN_LABEL],
handler=self.popup_remove_handler
)
popup.button_0.css = 'basic red'
# update the parent list
if self.POPULATE_PARENT and self.parent:
self.parent.populate_list()
[docs] def has_add_permissions(self):
"""
The functions returns if the user has permissions to add objects or not.
Returns:
bool: True if has add permissions, False otherwise.
"""
if hasattr(self, 'parent') and self.parent and not self.parent.has_add_permissions():
return False
queryset = self.model.objects.all()
if hasattr(queryset, 'has_add_permissions'):
return queryset.has_add_permissions(
PyFormsMiddleware.user()
)
else:
return True
[docs] def has_view_permissions(self):
"""
The functions returns if the user has permissions to view the queryset or not.
Returns:
bool: True if has view permissions, False otherwise.
"""
if self.model_object is None: return True
if hasattr(self, 'parent') and self.parent and not isinstance(self.parent, ModelFormWidget) and not self.parent.has_view_permissions(self.model_object):
return False
queryset = self.model.objects.filter(pk=self.object_pk)
if hasattr(queryset, 'has_view_permissions'):
user = PyFormsMiddleware.user()
return queryset.has_view_permissions( user )
else:
return True
[docs] def has_remove_permissions(self):
"""
The functions returns if the user has permissions to remove the current queryset or not.
Returns:
bool: True if has remove permissions, False otherwise.
"""
if hasattr(self, 'parent') and self.parent and not isinstance(self.parent, ModelFormWidget) and not self.parent.has_remove_permissions(self.model_object):
return False
queryset = self.model.objects.filter(pk=self.object_pk)
if hasattr(queryset, 'has_remove_permissions'):
return queryset.has_remove_permissions( PyFormsMiddleware.user() )
else:
return True
[docs] def has_update_permissions(self):
"""
The functions returns if the user has permissions to update the current queryset or not.
Returns:
bool: True if has update permissions, False otherwise.
"""
if hasattr(self, 'parent') and self.parent and not isinstance(self.parent, ModelFormWidget) and not self.parent.has_update_permissions(self.model_object):
return False
queryset = self.model.objects.filter(pk=self.object_pk)
if hasattr(queryset, 'has_update_permissions'):
return queryset.has_update_permissions( PyFormsMiddleware.user() )
else:
return True