Loaddata not dealing with timestamps and timezones

2020-03-03 05:29发布

问题:

I'm using django 1.4.1 with mysql and timezones enabled. I did a dump data to yaml, modified some fields to create some test data, and am trying to load it back in. however, Django keeps complaining about naive datetimes even though a tz is specified

specifically, my loaddata has:

fields: {created_date: !!timestamp '2012-09-15 22:17:44+00:00', ...

but loaddata gives the error:

RuntimeWarning: DateTimeField received a naive datetime (2012-09-15 22:17:44) while time zone support is active.

This doesn't make much sense to me, seeing as its:

  1. a UTC timestamp
  2. the same exact format Django exported using dumpdata

is there some way i can tell django this is a UTC date?

回答1:

From the docs...

When serializing an aware datetime, the UTC offset is included, like this:

"2011-09-01T13:20:30+03:00"

For a naive datetime, it obviously isn't:

"2011-09-01T13:20:30"

...so instead of...

created_date: !!timestamp '2012-09-15 22:17:44+00:00'

...either of...

created_date: '2012-09-15T22:17:44+00:00'

...or...

created_date: '2012-09-15T22:17:44Z'

...will work.



回答2:

The problem stems from PyYAML. When loaddata hands off the datetime to PyYAML, it takes the aware datetime, adjusts the time to UTC, and then returns a naive datetime, which generates the warning.

There is a Django ticket, as well as a PyYAML ticket concerning the issue. Both go into far greater detail concerning the unexpected behavior above. Judging by the comments in the tickets, this issue seems unlikely to be resolved anytime soon.

Is you set TIME_ZONE = 'UTC' in settings.py of your project, you will load in the correct time, but you will still get warnings. Should your timezone be set to anything else, Django will treat the datetime as local, and adjust it to UTC, which is probably undesired.

The best way to avoid this is to use JSON as a serialization format.

Hope that helps.



回答3:

You can copy django/core/serializers/pyyaml.py to your project dir, and replace following code (maybe 78-79 lines in the case of ver.1.9.9)

for obj in PythonDeserializer(yaml.load(stream, Loader=SafeLoader), **options):
    yield obj

to

output = yaml.load(stream, Loader=SafeLoader)
for a_model in output:
    for key, value in a_model.items():
        if key == 'fields':
            for vkey, vvalue in value.items():
                if isinstance(vvalue, datetime.datetime):
                    value[vkey] = vvalue.replace(tzinfo=pytz.utc)
for obj in PythonDeserializer(output, **options):
    yield obj

of course pytz already installed and

import pytz

is needed.

This code will convert all naive datetime values to UTC aware.

To override default serializer, add SERIALIZATION_MODULES in settings.py:

SERIALIZATION_MODULES = {'yaml': 'yourproj.pyyaml'}

I hope this monkey patch works fine.



回答4:

I wanted to continue to use YAML instead of JSON fixtures so I could have comments in the data. The workaround from here fixed the problem for me: https://code.djangoproject.com/ticket/18867

Namely, manually changing the YAML fixture so it:

  • Doesn't use the !!timestamp YAML tag
  • Encloses the timestamp values in quotes
  • Includes timezone information in the timestamp value

...and apparently that triggers Django's timestamp parsing logic instead of the broken PyYAML logic.