Example #1
0
    def update(self, instance, validated_data):
        """
        Override default update method to counteract race conditions.
        (See https://github.com/encode/django-rest-framework/issues/5897)
        :param instance:
        :param validated_data:
        :return:
        """
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)
        update_fields = []

        for attr, value in validated_data.items():
            update_fields.append(attr)
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        # if instance.variable_info and instance.epsilon \
        #         and instance.user_step == instance.DepositorSteps.STEP_0600_EPSILON_SET:
        #     instance.is_complete = True
        # else:
        #     instance.is_complete = False

        # This needs to be added to tell the save method to update the value.
        update_fields.append('is_complete')

        # Specifically for this model, we are overriding the update method with an explicit list of
        # update_fields, so we need to set the updated field manually.
        # All other models will be updated without this step due to the auto_now option from the parent class.
        instance.updated = timezone.now()
        instance.save(update_fields=update_fields)

        return instance
Example #2
0
    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)
        if 'name' in validated_data:
            model = Album
            name = validated_data['name']
            validated_data['slug'] = slug_generator(name, model)
        from_zone = pytz.timezone(TIME_ZONE)
        if not 'created_at' in validated_data:
            validated_data['created_at'] = instance.created_at
        else:
            validated_data['created_at'] = validated_data[
                'created_at'].astimezone(from_zone)
        if not 'updated_at' in validated_data:
            validated_data['updated_at'] = timezone.now()
        else:
            validated_data['updated_at'] = validated_data[
                'updated_at'].astimezone(from_zone)
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        instance.save()

        return instance
Example #3
0
    def equal_to_current(cls, json, fields_to_ignore=("id", "change_date", "changed_by")):
        """
        Compares for equality this instance to a model instance constructed from the supplied JSON.
        This will ignore any fields in `fields_to_ignore`.

        Note that this method cannot handle fields with many-to-many associations, as those can only
        be set on a saved model instance (and saving the model instance will create a new entry).
        All many-to-many field entries will be removed before the equality comparison is done.

        Args:
            json: json representing an entry to compare
            fields_to_ignore: List of fields that should not be compared for equality. By default
            includes `id`, `change_date`, and `changed_by`.

        Returns: True if the checked fields are all equivalent, else False
        """

        # Remove many-to-many relationships from json.
        # They require an instance to be already saved.
        info = model_meta.get_field_info(cls)
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in json):
                json.pop(field_name)

        new_instance = cls(**json)
        key_field_args = tuple(getattr(new_instance, key) for key in cls.KEY_FIELDS)
        current = cls.current(*key_field_args)
        # If current.id is None, no entry actually existed and the "current" method created it.
        if current.id is not None:
            return current.fields_equal(new_instance, fields_to_ignore)

        return False
    def create(self, validated_attrs):
        # Check that the user isn't trying to handle a writable nested field.
        # If we don't do this explicitly they'd likely get a confusing
        # error at the point of calling `Model.objects.create()`.
        assert not any(
            isinstance(field, BaseSerializer) and not field.read_only
            for field in self.fields.values()
        ), (
            'The `.create()` method does not suport nested writable fields '
            'by default. Write an explicit `.create()` method for serializer '
            '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
            (self.__class__.__module__, self.__class__.__name__)
        )

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_attrs.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_attrs):
                many_to_many[field_name] = validated_attrs.pop(field_name)

        instance = ModelClass.objects.create(**validated_attrs)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
Example #5
0
    def _filter_fields_by_permissions(self, fields, permissions_kind):
        """
        Filter serializer fields by permissions kind
        :param fields: serializer fields list
        :param permissions_kind: edit/view
        :return: fields allowed to interact with
        """
        model = self.Meta.model
        targets_map = {
            Permission.get_target(model, field): field
            for field in fields
        }

        pk_fields = []
        pk_target = Permission.get_target(model, 'pk')
        if pk_target in targets_map:
            pk_fields.append(targets_map.pop(pk_target))

        pk_field = model_meta.get_field_info(model).pk
        pk_target = Permission.get_target(model, pk_field)
        if pk_target in targets_map:
            pk_fields.append(targets_map.pop(pk_target))

        allowed_targets = Permission.apply_permissions(self.permissions,
                                                       targets_map.keys(),
                                                       permissions_kind)

        allowed_fields = list(
            map(lambda target: targets_map[target], allowed_targets))

        if allowed_fields:
            allowed_fields.extend(pk_fields)

        return allowed_fields
Example #6
0
    def update(self, instance, validated_data):
        # 修改商品数量
        nums = validated_data["nums"]
        if instance.goods.online and nums != 1:
            raise serializers.ValidationError({'nums': '线上课程数目只能为1'})
        else:
            from rest_framework.utils import model_meta
            info = model_meta.get_field_info(instance)
            # Simply set each attribute on the instance, and then save it.
            # Note that unlike `.create()` we don't need to treat many-to-many
            # relationships as being a special case. During updates we already
            # have an instance pk for the relationships to be associated with.
            m2m_fields = []
            for attr, value in validated_data.items():
                if attr in info.relations and info.relations[attr].to_many:
                    m2m_fields.append((attr, value))
                else:
                    setattr(instance, attr, value)

            instance.save()

            # Note that many-to-many fields are set after updating instance.
            # Setting m2m fields triggers signals which could potentially change
            # updated instance and we do not want it to collide with .update()
            for attr, value in m2m_fields:
                field = getattr(instance, attr)
                field.set(value)

        return instance
    def create(self, validated_attrs):
        # Check that the user isn't trying to handle a writable nested field.
        # If we don't do this explicitly they'd likely get a confusing
        # error at the point of calling `Model.objects.create()`.
        assert not any(
            isinstance(field, BaseSerializer) and not field.read_only
            for field in self.fields.values()
        ), ('The `.create()` method does not suport nested writable fields '
            'by default. Write an explicit `.create()` method for serializer '
            '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
            (self.__class__.__module__, self.__class__.__name__))

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_attrs.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_attrs):
                many_to_many[field_name] = validated_attrs.pop(field_name)

        instance = ModelClass.objects.create(**validated_attrs)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
