Repository: googleapis/python-spanner-django

Issue:
django: AttributeError: 'str' object has no attribute 'utcoffset' when viewing Questions
After PR #71 lands, please try to login as an admin and then create a question and then try to list/view it.

## Create it
<img width="1517" alt="Screen Shot 2019-11-20 at 11 19 47 AM" src="https://user-images.githubusercontent.com/4898263/69257721-8485ae80-0b89-11ea-94e3-95b51188237b.png">

## Try to view it
<img width="1519" alt="Screen Shot 2019-11-20 at 11 20 12 AM" src="https://user-images.githubusercontent.com/4898263/69257722-8485ae80-0b89-11ea-8a5d-319e6fcefb01.png">

## Crash
<img width="1522" alt="Screen Shot 2019-11-20 at 11 20 27 AM" src="https://user-images.githubusercontent.com/4898263/69257723-8485ae80-0b89-11ea-860e-91dcce11ac2f.png">

and here is the  stack trace

<details>

```shell
[20/Nov/2019 16:20:01] "GET /admin/jsi18n/ HTTP/1.1" 200 3223
Internal Server Error: /admin/polls/question/1448548276034252166/change/
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/handlers/base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/handlers/base.py", line 143, in _get_response
    response = response.render()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/response.py", line 106, in render
    self.content = self.rendered_content
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/response.py", line 83, in rendered_content
    content = template.render(context, self._request)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 171, in render
    return self._render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
    return self.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
    return self.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
    return self.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/defaulttags.py", line 209, in render
    nodelist.append(node.render_annotated(context))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/loader_tags.py", line 188, in render
    return template.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 173, in render
    return self._render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
    return self.nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/defaulttags.py", line 209, in render
    nodelist.append(node.render_annotated(context))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/defaulttags.py", line 209, in render
    nodelist.append(node.render_annotated(context))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/defaulttags.py", line 309, in render
    return nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/defaulttags.py", line 309, in render
    return nodelist.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 937, in render
    bit = node.render_annotated(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
    return self.render(context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 993, in render
    return render_value_in_context(output, context)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/template/base.py", line 972, in render_value_in_context
    value = str(value)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/utils/html.py", line 388, in <lambda>
    klass.__str__ = lambda self: mark_safe(klass_str(self))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/boundfield.py", line 33, in __str__
    return self.as_widget()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/boundfield.py", line 93, in as_widget
    renderer=self.form.renderer,
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/widgets.py", line 241, in render
    context = self.get_context(name, value, attrs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/contrib/admin/widgets.py", line 90, in get_context
    context = super().get_context(name, value, attrs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/widgets.py", line 819, in get_context
    value = self.decompress(value)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/widgets.py", line 907, in decompress
    value = to_current_timezone(value)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/forms/utils.py", line 176, in to_current_timezone
    if settings.USE_TZ and value is not None and timezone.is_aware(value):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/utils/timezone.py", line 248, in is_aware
    return value.utcoffset() is not None
AttributeError: 'str' object has no attribute 'utcoffset'
[20/Nov/2019 16:20:19] "GET /admin/polls/question/1448548276034252166/change/ HTTP/1.1" 500 434933
```
</details>

Perhaps it has to do with our Date* objects?


Discussion:

Resolving patch:
diff --git a/spanner/django/operations.py b/spanner/django/operations.py
--- a/spanner/django/operations.py
+++ b/spanner/django/operations.py
@@ -1,3 +1,5 @@
+from datetime import datetime
+
 from django.db.backends.base.operations import BaseDatabaseOperations
 
 
@@ -15,3 +17,18 @@ def sql_flush(self, style, tables, sequences, allow_cascade=False):
         # need to be implemented somehow to support the normal way of testing
         # in Django: https://github.com/orijtech/spanner-orm/issues/67
         return []
+
+    def get_db_converters(self, expression):
+        converters = super().get_db_converters(expression)
+        internal_type = expression.output_field.get_internal_type()
+        if internal_type == 'DateTimeField':
+            converters.append(self.convert_datetimefield_value)
+        return converters
+
+    def convert_datetimefield_value(self, value, expression, connection):
+        if value is not None:
+            try:
+                value = datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f')
+            except ValueError:  # time data does not match format (no microsecond?)
+                value = datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
+        return value


Test patch: