def remove_invalid_fields(self, queryset, fields, view, request): """Remove invalid fields.""" order_fields = [] for field in fields: # When a dot is present in the order field, treat it as a JSON path. if '.' not in field: order_fields.extend( super(JsonOrderingFilter, self).remove_invalid_fields(queryset, [field], view, request)) continue path = field.split('.') base_field = path[0] if len(path) < 2: continue if not super(JsonOrderingFilter, self).remove_invalid_fields( queryset, [base_field], view, request): continue reverse = False if base_field[0] == '-': base_field = base_field[1:] reverse = True # Resolve base field. try: quote_name = connection.ops.quote_name model_meta = queryset.model._meta # pylint: disable=protected-access base_field = '{}.{}'.format( quote_name(model_meta.db_table), quote_name(model_meta.get_field(base_field).column), ) except FieldDoesNotExist: continue placeholders = '->'.join(['%s'] * (len(path) - 2)) # The last placeholder should be accessed via the ->> operator. if placeholders: placeholders = '->{}->>%s'.format(placeholders) else: placeholders = '->>%s' expression = RawSQL( # We can use base_field here directly because we've resolved it via Django ORM. '{}{}'.format(base_field, placeholders), params=path[1:], ) if reverse: expression = expression.desc() order_fields.append(expression) return order_fields
def order_by_json_path(self, json_path, language_code=None, order='asc'): """ Orders a queryset by the value of the specified `json_path`. More about the `#>>` operator and the `json_path` arg syntax: https://www.postgresql.org/docs/current/static/functions-json.html More about Raw SQL expressions: https://docs.djangoproject.com/en/dev/ref/models/expressions/#raw-sql-expressions Usage example: MyModel.objects.language('en_us').filter(is_active=True).order_by_json_path('title') """ language_code = (language_code or self._language_code or self.get_language_key(language_code)) json_path = '{%s,%s}' % (language_code, json_path) # Our jsonb field is named `translations`. raw_sql_expression = RawSQL("translations#>>%s", (json_path, )) if order == 'desc': raw_sql_expression = raw_sql_expression.desc() return self.order_by(raw_sql_expression)