Example #8
0
    def _build_field(self):
        model = getattr(self.parent.Meta, 'model')
        depth = getattr(self.parent.Meta, 'depth', 0)
        info = model_meta.get_field_info(model)

        # Determine any extra field arguments and hidden fields that
        # should be included
        extra_kwargs = self.parent.get_extra_kwargs()
        extra_kwargs.update(self._kwargs)
        extra_kwargs, hidden_fields = self.parent.get_uniqueness_extra_kwargs(
            [self.field_name], [self], extra_kwargs
        )
        extra_field_kwargs = {
            key: value for key, value in self._kwargs.items()
            if key not in ['read_field']
        }

        # Determine the serializer field class and keyword arguments.
        field_class, field_kwargs = self.parent.build_field(
            self.field_name, info, model, depth
        )

        # Include any kwargs defined in `Meta.extra_kwargs`
        extra_field_kwargs.update(
            extra_kwargs.get(self.field_name, {})
        )
        field_kwargs = self.parent.include_extra_kwargs(
            field_kwargs, extra_field_kwargs
        )

        # Create the serializer field.
        return field_class(**field_kwargs)
    def create(self, validated_data):
        """Override of create method."""
        """
        We have a bit of extra checking around this in order to provide
        descriptive messages when something goes wrong, but this method is
        essentially just:
            return ExampleModel.objects.create(**validated_data)
        If there are many to many fields present on the instance then they
        cannot be set until the model is instantiated, in which case the
        implementation is like so:
            example_relationship = validated_data.pop('example_relationship')
            instance = ExampleModel.objects.create(**validated_data)
            instance.example_relationship = example_relationship
            return instance
        The default implementation also does not handle nested relationships.
        If you want to support writable nested relationships you'll need
        to write an explicit `.create()` method.
        """
        raise_errors_on_nested_writes("create", self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            for request_tenant in schema_handler(
                    self.context["request"].tenant):
                instance = ModelClass._default_manager.create(
                    **validated_data, tenant=request_tenant)
        except TypeError:
            tb = traceback.format_exc()
            msg = ("Got a `TypeError` when calling `%s.%s.create()`. "
                   "This may be because you have a writable field on the "
                   "serializer class that is not a valid argument to "
                   "`%s.%s.create()`. You may need to make the field "
                   "read-only, or override the %s.create() method to handle "
                   "this correctly.\nOriginal exception was:\n %s" % (
                       ModelClass.__name__,
                       ModelClass._default_manager.name,
                       ModelClass.__name__,
                       ModelClass._default_manager.name,
                       self.__class__.__name__,
                       tb,
                   ))
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance
Example #10
0
    def update(self, instance, validated_data):
        info = model_meta.get_field_info(instance)
        if validated_data.get('tags'):
            tags = validated_data.pop('tags')
            instance.tags = []
            if len(tags) > 0:
                for tag in tags:
                    new_tag, created = tag_model.objects.get_or_create(
                        name=tag["name"])
                    instance.tags.add(new_tag)
                instance.save()
        # We shouldn't use this endpoint to update images.
        # This popping is just to make sure we dont get any internal errors
        # when clients sends images in update.
        if validated_data.get('images'):
            validated_data.pop('images')
        # We shouldn't use this endpoint to update comments.
        # This popping is just to make sure we dont get any internal errors
        # when clients sends comments in update.
        if validated_data.get('comments'):
            validated_data.pop('comments')
        # Simply set each attribute on the instance, and then save it.
        # Note that unlike `.create()` we don't need to treat many-to-many
        # relationships as being a special case. During updates we already
        # have an instance pk for the relationships to be associated with.
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        instance.save()

        return instance
Example #11
0
 def validate(self, attrs):
     # Since DRF 3.0 the model clean() method is no longer called
     # automatically. And this is probably not the best solution to do the
     # validation, but at the moment it's the simplest one.
     if self.instance is None:
         lsr = attrs.pop('lsr', None)
         instance = WebHookMapping(**attrs)
         instance.clean()
         if lsr is not None:
             attrs['lsr'] = lsr
     else:
         info = model_meta.get_field_info(self.instance)
         for attr, value in attrs.items():
             if attr == 'lsr':
                 # lsr can not be set directly at the moment, so skip it
                 continue
             if attr in info.relations and info.relations[attr].to_many:
                 # We don't have any to-many relations at the moment, but
                 # this is to avoid setting them in the future if they are
                 # added. Manipulating to-many relation directly changes
                 # the DB so it can't be done here.
                 continue
             else:
                 setattr(self.instance, attr, value)
             self.instance.clean()
     return attrs
Example #12
0
    def _create(self, validated_data):
        validated_data = validated_data.copy()  # We're going to pop stuff off here.
        # This logic is mostly copied from super().create(),
        # aside from the concrete_data stuff and `.clean()`ing the model before saving.
        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        # Build a dict of fields we're guaranteed to be able to pass to `Model.create()`
        concrete_fields = set(info.fields) | set(info.forward_relations)
        concrete_data = {
            field: value
            for (field, value)
            in validated_data.items()
            if field in concrete_fields
        }
        instance = ModelClass(**concrete_data)
        instance.clean()
        instance.save()
        for field_name, value in many_to_many.items():
            setattr(instance, field_name, value)
        return instance
Example #13
0
    def create(self, validated_data):
        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError as exc:
            msg = (
                "Got a `TypeError` when calling `%s.objects.create()`. "
                "This may be because you have a writable field on the "
                "serializer class that is not a valid argument to "
                "`%s.objects.create()`. You may need to make the field "
                "read-only, or override the %s.create() method to handle "
                "this correctly.\nOriginal exception text was: %s."
                % (ModelClass.__name__, ModelClass.__name__, self.__class__.__name__, exc)
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                if field_name == "permissions":
                    value = self._get_or_create_permissons(value)
                setattr(instance, field_name, value)

        return instance
Example #14
0
    def create(self, validated_data):
        ModelClass = self.Meta.model

        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError as exc:
            msg = ("Got a `TypeError` when calling `%s.objects.create()`. "
                   "This may be because you have a writable field on the "
                   "serializer class that is not a valid argument to "
                   "`%s.objects.create()`. You may need to make the field "
                   "read-only, or override the %s.create() method to handle "
                   "this correctly.\nOriginal exception text was: %s." %
                   (ModelClass.__name__, ModelClass.__name__,
                    self.__class__.__name__, exc))
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
Example #15
0
    def update(self, instance, validated_data):
        """
        Default `.update()` method, but with overwritten friends' m2m assignment and return object annotation
        :param instance:
        :param validated_data:
        :return:
        """
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)

        m2m_fields = []
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                m2m_fields.append((attr, value))
            else:
                setattr(instance, attr, value)

        instance.save()

        for attr, value in m2m_fields:
            field = getattr(instance, attr)
            if attr == 'friends':
                field.set(value, through_defaults={'created_at': timezone.now()})

        return self.context['view'].get_object()
Example #16
0
    def update(self, instance, validated_data):
        if 'params' in validated_data:
            answer_ids_new = []
            answer_ids_pre = App_Param.objects.all().filter(app=instance).values_list('id', flat=True)
            for param in validated_data.pop('params'):
                isSet = param.pop("isSet",None)
                id = param.pop("id",None)
                param["app"]=instance
                ans, _created = App_Param.objects.update_or_create(id=id, defaults={**param})
                # if _created:
                #     ans.app = instance
                #     ans.save()
                answer_ids_new.append(ans.id)

            delete_ids = set(answer_ids_pre) - set(answer_ids_new)
            App_Param.objects.filter(id__in=delete_ids).delete()

        from rest_framework.utils import model_meta
        info = model_meta.get_field_info(instance)
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
Example #17
0
    def _filter_fields_by_permissions(self, fields, permissions_kind):
        """
        Filter serializer fields by permissions kind
        :param fields: serializer fields list
        :param permissions_kind: edit/view
        :return: fields allowed to interact with
        """
        model = self.Meta.model
        targets_map = {Permission.get_target(model, field): field for field in fields}

        pk_fields = []
        pk_target = Permission.get_target(model, 'pk')
        if pk_target in targets_map:
            pk_fields.append(targets_map.pop(pk_target))

        pk_field = model_meta.get_field_info(model).pk
        pk_target = Permission.get_target(model, pk_field)
        if pk_target in targets_map:
            pk_fields.append(targets_map.pop(pk_target))

        allowed_targets = Permission.apply_permissions(self.permissions, targets_map.keys(), permissions_kind)

        allowed_fields = list(map(lambda target: targets_map[target], allowed_targets))

        if allowed_fields:
            allowed_fields.extend(pk_fields)

        return allowed_fields
    def update(self, instance, validated_data):
        # print('查看auth_permissions:', validated_data.get('auth_permissions'))
        if validated_data.get('auth_permissions'):
            auth_permissions_data = validated_data.pop('auth_permissions')
            # 修改时创建权限菜单的方法
            need_dels = AuthPermission.objects.filter(auth_id=instance.id)
            for item in need_dels:
                item.delete()
            for item in auth_permissions_data:
                # print('查看:', item)
                # print('查看id:', item.get('id'))
                AuthPermission.objects.create(auth=instance, **item)
            # 开多线程优化代码
            # del_work = threading.Thread(target=del_worker,args=(need_dels,))
            # del_work.start()
            # save_work = threading.Thread(target=save_worker,args=(instance,auth_permissions_data,))
            # save_work.start()
            # save_work.join()

        # 继承自父类的方法
        info = model_meta.get_field_info(instance)
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
Example #19
0
    def update_id_fields(self, fields):
        if 'id' not in self.fields:
            conf = get_ct(self.Meta.model).get_config() or {}
            lookup = conf.get('lookup', None)
            if lookup and lookup != 'id':
                fields['id'] = serializers.ReadOnlyField(source=lookup)

        info = model_meta.get_field_info(self.Meta.model)

        # In list views, remove [fieldname] as an attribute in favor of
        # [fieldname]_id.
        for name, field in info.forward_relations.items():
            include = getattr(self.Meta, "fields", [])
            exclude = getattr(self.Meta, "exclude", [])
            if name in exclude or (include and name not in include):
                fields.pop(name, None)
                continue

            id_field, id_field_kwargs = self.build_relational_field(
                name, field)
            id_field_kwargs['source'] = name
            fields[name + '_id'] = id_field(**id_field_kwargs)

            if name in fields:
                # Update/remove DRF default foreign key field (w/o _id)
                if self.is_detail and isinstance(fields[name],
                                                 serializers.Serializer):
                    # Nested representation, keep for detail template context
                    fields[name].read_only = True
                else:
                    # Otherwise we don't need this field
                    del fields[name]

        return fields
    def create(self, validated_data):
        ModelClass = self.Meta.model

        # Remove to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError as exc:
            msg = ('Got a `TypeError` when calling `%s.objects.create()`. '
                   'This may be because you have a writable field on the '
                   'serializer class that is not a valid argument to '
                   '`%s.objects.create()`. You may need to make the field '
                   'read-only, or override the %s.create() method to handle '
                   'this correctly.\nOriginal exception text was: %s.' %
                   (ModelClass.__name__, ModelClass.__name__,
                    self.__class__.__name__, exc))
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if to_many:
            for field_name, value in to_many.items():
                related_field = getattr(instance, field_name)
                for record in value:
                    related_field.create(**record)

        return instance
Example #21
0
    def _configure_sideloads(self, meta):
        """
        Assemble configuration for each sideload.
        """
        self.sideloads = []
        configs = []
        for conf in getattr(meta, 'sideloads', []):
            assert isinstance(conf, tuple) and len(conf) >= 2 \
                and len(conf) <= 3, (
                '`Meta.sideloads` must be a list of tuples in the following '
                'format: (<model class>, <serializer class>, '
                '<queryset instance (optional)>)'
            )
            model, serializer = conf[:2]
            queryset = conf[0].objects.all() if (len(conf) == 2) else conf[2]
            configs.append((model, serializer, queryset))

        relations = get_field_info(self.model).relations
        fields = self.base_serializer.fields.values()
        for field_name, info in relations.items():
            try:
                conf = configs[[t[0] for t in configs].index(info.related)]
            except ValueError:
                continue
            field = fields[[f.source for f in fields].index(field_name)]
            key_name = getattr(conf[1].Meta, 'base_key',
                               underscore(conf[0].__name__))
            self.sideloads.append(Sideload(
                field=field, model=conf[0], serializer=conf[1],
                queryset=conf[2], key_name=pluralize(key_name)
            ))
    def create(self, validated_data):

        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)
        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError:
            tb = traceback.format_exc()
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception was:\n %s' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    tb
                )
            )
            raise TypeError(msg)

        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance
