DetailForm: Use Meta class instead

Instead of class attributes, make a generic reusable the
FormOption class and update the DetailForm to use a Meta
class for options.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-23 14:48:23 -04:00
parent 3f48a5549e
commit 8c085331f1
2 changed files with 103 additions and 43 deletions

View File

@@ -4,6 +4,7 @@ import os
from django import forms
from django.conf import settings
from django.contrib.admin.utils import label_for_field
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.module_loading import import_string
@@ -13,7 +14,7 @@ from mayan.apps.acls.models import AccessControlList
from .classes import Package
from .models import UserLocaleProfile
from .utils import resolve_attribute
from .utils import introspect_attribute, resolve_attribute
from .widgets import DisableableSelectWidget, PlainWidget, TextAreaDiv
@@ -41,26 +42,89 @@ class ChoiceForm(forms.Form):
selection = forms.MultipleChoiceField(widget=DisableableSelectWidget())
class FormOptions(object):
def __init__(self, form, kwargs, options=None):
"""
Option definitions will be iterated. The option value will be
determined in the following order: as passed via keyword
arguments during form intialization, as form get_... method or
finally as static Meta options. This is to allow a form with
Meta options or method to be overrided at initialization
and increase the usability of a single class.
"""
for option_definition in self.option_definitions:
name = option_definition.keys()[0]
default_value = option_definition.values()[0]
try:
# Check for a runtime value via kwargs
value = kwargs.pop(name)
except KeyError:
try:
# Check if there is a get_... method
value = getattr(self, 'get_{}'.format(name))()
except AttributeError:
try:
# Check the meta class options
value = getattr(options, name)
except AttributeError:
value = default_value
setattr(self, name, value)
class DetailFormOption(FormOptions):
# Dictionary list of option names and default values
option_definitions = (
{'extra_fields': []},
)
class DetailForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.extra_fields = kwargs.pop('extra_fields', ())
self.opts = DetailFormOption(
form=self, kwargs=kwargs, options=getattr(self, 'Meta', None)
)
super(DetailForm, self).__init__(*args, **kwargs)
for extra_field in self.extra_fields:
result = resolve_attribute(obj=self.instance, attribute=extra_field['field'])
label = 'label' in extra_field and extra_field['label'] or None
for extra_field in self.opts.extra_fields:
obj = extra_field.get('object', self.instance)
field = extra_field['field']
result = resolve_attribute(
attribute=field, obj=obj
)
label = extra_field.get('label', None)
if not label:
attribute_name, obj = introspect_attribute(
attribute_name=field, obj=obj
)
if not obj:
label = _('None')
else:
try:
label = getattr(
getattr(obj, attribute_name), 'short_description'
)
except AttributeError:
label = label_for_field(
name=attribute_name, model=obj
)
# TODO: Add others result types <=> Field types
if isinstance(result, models.query.QuerySet):
self.fields[extra_field['field']] = \
forms.ModelMultipleChoiceField(
queryset=result, label=label)
self.fields[field] = forms.ModelMultipleChoiceField(
queryset=result, label=label
)
else:
self.fields[extra_field['field']] = forms.CharField(
label=extra_field['label'],
self.fields[field] = forms.CharField(
initial=resolve_attribute(
obj=self.instance,
attribute=extra_field['field']
),
obj=obj,
attribute=field
), label=label,
widget=extra_field.get('widget', PlainWidget)
)
@@ -131,7 +195,7 @@ class FileDisplayForm(forms.Form):
self.fields['text'].initial = file_object.read()
class FilteredSelectionFormOptions(object):
class FilteredSelectionFormOptions(FormOptions):
# Dictionary list of option names and default values
option_definitions = (
{'allow_multiple': False},
@@ -147,35 +211,6 @@ class FilteredSelectionFormOptions(object):
{'widget_attributes': {'size': '10'}},
)
def __init__(self, form, kwargs, options=None):
"""
Option definitions will be iterated. The option value will be
determined in the following order: as passed via keyword
arguments during form intialization, as form get_... method or
finally as static Meta options. This is to allow a form with
Meta options or method to be overrided at initialization
and increase the usability of a single class.
"""
for option_definition in self.option_definitions:
name = option_definition.keys()[0]
default_value = option_definition.values()[0]
try:
# Check for a runtime value via kwargs
value = kwargs.pop(name)
except KeyError:
try:
# Check if there is a get_... method
value = getattr(self, 'get_{}'.format(name))()
except AttributeError:
try:
# Check the meta class options
value = getattr(options, name)
except AttributeError:
value = default_value
setattr(self, name, value)
class FilteredSelectionForm(forms.Form):
"""

View File

@@ -6,6 +6,7 @@ import shutil
import tempfile
from django.conf import settings
from django.core.exceptions import FieldDoesNotExist
from django.db.models.constants import LOOKUP_SEP
from django.urls import resolve as django_resolve
from django.urls.base import get_script_prefix
@@ -142,6 +143,30 @@ def get_storage_subclass(dotted_path):
return StorageSubclass
def introspect_attribute(attribute_name, obj):
try:
# Try as a related field
obj._meta.get_field(field_name=attribute_name)
except (AttributeError, FieldDoesNotExist):
attribute_name = attribute_name.replace('__', '.')
try:
# If there are separators in the attribute name, traverse them
# to the final attribute
attribute_part, attribute_remaining = attribute_name.split(
'.', 1
)
except ValueError:
return attribute_name, obj
else:
return introspect_attribute(
attribute_name=attribute_part,
obj=related_field.related_model,
)
else:
return attribute_name, obj
def TemporaryFile(*args, **kwargs):
kwargs.update({'dir': setting_temporary_directory.value})
return tempfile.TemporaryFile(*args, **kwargs)