如何将 Django 中 Count() 的输出转换为 Django 1.8 中的 FloatField
how to cast the output of Count() in django to FloatField in django 1.8
我正在使用 django 1.8。
我有两个模型:-
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')
现在我想根据 "number of tags" 和查询的 "post_score" 的总和对查询进行排序。
我正在寻找这样的东西:-
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 在 django 1.10 中提供。那么我可以使用什么替代方案?
正如我在评论中所说,您可以简单地复制 Cast
class 的源代码并将其保存在您的项目中并使用它。
事实证明,事情并不那么简单。您还需要更改 Cast.as_sql
as it is in v1.10。而且,无需对 Func.as_sql
进行 super
调用。
只需复制以下代码并将其保存在您的项目中。我已经测试过它并且有效。
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')
正如您在 django 1.10 的问题中提到的,您可以使用内置的 Cast 函数。对于旧版本,您可以使用 Func() expression
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')
您可以将 Cast
定义为
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,
)
我正在使用 django 1.8。
我有两个模型:-
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')
现在我想根据 "number of tags" 和查询的 "post_score" 的总和对查询进行排序。 我正在寻找这样的东西:-
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 在 django 1.10 中提供。那么我可以使用什么替代方案?
正如我在评论中所说,您可以简单地复制 Cast
class 的源代码并将其保存在您的项目中并使用它。
事实证明,事情并不那么简单。您还需要更改 Cast.as_sql
as it is in v1.10。而且,无需对 Func.as_sql
进行 super
调用。
只需复制以下代码并将其保存在您的项目中。我已经测试过它并且有效。
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')
正如您在 django 1.10 的问题中提到的,您可以使用内置的 Cast 函数。对于旧版本,您可以使用 Func() expression
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')
您可以将 Cast
定义为
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,
)