Example #23
0
    def _validate_model_clean(self, attrs):
        """
        Run validation using model's clean method.
        """
        data = attrs.copy()
        ModelClass = self.Meta.model
        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the model initializer.
        info = model_meta.get_field_info(ModelClass)
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in data):
                data.pop(field_name)

        # remove tags field
        self._pop_tags(data)

        # self.instance is set in case of update (PATCH/PUT) call
        # otherwise create new model instance
        instance = self.instance or ModelClass()
        for k, v in data.items():
            setattr(instance, k, v)
        try:
            instance.clean()
        except DjangoValidationError as e:
            raise self._django_validation_error_to_drf_validation_error(e)
        self._extra_instance_validation(instance)
Example #24
0
    def update_id_fields(self, fields):
        if 'id' not in self.fields:
            conf = get_ct(self.Meta.model).get_config() or {}
            lookup = conf.get('lookup', None)
            if lookup and lookup != 'id':
                fields['id'] = serializers.ReadOnlyField(source=lookup)

        info = model_meta.get_field_info(self.Meta.model)

        # In list views, remove [fieldname] as an attribute in favor of
        # [fieldname]_id.
        for name, field in info.forward_relations.items():
            include = getattr(self.Meta, "fields", [])
            exclude = getattr(self.Meta, "exclude", [])
            if name in exclude or (include and name not in include):
                fields.pop(name, None)
                continue

            id_field, id_field_kwargs = self.build_relational_field(
                name, field
            )
            id_field_kwargs['source'] = name
            fields[name + '_id'] = id_field(**id_field_kwargs)

            if name in fields:
                # Update/remove DRF default foreign key field (w/o _id)
                if self.is_detail and isinstance(
                        fields[name], serializers.Serializer):
                    # Nested representation, keep for detail template context
                    fields[name].read_only = True
                else:
                    # Otherwise we don't need this field
                    del fields[name]

        return fields
