Fix bug saving empty dicts/lists/False to admin. Resolve #1841
This commit is contained in:
parent
3416db98d7
commit
432674ac7c
3 changed files with 26 additions and 28 deletions
|
|
@ -73,6 +73,8 @@ Update to Python 3
|
||||||
- Prettifies Django 'change password' workflow
|
- Prettifies Django 'change password' workflow
|
||||||
- Bugfixes
|
- Bugfixes
|
||||||
- Fixes bug on login page where error messages were not being displayed
|
- Fixes bug on login page where error messages were not being displayed
|
||||||
|
- Remove strvalue field from admin; it made no sense to have here, being an optimization field
|
||||||
|
for internal use.
|
||||||
|
|
||||||
### Prototypes
|
### Prototypes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
import traceback
|
||||||
|
from datetime import datetime
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from evennia.typeclasses.models import Tag
|
from evennia.typeclasses.models import Tag
|
||||||
from django import forms
|
from django import forms
|
||||||
from evennia.utils.picklefield import PickledFormField
|
from evennia.utils.picklefield import PickledFormField
|
||||||
from evennia.utils.dbserialize import from_pickle, _SaverSet
|
from evennia.utils.dbserialize import from_pickle, _SaverSet
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
class TagAdmin(admin.ModelAdmin):
|
class TagAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -170,22 +171,18 @@ class AttributeForm(forms.ModelForm):
|
||||||
help_text="Internal use. Either unset (normal Attribute) or \"nick\"",
|
help_text="Internal use. Either unset (normal Attribute) or \"nick\"",
|
||||||
required=False,
|
required=False,
|
||||||
max_length=16)
|
max_length=16)
|
||||||
attr_strvalue = forms.CharField(label="String Value",
|
|
||||||
help_text="Only set when using the Attribute as a string-only store",
|
|
||||||
required=False,
|
|
||||||
widget=forms.Textarea(attrs={"rows": 1, "cols": 6}))
|
|
||||||
attr_lockstring = forms.CharField(label="Locks",
|
attr_lockstring = forms.CharField(label="Locks",
|
||||||
required=False,
|
required=False,
|
||||||
help_text="Lock string on the form locktype:lockdef;lockfunc:lockdef;...",
|
help_text="Lock string on the form locktype:lockdef;lockfunc:lockdef;...",
|
||||||
widget=forms.Textarea(attrs={"rows": 1, "cols": 8}))
|
widget=forms.Textarea(attrs={"rows": 1, "cols": 8}))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ("attr_key", "attr_value", "attr_category", "attr_strvalue", "attr_lockstring", "attr_type")
|
fields = ("attr_key", "attr_value", "attr_category", "attr_lockstring", "attr_type")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
If we have an Attribute, then we'll prepopulate our instance with the fields we'd expect it
|
If we have an Attribute, then we'll prepopulate our instance with the fields we'd expect it
|
||||||
to have based on the Attribute. attr_key, attr_category, attr_value, attr_strvalue, attr_type,
|
to have based on the Attribute. attr_key, attr_category, attr_value, attr_type,
|
||||||
and attr_lockstring all refer to the corresponding Attribute fields. The initial data of the form fields will
|
and attr_lockstring all refer to the corresponding Attribute fields. The initial data of the form fields will
|
||||||
similarly be populated.
|
similarly be populated.
|
||||||
|
|
||||||
|
|
@ -194,30 +191,28 @@ class AttributeForm(forms.ModelForm):
|
||||||
attr_key = None
|
attr_key = None
|
||||||
attr_category = None
|
attr_category = None
|
||||||
attr_value = None
|
attr_value = None
|
||||||
attr_strvalue = None
|
|
||||||
attr_type = None
|
attr_type = None
|
||||||
attr_lockstring = None
|
attr_lockstring = None
|
||||||
if hasattr(self.instance, 'attribute'):
|
if hasattr(self.instance, 'attribute'):
|
||||||
attr_key = self.instance.attribute.db_key
|
attr_key = self.instance.attribute.db_key
|
||||||
attr_category = self.instance.attribute.db_category
|
attr_category = self.instance.attribute.db_category
|
||||||
attr_value = self.instance.attribute.db_value
|
attr_value = self.instance.attribute.db_value
|
||||||
attr_strvalue = self.instance.attribute.db_strvalue
|
|
||||||
attr_type = self.instance.attribute.db_attrtype
|
attr_type = self.instance.attribute.db_attrtype
|
||||||
attr_lockstring = self.instance.attribute.db_lock_storage
|
attr_lockstring = self.instance.attribute.db_lock_storage
|
||||||
self.fields['attr_key'].initial = attr_key
|
self.fields['attr_key'].initial = attr_key
|
||||||
self.fields['attr_category'].initial = attr_category
|
self.fields['attr_category'].initial = attr_category
|
||||||
self.fields['attr_type'].initial = attr_type
|
self.fields['attr_type'].initial = attr_type
|
||||||
self.fields['attr_value'].initial = attr_value
|
self.fields['attr_value'].initial = attr_value
|
||||||
self.fields['attr_strvalue'].initial = attr_strvalue
|
|
||||||
self.fields['attr_lockstring'].initial = attr_lockstring
|
self.fields['attr_lockstring'].initial = attr_lockstring
|
||||||
self.instance.attr_key = attr_key
|
self.instance.attr_key = attr_key
|
||||||
self.instance.attr_category = attr_category
|
self.instance.attr_category = attr_category
|
||||||
self.instance.attr_value = attr_value
|
self.instance.attr_value = attr_value
|
||||||
# prevent set from being transformed to str
|
|
||||||
if isinstance(attr_value, set) or isinstance(attr_value, _SaverSet):
|
# prevent from being transformed to str
|
||||||
|
if isinstance(attr_value, (set, _SaverSet)):
|
||||||
self.fields['attr_value'].disabled = True
|
self.fields['attr_value'].disabled = True
|
||||||
|
|
||||||
self.instance.deserialized_value = from_pickle(attr_value)
|
self.instance.deserialized_value = from_pickle(attr_value)
|
||||||
self.instance.attr_strvalue = attr_strvalue
|
|
||||||
self.instance.attr_type = attr_type
|
self.instance.attr_type = attr_type
|
||||||
self.instance.attr_lockstring = attr_lockstring
|
self.instance.attr_lockstring = attr_lockstring
|
||||||
|
|
||||||
|
|
@ -232,22 +227,22 @@ class AttributeForm(forms.ModelForm):
|
||||||
instance = self.instance
|
instance = self.instance
|
||||||
instance.attr_key = self.cleaned_data['attr_key'] or "no_name_entered_for_attribute"
|
instance.attr_key = self.cleaned_data['attr_key'] or "no_name_entered_for_attribute"
|
||||||
instance.attr_category = self.cleaned_data['attr_category'] or None
|
instance.attr_category = self.cleaned_data['attr_category'] or None
|
||||||
instance.attr_value = self.cleaned_data['attr_value'] or None
|
instance.attr_value = self.cleaned_data['attr_value']
|
||||||
# convert the serialized string value into an object, if necessary, for AttributeHandler
|
# convert the serialized string value into an object, if necessary, for AttributeHandler
|
||||||
instance.attr_value = from_pickle(instance.attr_value)
|
instance.attr_value = from_pickle(instance.attr_value)
|
||||||
instance.attr_strvalue = self.cleaned_data['attr_strvalue'] or None
|
|
||||||
instance.attr_type = self.cleaned_data['attr_type'] or None
|
instance.attr_type = self.cleaned_data['attr_type'] or None
|
||||||
instance.attr_lockstring = self.cleaned_data['attr_lockstring']
|
instance.attr_lockstring = self.cleaned_data['attr_lockstring']
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def clean_attr_value(self):
|
def clean_attr_value(self):
|
||||||
"""
|
"""
|
||||||
Prevent Sets from being cleaned due to literal_eval failing on them. Otherwise they will be turned into str.
|
Prevent certain data-types from being cleaned due to literal_eval
|
||||||
|
failing on them. Otherwise they will be turned into str.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
data = self.cleaned_data['attr_value']
|
data = self.cleaned_data['attr_value']
|
||||||
initial = self.instance.attr_value
|
initial = self.instance.attr_value
|
||||||
if isinstance(initial, set) or isinstance(initial, _SaverSet):
|
if isinstance(initial, (set, _SaverSet, datetime)):
|
||||||
return initial
|
return initial
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
@ -270,16 +265,19 @@ class AttributeFormSet(forms.BaseInlineFormSet):
|
||||||
handler_name = "attributes"
|
handler_name = "attributes"
|
||||||
return getattr(related, handler_name)
|
return getattr(related, handler_name)
|
||||||
instances = super().save(commit=False)
|
instances = super().save(commit=False)
|
||||||
# self.deleted_objects is a list created when super of save is called, we'll remove those
|
|
||||||
for obj in self.deleted_objects:
|
for obj in self.deleted_objects:
|
||||||
|
# self.deleted_objects is a list created when super of save is called, we'll remove those
|
||||||
handler = get_handler(obj)
|
handler = get_handler(obj)
|
||||||
handler.remove(obj.attr_key, category=obj.attr_category)
|
handler.remove(obj.attr_key, category=obj.attr_category)
|
||||||
|
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
handler = get_handler(instance)
|
handler = get_handler(instance)
|
||||||
strattr = True if instance.attr_strvalue else False
|
|
||||||
value = instance.attr_value or instance.attr_strvalue
|
value = instance.attr_value
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handler.add(instance.attr_key, value, category=instance.attr_category, strattr=strattr,
|
handler.add(instance.attr_key, value,
|
||||||
|
category=instance.attr_category, strattr=False,
|
||||||
lockstring=instance.attr_lockstring)
|
lockstring=instance.attr_lockstring)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
# catch errors in nick templates and continue
|
# catch errors in nick templates and continue
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,11 @@ class PickledWidget(Textarea):
|
||||||
pass
|
pass
|
||||||
return super().render(name, value, attrs=attrs, renderer=renderer)
|
return super().render(name, value, attrs=attrs, renderer=renderer)
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, files, name):
|
||||||
|
dat = data.get(name)
|
||||||
|
# import evennia;evennia.set_trace()
|
||||||
|
return dat
|
||||||
|
|
||||||
|
|
||||||
class PickledFormField(CharField):
|
class PickledFormField(CharField):
|
||||||
"""
|
"""
|
||||||
|
|
@ -173,12 +178,6 @@ class PickledFormField(CharField):
|
||||||
except (ValueError, SyntaxError):
|
except (ValueError, SyntaxError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# handle datetime objects
|
|
||||||
try:
|
|
||||||
return datetime.strptime(value, "%Y-%m-%d %H:%M:%S.%f")
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# fall through to parsing the repr() of the data
|
# fall through to parsing the repr() of the data
|
||||||
try:
|
try:
|
||||||
value = repr(value)
|
value = repr(value)
|
||||||
|
|
@ -222,7 +221,6 @@ class PickledObjectField(models.Field):
|
||||||
# If the field doesn't have a default, then we punt to models.Field.
|
# If the field doesn't have a default, then we punt to models.Field.
|
||||||
return super().get_default()
|
return super().get_default()
|
||||||
|
|
||||||
# def to_python(self, value):
|
|
||||||
def from_db_value(self, value, *args):
|
def from_db_value(self, value, *args):
|
||||||
"""
|
"""
|
||||||
B64decode and unpickle the object, optionally decompressing it.
|
B64decode and unpickle the object, optionally decompressing it.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue