在 Django ORM 中查询时如何将 char 转换为整数?

How do i cast char to integer while querying in django ORM?

最近开始使用 Django ORM.I 想执行此查询

 select student_id from students where student_id like "%97318%" order by CAST(student_id as UNSIGNED) desc;   

其中 student_id 是一个 CharField,我想将其作为查询的整数。 我试过

  students.objects.filter(student_id__contains "97318").order('-student_id')

工作正常。但是不知道也找不到如何将 "student_id" 转换为 int,就像上面提到的 "Django ORM" 实际的 MySQL 查询一样。我应该使用原始查询还是有出路? 让我知道你的建议。

使用查询集的extra()方法:

students.objects.filter(student_id__contains="97318") \
                .extra({'stident_id_uint': "CAST(student_id as UNSIGNED)"}) \
                .order_by('-student_id_uint')

不需要使用 extra 的更新替代方法是转换函数(Django 1.10 中的新功能):

>>> from django.db.models import FloatField
>>> from django.db.models.functions import Cast
>>> Value.objects.create(integer=4)
>>> value = Value.objects.annotate(as_float=Cast('integer', FloatField())).get()> 
>>> print(value.as_float)
4.0

来自https://docs.djangoproject.com/en/1.10/ref/models/database-functions/#cast

我尝试了 extra()annotate()CAST,但它们不能很好地处理相关字段并且有时会生成 JOINS 导致意外的查询集。

我最终创建了一个自定义查找。

文档很少,但可以在 here and here

找到

这是我的例子:

@Field.register_lookup
class IntegerValue(Transform):
    # Register this before you filter things, for example in models.py
    lookup_name = 'int'  # Used as object.filter(LeftField__int__gte, "777")
    bilateral = True  # To cast both left and right

    def as_sql(self, compiler, connection):
        sql, params = compiler.compile(self.lhs)
        sql = 'CAST(%s AS UNSIGNED)' % sql
        return sql, params

然后下面应该工作:

students.objects.filter(student_id__int__gte="97318").order('-student_id')

使用 Django 中的 cast。 如果您使用的是 Django CBV(class 基于视图),您可以这样做:

models.py:

class Document(TenantAwareModel):
    document_data = JSONField(null=True)

views.py:

from django.db.models import TextField
from django.db.models.functions import Cast

def get_queryset(self):
    document = Document.objects.annotate(document_data_as_text=Cast('document_data', TextField()))
    user = self.request.user
    tenant = self.request.user.tenant
    return document.filter(tenant=tenant)

然后,在您的方法中,您可以使用 document_data_as_text 作为普通字段来过滤它:

def list(self, request, *args, **kwargs):
    document_part_filter_param = request.query_params.get("documentPart")
    if document_part_filter_param:
        queryset = queryset.filter(document_data_as_text__unaccent__icontains=document_part_filter_param)