Example #25
0
def _get_filter_strings_for_model(model, depth, exclude=[]):
    info = model_meta.get_field_info(model)
    # Add base fields
    fields = info.fields.keys()
    if depth > 0:
        depth -= 1
        # Add related fields one level deep
        for field_name, rel in info.relations.iteritems():
            # Will recursively add related fields according to value of `depth`
            rel_field_names = [field_name + "__" + rel_field_name for rel_field_name in _get_filter_strings_for_model(rel.related_model, depth, exclude=exclude)]
            rel_field_names.append(field_name)
            fields += rel_field_names
    # filters_for_model is used by django_filters to determine
    # if fields we request to filter on are valid. So use that
    # to silently pass out any fields which django_filters will
    # complain about.
    all_filters = filters_for_model(
        model,
        fields=fields,
        exclude=exclude,
        filter_for_field=FilterSet.filter_for_field,
        filter_for_reverse_field=FilterSet.filter_for_reverse_field
    )
    tmp_fields = []
    for f in fields:
        if all_filters.get(f, False) == None:
            continue
        tmp_fields.append(f)
    fields = tmp_fields
    return fields
 def _build_standard_proto_type(self, model_field):
     if model_field.one_to_one and model_field.primary_key:
         info = model_meta.get_field_info(model_field.related_model)
         return self.build_proto_type(info.pk.name, info,
                                      model_field.related_model)
     else:
         return self.type_mapping[model_field]
Example #27
0
    def _compare_objects(self, db_obj, api_obj, serializer=None):
        """
        Compare two objects with each other based on the fields of the API serializer.
        """
        serializer = serializer if serializer else self.serializer_cls()
        serializer_field_list = serializer.get_field_names(
            serializer._declared_fields,
            model_meta.get_field_info(self.model_cls)
        )

        model_field_list = [f.name for f in self.model_cls._meta.get_fields()]

        for field in serializer_field_list:
            if field in model_field_list and not field:
                # Make sure the field is in the response.
                self.assertIn(field, api_obj)

                db_value = getattr(db_obj, field)
                api_value = api_obj.get(field)

                db_value = self._transform_value(db_value)

                if isinstance(db_value, Model) or isinstance(db_value, Manager):
                    # Relationships can't be checked generically
                    continue

                # Make sure the field value matches that of the factory object.
                self.assertEqual(api_value, db_value)
Example #28
0
    def _configure_sideloads(self, meta):
        """
        Assemble configuration for each sideload.
        """
        self.sideloads = []
        configs = []
        for conf in getattr(meta, 'sideloads', []):
            assert isinstance(conf, tuple) and len(conf) >= 2 \
                and len(conf) <= 3, (
                '`Meta.sideloads` must be a list of tuples in the following '
                'format: (<model class>, <serializer class>, '
                '<queryset instance (optional)>)'
            )
            model, serializer = conf[:2]
            queryset = conf[0].objects.all() if (len(conf) == 2) else conf[2]
            configs.append((model, serializer, queryset))

        relations = get_field_info(self.model).relations
        fields = self.base_serializer.fields.values()
        for field_name, relation_info in relations.items():
            try:
                related_model = compat.get_related_model(relation_info)
                conf = configs[[t[0] for t in configs].index(related_model)]
            except ValueError:
                continue
            field = fields[[f.source for f in fields].index(field_name)]
            key_name = getattr(conf[1].Meta, 'base_key',
                               underscore(conf[0].__name__))
            self.sideloads.append(
                Sideload(field=field,
                         model=conf[0],
                         serializer=conf[1],
                         queryset=conf[2],
                         key_name=pluralize(key_name)))
