예제 #1
0
    def get_id_fields(self):
        """
        Called to return a list of fields consisting of, at minimum,
        the PK field name. The output of this method is used to
        construct a Prefetch object with a .only() queryset
        when this field is not being sideloaded but we need to
        return a list of IDs.
        """
        model = self.get_model()
        meta = Meta(model)

        out = [meta.get_pk_field().attname]

        # If this is being called, it means it
        # is a many-relation  to its parent.
        # Django wants the FK to the parent,
        # but since accurately inferring the FK
        # pointing back to the parent is less than trivial,
        # we will just pull all ID fields.
        # TODO: We also might need to return all non-nullable fields,
        #    or else it is possible Django will issue another request.
        for field in meta.get_fields():
            if isinstance(field, models.ForeignKey):
                out.append(field.attname)

        return out
예제 #2
0
    def resolve(self, query):
        """Resolves a query into model and serializer fields.

        Arguments:
            query: an API field path, in dot-nation
                e.g: "creator.location_name"

        Returns:
            (model_fields, api_fields)
                e.g:
                    [
                        Blog._meta.fields.user,
                        User._meta.fields.location,
                        Location._meta.fields.name
                    ],
                    [
                        DynamicRelationField(source="user"),
                        DynamicCharField(source="location.name")
                    ]

        Raises:
            ValidationError if the query is invalid,
                e.g. references a method field or an undefined field
        ```

        Note that the lists do not necessarily contain the
        same number of elements because API fields can reference nested model fields.
        """  # noqa
        if not isinstance(query, six.string_types):
            parts = query
            query = '.'.join(query)
        else:
            parts = query.split('.')

        model_fields = []
        api_fields = []

        serializer = self

        model = serializer.get_model()
        resource_name = serializer.get_name()
        meta = Meta(model)
        api_name = parts[0]
        other = parts[1:]

        try:
            api_field = serializer.get_field(api_name)
        except:
            api_field = None

        if other:
            if not (api_field and isinstance(api_field, DynamicRelationField)):
                raise ValidationError({
                    api_name:
                    'Could not resolve "%s": '
                    '"%s.%s" is not an API relation' %
                    (query, resource_name, api_name)
                })

            source = api_field.source or api_name
            related = api_field.serializer_class()
            other = '.'.join(other)
            model_fields, api_fields = related.resolve(other)

            try:
                model_field = meta.get_field(source)
            except AttributeError:
                raise ValidationError({
                    api_name:
                    'Could not resolve "%s": '
                    '"%s.%s" is not a model relation' %
                    (query, meta.get_name(), source)
                })

            model_fields.insert(0, model_field)
            api_fields.insert(0, api_field)
        else:
            if api_name == 'pk':
                # pk is an alias for the id field
                model_field = meta.get_pk_field()
                model_fields.append(model_field)
                if api_field:
                    # the pk field may not exist
                    # on the serializer
                    api_fields.append(api_field)
            else:
                if not api_field:
                    raise ValidationError({
                        api_name:
                        'Could not resolve "%s": '
                        '"%s.%s" is not an API field' %
                        (query, resource_name, api_name)
                    })

                api_fields.append(api_field)

                if api_field.source == '*':
                    # a method field was requested, model field is unknown
                    return (model_fields, api_fields)

                source = api_field.source or api_name
                if '.' in source:
                    fields = source.split('.')
                    for field in fields[:-1]:
                        related_model = None
                        try:
                            model_field = meta.get_field(field)
                            related_model = model_field.related_model
                        except:
                            pass

                        if not related_model:
                            raise ValidationError({
                                api_name:
                                'Could not resolve "%s": '
                                '"%s.%s" is not a model relation' %
                                (query, meta.get_name(), field)
                            })
                        model = related_model
                        meta = Meta(model)
                        model_fields.append(model_field)
                    field = fields[-1]
                    try:
                        model_field = meta.get_field(field)
                    except:
                        raise ValidationError({
                            api_name:
                            'Could not resolve: "%s", '
                            '"%s.%s" is not a model field' %
                            (query, meta.get_name(), field)
                        })
                    model_fields.append(model_field)
                else:
                    try:
                        model_field = meta.get_field(source)
                    except:
                        raise ValidationError({
                            api_name:
                            'Could not resolve "%s": '
                            '"%s.%s" is not a model field' %
                            (query, meta.get_name(), source)
                        })
                    model_fields.append(model_field)

        return (model_fields, api_fields)