diff --git a/dynamic_rest/filters.py b/dynamic_rest/filters.py index eb3617b9..e38fdeb3 100644 --- a/dynamic_rest/filters.py +++ b/dynamic_rest/filters.py @@ -462,6 +462,15 @@ def _get_queryset(self, queryset=None, serializer=None): def _serializer_filter(self, serializer=None, queryset=None): return serializer.filter_queryset(queryset) + def _get_id_fields(self, serializer, fields, is_root): + """ Return FK field names, with `_id` appended. """ + get_id_fields = getattr(serializer, 'get_id_fields', lambda a: []) + if not is_root: + # For sideloads, return all ID fields they may be needed + # to related them to parent objects. + fields = None + return get_id_fields(fields) + def _build_queryset( self, serializer=None, @@ -538,7 +547,7 @@ def _build_queryset( not self.view.is_update() and not self.view.is_delete() ): - id_fields = getattr(serializer, 'get_id_fields', lambda: [])() + id_fields = self._get_id_fields(serializer, fields, is_root_level) # only include local model fields only = [ field for field in set( @@ -546,6 +555,11 @@ def _build_queryset( ) if is_model_field(model, field) and not is_field_remote(model, field) ] + ''' + print "Requirements: %s" % requirements + print "Fields: %s" % fields.keys() + print "Only: %s" % only + ''' queryset = queryset.only(*only) # add request filters @@ -622,6 +636,10 @@ def _serializer_filter(self, serializer=None, queryset=None): ) return queryset + def _get_id_fields(self, serializer, fields, is_root_level): + get_id_fields = getattr(serializer, 'get_id_fields', lambda a: []) + return get_id_fields(fields) + class DynamicSortingFilter(OrderingFilter): diff --git a/dynamic_rest/prefetch.py b/dynamic_rest/prefetch.py index 2660c5f0..af983cd0 100644 --- a/dynamic_rest/prefetch.py +++ b/dynamic_rest/prefetch.py @@ -173,10 +173,8 @@ def prefetch_related(self, *args): return self def only(self, *fields): - # TODO: support this for realz - ''' - self.fields = set(self.fields) + set(fields) - ''' + self.fields |= set(fields) + self.queryset = self.queryset.only(*fields) return self def exclude(self, *args, **kwargs): @@ -251,7 +249,7 @@ def __init__(self, queryset): self.queryset = queryset self.model = queryset.model self.prefetches = {} - self.fields = None + self.fields = set() self.pk_field = queryset.model._meta.pk.attname self._data = None self._my_ids = None @@ -267,7 +265,10 @@ def execute(self): use_fastquery = getattr(self.model, 'USE_FASTQUERY', True) if use_fastquery: - data = list(qs.values()) + # print "Fields: %s" % self.fields + # print qs.query.__str__() + data = list(qs.values(*list(self.fields))) + # print data[0] if data else '' self.merge_prefetch(data) self._data = FastList( @@ -412,7 +413,10 @@ def merge_o2or(self, data, field, prefetch, m2o_mode=False): filter_args = {remote_filter_key: my_ids} # Fetch remote objects - remote_objects = prefetch.query.filter(**filter_args).execute() + query = prefetch.query + if query.fields: + query = query.only(remote_field) + remote_objects = query.filter(**filter_args).execute() id_map = self._make_id_map(data, pk_field=self.pk_field) field_name = prefetch.field diff --git a/dynamic_rest/serializers.py b/dynamic_rest/serializers.py index be24225e..7c22608c 100644 --- a/dynamic_rest/serializers.py +++ b/dynamic_rest/serializers.py @@ -626,7 +626,10 @@ def to_representation(self, instance): Otherwise, a tagged data dict representation. """ if self.id_only(): - return instance.pk + try: + return instance.pk + except AttributeError: + return instance else: if self.enable_optimization: representation = self._faster_to_representation(instance) @@ -720,7 +723,7 @@ class WithDynamicModelSerializerMixin(WithDynamicSerializerMixin): def get_model(cls): return getattr(cls.Meta, 'model', None) - def get_id_fields(self): + def get_id_fields(self, fields=None): """ Called to return a list of fields consisting of, at minimum, the PK field name. The output of this method is used to @@ -732,6 +735,11 @@ def get_id_fields(self): out = [model._meta.pk.name] # get PK field name + if fields is not None: + sources = [f.source for f in fields.values()] + else: + sources = None # return all + # If this is being called, it means it # is a many-relation to its parent. # Django wants the FK to the parent, @@ -741,6 +749,8 @@ def get_id_fields(self): # TODO: We also might need to return all non-nullable fields, # or else it is possible Django will issue another request. for field in model._meta.fields: + if sources is not None and field.name not in sources: + continue if isinstance(field, models.ForeignKey): out.append(field.name + '_id')