Example #29
0
    def create(self, validated_data):
        # To ensure caution, require nested_writes to be explicitly allowed
        if not (hasattr(self.Meta, "nested_writes") and self.Meta.nested_writes):
            raise_errors_on_nested_writes("create", self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                raise ValueError(
                    "Many to many fields must be explicitly handled", field_name
                )
            elif not relation_info.reverse and (field_name in validated_data):
                if not isinstance(
                    validated_data[field_name], relation_info.related_model
                ):
                    # Trying to set a foreign key but do not have the object, only the key
                    validated_data[
                        relation_info.model_field.attname
                    ] = validated_data.pop(field_name)

        instance = ModelClass(**validated_data)

        if hasattr(instance, "on_create") and callable(instance.on_create):
            instance.on_create()

        if not getattr(self, "parent", False):
            instance.save()

        return instance
Example #30
0
    def _writable_fields(self):
        fields = super(PermissionsBasedSerializerMixin, self)._writable_fields

        allowed_permissions = filter(
            lambda p:
            p.permission == self.Meta.permission_class.PERMISSIONS.edit and
            p.permission_type == self.Meta.permission_class.TYPES.allow,
            self.permissions
        )
        disallowed_permissions = filter(
            lambda p:
            p.permission in [self.Meta.permission_class.PERMISSIONS.edit,
                             self.Meta.permission_class.PERMISSIONS.view] and
            p.permission_type == self.Meta.permission_class.TYPES.disallow,
            self.permissions
        )

        allowed_fields_names = [p.target.split('.')[-1] for p in allowed_permissions]
        disallowed_fields_names = [p.target.split('.')[-1] for p in disallowed_permissions]
        # PK allowed be default
        if allowed_fields_names:
            model = self.Meta.model
            info = model_meta.get_field_info(model)
            allowed_fields_names.extend(['pk', info.pk.name])

        if '*' in allowed_fields_names:
            filtered_fields = fields
        else:
            filtered_fields = filter(lambda f: f.field_name in allowed_fields_names, fields)

        filtered_fields = filter(lambda f: f.field_name not in disallowed_fields_names, filtered_fields)

        return list(filtered_fields)
Example #31
0
    def _compare_objects(self, db_obj, api_obj, serializer=None):
        """
        Compare two objects with eachother based on the fields of the API serializer.
        """
        serializer = serializer if serializer else self.serializer_cls()
        serializer_field_list = serializer.get_field_names(
            serializer._declared_fields,
            model_meta.get_field_info(self.model_cls)
        )
        model_field_list = self.model_cls._meta.get_all_field_names()

        for field in serializer_field_list:
            if field in model_field_list:
                # Make sure the field is in the response
                self.assertIn(field, api_obj)

                db_value = getattr(db_obj, field)
                api_value = api_obj.get(field)

                db_value = self._transform_value(db_value)

                if isinstance(db_value, Model) or isinstance(db_value, Manager):
                    # Relationships can't be checked generically
                    continue

                # Make sure the field value matches that of the factory object
                self.assertEqual(api_value, db_value)
    def _create(self, validated_data):
        validated_data = validated_data.copy(
        )  # We're going to pop stuff off here.
        # This logic is mostly copied from super().create(),
        # aside from the concrete_data stuff and `.clean()`ing the model before saving.
        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        # Build a dict of fields we're guaranteed to be able to pass to `Model.create()`
        concrete_fields = set(info.fields) | set(info.forward_relations)
        concrete_data = {
            field: value
            for (field, value) in validated_data.items()
            if field in concrete_fields
        }
        instance = ModelClass(**concrete_data)
        instance.clean()
        instance.save()
        for field_name, value in many_to_many.items():
            setattr(instance, field_name, value)
        return instance
Example #33
0
    def update(self, instance, validated_data):
        new_goods = []
        if 'goods' in validated_data:
            ids_new = []
            ids_pre = StudentGoods.objects.all().filter(student=instance).values_list('id', flat=True)
            for param in validated_data.pop('goods'):
                param["student"] = instance
                id = param.pop("id", None)
                ans, _created = StudentGoods.objects.update_or_create(id=id, defaults={**param})
                ids_new.append(ans.id)
                new_goods.append(ans)

            delete_ids = set(ids_pre) - set(ids_new)
            StudentGoods.objects.filter(id__in=delete_ids).delete()

        from rest_framework.utils import model_meta
        info = model_meta.get_field_info(instance)
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
Example #34
0
    def update(self, instance: Employee, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)

        # Simply set each attribute on the instance, and then save it.
        # Note that unlike `.create()` we don't need to treat many-to-many
        # relationships as being a special case. During updates we already
        # have an instance pk for the relationships to be associated with.
        m2m_fields = []
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                m2m_fields.append((attr, value))
            else:
                setattr(instance, attr, value)
                # instance.title='工程师'
                # setattr(instance,'title','工程师')
        with transaction.atomic():
            instance.save()
            instance.salary.basesalary = validated_data.get('basesalary')
            instance.salary.titlesalary = validated_data.get('titlesalary')
            instance.salary.save()
        # Note that many-to-many fields are set after updating instance.
        # Setting m2m fields triggers signals which could potentially change
        # updated instance and we do not want it to collide with .update()
        for attr, value in m2m_fields:
            field = getattr(instance, attr)
            field.set(value)

        return instance
Example #35
0
    def pk_field(self):
        if self._pk_field:
            return self._pk_field

        assert hasattr(self, 'Meta'), (
            'Class {serializer_class} missing "Meta" attribute'.format(
                serializer_class=self.__class__.__name__))
        assert hasattr(self.Meta, 'model'), (
            'Class {serializer_class} missing "Meta.model" attribute'.format(
                serializer_class=self.__class__.__name__))
        if model_meta.is_abstract_model(self.Meta.model):
            raise ValueError(
                'Cannot use ModelSerializer with Abstract Models.')

        model = self.Meta.model
        info = model_meta.get_field_info(model)

        if 'pk' in self.fields:
            self._pk_field = self.fields['pk']
            return self._pk_field

        if info.pk.name in self.fields:
            self._pk_field = self.fields[info.pk.name]
            return self._pk_field

        assert False, 'Serializer {serializer_class} doesn\'t contain primary key field. ' \
                      'Add `pk` or `{pk_name}` to fields attribute.'.format(serializer_class=self.__class__.__name__,
                                                                            pk_name=info.pk.name)
Example #36
0
    def update(self, instance, validated_data):
        if validated_data["order_pay"] == 3:
            # 待退款
            time_obj = Time.objects.filter(
                time_id=validated_data["order_timeid"].time_id).first()
            time_obj.time_surplus += 1
            time_obj.save()

        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)

        m2m_fields = []
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                m2m_fields.append((attr, value))
            else:
                setattr(instance, attr, value)

        instance.save()

        for attr, value in m2m_fields:
            field = getattr(instance, attr)
            field.set(value)

        return instance
Example #37
0
    def _map_model_field(self, model_field, direction):
        assert isinstance(model_field, models.Field)
        # to get a fully initialized serializer field we use DRF's own init logic
        try:
            field_cls, field_kwargs = serializers.ModelSerializer().build_field(
                field_name=model_field.name,
                info=get_field_info(model_field.model),
                model_class=model_field.model,
                nested_depth=0,
            )
            field = field_cls(**field_kwargs)
        except:  # noqa
            field = None

        # For some cases, the DRF init logic either breaks (custom field with internal type) or
        # the resulting field is underspecified with regards to the schema (ReadOnlyField).
        if field and not anyisinstance(field, [serializers.ReadOnlyField, serializers.ModelField]):
            return self._map_serializer_field(field, direction)
        elif isinstance(model_field, models.ForeignKey):
            return self._map_model_field(model_field.target_field, direction)
        elif hasattr(models, model_field.get_internal_type()):
            # be graceful when the model field is not explicitly mapped to a serializer
            internal_type = getattr(models, model_field.get_internal_type())
            field_cls = serializers.ModelSerializer.serializer_field_mapping[internal_type]
            return self._map_serializer_field(field_cls(), direction)
        else:
            error(
                f'could not resolve model field "{model_field}". failed to resolve through '
                f'serializer_field_mapping, get_internal_type(), or any override mechanism. '
                f'defaulting to "string"'
            )
            return build_basic_type(OpenApiTypes.STR)
