Estoy usando django 1.8.
Tengo dos modelos:-
class Query(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL) title = models.TextField() details = models.TextField() pub_date = models.DateTimeField('date published') post_score=models.FloatField(default=0.0) .... class Tags(models.Model): """docstring for Tags""" tagname=models.TextField() grouppostlist=models.ManyToManyField(Query,through='GroupPostsTag',through_fields=('tag','query')) # and another model(it stores tags for a query) class GroupPostsTag(models.Model): query=models.ForeignKey('Query') tag=models.models.ForeignKey('Tags')Ahora quiero ordenar las consultas según la suma de "número de etiquetas" y "post_score" de la consulta. Estoy buscando algo como esto: -
tagged_user_posts = Query.objects.filter(Q(tags__id__in=tags_list)).annotate(num_tags=Cast(Count('tags'),models.FloatField())).annotate(post_scores=F('num_tags')+F('post_score')).order_by('-post_scores')Cast se proporciona en Django 1.10. Entonces, ¿qué alternativa puedo usar?
Como dije en mi comentario, simplemente puede copiar el código fuente de la clase Cast y guardarlo en sus proyectos y usarlo.
Resulta que no fue tan simple como eso . También deberá cambiar Cast.as_sql como está en v1.10 . Y no es necesario hacer una super llamada a Func.as_sql .
Simplemente copie el siguiente código y guárdelo en su proyecto. Lo he probado y funciona.
from django.db.models import Func, fields class Cast(Func): """ Coerce an expression to a new field type. """ function = 'CAST' template = '%(function)s(%(expressions)s AS %(db_type)s)' mysql_types = { fields.CharField: 'char', fields.IntegerField: 'signed integer', fields.FloatField: 'signed', } def __init__(self, expression, output_field): super(Cast, self).__init__(expression, output_field=output_field) def as_sql(self, compiler, connection, function=None, template=None, arg_joiner=None, **extra_context): if 'db_type' not in extra_context: extra_context['db_type'] = self._output_field.db_type(connection) connection.ops.check_expression_support(self) sql_parts = [] params = [] for arg in self.source_expressions: arg_sql, arg_params = compiler.compile(arg) sql_parts.append(arg_sql) params.extend(arg_params) data = self.extra.copy() data.update(**extra_context) # Use the first supplied value in this order: the parameter to this # method, a value supplied in __init__()'s **extra (the value in # `data`), or the value defined on the class. if function is not None: data['function'] = function else: data.setdefault('function', self.function) template = template or data.get('template', self.template) arg_joiner = arg_joiner or data.get('arg_joiner', self.arg_joiner) data['expressions'] = data['field'] = arg_joiner.join(sql_parts) return template % data, params def as_mysql(self, compiler, connection): extra_context = {} output_field_class = type(self._output_field) if output_field_class in self.mysql_types: extra_context['db_type'] = self.mysql_types[output_field_class] return self.as_sql(compiler, connection, **extra_context) def as_postgresql(self, compiler, connection): # CAST would be valid too, but the :: shortcut syntax is more readable. return self.as_sql(compiler, connection, template='%(expressions)s::%(db_type)s')Como mencionó en la pregunta para django 1.10, puede usar la función Cast incorporada. Para versiones anteriores, puede usar la expresión Func()
from django.db.models import Count, Func, F, ExpressionWrapper, FloatField tagged_user_posts = Query.objects.filter(Q(tags__id__in=tags_list)). annotate(num_tags=Func(Count('tags'), template='%(function)s(%(expressions)s AS %(type)s)', function='Cast', type='float') ). annotate(post_scores=ExpressionWrapper(F('num_tags')+F('post_score'), output_field=FloatField())). order_by('-post_scores')Podrías definir Cast como
def Cast(what, as_type): if isinstance(as_type, Field): as_type = as_type.db_type(connection) return Func( what, template='%(function)s(%(expressions)s AS %(type)s)', function='Cast', type=as_type, )