Support datetime in admin pickle widgets/forms

This commit is contained in:
Griatch 2019-06-06 19:56:10 +02:00
parent 5064242ef8
commit 3416db98d7

View file

@ -30,6 +30,7 @@ Modified for Evennia by Griatch and the Evennia community.
""" """
from builtins import object from builtins import object
from ast import literal_eval from ast import literal_eval
from datetime import datetime
from copy import deepcopy from copy import deepcopy
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
@ -38,9 +39,8 @@ from zlib import compress, decompress
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.forms import CharField, Textarea from django.forms.fields import CharField
from django.forms.utils import flatatt from django.forms.widgets import Textarea
from django.utils.html import format_html
from pickle import loads, dumps from pickle import loads, dumps
from django.utils.encoding import force_text from django.utils.encoding import force_text
@ -107,36 +107,41 @@ def dbsafe_decode(value, compress_object=False):
class PickledWidget(Textarea): class PickledWidget(Textarea):
"""
This is responsible for outputting HTML representing a given field.
"""
def render(self, name, value, attrs=None, renderer=None): def render(self, name, value, attrs=None, renderer=None):
"""Display of the PickledField in django admin""" """Display of the PickledField in django admin"""
value = repr(value) repr_value = repr(value)
try:
# necessary to convert it back after repr(), otherwise validation errors will mutate it
value = literal_eval(value)
except ValueError:
return value
# fix since the signature of build_attrs changed in Django 1.11 # analyze represented value to see how big the field should be
if attrs is not None: if attrs is not None:
attrs["name"] = name attrs["name"] = name
else: else:
attrs = {"name": name} attrs = {"name": name}
attrs['cols'] = 30 attrs['cols'] = 30
# adapt rows to width # adapt number of rows to number of lines in string
rows = 1 rows = 1
if isinstance(value, str) and "\n" in value: if isinstance(value, str) and "\n" in repr_value:
rows = max(1, len(value.split('\n'))) rows = max(1, len(value.split('\n')))
attrs['rows'] = rows attrs['rows'] = rows
attrs = self.build_attrs(attrs) attrs = self.build_attrs(attrs)
try:
# necessary to convert it back after repr(), otherwise validation errors will mutate it
value = literal_eval(repr_value)
except ValueError:
pass
return super().render(name, value, attrs=attrs, renderer=renderer) return super().render(name, value, attrs=attrs, renderer=renderer)
# return format_html('<textarea{0}>\r\n{1}</textarea>',
# flatatt(final_attrs),
# value)
class PickledFormField(CharField): class PickledFormField(CharField):
"""
This represents one input field for the form.
"""
widget = PickledWidget widget = PickledWidget
default_error_messages = dict(CharField.default_error_messages) default_error_messages = dict(CharField.default_error_messages)
default_error_messages['invalid'] = ( default_error_messages['invalid'] = (
@ -146,27 +151,40 @@ class PickledFormField(CharField):
"surrounded by quote marks. We have converted it to a string for your " "surrounded by quote marks. We have converted it to a string for your "
"convenience. If it is acceptable, please hit save again.") "convenience. If it is acceptable, please hit save again.")
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# This needs to fall through to literal_eval. # This needs to fall through to literal_eval.
kwargs['required'] = False kwargs['required'] = False
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def clean(self, value): def clean(self, value):
value = super().clean(value)
# handle empty input
try: try:
if not value.strip(): if not value.strip():
# Field was left blank. Make this None. # Field was left blank. Make this None.
value = 'None' value = 'None'
except AttributeError: except AttributeError:
value = 'None' pass
# parse raw Python
try: try:
return literal_eval(value) return literal_eval(value)
except (ValueError, SyntaxError): except (ValueError, SyntaxError):
try: pass
value = repr(value)
return literal_eval(value) # handle datetime objects
except (ValueError, SyntaxError): try:
raise ValidationError(self.error_messages['invalid']) return datetime.strptime(value, "%Y-%m-%d %H:%M:%S.%f")
except ValueError:
pass
# fall through to parsing the repr() of the data
try:
value = repr(value)
return literal_eval(value)
except (ValueError, SyntaxError):
raise ValidationError(self.error_messages['invalid'])
class PickledObjectField(models.Field): class PickledObjectField(models.Field):