Example #38
0
    def get_initial(self):
        """
        Construct initial data for the serializer.
        Use the 'default' values specified by the django model definition
        """

        initials = super().get_initial().copy()

        # Are we creating a new instance?
        if self.instance is None:
            ModelClass = self.Meta.model

            fields = model_meta.get_field_info(ModelClass)

            for field_name, field in fields.fields.items():

                if field.has_default() and field_name not in initials:

                    value = field.default

                    # Account for callable functions
                    if callable(value):
                        try:
                            value = value()
                        except:
                            continue

                    initials[field_name] = value

        return initials
Example #39
0
    def equal_to_current(cls,
                         json,
                         fields_to_ignore=("id", "change_date", "changed_by")):
        """
        Compares for equality this instance to a model instance constructed from the supplied JSON.
        This will ignore any fields in `fields_to_ignore`.

        Note that this method cannot handle fields with many-to-many associations, as those can only
        be set on a saved model instance (and saving the model instance will create a new entry).
        All many-to-many field entries will be removed before the equality comparison is done.

        Args:
            json: json representing an entry to compare
            fields_to_ignore: List of fields that should not be compared for equality. By default
            includes `id`, `change_date`, and `changed_by`.

        Returns: True if the checked fields are all equivalent, else False
        """

        # Remove many-to-many relationships from json.
        # They require an instance to be already saved.
        info = model_meta.get_field_info(cls)
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in json):
                json.pop(field_name)

        new_instance = cls(**json)
        key_field_args = tuple(
            getattr(new_instance, key) for key in cls.KEY_FIELDS)
        current = cls.current(*key_field_args)
        # If current.id is None, no entry actually existed and the "current" method created it.
        if current.id is not None:
            return current.fields_equal(new_instance, fields_to_ignore)

        return False
    def create(self, validated_data):
        """
        We have a bit of extra checking around this in order to provide
        descriptive messages when something goes wrong, but this method is
        essentially just:

            return ExampleModel.objects.create(**validated_data)

        If there are many to many fields present on the instance then they
        cannot be set until the model is instantiated, in which case the
        implementation is like so:

            example_relationship = validated_data.pop('example_relationship')
            instance = ExampleModel.objects.create(**validated_data)
            instance.example_relationship = example_relationship
            return instance

        The default implementation also does not handle nested relationships.
        If you want to support writable nested relationships you'll need
        to write an explicit `.create()` method.
        """
        raise_errors_on_nested_writes('create', self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError as exc:
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception text was: %s.' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    exc
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
Example #41
0
 def perform_update(self, serializer):
     ModelClass = serializer.Meta.model
     field_info = model_meta.get_field_info(ModelClass)
     kwargs = {}
     if 'last_updater' in field_info.relations:
         kwargs['last_updater'] = self.request.user
     instance = serializer.save(**kwargs)
     if self._should_log_activity(instance):
         self.make_log_activity(instance, CHANGE)
Example #42
0
 def update(self, instance, validated_data):
     #This is the same implementation as in ModelSerializer, but minus the assertion about nested writes!
     info = model_meta.get_field_info(instance)
     for attr, value in validated_data.items():
         if attr in info.relations and info.relations[attr].to_many:
             field = getattr(instance, attr)
             field.set(value)
         else:
             setattr(instance, attr, value)
     instance.save()
     return instance
Example #43
0
 def build_natural_key_fields(self):
     info = model_meta.get_field_info(self.Meta.model)
     fields = OrderedDict()
     for field, relation_info in info.relations.items():
         if not issubclass(relation_info.related_model, NaturalKeyModel):
             continue
         field_class, field_kwargs = self.build_nested_field(
             field, relation_info, 1
         )
         fields[field] = field_class(**field_kwargs)
     return fields
    def get_validators(self):
        # If the validators have been declared explicitly then use that.
        validators = getattr(getattr(self, 'Meta', None), 'validators', None)
        if validators is not None:
            return validators

        # Determine the default set of validators.
        validators = []
        model_class = self.Meta.model
        field_names = set([
            field.source for field in self.fields.values()
            if (field.source != '*') and ('.' not in field.source)
        ])

        # Note that we make sure to check `unique_together` both on the
        # base model class, but also on any parent classes.
        for parent_class in [model_class] + list(model_class._meta.parents.keys()):
            for unique_together in parent_class._meta.unique_together:
                if field_names.issuperset(set(unique_together)):
                    validator = UniqueTogetherValidator(
                        queryset=parent_class._default_manager,
                        fields=unique_together
                    )
                    validators.append(validator)

        # Add any unique_for_date/unique_for_month/unique_for_year constraints.
        info = model_meta.get_field_info(model_class)
        for field_name, field in info.fields_and_pk.items():
            if field.unique_for_date and field_name in field_names:
                validator = UniqueForDateValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_date
                )
                validators.append(validator)

            if field.unique_for_month and field_name in field_names:
                validator = UniqueForMonthValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_month
                )
                validators.append(validator)

            if field.unique_for_year and field_name in field_names:
                validator = UniqueForYearValidator(
                    queryset=model_class._default_manager,
                    field=field_name,
                    date_field=field.unique_for_year
                )
                validators.append(validator)

        return validators
