def set_default_field_value(self, field_name, value): """ updates the initial value of a form field, both on the form itself and on the ng model it is bound to :param field_name: name of field to update :param value: value to set :return: None """ field = self.fields[field_name] field.initial = value # TODO: ANOTHER WAY TO GET INITIAL DATA FROM THE CUSTOMIZATION # TODO: IS TO ADD SOMETHING LIKE: "ng-init='{{form.get_initial_data|jsonify|safe}}'" # TODO: TO THE TEMPLATE (THE RESULT NEEDS TO BE MASSAGED A BIT TO USE QUALIFIED NAMES, # TODO: I'M NOT SURE WHICH APPROACH IS BETTER; FOR NOW I'M DOING IT EXPLICITLY HERE update_field_widget_attributes( field, { # notice I am calling the "init_value" fn, rather than just specifying "field_name=value" in ng-init # this bit of indirection ensures the property is loaded by the controller before setting its value # see comments in "q_ng_editor.js#init_value" for more info "ng-init": "init_value('{0}', {1})".format(field_name, json.dumps(value)) }, )
def bootstrap_field(field): bootstrap_classes = { "class": "form-control", } update_field_widget_attributes(field, bootstrap_classes) if not isinstance(field, BooleanField): set_field_widget_attributes(field, { "placeholder": field.label, })
def __init__(self, *args, **kwargs): super(QCategoryCustomizationForm, self).__init__(*args, **kwargs) if not self.instance.pk: # I don't need to reset the key b/c "get_new_customizations" passes it in via initial pass else: # TODO: DOUBLE-CHECK THAT I SHOULD SET ".initial[key]" and not ".fields[key].initial" # self.fields["key"].initial = self.instance.get_key() self.initial["key"] = self.instance.get_key() set_field_widget_attributes(self.fields["documentation"], {"rows": 2}) update_field_widget_attributes(self.fields["order"], {"ng-disabled": "true"})
def __init__(self, *args, **kwargs): super(QUserProfileForm, self).__init__(*args, **kwargs) profile = self.instance user = profile.user self.fields["first_name"].initial = user.first_name self.fields["last_name"].initial = user.last_name self.fields["email"].initial = user.email update_field_widget_attributes(self.fields["email"], {"readonly": True}) set_field_widget_attributes(self.fields["description"], {"rows": 2}) update_field_widget_attributes(self.fields["institute"], {"class": "select single show-tick"})
def __init__(self, *args, **kwargs): super(QModelCustomizationForm, self).__init__(*args, **kwargs) # deal w/ customization_fields... self.unbootstrap_field("is_default") set_field_widget_attributes(self.fields["description"], {"rows": 2}) # deal w/ document_fields... self.unbootstrap_field("model_show_all_categories") set_field_widget_attributes(self.fields["model_description"], {"rows": 2}) # TODO: MAKE SURE THIS LINE IS NOT REPLICATED IN SUBFORMS update_field_widget_attributes(self.fields["name"], { # notice I'm using ng-blur instead of ng-change # ...this is much more efficient "ng-blur": "update_names({field_scope})".format( field_scope=self.get_qualified_model_field_name("name"), ), })
def __init__(self, *args, **kwargs): super(QModelCustomizationForm, self).__init__(*args, **kwargs) # deal w/ customization_fields... self.unbootstrap_field("is_default") self.add_server_errors_to_field("name") self.add_server_errors_to_field("is_default") set_field_widget_attributes(self.fields["documentation"], {"rows": 2}) # deal w/ document_fields... self.unbootstrap_field("model_show_empty_categories") set_field_widget_attributes(self.fields["model_description"], {"rows": 2}) update_field_widget_attributes(self.fields["name"], { # notice I'm using ng-blur instead of ng-change # ...this is much more efficient "ng-blur": "update_names({field_scope})".format( field_scope=self.get_qualified_model_field_name("name"), ), })
def __init__(self, *args, **kwargs): super(QCategoryCustomizationForm, self).__init__(*args, **kwargs) self.unbootstrap_field("is_hidden") update_field_widget_attributes(self.fields["category_title"], {"class": "form-control form-control-small"}) update_field_widget_attributes(self.fields["category_description"], {"class": "form-control form-control-small"}) set_field_widget_attributes(self.fields["category_description"], {"rows": 2}) update_field_widget_attributes(self.fields["order"], {"ng-disabled": "true", "disabled": "disabled"})
def __init__(self, *args, **kwargs): # customizers was passed in via curry() in the factory function below customizers = kwargs.pop("customizers", None) # RIGHT, THIS CAUSED AN AWFUL LOT OF CONFUSION # THE CALL TO super() CAN TAKE A "customizer" ARGUMENT # BUT I CAN ONLY FIND THAT CUSTOMIZER BY MATCHING IT AGAINST THE VALUE OF "proxy" # THAT REQUIRES CALLING get_current_field_value() WHICH CHECKS THE PREFIX OF A FORM # BUT THAT PREFIX - AND SOME OTHER THINGS TOO - IS ONLY SET FROM DEEP W/IN THE __init__ FN # OF A BASE CLASS; SO I CALL super FIRST W/OUT A "customizer" ARGUMENT AND THEN CUSTOMIZE # AT THE END OF THIS __init__ FN super(MetadataScientificPropertyForm, self).__init__(*args, **kwargs) if customizers: proxy_pk = int(self.get_current_field_value("proxy")) customizer = find_in_sequence(lambda c: c.proxy.pk == proxy_pk, customizers) assert(customizer.name == self.get_current_field_value("name")) # this is new code; just make sure it works else: customizer = None is_enumeration = self.get_current_field_value("is_enumeration", False) if self.instance.pk and is_enumeration: # ordinarily, this is done in create_scientific_property_form_data above # but if this is an existing model, I still need to do this jiggery-pokery someplace current_enumeration_value = self.get_current_field_value("enumeration_value") if isinstance(current_enumeration_value, basestring) and customizer.enumeration_multi: self.initial["enumeration_value"] = current_enumeration_value.split("|") if not is_enumeration: update_field_widget_attributes(self.fields["atomic_value"], {"onchange": "copy_value(this,'%s-scientific_property_value');" % self.prefix}) update_field_widget_attributes(self.fields["atomic_value"], {"class": "atomic_value changer"}) else: # this is handled via the "multiselect" widget in JS rather than here (b/c the widget is created dynamically via JS and has no _standard_ onchange event) # update_field_widget_attributes(self.fields["enumeration_value"], {"onchange": "copy_value(this,'%s-scientific_property_value');" % self.prefix}) update_field_widget_attributes(self.fields["enumeration_value"], {"class": "multiselect"}) if customizer: # HUH, WHY AM I CALLING THIS EXPLICITLY HERE, WHEN IT OUGHT TO BE CALLED AUTOMATICALLY IN super() ABOVE? # B/C customizer IS NOT PASSED TO super() B/C IT NEEDS TO BE FOUND BASED ON THE CURRENT PROXY # WHICH GETS RETURNED BY get_current_field_value() # WHICH HAS TO HAVE THIS FORM MOSTLY SET UP BEFORE IT WILL WORK # WHICH HAPPENS IN THE CALL TO super() # (SEE ABOVE) self.customize(customizer)
def __init__(self, *args, **kwargs): super(QScientificPropertyCustomizationForm, self).__init__(*args, **kwargs) is_new_property = not self.instance.pk proxy = self.get_proxy() field_type = self.get_field_type() if field_type == QPropertyTypes.ATOMIC: # things to do for ATOMIC fields... # TODO: THIS IS NO LONGER TRUE; "atomic_type" IS NOW A REQUIRED FIELD W/ A DEFAULT VALUE # # since atomic_fields only shows up if this is an ATOMIC field, it cannot be required # # but when I do display it, I can force users to make a choice # atomic_type_field = self.fields["atomic_type"] # atomic_type_field.required = True # atomic_type_field.empty_label = None # atomic_type_field.choices.remove(EMPTY_CHOICE[0]) set_field_widget_attributes(self.fields["atomic_suggestions"], {"rows": 2}) update_field_widget_attributes(self.fields["atomic_type"], {"class": "select single show-tick"}) elif field_type == QPropertyTypes.ENUMERATION: # things to do for ENUMERATION fields... self.unbootstrap_fields(["enumeration_open", "enumeration_multi", "enumeration_nullable", ]) choices = proxy.enumeration_choices.split('|') enumeration_choices_field = self.fields["enumeration_choices"] enumeration_default_field = self.fields["enumeration_default"] enumeration_choices_field.set_choices(choices) enumeration_default_field.set_choices(choices) update_field_widget_attributes(enumeration_choices_field, {"class": "select multiple show-tick"}) update_field_widget_attributes(enumeration_default_field, {"class": "select multiple show-tick"}) if is_new_property: self.initial["enumeration_choices"] = choices else: # field_type == QPropertyTypes.RELATIONSHIP msg = "ScientificProperties cannot be RELATIONSHIPS" raise QError(msg) # things to do for ALL fields... # TODO: DOUBLE-CHECK THAT I SHOULD SET ".initial[key]" and not ".fields[key].initial" self.initial["key"] = self.instance.get_key() self.initial["category_key"] = self.instance.category.get_key() self.unbootstrap_fields(self._extra_fields + ["displayed", "required", "editable", "unique", "inline_help", ]) set_field_widget_attributes(self.fields["documentation"], {"rows": 2})
def __init__(self, *args, **kwargs): super(QStandardPropertyCustomizationForm, self).__init__(*args, **kwargs) is_new_property = not self.instance.pk proxy = self.get_proxy() field_type = self.get_field_type() if field_type == QPropertyTypes.ATOMIC: # things to do for ATOMIC fields... # TODO: THIS IS NO LONGER TRUE; "atomic_type" IS NOW A REQUIRED FIELD W/ A DEFAULT VALUE # # since atomic_fields only shows up if this is an ATOMIC field, it cannot be required # # but when I do display it, I can force users to make a choice # atomic_type_field = self.fields["atomic_type"] # atomic_type_field.required = True # atomic_type_field.empty_label = None # atomic_type_field.choices.remove(EMPTY_CHOICE[0]) set_field_widget_attributes(self.fields["atomic_suggestions"], {"rows": 2}) update_field_widget_attributes(self.fields["atomic_type"], {"class": "select single show-tick"}) elif field_type == QPropertyTypes.ENUMERATION: # things to do for ENUMERATION fields... self.unbootstrap_fields(["enumeration_open", "enumeration_multi", "enumeration_nullable", ]) choices = proxy.enumeration_choices.split('|') enumeration_choices_field = self.fields["enumeration_choices"] enumeration_default_field = self.fields["enumeration_default"] enumeration_choices_field.set_choices(choices) enumeration_default_field.set_choices(choices) update_field_widget_attributes(enumeration_choices_field, {"class": "select multiple show-tick"}) update_field_widget_attributes(enumeration_default_field, {"class": "select multiple show-tick"}) if is_new_property: self.initial["enumeration_choices"] = choices else: # field_type == QPropertyTypes.RELATIONSHIP # things to do for RELATIONSHIP fields... self.unbootstrap_fields(["relationship_show_subform"]) update_field_widget_attributes(self.fields["relationship_show_subform"], { "ng-disabled": "true", "readonly": "readonly", }) update_field_widget_attributes(self.fields["cardinality"], { "class": "cardinality", }) # things to do for ALL fields... # TODO: DOUBLE-CHECK THAT I SHOULD SET ".initial[key]" and not ".fields[key].initial" self.initial["key"] = self.instance.get_key() try: self.initial["category_key"] = self.instance.category.get_key() except AttributeError: # sometimes properties in subforms don't have categories # ...that's okay pass if proxy.is_required(): # if a property is required by the CIM # (then property.reset() will have set the "required" field to True) # then don't allow users to change the "required" or "displayed" fields update_field_widget_attributes(self.fields["required"], { "ng-disabled": "true", "readonly": "readonly", }) update_field_widget_attributes(self.fields["displayed"], { "ng-disabled": "true", "readonly": "readonly", }) self.unbootstrap_fields(["displayed", "required", "editable", "unique", "inline_help", "inherited", ]) set_field_widget_attributes(self.fields["documentation"], {"rows": 2})
def customize(self, customization): field_type = self.get_current_field_value("field_type") proxy = self.instance.proxy assert customization.proxy == proxy, "in QPropertyRealizationForm, customization.proxy doesn't equal instance.proxy" # customize form... self.inline_help = customization.inline_help self.is_nillable = customization.is_nillable self.is_required = customization.is_required self.is_hidden = customization.is_hidden self.is_editable = customization.is_editable self.is_hierarchical = customization.relationship_is_hierarchical self.render = not (self.is_hidden or self.is_hierarchical) # tells the template whether or not I plan on rendering the form # customize fields... self.value_field.help_text = customization.property_description self.value_field.label = customization.property_title self.value_field.required = customization.is_required self.value_field.editable = customization.is_editable if not customization.is_editable: set_field_widget_attributes(self.value_field, { "ng-disabled": "true", "readonly": "readonly", }) set_field_widget_attributes(self.fields["is_nil"], { "ng-disabled": "true", "readonly": "readonly", }) set_field_widget_attributes(self.fields["nil_reason"], { "ng-disabled": "true", "readonly": "readonly", }) # more in-depth customization... if field_type == QPropertyTypes.ATOMIC: atomic_value_field = self.fields["atomic_value"] existing_widget_attrs = atomic_value_field.widget.attrs custom_widget_class, custom_widget_args = ATOMIC_PROPERTY_MAP[customization.atomic_type] atomic_value_field.widget = custom_widget_class(custom_widget_args) update_field_widget_attributes(atomic_value_field, existing_widget_attrs) if self.instance.is_new: default_values = customization.default_values if default_values: # TODO: WILL NEED TO REWRITE THIS TO COPE W/ "cardinality_max" > 1 FOR ATOMIC FIELDS # TODO: (IN THAT CASE, I SHOULD PROBABLY MAKE "atomic_value" A QJSONField assert len(default_values) == 1, "need to rewrite this to cope w/ 'cardinality_max' > 1 for atomic fields" self.set_default_field_value("atomic_value", default_values[0]) self.set_default_field_value("is_complete", True) if customization.is_required: update_field_widget_attributes(atomic_value_field, { "ng-blur": "update_property_completion()", }) else: self.set_default_field_value("is_complete", True) if customization.atomic_suggestions: atomic_suggestions_list = customization.atomic_suggestions.split('|') set_field_widget_attributes(atomic_value_field, { "uib-typeahead": "option for option in [{0}] | filter:$viewValue | limitTo:{1}".format( ",".join(["'{0}'".format(mark_safe(suggestion)) for suggestion in atomic_suggestions_list]), TYPEAHEAD_LIMIT ) }) elif field_type == QPropertyTypes.ENUMERATION: enumeration_value_field = self.fields["enumeration_value"] enumeration_other_value_field = self.fields["enumeration_other_value"] enumeration_choices = copy.copy(proxy.enumeration_choices) # (make a copy of the value so that possibly updating it below doesn't modify the original if customization.enumeration_is_open: enumeration_choices.append({ "value": ENUMERATION_OTHER_CHOICE, "documentation": ENUMERATION_OTHER_DOCUMENTATION, "order": len(enumeration_choices) + 1, }) enumeration_value_field._complete_choices = enumeration_choices if customization.is_required: update_field_widget_attributes(enumeration_value_field, { "ng-blur": "update_property_completion()", }) update_field_widget_attributes(enumeration_other_value_field, { "ng-blur": "update_property_completion()", }) else: self.set_default_field_value("is_complete", True) update_field_widget_attributes(enumeration_other_value_field, { "ng-blur": "update_property_completion()", }) else: # field_type == QPropertyTypes.RELATIONSHIP if not customization.relationship_is_hierarchical: # only have to render non-hierarchical relationships in a property form... self.use_subforms = customization.use_subforms self.use_references = customization.use_references self.cardinality_min = customization.cardinality_min self.cardinality_max = customization.cardinality_max if not customization.is_required: self.set_default_field_value("is_complete", True) self.customization = customization
def __init__(self, *args, **kwargs): super(QPropertyCustomizationForm, self).__init__(*args, **kwargs) set_field_widget_attributes(self.fields["property_description"], {"rows": 2}) set_field_widget_attributes(self.fields["cardinality"], {"readonly": True, "ng-disabled": "true"}) update_field_widget_attributes(self.fields["cardinality"], {"class": "form-control-auto"}) self.unbootstrap_fields(["inline_help", "is_required", "is_hidden", "is_editable", "is_nillable"]) field_type = self.get_current_field_value("field_type") if field_type == QPropertyTypes.ATOMIC: update_field_widget_attributes(self.fields["atomic_type"], {"class": "select single show-tick"}) set_field_widget_attributes(self.fields["atomic_suggestions"], {"rows": 2}) set_field_widget_attributes(self.fields["default_values"], {"rows": 2}) elif field_type == QPropertyTypes.ENUMERATION: set_field_widget_attributes(self.fields["default_values"], {"rows": 2}) self.unbootstrap_fields(["enumeration_is_open"]) elif field_type == QPropertyTypes.RELATIONSHIP: update_field_widget_attributes(self.fields["relationship_show_subforms"], {"readonly": True, "ng-disabled": "true"}) update_field_widget_attributes(self.fields["relationship_is_hierarchical"], {"readonly": True, "ng-disabled": "true"}) self.unbootstrap_fields(["relationship_show_subforms", "relationship_is_hierarchical"]) else: msg = "Unknown field type: {0}".format(field_type) raise QError(msg) if self.instance.proxy.is_required: update_field_widget_attributes(self.fields["is_required"], {"readonly": True, "ng-disabled": "true"}) if self.instance.has_specialized_values: update_field_widget_attributes(self.fields["default_values"], {"readonly": True, "ng-disabled": "true"}) update_field_widget_attributes(self.fields["is_editable"], {"readonly": True, "ng-disabled": "true"})
def __init__(self, *args, **kwargs): super(QProjectForm, self).__init__(*args, **kwargs) for field in self.fields.itervalues(): update_field_widget_attributes(field, {"class": "form-control form-control-md"}) set_field_widget_attributes(self.fields["name"], {"readonly": True, "ng-disabled": "true"}) set_field_widget_attributes(self.fields["description"], {"rows": 2})
def __init__(self, *args, **kwargs): super(QOntologyAdminForm,self).__init__(*args, **kwargs) current_model_proxies = self.instance.model_proxies.all() self.fields["model_proxies"].queryset = current_model_proxies self.fields["model_proxies"].initial = current_model_proxies update_field_widget_attributes(self.fields["model_proxies"], {"disabled": "disabled"})
def __init__(self, *args, **kwargs): super(QVocabularyAdminForm, self).__init__(*args, **kwargs) current_component_proxies = self.instance.component_proxies.all() self.fields["component_proxies"].queryset = current_component_proxies self.fields["component_proxies"].initial = current_component_proxies update_field_widget_attributes(self.fields["component_proxies"], {"disabled": "disabled"})
def __init__(self, *args, **kwargs): super(QPropertyCustomizationForm, self).__init__(*args, **kwargs) proxy = self.get_proxy() field_type = self.get_field_type() if field_type == QPropertyTypes.ATOMIC: # things to do for ATOMIC fields... # TODO: THIS IS NO LONGER TRUE; "atomic_type" IS NOW A REQUIRED FIELD W/ A DEFAULT VALUE # # since atomic_fields only shows up if this is an ATOMIC field, it cannot be required # # but when I do display it, I can force users to make a choice # atomic_type_field = self.fields["atomic_type"] # atomic_type_field.required = True # atomic_type_field.empty_label = None # atomic_type_field.choices.remove(EMPTY_CHOICE[0]) set_field_widget_attributes(self.fields["atomic_suggestions"], {"rows": 2}) update_field_widget_attributes(self.fields["atomic_type"], {"class": "select single show-tick"}) elif field_type == QPropertyTypes.ENUMERATION: # things to do for ENUMERATION fields... self.unbootstrap_fields(["enumeration_open", ]) # choices = proxy.enumeration_choices.split('|') # enumeration_choices_field = self.fields["enumeration_choices"] # enumeration_default_field = self.fields["enumeration_default"] # enumeration_choices_field.set_choices(choices) # enumeration_default_field.set_choices(choices) # update_field_widget_attributes(enumeration_choices_field, {"class": "select multiple show-tick"}) # update_field_widget_attributes(enumeration_default_field, {"class": "select multiple show-tick"}) # if is_new_property: # self.initial["enumeration_choices"] = choices else: # field_type == QPropertyTypes.RELATIONSHIP # things to do for RELATIONSHIP fields... self.unbootstrap_fields(["relationship_show_subform"]) update_field_widget_attributes(self.fields["relationship_show_subform"], { "ng-disabled": "true", "readonly": "readonly", }) self.fields["subform_targets"].choices = [ (relationship_subform_customization.get_key(), str(relationship_subform_customization.proxy)) for relationship_subform_customization in self.instance.relationship_target_model_customizations(manager="allow_unsaved_relationship_target_model_customizations_manager").all() ] update_field_widget_attributes(self.fields["subform_targets"], {"class": "select single show-tick"}) # things to do for ALL fields... # TODO: DOUBLE-CHECK THAT I SHOULD SET ".initial[key]" and not ".fields[key].initial" self.initial["key"] = self.instance.get_key() self.initial["category_key"] = self.instance.category.get_key() if proxy.is_required(): # if a property is required by the CIM # (then property.reset() will have set the "required" field to True) # then don't allow users to change the "is_required" or "is_hidden" fields update_field_widget_attributes(self.fields["is_required"], { "ng-disabled": "true", "readonly": "readonly", }) update_field_widget_attributes(self.fields["is_hidden"], { "ng-disabled": "true", "readonly": "readonly", }) self.unbootstrap_fields(["is_hidden", "is_required", "is_editable", "is_nillable", "inline_help", ]) set_field_widget_attributes(self.fields["documentation"], {"rows": 2})
def customize(self, customizer): # customization is done in the form and in the template value_field_name = self.get_value_field_name() self.fields[value_field_name].help_text = customizer.documentation # you can customize the category of scientific properties, # so set it here... category_customizer = customizer.category self.initial["category_key"] = category_customizer.get_key() if customizer.field_type != "ENUMERATION": atomic_type = customizer.atomic_type if atomic_type: if atomic_type != MetadataAtomicFieldTypes.DEFAULT: custom_widget_class = METADATA_ATOMICFIELD_MAP[atomic_type][0] custom_widget_args = METADATA_ATOMICFIELD_MAP[atomic_type][1] self.fields["atomic_value"].widget = custom_widget_class(**custom_widget_args) # if I changed the widget, then I have to re-add the attributes that were updated in __init__ above # b/c they will have been lost (I only have to do this for atomic fields b/c the widget for enumerations cannot change) update_field_widget_attributes(self.fields["atomic_value"], {"onchange": "copy_value(this,'%s-scientific_property_value');" % self.prefix}) update_field_widget_attributes(self.fields["atomic_value"], {"class": "atomic_value changer"}) update_field_widget_attributes(self.fields["atomic_value"], {"class": atomic_type.lower()}) else: widget_attributes = {"class": "multiselect", } all_enumeration_choices = [(choice, choice) for choice in customizer.enumeration_choices] if customizer.enumeration_nullable: all_enumeration_choices += NULL_CHOICE widget_attributes["class"] += " nullable" if customizer.enumeration_open: all_enumeration_choices += OTHER_CHOICE widget_attributes["class"] += " open" if customizer.enumeration_multi: widget_attributes["class"] += " multiple" self.fields["enumeration_value"].set_choices(all_enumeration_choices, multi=True) else: widget_attributes["class"] += " single" # all_enumeration_choices = EMPTY_CHOICE + all_enumeration_choices self.fields["enumeration_value"].set_choices(all_enumeration_choices, multi=False) update_field_widget_attributes(self.fields["enumeration_value"], widget_attributes) update_field_widget_attributes(self.fields["enumeration_other_value"], {"class": "other"}) # extra_attributes... if not customizer.edit_extra_standard_name: update_field_widget_attributes(self.fields["extra_standard_name"], {"class": "readonly", "readonly": "readonly"}) if not customizer.edit_extra_description: update_field_widget_attributes(self.fields["extra_description"], {"class": "readonly", "readonly": "readonly"}) if not customizer.edit_extra_units: update_field_widget_attributes(self.fields["extra_units"], {"class": "readonly", "readonly": "readonly"}) set_field_widget_attributes(self.fields["extra_description"], {"cols": "60", "rows": "4"}) self.customizer = customizer