def getInitialLinkDataFields(self): """ Returns a dict mapping fields to be pre-populated with the corresponding model and model-field """ initial = {} for section in self.page: for field in section: ftype = field['field_type'] if cf_cache.isLinkField(ftype): field_name = 'question_%d' % field['id'] initial[field_name] = { 'model': cf_cache.modelForLinkField(ftype) } """ if 'combo' in link_fields[ftype]: initial[field_name]['field']=[] for f in link_fields[ftype]['combo']: initial[field_name]['field'].append(link_fields[f]['model_field']) else: """ model_field = cf_cache.getLinkFieldData( ftype)['model_field'] if cf_cache.isCompoundLinkField( initial[field_name]['model'], model_field): model_field = cf_cache.getCompoundLinkFields( initial[field_name]['model'], model_field) initial[field_name]['model_field'] = model_field return initial
def get_field_name(self, field): """ Returns the field name for this field. Returns the field name corresponding to the linked model for link fields. """ if cf_cache.isLinkField(field.field_type): model = cf_cache.modelForLinkField(field.field_type) return 'link_'+model.__name__ return "question_%d" % field.id
def get_field_name(self, field): """ Returns the field name for this field. Returns the field name corresponding to the linked model for link fields. """ if cf_cache.isLinkField(field.field_type): model = cf_cache.modelForLinkField(field.field_type) return 'link_'+model.__name__ return "question_%d" % field.id
def removeLinkField(self, field): """ Removes the FK-column corresponding to a link field if present. """ if not cf_cache.isLinkField(field.field_type): return model_cls = cf_cache.modelForLinkField(field.field_type) if model_cls.__name__ in self.link_models_list: field_name = 'link_%s' % model_cls.__name__ db.delete_column(self._tname, "%s_id" % field_name) self.link_models_list.remove(model_cls.__name__)
def addLinkFieldColumn(self, field): """ Checks if the FK-column corresponding to this link field is already present. If not, it adds in the column. """ if not cf_cache.isLinkField(field.field_type): return model_cls = cf_cache.modelForLinkField(field.field_type) if model_cls.__name__ not in self.link_models_list: # Add in the FK-column for this model field_name = self.get_field_name(field) db.add_column(self._tname, field_name, models.ForeignKey(model_cls, null=True, blank=True, on_delete=models.SET_NULL)) self.link_models_list.append(model_cls.__name__)
def removeLinkField(self, field): """ Removes the FK-column corresponding to a link field if present. """ if not cf_cache.isLinkField(field.field_type): return with connection.schema_editor() as schema_editor: model = self.createDynModel() link_model_cls = cf_cache.modelForLinkField(field.field_type) if link_model_cls.__name__ in self.link_models_list: field_name = 'link_%s' % link_model_cls.__name__ schema_editor.remove_field(model, model._meta.get_field(field_name)) self.link_models_list.remove(link_model_cls.__name__)
def addLinkFieldColumn(self, field): """ Checks if the FK-column corresponding to this link field is already present. If not, it adds in the column. """ if not cf_cache.isLinkField(field.field_type): return with connection.schema_editor() as schema_editor: link_model_cls = cf_cache.modelForLinkField(field.field_type) if link_model_cls.__name__ not in self.link_models_list: # Add in the FK-column for this model model = self.createDynModel() field_name = self.get_field_name(field) schema_editor.add_field(model, model._meta.get_field(field_name)) self.link_models_list.append(link_model_cls.__name__)
def _getModelFieldList(self): """ Returns a list of Model Field tuples given a list of field_types (from the metadata) - An entry would be like (field_name, field) - An 'id' field is automatically added, as is a 'user_id' field, based on whether the form is anonymous or not. - A field-name is of the form 'question_23', where '23' is the ID of the corresponding question - For custom fields like address, a field-name would be of the form 'question_23_zip' """ link_models = [] if not self.fields: self.fields = self._getFieldsForForm(self.form) self.field_list.append( ('id', models.AutoField(primary_key = True) ) ) if not self.form.anonymous: self.field_list.append( ('user', self._getLinkModelField(ESPUser) ) ) # Checking for only_fkey links if self.form.link_type != '-1': model_cls = cf_cache.only_fkey_models[self.form.link_type] self.field_list.append( ('link_%s' % model_cls.__name__, self._getLinkModelField(model_cls)) ) # Check for linked fields- # Insert a foreign-key to the parent model for link fields # Insert a regular column for non-link fields for field_id, field in self.fields: if cf_cache.isLinkField(field): lm = cf_cache.modelForLinkField(field) if lm not in link_models: link_models.append(lm) else: new_field = self._getModelField(field) if new_field: self.field_list.append( ('question_%s' % str(field_id), new_field) ) for model in link_models: if model: new_field = self._getLinkModelField(model) self.field_list.append( ('link_%s' % model.__name__, new_field) ) self.link_models_list.append(model.__name__) return self.field_list
def _getModelFieldList(self): """ Returns a list of Model Field tuples given a list of field_types (from the metadata) - An entry would be like (field_name, field) - An 'id' field is automatically added, as is a 'user_id' field, based on whether the form is anonymous or not. - A field-name is of the form 'question_23', where '23' is the ID of the corresponding question - For custom fields like address, a field-name would be of the form 'question_23_zip' """ link_models = [] if not self.fields: self.fields = self._getFieldsForForm(self.form) self.field_list.append( ('id', models.AutoField(primary_key = True) ) ) if not self.form.anonymous: self.field_list.append( ('user', models.ForeignKey(ESPUser, null = True, blank = True, on_delete = models.SET_NULL) ) ) # Checking for only_fkey links if self.form.link_type != '-1': model_cls = cf_cache.only_fkey_models[self.form.link_type] self.field_list.append( ('link_%s' % model_cls.__name__, models.ForeignKey(model_cls, null=True, blank=True, on_delete=models.SET_NULL)) ) # Check for linked fields- # Insert a foreign-key to the parent model for link fields # Insert a regular column for non-link fields for field_id, field in self.fields: if cf_cache.isLinkField(field): lm = cf_cache.modelForLinkField(field) if lm not in link_models: link_models.append(lm) else: new_field = self._getModelField(field) if new_field: self.field_list.append( ('question_%s' % str(field_id), new_field) ) # Adding foreign key fields for link-field models for model in link_models: if model: self.field_list.append( ('link_%s' % model.__name__, models.ForeignKey(model, null=True, blank=True, on_delete=models.SET_NULL) ) ) self.link_models_list.append(model.__name__) return self.field_list
def getInitialLinkDataFields(self): """ Returns a dict mapping fields to be pre-populated with the corresponding model and model-field """ initial = {} for section in self.page: for field in section: ftype = field['field_type'] if cf_cache.isLinkField(ftype): field_name = 'question_%d' % field['id'] initial[field_name] = {'model': cf_cache.modelForLinkField(ftype)} """ if 'combo' in link_fields[ftype]: initial[field_name]['field']=[] for f in link_fields[ftype]['combo']: initial[field_name]['field'].append(link_fields[f]['model_field']) else: """ model_field = cf_cache.getLinkFieldData(ftype)['model_field'] if cf_cache.isCompoundLinkField(initial[field_name]['model'], model_field): model_field = cf_cache.getCompoundLinkFields(initial[field_name]['model'], model_field) initial[field_name]['model_field'] = model_field return initial
def getResponseData(self, form): """ Returns the response data for this form, along with the questions """ dmh = DMH(form=form) dyn = dmh.createDynModel() response_data = {'questions': [], 'answers': []} responses = dyn.objects.all().order_by('id').values() fields = Field.objects.filter(form=form).order_by('section__page__seq', 'section__seq', 'seq').values('id', 'field_type', 'label') # Let's first do a bit of introspection to figure out # what the linked models are, and what values need to be added to the # response data from these linked models. # And since we're already iterating over fields, # let's also set the questions in the process. add_fields = {} # Add in the user column if form is not anonymous if not form.anonymous: response_data['questions'].append(['user_id', 'User', 'fk']) # Add in the column for link fields, if any if form.link_type != "-1": only_fkey_model = cf_cache.only_fkey_models[form.link_type] response_data['questions'].append(["link_%s_id" % only_fkey_model.__name__, form.link_type, 'fk']) else: only_fkey_model = None for field in fields: # I'll do a lot of merging here later qname = 'question_%d' % field['id'] ftype = field['field_type'] if cf_cache.isLinkField(ftype): # Let's grab the model first model = cf_cache.modelForLinkField(ftype) # Now let's see what fields need to be set add_fields[qname] = [model, cf_cache.getLinkFieldData(ftype)['model_field']] response_data['questions'].append([qname, field['label'], ftype]) # Include this field only if it isn't a dummy field elif generic_fields[ftype]['typeMap'] is not DummyField: response_data['questions'].append([qname, field['label'], ftype]) # Now let's set up the responses for response in responses: link_instances_cache={} # Add in user if form is not anonymous if not form.anonymous: response['user_id'] = unicode(response['user_id']) # Add in links if only_fkey_model is not None: if only_fkey_model.objects.filter(pk=response["link_%s_id" % only_fkey_model.__name__]).exists(): inst = only_fkey_model.objects.get(pk=response["link_%s_id" % only_fkey_model.__name__]) else: inst = None response["link_%s_id" % only_fkey_model.__name__] = unicode(inst) # Now, put in the additional fields in response for qname, data in add_fields.items(): if data[0].__name__ not in link_instances_cache: if data[0].objects.filter(pk=response["link_%s_id" % data[0].__name__]).exists(): link_instances_cache[data[0].__name__] = data[0].objects.get(pk=response["link_%s_id" % data[0].__name__]) else: link_instances_cache[data[0].__name__] = None if cf_cache.isCompoundLinkField(data[0], data[1]): if link_instances_cache[data[0].__name__] is None: response[qname] = [] else: response[qname] = [link_instances_cache[data[0].__name__].__dict__[x] for x in cf_cache.getCompoundLinkFields(data[0], data[1])] else: if link_instances_cache[data[0].__name__] is None: response[qname]='' else: response[qname] = link_instances_cache[data[0].__name__].__dict__[data[1]] # Add responses to response_data response_data['answers'].extend(responses) return response_data
def done(self, form_list, **kwargs): data = {} dyn = DMH(form=self.form) dynModel = dyn.createDynModel() fields = dict(dyn.fields) link_models_cache = {} # Plonking in user_id if the form is non-anonymous if not self.form.anonymous: data['user'] = self.curr_request.user # Populating data with the values that need to be inserted for form in form_list: for k,v in form.cleaned_data.items(): # Check for only_fkey link models first if k.split('_')[1] in cf_cache.only_fkey_models: data[k] = v continue field_id = int(k.split("_")[1]) ftype = fields[field_id] # Now check for link fields if cf_cache.isLinkField(ftype): model = cf_cache.modelForLinkField(ftype) if model.__name__ not in link_models_cache: link_models_cache[model.__name__] = {'model': model, 'data': {}} pre_instance = self.form_handler.getInstanceForLinkField(k, model) if pre_instance is not None: link_models_cache[model.__name__]['instance'] = pre_instance else: link_models_cache[model.__name__]['instance'] = getattr(model, 'cf_link_instance')(self.curr_request) ftype_parts = ftype.split('_') if len(ftype_parts) > 1 and cf_cache.isCompoundLinkField(model, '_'.join(ftype_parts[1:])): # Try to match a model field to the last part of the key we have. partial_field_name = str(field_id).join(k.split(str(field_id))[1:]).lstrip('_') target_fields = cf_cache.getCompoundLinkFields(model, '_'.join(ftype_parts[1:])) for f in target_fields: if f.endswith(partial_field_name): model_field = f break else: model_field = cf_cache.getLinkFieldData(ftype)['model_field'] link_models_cache[model.__name__]['data'].update({model_field: v}) else: data[k] = v # Create/update instances corresponding to link fields # Also, populate 'data' with foreign-keys that need to be inserted into the response table for k,v in link_models_cache.items(): if v['instance'] is not None: # TODO-> the following update won't work for fk fields. v['instance'].__dict__.update(v['data']) v['instance'].save() curr_instance = v['instance'] else: try: new_instance = v['model'].objects.create(**v['data']) except: # show some error message pass if v['instance'] is not None: data['link_%s' % v['model'].__name__] = v['instance'] # Saving response initial_keys = data.keys() for key in initial_keys: # Check that we didn't already handle this value as a linked field if key.split('_')[0] in cf_cache.link_fields: del data[key] # Check that this value didn't come from a dummy field if key.split('_')[0] == 'question' and generic_fields[fields[int(key.split('_')[1])]]['typeMap'] == DummyField: del data[key] dynModel.objects.create(**data) return HttpResponseRedirect('/customforms/success/%d/' % self.form.id)
def _getFields(self): """ Sets self.fields and self.fieldsets for this page """ model_fields_cache = {} for section in self.page: curr_fieldset = [] curr_fieldset.extend([section[0]['section__title'], {'fields':[], 'classes':['section',]}]) curr_fieldset[1]['description'] = section[0]['section__description'] # Check for only_fkey models. # If any, insert the relevant field into the first section of the fist page if section[0]['section__seq'] == 0 and self.seq == 0: if self.form.link_type != '-1': label = 'Please pick the %s you want to fill the form for' % self.form.link_type link_cls = cf_cache.only_fkey_models[self.form.link_type] if self.form.link_id == -1: # User needs to be shown a list of instances from which to select queryset = link_cls.objects.all() widget = forms.Select() else: queryset = link_cls.objects.filter(pk=self.form.link_id) widget = forms.HiddenInput() fld = forms.ModelChoiceField(queryset = queryset, label = label, initial = queryset[0], widget = widget, required = True, empty_label = None) self.fields.append(['link_%s' % link_cls.__name__, fld ]) curr_fieldset[1]['fields'].append('link_%s' % link_cls.__name__) for field in section: field_name = 'question_%d' % field['id'] field_attrs = {'label': field['label'], 'help_text': field['help_text'], 'required': field['required']} # Setting the 'name' attribute for combo fields """ if field['field_type'] in self._combo_fields: field_attrs['name']=field_name """ # Extract form attributes for further use below other_attrs = [] for attr_name in field['attributes']: other_attrs.append({'attr_type': attr_name, 'value': field['attributes'][attr_name]}) # Create dynamic validators to check results if the correct answer has # been specified by the form author if attr_name == 'correct_answer' and len(field['attributes'][attr_name].strip()) > 0: if field['field_type'] in ['dropdown', 'radio']: value_choices = field['attributes']['options'].split('|') target_value = value_choices[int(field['attributes'][attr_name])] elif field['field_type'] in ['checkboxes']: value_choices = field['attributes']['options'].split('|') target_value = [value_choices[int(index)] for index in field['attributes'][attr_name].split(',')] else: target_value = field['attributes'][attr_name] field_attrs['validators'] = [matches_answer(target_value)] if other_attrs: field_attrs.update(self._getAttrs(other_attrs)) # First, check for link fields if cf_cache.isLinkField(field['field_type']): # Get all form fields for this model, if it hasn't already been done link_model = cf_cache.modelForLinkField(field['field_type']) if not link_model: continue if link_model.__name__ not in model_fields_cache: model_fields_cache[link_model.__name__] = {} model_fields_cache[link_model.__name__].update(fields_for_model(link_model, widgets=getattr(link_model, 'link_fields_widgets', None))) model_field = cf_cache.getLinkFieldData(field['field_type'])['model_field'] field_is_custom = False if model_field in model_fields_cache[link_model.__name__]: form_field = model_fields_cache[link_model.__name__][model_field] else: # See if there's a custom field if model_field in custom_fields: form_field = cf_cache.getCustomFieldInstance(model_field, field_name) field_is_custom = True else: raise Exception('Could not find linked field: %s' % model_field) # TODO -> enforce "Required" constraint server-side as well, or trust the client-side code? form_field.__dict__.update(field_attrs) form_field.widget.attrs.update({'class': ''}) if form_field.required: # Add a class 'required' to the widget form_field.widget.attrs['class'] += 'required ' form_field.widget.is_required = True if not field_is_custom: # Add in other classes for validation generic_type = cf_cache.getGenericType(form_field) if 'widget_attrs' in self._field_types[generic_type] and 'class' in self._field_types[generic_type]['widget_attrs']: form_field.widget.attrs['class'] += self._field_types[generic_type]['widget_attrs']['class'] # Adding to field list self.fields.append([field_name, form_field]) curr_fieldset[1]['fields'].append(field_name) continue # Generic field widget_attrs = {} if 'attrs' in self._field_types[field['field_type']]: field_attrs.update(self._field_types[field['field_type']]['attrs']) if 'widget_attrs' in self._field_types[field['field_type']]: widget_attrs.update(self._field_types[field['field_type']]['widget_attrs']) typeMap = self._field_types[field['field_type']]['typeMap'] # Setting classes required for front-end validation if field['required']: widget_attrs['class'] += ' required' if 'min_value' in field_attrs: widget_attrs['min'] = field_attrs['min_value'] if 'max_value' in field_attrs: widget_attrs['max'] = field_attrs['max_value'] if 'min_length' in field_attrs: widget_attrs['minlength'] = field_attrs['min_length'] if 'max_length' in field_attrs: widget_attrs['maxlength'] = field_attrs['max_length'] if 'min_words' in field_attrs: widget_attrs['minWords'] = field_attrs['min_words'] if 'max_words' in field_attrs: widget_attrs['maxWords'] = field_attrs['max_words'] # For combo fields, classes need to be passed in to the field if field['field_type'] in self._combo_fields: field_attrs.update(widget_attrs) # Setting the queryset for a courses field if field['field_type'] == 'courses': if self.form.link_type == 'program' or self.form.link_type == 'Program': field_attrs['queryset'] = Program.objects.get(pk = self.form.link_id).classsubject_set.all() # Initializing widget if field_attrs['widget'] is not None: try: field_attrs['widget'] = field_attrs['widget'](attrs = widget_attrs) except KeyError: pass self.fields.append([field_name, typeMap(**field_attrs) ]) curr_fieldset[1]['fields'].append(field_name) self.fieldsets.append(tuple(curr_fieldset))
def getResponseData(self, form): """ Returns the response data for this form, along with the questions """ dmh = DMH(form=form) dyn = dmh.createDynModel() response_data = {'questions': [], 'answers': []} responses = dyn.objects.all().order_by('id').values() fields = Field.objects.filter(form=form).order_by('section__page__seq', 'section__seq', 'seq').values('id', 'field_type', 'label') # Let's first do a bit of introspection to figure out # what the linked models are, and what values need to be added to the # response data from these linked models. # And since we're already iterating over fields, # let's also set the questions in the process. add_fields = {} # Add in the user column if form is not anonymous if not form.anonymous: response_data['questions'].append(['user_id', 'User ID', 'fk']) response_data['questions'].append(['user_display', 'User', 'textField']) response_data['questions'].append(['user_email', 'User e-mail', 'textField']) response_data['questions'].append(['username', 'Username', 'textField']) # Add in the column for link fields, if any if form.link_type != "-1": only_fkey_model = cf_cache.only_fkey_models[form.link_type] response_data['questions'].append(["link_%s_id" % only_fkey_model.__name__, form.link_type, 'fk']) else: only_fkey_model = None for field in fields: # I'll do a lot of merging here later qname = 'question_%d' % field['id'] ftype = field['field_type'] if cf_cache.isLinkField(ftype): # Let's grab the model first model = cf_cache.modelForLinkField(ftype) # Now let's see what fields need to be set add_fields[qname] = [model, cf_cache.getLinkFieldData(ftype)['model_field']] response_data['questions'].append([qname, field['label'], ftype]) # Include this field only if it isn't a dummy field elif generic_fields[ftype]['typeMap'] is not DummyField: response_data['questions'].append([qname, field['label'], ftype]) users = ESPUser.objects.in_bulk(map(lambda response: response['user_id'], responses)) # Now let's set up the responses for response in responses: link_instances_cache={} # Add in user if form is not anonymous if not form.anonymous: user = users[response['user_id']] response['user_id'] = unicode(response['user_id']) response['user_display'] = user.name() response['user_email'] = user.email response['username'] = user.username # Add in links if only_fkey_model is not None: if only_fkey_model.objects.filter(pk=response["link_%s_id" % only_fkey_model.__name__]).exists(): inst = only_fkey_model.objects.get(pk=response["link_%s_id" % only_fkey_model.__name__]) else: inst = None response["link_%s_id" % only_fkey_model.__name__] = unicode(inst) # Now, put in the additional fields in response for qname, data in add_fields.items(): if data[0].__name__ not in link_instances_cache: if data[0].objects.filter(pk=response["link_%s_id" % data[0].__name__]).exists(): link_instances_cache[data[0].__name__] = data[0].objects.get(pk=response["link_%s_id" % data[0].__name__]) else: link_instances_cache[data[0].__name__] = None if cf_cache.isCompoundLinkField(data[0], data[1]): if link_instances_cache[data[0].__name__] is None: response[qname] = [] else: response[qname] = [link_instances_cache[data[0].__name__].__dict__[x] for x in cf_cache.getCompoundLinkFields(data[0], data[1])] else: if link_instances_cache[data[0].__name__] is None: response[qname]='' else: response[qname] = link_instances_cache[data[0].__name__].__dict__[data[1]] # Add responses to response_data response_data['answers'].extend(responses) return response_data
def done(self, form_list, **kwargs): data = {} dyn = DMH(form=self.form) dynModel = dyn.createDynModel() fields = dict(dyn.fields) link_models_cache = {} # Plonking in user_id if the form is non-anonymous if not self.form.anonymous: data['user'] = self.curr_request.user # Populating data with the values that need to be inserted for form in form_list: for k,v in form.cleaned_data.items(): # Check for only_fkey link models first if k.split('_')[1] in cf_cache.only_fkey_models: data[k] = v continue field_id = int(k.split("_")[1]) ftype = fields[field_id] # Now check for link fields if cf_cache.isLinkField(ftype): model = cf_cache.modelForLinkField(ftype) if model.__name__ not in link_models_cache: link_models_cache[model.__name__] = {'model': model, 'data': {}} pre_instance = self.form_handler.getInstanceForLinkField(k, model) if pre_instance is not None: link_models_cache[model.__name__]['instance'] = pre_instance else: link_models_cache[model.__name__]['instance'] = getattr(model, 'cf_link_instance')(self.curr_request) ftype_parts = ftype.split('_') if len(ftype_parts) > 1 and cf_cache.isCompoundLinkField(model, '_'.join(ftype_parts[1:])): # Try to match a model field to the last part of the key we have. partial_field_name = str(field_id).join(k.split(str(field_id))[1:]).lstrip('_') target_fields = cf_cache.getCompoundLinkFields(model, '_'.join(ftype_parts[1:])) for f in target_fields: if f.endswith(partial_field_name): model_field = f break else: model_field = cf_cache.getLinkFieldData(ftype)['model_field'] link_models_cache[model.__name__]['data'].update({model_field: v}) else: data[k] = v # Create/update instances corresponding to link fields # Also, populate 'data' with foreign-keys that need to be inserted into the response table for k,v in link_models_cache.items(): if v['instance'] is not None: # TODO-> the following update won't work for fk fields. v['instance'].__dict__.update(v['data']) v['instance'].save() curr_instance = v['instance'] else: try: new_instance = v['model'].objects.create(**v['data']) except: # show some error message pass if v['instance'] is not None: data['link_%s' % v['model'].__name__] = v['instance'] # Saving response initial_keys = data.keys() for key in initial_keys: # Check that we didn't already handle this value as a linked field if key.split('_')[0] in cf_cache.link_fields: del data[key] # Check that this value didn't come from a dummy field if key.split('_')[0] == 'question' and generic_fields[fields[int(key.split('_')[1])]]['typeMap'] == DummyField: del data[key] dynModel.objects.create(**data) return HttpResponseRedirect('/customforms/success/%d/' % self.form.id)
def _getFields(self): """ Sets self.fields and self.fieldsets for this page """ model_fields_cache = {} for section in self.page: curr_fieldset = [] curr_fieldset.extend([section[0]['section__title'], {'fields':[], 'classes':['section',]}]) curr_fieldset[1]['description'] = section[0]['section__description'] # Check for only_fkey models. # If any, insert the relevant field into the first section of the fist page if section[0]['section__seq'] == 0 and self.seq == 0: if self.form.link_type != '-1': label = 'Please pick the %s you want to fill the form for' % self.form.link_type link_cls = cf_cache.only_fkey_models[self.form.link_type] if self.form.link_id == -1: # User needs to be shown a list of instances from which to select queryset = link_cls.objects.all() widget = forms.Select() else: queryset = link_cls.objects.filter(pk=self.form.link_id) widget = forms.HiddenInput() fld = forms.ModelChoiceField(queryset = queryset, label = label, initial = queryset[0], widget = widget, required = True, empty_label = None) self.fields.append(['link_%s' % link_cls.__name__, fld ]) curr_fieldset[1]['fields'].append('link_%s' % link_cls.__name__) for field in section: field_name = 'question_%d' % field['id'] field_attrs = {'label': field['label'], 'help_text': field['help_text'], 'required': field['required']} # Setting the 'name' attribute for combo fields """ if field['field_type'] in self._combo_fields: field_attrs['name']=field_name """ # Extract form attributes for further use below other_attrs = [] for attr_name in field['attributes']: other_attrs.append({'attr_type': attr_name, 'value': field['attributes'][attr_name]}) # Create dynamic validators to check results if the correct answer has # been specified by the form author if attr_name == 'correct_answer' and len(field['attributes'][attr_name].strip()) > 0: if field['field_type'] in ['dropdown', 'radio']: value_choices = field['attributes']['options'].split('|') target_value = value_choices[int(field['attributes'][attr_name])] elif field['field_type'] in ['checkboxes']: value_choices = field['attributes']['options'].split('|') target_value = [value_choices[int(index)] for index in field['attributes'][attr_name].split(',')] else: target_value = field['attributes'][attr_name] field_attrs['validators'] = [matches_answer(target_value)] if other_attrs: field_attrs.update(self._getAttrs(other_attrs)) # First, check for link fields if cf_cache.isLinkField(field['field_type']): # Get all form fields for this model, if it hasn't already been done link_model = cf_cache.modelForLinkField(field['field_type']) if not link_model: continue if link_model.__name__ not in model_fields_cache: model_fields_cache[link_model.__name__] = {} model_fields_cache[link_model.__name__].update(fields_for_model(link_model, widgets=getattr(link_model, 'link_fields_widgets', None))) model_field = cf_cache.getLinkFieldData(field['field_type'])['model_field'] field_is_custom = False if model_field in model_fields_cache[link_model.__name__]: form_field = model_fields_cache[link_model.__name__][model_field] else: # See if there's a custom field if model_field in custom_fields: form_field = cf_cache.getCustomFieldInstance(model_field, field_name) field_is_custom = True else: raise Exception('Could not find linked field: %s' % model_field) # TODO -> enforce "Required" constraint server-side as well, or trust the client-side code? form_field.__dict__.update(field_attrs) form_field.widget.attrs.update({'class': ''}) if form_field.required: # Add a class 'required' to the widget form_field.widget.attrs['class'] += 'required ' form_field.widget.is_required = True if not field_is_custom: # Add in other classes for validation generic_type = cf_cache.getGenericType(form_field) if 'widget_attrs' in self._field_types[generic_type] and 'class' in self._field_types[generic_type]['widget_attrs']: form_field.widget.attrs['class'] += self._field_types[generic_type]['widget_attrs']['class'] # Adding to field list self.fields.append([field_name, form_field]) curr_fieldset[1]['fields'].append(field_name) continue # Generic field widget_attrs = {} if 'attrs' in self._field_types[field['field_type']]: field_attrs.update(self._field_types[field['field_type']]['attrs']) if 'widget_attrs' in self._field_types[field['field_type']]: widget_attrs.update(self._field_types[field['field_type']]['widget_attrs']) typeMap = self._field_types[field['field_type']]['typeMap'] # Setting classes required for front-end validation if field['required']: widget_attrs['class'] += ' required' if 'min_value' in field_attrs: widget_attrs['min'] = field_attrs['min_value'] if 'max_value' in field_attrs: widget_attrs['max'] = field_attrs['max_value'] if 'min_length' in field_attrs: widget_attrs['minlength'] = field_attrs['min_length'] if 'max_length' in field_attrs: widget_attrs['maxlength'] = field_attrs['max_length'] if 'min_words' in field_attrs: widget_attrs['minWords'] = field_attrs['min_words'] if 'max_words' in field_attrs: widget_attrs['maxWords'] = field_attrs['max_words'] # For combo fields, classes need to be passed in to the field if field['field_type'] in self._combo_fields: field_attrs.update(widget_attrs) # Setting the queryset for a courses field if field['field_type'] == 'courses': if self.form.link_type == 'program' or self.form.link_type == 'Program': field_attrs['queryset'] = Program.objects.get(pk = self.form.link_id).classsubject_set.all() # Initializing widget if field_attrs['widget'] is not None: try: field_attrs['widget'] = field_attrs['widget'](attrs = widget_attrs) except KeyError: pass self.fields.append([field_name, typeMap(**field_attrs) ]) curr_fieldset[1]['fields'].append(field_name) self.fieldsets.append(tuple(curr_fieldset))
def onModify(request): """ Handles form modifications """ if request.is_ajax(): if request.method == 'POST': metadata = json.loads(request.body) try: form = Form.objects.get(id=int(metadata['form_id'])) except: raise ESPError('Form %s not found' % metadata['form_id'], log=False) dmh = DMH(form=form) link_models_list = [ ] # Stores a cache of link models that should not be removed # Populating the old fields list dmh._getModelFieldList() # NOT updating 'anonymous' form.__dict__.update(title=metadata['title'], description=metadata['desc'], perms=metadata['perms'], success_message=metadata['success_message'], success_url=metadata['success_url']) form.save() # Check if only_fkey links have changed if form.link_type != metadata['link_type']: dmh.change_only_fkey(form, form.link_type, metadata['link_type'], link_id) curr_keys = {'pages': [], 'sections': [], 'fields': []} old_pages = Page.objects.filter(form=form) old_sections = Section.objects.filter(page__in=old_pages) old_fields = Field.objects.filter(form=form) for page in metadata['pages']: curr_page = get_new_or_altered_obj(Page, page['parent_id'], form=form, seq=int(page['seq'])) curr_keys['pages'].append(curr_page.id) for section in page['sections']: curr_sect = get_new_or_altered_obj( Section, section['data']['parent_id'], page=curr_page, title=section['data']['question_text'], description=section['data']['help_text'], seq=int(section['data']['seq'])) curr_keys['sections'].append(curr_sect.id) for field in section['fields']: (curr_field, old_field, field_created) = get_or_create_altered_obj( Field, field['data']['parent_id'], form=form, section=curr_sect, field_type=field['data']['field_type'], seq=int(field['data']['seq']), label=field['data']['question_text'], help_text=field['data']['help_text'], required=field['data']['required']) if field_created: # Check for link field if cf_cache.isLinkField(curr_field.field_type): dmh.addLinkFieldColumn(curr_field) else: dmh.addField(curr_field) elif not cf_cache.isLinkField(curr_field.field_type): dmh.updateField(curr_field, old_field) # Store a reference to the linked model so that we don't drop it from the table. if cf_cache.isLinkField(curr_field.field_type): model_cls = cf_cache.modelForLinkField( curr_field.field_type) if model_cls.__name__ not in link_models_list: link_models_list.append(model_cls.__name__) for atype, aval in field['data']['attrs'].items(): curr_field.set_attribute(atype, aval) curr_keys['fields'].append(curr_field.id) del_fields = old_fields.exclude(id__in=curr_keys['fields']) for df in del_fields: # Check for link fields if cf_cache.isLinkField(df.field_type): model_cls = cf_cache.modelForLinkField(df.field_type) if model_cls.__name__ not in link_models_list: # This column needs to be dropped dmh.removeLinkField(df) else: dmh.removeField(df) del_fields.delete() old_sections.exclude(id__in=curr_keys['sections']).delete() old_pages.exclude(id__in=curr_keys['pages']).delete() return HttpResponse('OK')
def onModify(request): """ Handles form modifications """ if request.is_ajax(): if request.method == 'POST': metadata = json.loads(request.body) try: form = Form.objects.get(id=int(metadata['form_id'])) except: raise ESPError('Form %s not found' % metadata['form_id'], log=False) dmh = DMH(form=form) link_models_list = [] # Stores a cache of link models that should not be removed # Populating the old fields list dmh._getModelFieldList() # NOT updating 'anonymous' form.__dict__.update(title=metadata['title'], description=metadata['desc'], perms=metadata['perms'], success_message=metadata['success_message'], success_url=metadata['success_url'] ) form.save() # Check if only_fkey links have changed if form.link_type != metadata['link_type']: dmh.change_only_fkey(form, form.link_type, metadata['link_type'], link_id) curr_keys = {'pages': [], 'sections': [], 'fields': []} old_pages = Page.objects.filter(form=form) old_sections = Section.objects.filter(page__in=old_pages) old_fields = Field.objects.filter(form=form) for page in metadata['pages']: curr_page = get_new_or_altered_obj(Page, page['parent_id'], form=form, seq=int(page['seq'])) curr_keys['pages'].append(curr_page.id) for section in page['sections']: curr_sect = get_new_or_altered_obj(Section, section['data']['parent_id'], page=curr_page, title=section['data']['question_text'], description=section['data']['help_text'], seq=int(section['data']['seq']) ) curr_keys['sections'].append(curr_sect.id) for field in section['fields']: (curr_field, old_field, field_created) = get_or_create_altered_obj(Field, field['data']['parent_id'], form=form, section=curr_sect, field_type=field['data']['field_type'], seq=int(field['data']['seq']), label=field['data']['question_text'], help_text=field['data']['help_text'], required=field['data']['required'] ) if field_created: # Check for link field if cf_cache.isLinkField(curr_field.field_type): dmh.addLinkFieldColumn(curr_field) else: dmh.addField(curr_field) elif not cf_cache.isLinkField(curr_field.field_type): dmh.updateField(curr_field, old_field) # Store a reference to the linked model so that we don't drop it from the table. if cf_cache.isLinkField(curr_field.field_type): model_cls = cf_cache.modelForLinkField(curr_field.field_type) if model_cls.__name__ not in link_models_list: link_models_list.append(model_cls.__name__) for atype, aval in field['data']['attrs'].items(): curr_field.set_attribute(atype, aval) curr_keys['fields'].append(curr_field.id) del_fields = old_fields.exclude(id__in=curr_keys['fields']) for df in del_fields: # Check for link fields if cf_cache.isLinkField(df.field_type): model_cls = cf_cache.modelForLinkField(df.field_type) if model_cls.__name__ not in link_models_list: # This column needs to be dropped dmh.removeLinkField(df) else: dmh.removeField(df) del_fields.delete() old_sections.exclude(id__in=curr_keys['sections']).delete() old_pages.exclude(id__in=curr_keys['pages']).delete() return HttpResponse('OK')