Example #45
0
 def perform_create(self, serializer):
     ModelClass = serializer.Meta.model
     field_info = model_meta.get_field_info(ModelClass)
     kwargs = {}
     kwargs['creator'] = self.request.user
     if 'last_updater' in field_info.relations:
         kwargs['last_updater'] = self.request.user
     if 'project' in field_info.relations:
         kwargs['project'] = self.request.project
     instance = serializer.save(**kwargs)
     if self._should_log_activity(instance):
         self.make_log_activity(instance, ADDITION)
    def create(self, validated_attrs):
        """
        We have a bit of extra checking around this in order to provide
        descriptive messages when something goes wrong, but this method is
        essentially just:

            return ExampleModel.objects.create(**validated_attrs)

        If there are many to many fields present on the instance then they
        cannot be set until the model is instantiated, in which case the
        implementation is like so:

            example_relationship = validated_attrs.pop('example_relationship')
            instance = ExampleModel.objects.create(**validated_attrs)
            instance.example_relationship = example_relationship
            return instance

        The default implementation also does not handle nested relationships.
        If you want to support writable nested relationships you'll need
        to write an explicit `.create()` method.
        """
        # Check that the user isn't trying to handle a writable nested field.
        # If we don't do this explicitly they'd likely get a confusing
        # error at the point of calling `Model.objects.create()`.
        assert not any(
            isinstance(field, BaseSerializer) and not field.read_only
            for field in self.fields.values()
        ), (
            'The `.create()` method does not suport nested writable fields '
            'by default. Write an explicit `.create()` method for serializer '
            '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
            (self.__class__.__module__, self.__class__.__name__)
        )

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_attrs.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_attrs):
                many_to_many[field_name] = validated_attrs.pop(field_name)

        instance = ModelClass.objects.create(**validated_attrs)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
    def create(self, validated_data):

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}

        # Save off the data
        for key, field in self.fields.items():
            if isinstance(field, serializers.BaseSerializer):
                if isinstance(validated_data.get(key), list):
                    # One-to-many...
                    nested_data = validated_data.pop(key)
                    many_to_many[key] = field.create(nested_data)

                elif isinstance(validated_data.get(key), dict):
                    import pdb
                    pdb.set_trace()
                    # ForeignKey
                    nested_data = validated_data.pop(key)
                    if nested_data.get("id", empty) is empty:
                        # we don't want to descend into creating objects, so throw a validation error
                        # here and inform the user to create the related object before saving the
                        # instance in operation
                        raise ValidationError("Nested objects must exist prior to creating this parent instance.")

                    else:
                        # Update
                        ChildClass = field.Meta.model
                        try:
                            child_instance = ChildClass.objects.get(pk=nested_data["id"])
                        except ChildClass.DoesNotExist:
                            child_instance = field.create(nested_data)
                        else:
                            del nested_data["id"]
                            child_instance = field.update(child_instance, nested_data)

                    validated_data[key] = child_instance

        # Create the base instance
        instance = ModelClass.objects.create(**validated_data)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
    def create(self, validated_data):

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}

        # Save off the data
        for key, field in self.fields.items():
            if isinstance(field, serializers.BaseSerializer):
                if isinstance(validated_data.get(key), list):
                    # One-to-many...
                    nested_data = validated_data.pop(key)
                    many_to_many[key] = field.create(nested_data)



                elif isinstance(validated_data.get(key), dict):
                    # ForeignKey
                    nested_data = validated_data.pop(key)
                    if nested_data.get("id", empty) is empty:
                        # No id, so it looks like we've got a create...

                        del nested_data["id"]
                        child_instance = field.create(nested_data)
                    else:
                        # Update
                        ChildClass = field.Meta.model

                        try:
                            child_instance = ChildClass.objects.get(pk=nested_data["id"])
                        except ChildClass.DoesNotExist:
                            child_instance = field.create(nested_data)
                        else:
                            del nested_data["id"]
                            child_instance = field.update(child_instance, nested_data)

                    validated_data[key] = child_instance

        # Create the base instance
        instance = ModelClass.objects.create(**validated_data)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        return instance
 def get_fields(self):
     fields = super(NaturalKeyModelSerializer, self).get_fields()
     info = model_meta.get_field_info(self.Meta.model)
     for field in fields:
         if field not in info.relations:
             continue
         relation_info = info.relations[field]
         if not issubclass(relation_info.related_model, NaturalKeyModel):
             continue
         field_class, field_kwargs = self.build_nested_field(
             field, relation_info, 1
         )
         fields[field] = field_class(**field_kwargs)
     return fields
Example #50
0
 def create(self, validated_data):
     #This is the same implementation as in ModelSerializer, but minus the assertion about nested writes!
     ModelClass = self.Meta.model
     info = model_meta.get_field_info(ModelClass)
     many_to_many = {}
     for field_name, relation_info in info.relations.items():
         if relation_info.to_many and (field_name in validated_data):
             many_to_many[field_name] = validated_data.pop(field_name)
     instance = ModelClass.objects.create(**validated_data)
     # Save many-to-many relationships after the instance is created.
     if many_to_many:
         for field_name, value in many_to_many.items():
             field = getattr(instance, field_name)
             field.set(value)
     return instance
    def test_related_resource_collection(self):
        serializer = self._get_serializer_for_view(
            'developer-detail',
            {'developer': self.developer1.pk},
        )

        field_info = get_field_info(Developer)
        relation_info = field_info.reverse_relations['programming_languages']
        field_class, field_kwargs = serializer.build_relational_field(
            'programming_languages',
            relation_info,
        )

        self._assert_field_is_related_field(field_class)
        self._check_field_kwargs(field_kwargs, serializer, 'language-list')
Example #52
0
    def get_label_fields(self, default_fields):
        if not self.add_label_fields:
            return {}
        fields = {}

        exclude = getattr(self.Meta, 'exclude', [])
        if 'label' not in exclude and 'label' not in default_fields:
            fields['label'] = serializers.ReadOnlyField(source='__str__')

        info = model_meta.get_field_info(self.Meta.model)

        # Add labels for dates and fields with choices
        for name, field in info.fields.items():
            if name in getattr(self.Meta, "exclude", []):
                continue
            if name + '_label' in default_fields:
                continue
            if isinstance(field, model_fields.DateTimeField):
                fields[name + '_label'] = LocalDateTimeField(source=name)
            if field.choices:
                fields[name + '_label'] = serializers.ReadOnlyField(
                    source='get_%s_display' % name
                )
            if isinstance(field, MODEL_BOOLEAN_FIELDS):
                fields[name + '_label'] = BooleanLabelField(
                    source=name,
                )

        # Add labels for related fields
        for name, field in info.forward_relations.items():
            if name in getattr(self.Meta, "exclude", []):
                continue
            if name + '_label' in default_fields:
                continue

            f, field_kwargs = self.build_relational_field(
                name, info.forward_relations[name],
            )
            label_field_kwargs = {
                'source': name,
            }
            if field_kwargs.get('many', None):
                label_field_kwargs['many'] = field_kwargs['many']
            fields[name + '_label'] = serializers.StringRelatedField(
                **label_field_kwargs
            )

        return fields
