def save(self): failed = False for form in self.forms: # Every form represents a CartPosition or OrderPosition with questions attached if not form.is_valid(): failed = True else: # This form was correctly filled, so we store the data as # answers to the questions / in the CartPosition object for k, v in form.cleaned_data.items(): if k == 'attendee_name': form.pos.attendee_name = v if v != '' else None form.pos.save() elif k.startswith('question_') and v is not None: field = form.fields[k] if hasattr(field, 'answer'): # We already have a cached answer object, so we don't # have to create a new one if v == '': field.answer.delete() else: self._save_to_answer(field, field.answer, v) field.answer.save() elif v != '': answer = QuestionAnswer( cartposition=(form.pos if isinstance(form.pos, CartPosition) else None), orderposition=(form.pos if isinstance(form.pos, OrderPosition) else None), question=field.question, ) self._save_to_answer(field, answer, v) answer.save() return not failed
def save(self): failed = False for form in self.forms: meta_info = form.pos.meta_info_data # Every form represents a CartPosition or OrderPosition with questions attached if not form.is_valid(): failed = True else: # This form was correctly filled, so we store the data as # answers to the questions / in the CartPosition object for k, v in form.cleaned_data.items(): if k == 'attendee_name_parts': form.pos.attendee_name_parts = v if v else None elif k == 'attendee_email': form.pos.attendee_email = v if v != '' else None elif k == 'company': form.pos.company = v if v != '' else None elif k == 'street': form.pos.street = v if v != '' else None elif k == 'zipcode': form.pos.zipcode = v if v != '' else None elif k == 'city': form.pos.city = v if v != '' else None elif k == 'country': form.pos.country = v if v != '' else None elif k == 'state': form.pos.state = v if v != '' else None elif k.startswith('question_'): field = form.fields[k] if hasattr(field, 'answer'): # We already have a cached answer object, so we don't # have to create a new one if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \ or (isinstance(v, QuerySet) and not v.exists()): if field.answer.file: field.answer.file.delete() field.answer.delete() else: self._save_to_answer(field, field.answer, v) field.answer.save() elif v != '' and v is not None: answer = QuestionAnswer( cartposition=(form.pos if isinstance(form.pos, CartPosition) else None), orderposition=(form.pos if isinstance(form.pos, OrderPosition) else None), question=field.question, ) self._save_to_answer(field, answer, v) answer.save() else: meta_info.setdefault('question_form_data', {}) if v is None: if k in meta_info['question_form_data']: del meta_info['question_form_data'][k] else: meta_info['question_form_data'][k] = v form.pos.meta_info = json.dumps(meta_info) form.pos.save() return not failed
def save(self): failed = False for form in self.forms: meta_info = form.pos.meta_info_data # Every form represents a CartPosition or OrderPosition with questions attached if not form.is_valid(): failed = True else: # This form was correctly filled, so we store the data as # answers to the questions / in the CartPosition object for k, v in form.cleaned_data.items(): if k == 'attendee_name_parts': form.pos.attendee_name_parts = v if v else None form.pos.save() elif k == 'attendee_email': form.pos.attendee_email = v if v != '' else None form.pos.save() elif k.startswith('question_'): field = form.fields[k] if hasattr(field, 'answer'): # We already have a cached answer object, so we don't # have to create a new one if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \ or (isinstance(v, QuerySet) and not v.exists()): if field.answer.file: field.answer.file.delete() field.answer.delete() else: self._save_to_answer(field, field.answer, v) field.answer.save() elif v != '' and v is not None: answer = QuestionAnswer( cartposition=(form.pos if isinstance(form.pos, CartPosition) else None), orderposition=(form.pos if isinstance(form.pos, OrderPosition) else None), question=field.question, ) self._save_to_answer(field, answer, v) answer.save() else: meta_info.setdefault('question_form_data', {}) if v is None: if k in meta_info['question_form_data']: del meta_info['question_form_data'][k] else: meta_info['question_form_data'][k] = v form.pos.meta_info = json.dumps(meta_info) form.pos.save(update_fields=['meta_info']) return not failed
def save(self): failed = False for form in self.forms: meta_info = form.pos.meta_info_data # Every form represents a CartPosition or OrderPosition with questions attached if not form.is_valid(): failed = True else: # This form was correctly filled, so we store the data as # answers to the questions / in the CartPosition object for k, v in form.cleaned_data.items(): if k == 'attendee_name_parts': form.pos.attendee_name_parts = v if v else None elif k == 'attendee_email': form.pos.attendee_email = v if v != '' else None elif k == 'company': form.pos.company = v if v != '' else None elif k == 'street': form.pos.street = v if v != '' else None elif k == 'zipcode': form.pos.zipcode = v if v != '' else None elif k == 'city': form.pos.city = v if v != '' else None elif k == 'country': form.pos.country = v if v != '' else None elif k == 'state': form.pos.state = v if v != '' else None elif k.startswith('question_'): field = form.fields[k] if hasattr(field, 'answer'): # We already have a cached answer object, so we don't # have to create a new one if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \ or (isinstance(v, QuerySet) and not v.exists()): if field.answer.file: field.answer.file.delete() field.answer.delete() else: self._save_to_answer(field, field.answer, v) field.answer.save() elif v != '' and v is not None: answer = QuestionAnswer( cartposition=(form.pos if isinstance( form.pos, CartPosition) else None), orderposition=(form.pos if isinstance( form.pos, OrderPosition) else None), question=field.question, ) try: self._save_to_answer(field, answer, v) answer.save() except IntegrityError: # Since we prefill ``field.answer`` at form creation time, there's a possible race condition # here if the users submits their save request a second time while the first one is still running, # thus leading to duplicate QuestionAnswer objects. Since Django doesn't support UPSERT, the "proper" # fix would be a transaction with select_for_update(), or at least fetching using get_or_create here # again. However, both of these approaches have a significant performance overhead for *all* requests, # while the issue happens very very rarely. So we opt for just catching the error and retrying properly. answer = QuestionAnswer.objects.get( cartposition=(form.pos if isinstance( form.pos, CartPosition) else None), orderposition=(form.pos if isinstance( form.pos, OrderPosition) else None), question=field.question, ) self._save_to_answer(field, answer, v) answer.save() else: meta_info.setdefault('question_form_data', {}) if v is None: if k in meta_info['question_form_data']: del meta_info['question_form_data'][k] else: meta_info['question_form_data'][k] = v form.pos.meta_info = json.dumps(meta_info) form.pos.save() return not failed
def save(self): failed = False for form in self.forms: meta_info = form.pos.meta_info_data # Every form represents a CartPosition or OrderPosition with questions attached if not form.is_valid(): failed = True else: if form.cleaned_data.get('saved_id'): prof = AttendeeProfile.objects.filter( customer=self.cart_customer, pk=form.cleaned_data.get( 'saved_id')).first() or AttendeeProfile( customer=getattr(self, 'cart_customer', None)) answers_key_to_index = { a.get('field_name'): i for i, a in enumerate(prof.answers) } else: prof = AttendeeProfile( customer=getattr(self, 'cart_customer', None)) answers_key_to_index = {} # This form was correctly filled, so we store the data as # answers to the questions / in the CartPosition object for k, v in form.cleaned_data.items(): if k in ('save', 'saved_id'): continue elif k == 'attendee_name_parts': form.pos.attendee_name_parts = v if v else None prof.attendee_name_parts = form.pos.attendee_name_parts prof.attendee_name_cached = form.pos.attendee_name elif k in ('attendee_email', 'company', 'street', 'zipcode', 'city', 'country', 'state'): v = v if v != '' else None setattr(form.pos, k, v) setattr(prof, k, v) elif k.startswith('question_'): field = form.fields[k] if hasattr(field, 'answer'): # We already have a cached answer object, so we don't # have to create a new one if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \ or (isinstance(v, QuerySet) and not v.exists()): if field.answer.file: field.answer.file.delete() field.answer.delete() else: self._save_to_answer(field, field.answer, v) field.answer.save() if isinstance( field, forms.ModelMultipleChoiceField ) or isinstance(field, forms.ModelChoiceField): answer_value = { o.identifier: str(o) for o in field.answer.options.all() } elif isinstance(field, forms.BooleanField): answer_value = bool(field.answer.answer) else: answer_value = str(field.answer.answer) answer_dict = { 'field_name': k, 'field_label': str(field.label), 'value': answer_value, 'question_type': field.question.type, 'question_identifier': field.question.identifier, } if k in answers_key_to_index: prof.answers[ answers_key_to_index[k]] = answer_dict else: prof.answers.append(answer_dict) elif v != '' and v is not None: answer = QuestionAnswer( cartposition=(form.pos if isinstance( form.pos, CartPosition) else None), orderposition=(form.pos if isinstance( form.pos, OrderPosition) else None), question=field.question, ) try: self._save_to_answer(field, answer, v) answer.save() except IntegrityError: # Since we prefill ``field.answer`` at form creation time, there's a possible race condition # here if the users submits their save request a second time while the first one is still running, # thus leading to duplicate QuestionAnswer objects. Since Django doesn't support UPSERT, the "proper" # fix would be a transaction with select_for_update(), or at least fetching using get_or_create here # again. However, both of these approaches have a significant performance overhead for *all* requests, # while the issue happens very very rarely. So we opt for just catching the error and retrying properly. answer = QuestionAnswer.objects.get( cartposition=(form.pos if isinstance( form.pos, CartPosition) else None), orderposition=(form.pos if isinstance( form.pos, OrderPosition) else None), question=field.question, ) self._save_to_answer(field, answer, v) answer.save() if isinstance(field, forms.ModelMultipleChoiceField ) or isinstance( field, forms.ModelChoiceField): answer_value = { o.identifier: str(o) for o in answer.options.all() } elif isinstance(field, forms.BooleanField): answer_value = bool(answer.answer) else: answer_value = str(answer.answer) answer_dict = { 'field_name': k, 'field_label': str(field.label), 'value': answer_value, 'question_type': field.question.type, 'question_identifier': field.question.identifier, } if k in answers_key_to_index: prof.answers[ answers_key_to_index[k]] = answer_dict else: prof.answers.append(answer_dict) else: field = form.fields[k] meta_info.setdefault('question_form_data', {}) if v is None: if k in meta_info['question_form_data']: del meta_info['question_form_data'][k] else: meta_info['question_form_data'][k] = v answer_dict = { 'field_name': k, 'field_label': str(field.label), 'value': str(v), 'question_type': None, 'question_identifier': None, } if k in answers_key_to_index: prof.answers[answers_key_to_index[k]] = answer_dict else: prof.answers.append(answer_dict) form.pos.meta_info = json.dumps(meta_info) form.pos.save() if form.cleaned_data.get('save') and not failed: prof.save() self.cart_session[ f'saved_attendee_profile_{form.pos.pk}'] = prof.pk return not failed