class AnswerSerializer(HyperlinkedModelSerializer): creator = SlugRelatedField(read_only=True, slug_field="username") question = HyperlinkedRelatedField( view_name="api:reader-studies-question-detail", queryset=Question.objects.all(), ) images = HyperlinkedRelatedField(many=True, queryset=Image.objects.all(), view_name="api:image-detail") answer_image = HyperlinkedRelatedField(read_only=True, view_name="api:image-detail") def validate(self, attrs): answer = attrs.get("answer") if self.instance: if (not self.instance.question.reader_study. allow_answer_modification): raise ValidationError( "This reader study does not allow answer modification.") if list(attrs.keys()) != ["answer"]: raise ValidationError("Only the answer field can be modified.") question = self.instance.question images = self.instance.images.all() creator = self.instance.creator else: question = attrs.get("question") images = attrs.get("images") creator = self.context.get("request").user Answer.validate( creator=creator, question=question, answer=answer, images=images, instance=self.instance, ) if self.instance: on_commit(lambda: add_scores.apply_async( kwargs={ "instance_pk": str(self.instance.pk), "pk_set": list( map(str, images.values_list("pk", flat=True))), })) return attrs if not self.instance else {"answer": answer} class Meta: model = Answer fields = ( "answer", "api_url", "created", "creator", "images", "pk", "question", "modified", "answer_image", ) swagger_schema_fields = { "properties": { "answer": { "title": "Answer", **ANSWER_TYPE_SCHEMA } } }
class SubEventSerializer(I18nAwareModelSerializer): item_price_overrides = SubEventItemSerializer(source='subeventitem_set', many=True, required=False) variation_price_overrides = SubEventItemVariationSerializer( source='subeventitemvariation_set', many=True, required=False) seat_category_mapping = SeatCategoryMappingField(source='*', required=False) event = SlugRelatedField(slug_field='slug', read_only=True) meta_data = MetaDataField(source='*') class Meta: model = SubEvent fields = ('id', 'name', 'date_from', 'date_to', 'active', 'date_admission', 'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'event', 'is_public', 'frontpage_text', 'seating_plan', 'item_price_overrides', 'variation_price_overrides', 'meta_data', 'seat_category_mapping', 'last_modified') def validate(self, data): data = super().validate(data) event = self.context['request'].event full_data = self.to_internal_value( self.to_representation(self.instance)) if self.instance else {} full_data.update(data) Event.clean_dates(data.get('date_from'), data.get('date_to')) Event.clean_presale(data.get('presale_start'), data.get('presale_end')) SubEvent.clean_items( event, [item['item'] for item in full_data.get('subeventitem_set', [])]) SubEvent.clean_variations(event, [ item['variation'] for item in full_data.get('subeventitemvariation_set', []) ]) return data def validate_item_price_overrides(self, data): return list(filter(lambda i: 'item' in i, data)) def validate_variation_price_overrides(self, data): return list(filter(lambda i: 'variation' in i, data)) def validate_seating_plan(self, value): if value and value.organizer != self.context['request'].organizer: raise ValidationError('Invalid seating plan.') if self.instance and self.instance.pk: try: validate_plan_change(self.context['request'].event, self.instance, value) except SeatProtected as e: raise ValidationError(str(e)) return value def validate_seat_category_mapping(self, value): item_cache = { i.pk: i for i in self.context['request'].event.items.all() } result = {} for k, item in value['seat_category_mapping'].items(): if item not in item_cache: raise ValidationError( 'Item \'{id}\' does not exist.'.format(id=item)) result[k] = item_cache[item] return {'seat_category_mapping': result} @cached_property def meta_properties(self): return { p.name: p for p in self.context['request'].organizer.meta_properties.all() } def validate_meta_data(self, value): for key, v in value['meta_data'].items(): if key not in self.meta_properties: raise ValidationError( _('Meta data property \'{name}\' does not exist.').format( name=key)) if self.meta_properties[key].allowed_values: if v not in [ _v.strip() for _v in self.meta_properties[key].allowed_values.splitlines() ]: raise ValidationError( _('Meta data property \'{name}\' does not allow value \'{value}\'.' ).format(name=key, value=v)) return value @cached_property def ignored_meta_properties(self): perm_holder = (self.context['request'].auth if isinstance( self.context['request'].auth, (Device, TeamAPIToken)) else self.context['request'].user) if perm_holder.has_organizer_permission( 'can_change_organizer_settings', request=self.context['request']): return [] return [k for k, p in self.meta_properties.items() if p.protected] @transaction.atomic def create(self, validated_data): item_price_overrides_data = validated_data.pop( 'subeventitem_set' ) if 'subeventitem_set' in validated_data else {} variation_price_overrides_data = validated_data.pop( 'subeventitemvariation_set' ) if 'subeventitemvariation_set' in validated_data else {} meta_data = validated_data.pop('meta_data', None) seat_category_mapping = validated_data.pop('seat_category_mapping', None) subevent = super().create(validated_data) for item_price_override_data in item_price_overrides_data: SubEventItem.objects.create(subevent=subevent, **item_price_override_data) for variation_price_override_data in variation_price_overrides_data: SubEventItemVariation.objects.create( subevent=subevent, **variation_price_override_data) # Meta data if meta_data is not None: for key, value in meta_data.items(): if key not in self.ignored_meta_properties: subevent.meta_values.create( property=self.meta_properties.get(key), value=value) # Seats if subevent.seating_plan: if seat_category_mapping is not None: for key, value in seat_category_mapping.items(): self.context[ 'request'].event.seat_category_mappings.create( product=value, layout_category=key, subevent=subevent) generate_seats( self.context['request'].event, subevent, subevent.seating_plan, { m.layout_category: m.product for m in self.context['request'].event. seat_category_mappings.select_related('product').filter( subevent=subevent) }) return subevent @transaction.atomic def update(self, instance, validated_data): item_price_overrides_data = validated_data.pop('subeventitem_set', None) variation_price_overrides_data = validated_data.pop( 'subeventitemvariation_set', None) meta_data = validated_data.pop('meta_data', None) seat_category_mapping = validated_data.pop('seat_category_mapping', None) subevent = super().update(instance, validated_data) if item_price_overrides_data is not None: existing_item_overrides = { item.item: item.id for item in SubEventItem.objects.filter(subevent=subevent) } for item_price_override_data in item_price_overrides_data: id = existing_item_overrides.pop( item_price_override_data['item'], None) SubEventItem(id=id, subevent=subevent, **item_price_override_data).save() SubEventItem.objects.filter( id__in=existing_item_overrides.values()).delete() if variation_price_overrides_data is not None: existing_variation_overrides = { item.variation: item.id for item in SubEventItemVariation.objects.filter( subevent=subevent) } for variation_price_override_data in variation_price_overrides_data: id = existing_variation_overrides.pop( variation_price_override_data['variation'], None) SubEventItemVariation(id=id, subevent=subevent, **variation_price_override_data).save() SubEventItemVariation.objects.filter( id__in=existing_variation_overrides.values()).delete() # Meta data if meta_data is not None: current = { mv.property: mv for mv in subevent.meta_values.select_related('property') } for key, value in meta_data.items(): if key not in self.ignored_meta_properties: prop = self.meta_properties.get(key) if prop in current: current[prop].value = value current[prop].save() else: subevent.meta_values.create( property=self.meta_properties.get(key), value=value) for prop, current_object in current.items(): if prop.name not in self.ignored_meta_properties: if prop.name not in meta_data: current_object.delete() # Seats if seat_category_mapping is not None or ( 'seating_plan' in validated_data and validated_data['seating_plan'] is None): current_mappings = { m.layout_category: m for m in self.context['request'].event.seat_category_mappings. filter(subevent=subevent) } if not subevent.seating_plan: seat_category_mapping = {} for key, value in seat_category_mapping.items(): if key in current_mappings: m = current_mappings.pop(key) m.product = value m.save() else: self.context[ 'request'].event.seat_category_mappings.create( product=value, layout_category=key, subevent=subevent) for m in current_mappings.values(): m.delete() if 'seating_plan' in validated_data or seat_category_mapping is not None: generate_seats( self.context['request'].event, subevent, subevent.seating_plan, { m.layout_category: m.product for m in self.context['request'].event. seat_category_mappings.select_related('product').filter( subevent=subevent) }) return subevent