Example #53
0
	def update(self, instance, validated_data):
		print("==========  CALLED UPDATE! ===========")
		print(str(validated_data))
		print(str(instance.id))
		fields = ModelSerializer.get_fields(self)
		info = model_meta.get_field_info(instance)
		for attr, value in validated_data.items():
			if attr in info.relations and info.relations[attr].to_many:
				print("setting multiple " + str(instance) + " " + str(attr) + " " + str(value))
				set_many(instance, attr, value)
			else:
				print("setting single " + str(instance) + " " + str(attr) + " " + str(value))
				setattr(instance, attr, value)
		instance.save()
		print(fields)
		return ModelSerializer.update(self, instance, validated_data)
Example #54
0
    def build_obj(self, data=None):
        """ Much of this is copied from the create method
        of ModelSerializer in DRF, as we need to be able
        to create an object using the serializer without saving it,
        which the create method does not allow.
        """
        from rest_framework.utils import model_meta
        from rest_framework.serializers import raise_errors_on_nested_writes

        # Copy as validated_data is mutated
        if data == None:
            data = self.validated_data
        validated_data = copy.copy(data)

        raise_errors_on_nested_writes('create', self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass(**validated_data)
        except TypeError as exc:
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception text was: %s.' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    exc
                )
            )
            raise TypeError(msg)

        return instance
    def test_related_resource_with_no_common_ancestor(self):
        serializer = self._get_serializer_for_view(
            'language-detail',
            {
                'developer': self.programming_language1.author.pk,
                'language': self.programming_language1.pk,
            },
        )

        field_info = get_field_info(ProgrammingLanguage)
        relation_info = field_info.forward_relations['website']
        field_class, field_kwargs = \
            serializer.build_relational_field('website', relation_info)

        self._assert_field_is_related_field(field_class)
        self._check_field_kwargs(field_kwargs, serializer, 'website-detail')
        self._check_lookup_url_kwarg_in_field_kwargs('website', field_kwargs)
Example #56
0
    def __new__(cls, name, bases, attrs):
        serializer_class = attrs.get('serializer_class', None)
        many_to_many_fields = many_to_one_fields = related_fields = []

        info = None
        base_forward_rel = list(attrs.pop('_base_forward_rel', ()))

        for base in reversed(bases):
            if hasattr(base, '_base_forward_rel'):
                base_forward_rel.extend(list(base._base_forward_rel))
        if serializer_class and issubclass(serializer_class, serializers.ModelSerializer):
            base_forward_rel.extend(
                list(getattr(serializer_class, '_related_fields', [])),
            )
            many_to_many_fields.extend(
                list(getattr(serializer_class, '_many_to_many_fields', [])),
            )
            many_to_one_fields.extend(
                list(getattr(serializer_class, '_many_to_one_fields', [])),
            )
            if hasattr(serializer_class.Meta, 'model'):
                info = model_meta.get_field_info(serializer_class.Meta.model)
                meta_fields = list(serializer_class.Meta.fields)
                many_to_many_fields.extend(meta_fields)
                many_to_one_fields.extend(meta_fields)
                base_forward_rel.extend(meta_fields)

        if info is not None:
            many_to_many_fields = cls.get_many_to_many_rel(info, set(many_to_many_fields))
            many_to_one_fields = cls.get_many_to_one_rel(info, set(many_to_one_fields))
            related_fields = cls.get_forward_rel(info, set(base_forward_rel))

        queryset = attrs.get('queryset', None)
        try:
            if queryset:
                if many_to_many_fields:
                    queryset = queryset.prefetch_related(
                        *normalize_prefetch_lookups(set(many_to_many_fields + many_to_one_fields)),
                    )
                if related_fields:
                    queryset = queryset.select_related(*related_fields)
                attrs['queryset'] = queryset.all()
        except ProgrammingError:
            pass
        return super(OptimizeRelatedModelViewSetMetaclass, cls).__new__(cls, name, bases, attrs)
Example #57
0
    def create(self, validated_data):
        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass(**validated_data)
            instance.set_password(validated_data['password'])
            instance.save()
        except TypeError as exc:
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception text was: %s.' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    exc
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        # All users are automatically added to the user group.
        # The user is created on boot so no try.
        group = Group.objects.get(name='user')
        instance.groups.add(group)

        return instance
Example #58
0
    def create(self, validated_data):
        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError as exc:
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception text was: %s.' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    exc
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if self.validated_data['tags']:
            tag_list = []
            for tag in self.validated_data['tags']:
                # tag_list.append(ContentTag.objects.get_or_create(tag_name=tag['tag_name'])[0])
                tag_list.append(ContentTag.objects.get_or_create(tag_name=tag)[0])
            setattr(instance, 'tags', tag_list)
            many_to_many.pop('tags')


        if many_to_many:
            for field_name, value in many_to_many.items():
                setattr(instance, field_name, value)

        instance.save()

        return instance
Example #59
0
    def create(self, validated_data):
        assert hasattr(self.Meta, "model"), 'Class {serializer_class} missing "Meta.model" attribute'.format(
            serializer_class=self.__class__.__name__
        )
        ModelClass = self.Meta.model
        model_info = model_meta.get_field_info(ModelClass)
        is_generic_relation = "content_type" in model_info.relations

        relations_data = {}
        m2m_relations_data = {}
        parents_query_dict = self.get_parents_query_dict()
        for parent_lookup in parents_query_dict:
            if parent_lookup in model_info.relations:
                parent_model = model_info.relations[parent_lookup].related_model
                parent_instance = get_object_or_404(
                    parent_model,
                    **{
                        (key.split("__", 1)[1] if "__" in key else "pk"): value
                        for key, value in parents_query_dict.items()
                    }
                )
                if isinstance(model_info.relations[parent_lookup].model_field, ManyToManyField):
                    m2m_relations_data[parent_lookup] = {
                        "parent_instance": parent_instance,
                        "related_name": model_info.relations[parent_lookup].model_field.name,
                        "has_through_model": model_info.relations[parent_lookup].has_through_model,
                    }
                elif is_generic_relation:
                    relations_data["content_object"] = parent_instance
                else:
                    parent_model_field = model_info.relations[parent_lookup].model_field
                    relations_data[parent_lookup] = parent_instance

        validated_data.update(relations_data)

        instance = super(NestedSerializerMixin, self).create(validated_data)

        for parent_lookup in m2m_relations_data:
            if not m2m_relations_data[parent_lookup]["has_through_model"]:
                getattr(instance, m2m_relations_data[parent_lookup]["related_name"]).add(
                    m2m_relations_data[parent_lookup]["parent_instance"]
                )

        return instance