【问题标题】:conversion of datetime Field to string in django queryset.values_list()conversion of datetime Field to string in django queryset.values_list()
【发布时间】:2022-11-30 21:03:38
【问题描述】:

I have a queryset like:

qs = MyModel.objects.filter(name='me').values_list('activation_date')

hereactivation_dateisDateTimeFieldin models. When I download excel sheet from this qs I am not getting activation date instring format.How can I convert this field('activation_date') in string or how to typecast it in qs?

【问题讨论】:

  • you are missing flat=True

标签: django python-2.7 django-models


【解决方案1】:

https://docs.djangoproject.com/en/2.2/ref/models/fields/#datetimefield

A date and time, represented in Python by a datetime.datetime instance.

You can get a string representation of a DateTimeField casting it directly:

str(obj)
# obj = qs[0][0] ? or qs[0][1] ?

You'll get result like this (in this example I use datetime.datetime.now() since a DateTimeField is represented by datetime.datetime is the same behavior):

>>> now = datetime.datetime.now()
>>> str(now)
'2013-06-26 00:14:26.260524'

if you want less information or formatted in other mode you can use strftime() function for format them. see:

>>> now.strftime('%Y-%m-%d %H:%M')
'2013-06-26 00:14'

【讨论】:

  • Is there any way to get the month directly in text time (e.g. 11 = November)?
  • datetime.datetime.now.strftime('%Y-%m-%d %H:%M') -> AttributeError: 'builtin_function_or_method' object has no attribute 'strftime'
  • @ar2015 now is a function, try this: datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
【解决方案2】:

extra is deprecated in Django 2.0

That's why I think the best solution to get a stringified datetime is:

foo_bar = FooBarModel.objects.annotate(
    str_datetime=Cast(
        TruncSecond('some_datetime_field', DateTimeField()), CharField()
    )
).values('str_datetime').first()

The result is:

foo_bar.str_datetime:
(str)'2014-03-28 15:36:55'

Also I'd like to mention that you can format it as well in any way you want like:

from django.db.models import Value

foo_bar = FooBarModel.objects.annotate(
    day=Cast(ExtractDay('some_datetime_field'), CharField()),
    hour=Cast(ExtractHour('some_datetime_field'), CharField()),
    str_datetime=Concat(
        Value('Days: '), 'day', Value(' Hours: '), 'hour', 
        output_field=CharField()
    )
).values('str_datetime').first()

The result is:

foo_bar.str_datetime:
(str)'Days: 28 Hours: 15'

【讨论】:

  • No, extra is not deprecated in Django 2.0. I use it myself even. They have not set a deadline on its deprecation. docs.djangoproject.com/en/2.0/ref/models/querysets/#extra
  • The first lines of code gives me a : django.db.utils.OperationalError: near "None": syntax error
  • @Aurélien that is weird. Do you mean foo_bar = FooBarModel.objects.annotate is an incorrect statement?
  • @MrCyberthrone indeed this is weird, because this code looks fine to me. But if you tried this, an error will pop: User.objects.all().annotate(year=Cast(ExtractYear('date_joined'), CharField()),month=Cast(ExtractMonth('date_joined'), CharField()),day=Cast(ExtractDay('date_joined'), CharField()),str_datetime=Concat('day', Value(' / '), 'month', Value(' / '), 'year', output_field=CharField())).values('str_datetime').first() returns: sqlite3.OperationalError: near "None": syntax error
  • FYI, I created a question about that: stackoverflow.com/questions/51174993/…
【解决方案3】:

extra() is an old API that Django aims to deprecate at some point in the future. I would avoid using it.

Try the following instead:

from django.db.models import F, Func, Value, CharField

qs.annotate(
  formatted_date=Func(
    F('date'),
    Value('dd.MM.yyyy hh:mm'),
    function='to_char',
    output_field=CharField()
  )
)

This works only with a database that supports the to_char date type formatting function. Postgres provides this function by default.

  • If you use a MSSQL backend you could swap to_char with FORMAT.
  • For MySQL use DATE_FORMAT.
  • For Oracle consult their documentation, etc.

After the queryset is evaluated this will add the annotation formatted_date to each object in the queryset that is returned.

【讨论】:

  • This was very helpful, thanks. However, the function for MySQL is DATE_FORMAT. FORMAT is for regular numbers, not dates.
  • I think you missread my answer. I was taking about Microsoft SQL not MySQL for the FORMAT function. I adapted my answer and added the function for MySQL as you pointed out. Thanks!
  • If you use this often, you can avoid having to type output_field and function all the time by defining a function by extending Func. I set arity = 2, function = 'to_char' (for postgres, mind you!), output_field = models.CharField()
  • Awesome, idea, can you provide a link how exactly you achieved this? Optionally just extend my answer to make this more performant!
