class BaseNHDField(DField): ''' Parent class for NHDField. NHDField is based on value HistoryRecordList, but this field only switches between two fields according to history flag. ''' def __init__(self, normal_field, history_field, *content, **kwd): self.normal_field = normal_field self.history_field = history_field self.current_field = None self._owner_detail = None self.displaying_history = False super(BaseNHDField, self).__init__(*content, **kwd) def _assign_current_field(self, new_current_field): self.current_field = new_current_field def value_from_data(self, data): value = data.get(self.name) if self.owner_detail.history: self._assign_current_field(self.history_field) else: self._assign_current_field(self.normal_field) return value def _set_value(self, value): if self.access: self._value = self.current_field.value = self.resolve_value(value) self.make_content() else: self._value = self.current_field.value = None self.make_content_no_access() def _get_value(self): if self.current_field: return self.current_field._value else: return fredtypes.Null() def _set_owner_detail(self, value): self._owner_detail = self.normal_field.owner_detail = self.history_field.owner_detail = value def _get_owner_detail(self): return self._owner_detail owner_detail = LateBindingProperty(_get_owner_detail, _set_owner_detail) def on_add(self): super(BaseNHDField, self).on_add() if self.current_field: self.current_field.parent_widget = self.parent_widget self.current_field.on_add() def make_content(self): if self.access: self.content = [] self.add(self.current_field)
class DField(WebWidget): ''' Base class for detail fields ''' creation_counter = 0 def __init__(self, name='', label=None, nperm=None, *content, **kwd): super(DField, self).__init__(*content, **kwd) self.tag = '' self.name = name self.label = label self._nperm = nperm self.owner_form = None self._value = fredtypes.Null() #None self.access = True # if user have nperm for this field, then this will be set to False in Detail.build_fields() self.no_access_content = div(attr(cssc='no_access'), _('CENSORED')) # Increase the creation counter, and save our local copy. self.creation_counter = DField.creation_counter DField.creation_counter += 1 def on_add(self): if not self.access and self.parent_widget: self.parent_widget.style = 'background-color: gray;' def make_content(self): self.content = [] if self._value == '' or self._value == fredtypes.Null(): self.add(div(attr(cssc='field_empty'))) else: self.add(self._value) def make_content_no_access(self): self.content = [] self.add(self.no_access_content) def resolve_value(self, value): if value is None: return fredtypes.Null() else: return value def _set_value(self, value): if self.access: self._value = self.resolve_value(value) self.make_content() else: self._value = fredtypes.Null() #None self.make_content_no_access() def _get_value(self): return self._value value = LateBindingProperty(_get_value, _set_value) def value_from_data(self, data): if data.get(self.name) is None: return fredtypes.Null() else: return data.get(self.name) def render(self, indent_level=0): return super(DField, self).render(indent_level) def get_nperm(self): if self._nperm: return self._nperm.lower() else: return self.name.lower()
def _get_changed_data(self): if self._changed_data is None: self._changed_data = [] # XXX: For now we're asking the individual widgets whether or not the # data has changed. It would probably be more efficient to hash the # initial data, store it in a hidden field, and compare a hash of the # submitted data, but we'd need a way to easily get the string value # for a given field. Right now, that logic is embedded in the render # method of each widget. for name, field in self.fields.items(): data_value = field.value_from_datadict(self.data) initial_value = self.initial.get(name, field.initial) if field._has_changed(initial_value, data_value): self._changed_data.append(name) return self._changed_data changed_data = LateBindingProperty(_get_changed_data) def reset(self): """Return this form to the state it was in before data was passed to it.""" self.data = {} self.is_bound = False self._errors = None def is_multipart(self): """ Returns True if the form needs to be multipart-encrypted, i.e. it has FileInput. Otherwise, False. """ for field in self.fields.values(): if field.widget.needs_multipart_form: return True
class MultiValueField(Field): """ A Field that aggregates the logic of multiple Fields. Its clean() method takes a "decompressed" list of values, which are then cleaned into a single value according to self.fields. Each value in this list is cleaned by the corresponding field -- the first value is cleaned by the first field, the second value is cleaned by the second field, etc. Once all fields are cleaned, the list of clean values is "compressed" into a single value. Subclasses should not have to implement clean(). Instead, they must implement compress(), which takes a list of valid values and returns a "compressed" version of those values -- a single value. """ tattr_list = span.tattr_list def __init__(self, name='', value='', fields=None, *args, **kwargs): # Set 'required' to False on the individual fields, because the # required validation will be handled by MultiValueField, not by those # individual fields. if fields is None: fields = [] for field in fields: field.required = False self.fields = fields self._name = '' super(MultiValueField, self).__init__(name, value, *args, **kwargs) self.tag = 'span' self.enclose_content = True def _set_name(self, value): self._name = value for i, field in enumerate(self.fields): field.name = self._name + '/%d' % i def _get_name(self): return self._name name = property(_get_name, _set_name) def _set_value(self, value): if not value: self._value = [None] * len(self.fields) for field in self.fields: field.value = None else: self._value = value if not isiterable(value) and len(value) != len(self.fields): raise TypeError( u'value of MultiValueField must be sequence with the same length as a number of fields in multifield (was % s)' % unicode(value)) for i, val in enumerate(value): self.fields[i].value = val def _get_value(self): return self._value value = LateBindingProperty(_get_value, _set_value) def make_content(self): self.content = [] for field in self.fields: label_str = field.label or '' if label_str: label_str += ':' self.add(label_str, field) def render(self, indent_level=0): self.make_content() return super(MultiValueField, self).render(indent_level) def clean(self): """ Validates every value of self.fields. For example, if this MultiValueField was instantiated with fields=(DateField(), TimeField()), clean() would call DateField.clean() and TimeField.clean(). """ clean_data = [] errors = ErrorList() for field in self.fields: if self.required and field.required and field.is_empty(): raise ValidationError(_(u'This field is required.')) try: clean_data.append(field.clean()) except ValidationError, e: # Collect all validation errors in a single list, which we'll # raise at the end of clean(), rather than raising a single # exception for the first error we encounter. errors.extend(e.messages) if errors: raise ValidationError(errors) return self.compress(clean_data)