I don't know if anybody knows this problem, or if it is due to my misuse. The problem is: with the filter 'OrderingFilter', I cannot sort queryset by a column of a referenced model. See the following example:
class Machine(models.Model):
pool = models.ForeignKey('pool')
......
class Pool(models.Model):
name = models.CharField(max_length=32)
...
Machine has a foreign key pointing to Pool, where Pool has a field called 'name'. What we want is to sort machines by the name of its pool. So I pass a parameter 'ordering=pool__name' from the URL (like http://.../machines?ordering=pool__name).
However, the queryset cannot be properly sorted. So I checked the source code. In /.../site-packages/rest_framework/filters.py, I saw two methods:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
156 def filter_queryset(self, request, queryset, view):
157 ordering = self.get_ordering(request)
158
159 if ordering:
160 # Skip any incorrect parameters
161 ordering = self.remove_invalid_fields(queryset, ordering, view)
162
163 if not ordering:
164 # Use 'ordering' attribute by default
165 ordering = self.get_default_ordering(view)
166
167 if ordering:
168 return queryset.order_by(*ordering)
169
170 return queryset
----------------------------------------------------------------------------------------------------------------------------------------------------------------
134 def remove_invalid_fields(self, queryset, ordering, view):
135 valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
136
137 if valid_fields is None:
138 # Default to allowing filtering on serializer fields
139 serializer_class = getattr(view, 'serializer_class')
140 if serializer_class is None:
141 msg = ("Cannot use %s on a view which does not have either a "
142 "'serializer_class' or 'ordering_fields' attribute.")
143 raise ImproperlyConfigured(msg % self.__class__.__name__)
144 valid_fields = [
145 field.source or field_name
146 for field_name, field in serializer_class().fields.items()
147 if not getattr(field, 'write_only', False)
148 ]
149 elif valid_fields == '__all__':
150 # View explictly allows filtering on any model field
151 valid_fields = [field.name for field in queryset.model._meta.fields] 152 valid_fields += queryset.query.aggregates.keys()
153
154 return [term for term in ordering if term.lstrip('-') in valid_fields]
----------------------------------------------------------------------------------------------------------------------------------------------------------------
As you can see, filter_queryset is based on "order_by" which will accept Django style, e.g. using "order_by('pool__name')". However, if I pass 'pool__name' into this method, it hits method 'remove_invalid_fields' and gets removed.
The problem is at line 151. Here, the model's fields are extracted. Therefore, 'pool_name' gets removed.