【解决方案4】:

If you are using Postgres, you can do it like this (date format options here). The solution is database dependent, but it sure beats looping though a long list in Python land after your perform the query.

qs = MyModel.objects.filter(name='me')
qs = qs.extra(select={'datestr':"to_char(activation_date, 'YYYY-MM-DD HH24:MI:SS')"})
qs = qs.values_list('datestr')

I am sure MySQL has some equivalent function as Postgres's to_char, but you'll have to find that on your own as I am not a MySQL guy.

【讨论】:

    【解决方案5】:

    Very surprised to see that no one suggested the cast to a simple TextField (note, I'm using Postgres so I can't confirm for other RDBMSes):

    from django.db.models.functions import Cast
    from django.db.models import TextField
    
    queryset = FooBarModel.objects.values(my_datetime=Cast('some_datetime_field', TextField()))
    foo_bar = queryset.first()
    foo_bar['my_datetime']
    >>> u'2019-10-03 17:59:37.979578+00'
    

    It similarly also works fine for nested fields:

    queryset = FooBarModel.objects.values(Cast('baz__some_datetime_field', TextField()))
    

    Alternatively, a custom Func can also be used (also specific to Postgres here, but can be modified for any other RDBMS):

    class FullDateTimeCast(Func):
       """
       Coerce an expression to a new field type.
       """
       function = 'TO_CHAR'
       template = '%(function)s(%(expressions)s, 'FMDay, Month DD, YYYY at HH12:MI:SS AM')'
    
    queryset = FooBarModel.objects.values(my_datetime=FullDateTimeCast('some_datetime_field', TextField()))
    foo_bar = queryset.first()
    foo_bar['my_datetime']
    >>> u' Thursday, October 03, 2019 at 17:59:37 PM'
    

    【讨论】:

    • I used this trick on sqlite database, and it works well!
    • It's FullDateTimeCast that worked without TextField()
    【解决方案6】:
    qs = MyModel.objects.filter(name='me')
    qs = qs.extra(select={'datestr':"DATE_FORMAT(activation_date, '%Y-%m-%d')"})
    qs = qs.values_list('datestr')
    

    【讨论】:

    • second line should be: qs = qs.extra(select={'datestr':"DATE_FORMAT(activation_date, '%%Y-%%m-%%d')"})
    • Yes, should be double % signs in the date format: '%%Y-%%m-%%d', otherwise, you will get error.
    【解决方案7】:

    You can also convert the date in queryset to string using map function. Example:

    qs = MyModel.objects.filter(name='me').values_list('activation_date', flat=True)
    data = map(str, qs)
    

    【讨论】:

    • This is not scalable with large queries
    【解决方案8】:

    I did it this way

    .annotate(date_str=ExpressionWrapper(
                Func(F('date'), Value('%d/%m/%Y %H:%i'), function='DATE_FORMAT'), output_field=CharField()
            ))
    

    【讨论】:

    • cx_Oracle.DatabaseError: ORA-00904: "DATE_FORMAT": invalid identifier. Where did you imported the DATE_FORMAT.
    • he is probably using an mssql backend
    【解决方案9】:

    If you are doing this once, refer to Yannic Hamann's answer. However if you find yourself converting to str from the database a lot, you can define the Func as a class to avoid having to type output_field and function a bunch.

    class DateToChar(models.Func):
        """
        Custom Func expression to convert datetimes to str's in database query
    
        Params for initializer
        ------
        expression_1
            expression resulting in a date: ex: F('date')
        expression_2
            Format string as an expression: Value('YYYY-MM-DD')
        """
        arity = 2
        function = 'to_char'
        output_field = models.CharField()
    

    Note that function will change depending on the database backend. This is written for Postgres.

    This can be used like

    qs = qs.annotate(date_str=DateToChar(F('date'), Value('YYYY-MM-DD'))
    

    or with any expression that results in a date/datetime/Integer/Float/Decimal field (See Postgres to_char. Varies by database) and an expression resulting in a CharField or TextField.

    See Func documentation for more information

    【讨论】:

      猜你喜欢
      • 2022-12-27
      • 2022-12-02
      • 1970-01-01
      • 2022-12-27
      • 2020-08-23
      • 2013-07-24
      • 2018-12-31
      • 1970-01-01
      • 2022-12-27
      相关资源
      最近更新 更多