From 3199faf5da34e6cb05fc7dab4890a6f32b1f5217 Mon Sep 17 00:00:00 2001 From: aaloy Date: Fri, 28 Jun 2019 17:43:43 +0200 Subject: [PATCH] added validation on types before saving to avoid the big crash it produces in admin and website to save a wrong type value. Black formatted --- options/__init__.py | 11 +-- options/admin.py | 6 +- options/apps.py | 3 +- options/context_processors.py | 2 - options/management/commands/export_options.py | 4 +- options/managers.py | 3 +- options/models.py | 68 ++++++++++--------- 7 files changed, 46 insertions(+), 51 deletions(-) diff --git a/options/__init__.py b/options/__init__.py index d207673..4389f5c 100644 --- a/options/__init__.py +++ b/options/__init__.py @@ -1,19 +1,14 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, division, absolute_import from django import get_version from django.utils.translation import ugettext_lazy as _ FLOAT, INT, STRING = (0, 1, 2) -TYPE_CHOICES = ( - (FLOAT, _("Float")), - (INT, _("Integer")), - (STRING, _("String")), -) +TYPE_CHOICES = ((FLOAT, _("Float")), (INT, _("Integer")), (STRING, _("String"))) -default_app_config = 'options.apps.ConfigurationsConfig' +default_app_config = "options.apps.ConfigurationsConfig" -VERSION = (1, 0, 0, 'final', 0) +VERSION = (1, 1, 0, "final", 0) __version__ = get_version(VERSION) diff --git a/options/admin.py b/options/admin.py index 0ae281e..84cbcd6 100644 --- a/options/admin.py +++ b/options/admin.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import from django.contrib import admin - from options.models import Option @@ -10,5 +8,5 @@ from options.models import Option class OptionAdmin(admin.ModelAdmin): """Manage configuration options.""" - list_display = ['public_name', 'value'] - search_fields = ['public_name', 'name'] + list_display = ["public_name", "value"] + search_fields = ["public_name", "name"] diff --git a/options/apps.py b/options/apps.py index 948aaa3..55899c6 100644 --- a/options/apps.py +++ b/options/apps.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, division, absolute_import - import logging import six from django.apps import AppConfig @@ -15,6 +13,7 @@ logger = logging.getLogger(__name__) def create_default_options(sender, **kwargs): """Creates the defaults configuration options if they don't exists.""" from options.models import Option + for key, data in six.iteritems(DEFAULT_OPTIONS): if not Option.objects.filter(name=key).exists(): try: diff --git a/options/context_processors.py b/options/context_processors.py index f55f90e..736d5bb 100644 --- a/options/context_processors.py +++ b/options/context_processors.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, absolute_import - from options.models import Option diff --git a/options/management/commands/export_options.py b/options/management/commands/export_options.py index cf2054b..ad83b0c 100644 --- a/options/management/commands/export_options.py +++ b/options/management/commands/export_options.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, division, absolute_import - import json from django.core.management import BaseCommand @@ -17,6 +15,6 @@ class Command(BaseCommand): export[option.name] = { "value": option.value, "type": option.type, - "public_name": option.public_name + "public_name": option.public_name, } self.stdout.write(json.dumps(export, indent=4, sort_keys=True)) diff --git a/options/managers.py b/options/managers.py index 8d4f8de..7df0a98 100644 --- a/options/managers.py +++ b/options/managers.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, division, absolute_import + from django.db import models from options.settings import DEFAULT_EXCLUDE_USER_OPTIONS + class OptionManager(models.Manager): """Manager for options.""" diff --git a/options/models.py b/options/models.py index 89ac8e0..bb2ba72 100644 --- a/options/models.py +++ b/options/models.py @@ -1,41 +1,30 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, division, absolute_import + import six from django.conf import settings from django.db import models -from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from options import STRING, TYPE_CHOICES, INT, FLOAT from options.managers import OptionManager, UserOptionManager -@python_2_unicode_compatible + class BaseOption(models.Model): """Base model for system options and configurations.""" name = models.CharField( - verbose_name=_("Parameter"), - max_length=255, - unique=True, - db_index=True + verbose_name=_("Parameter"), max_length=255, unique=True, db_index=True ) public_name = models.CharField( verbose_name=_("Public name of the parameter"), max_length=255, unique=False, - db_index=True - ) - type = models.PositiveIntegerField( - choices=TYPE_CHOICES, - default=STRING + db_index=True, ) + type = models.PositiveIntegerField(choices=TYPE_CHOICES, default=STRING) value = models.CharField( - null=True, - blank=True, - default=None, - max_length=256, - verbose_name=_("Value") + null=True, blank=True, default=None, max_length=256, verbose_name=_("Value") ) is_list = models.BooleanField(default=False) @@ -45,19 +34,38 @@ class BaseOption(models.Model): def __str__(self): return "%s" % self.public_name + def _convert_value(self, value, type): + converter = {INT: int, FLOAT: float, STRING: six.text_type} + default_values = {INT: 0, FLOAT: 1.0, STRING: ""} + try: + option_value = converter.get(self.type, six.text_type)(self.value) + except ValueError: + option_value = default_values.get(self.type) + return option_value + def get_value(self): - """Gets the value with the proper type.""" - converter = { - INT: int, - FLOAT: float, - STRING: six.text_type - } + """Gets the value with the proper type. If the type is not + valid it would return the default value for the field, to avoid + problems with manual database modifications""" + if not self.is_list: - return converter.get(self.type, six.text_type)(self.value) + return self._convert_value(self.value, self.type) else: values = self.value.split(",") - return list(map(lambda item: converter.get(self.type, six.text_type)(item), values)) + return [self._convert_value(self.type, item) for item in values] + def clean(self): + from django.core.exceptions import ValidationError + + converter = {INT: int, FLOAT: float, STRING: six.text_type} + try: + converter.get(self.type, six.text_type)(self.value) + except ValueError: + raise ValidationError(_("Invalid value for this type.")) + + def save(self, *args, **kwargs): + self.clean() + super().save(*args, **kwargs) class Option(BaseOption): @@ -69,15 +77,13 @@ class Option(BaseOption): ordering = ["public_name"] - class UserOption(BaseOption): """Custom option for a user.""" - - user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="options", on_delete=models.CASCADE) - name = models.CharField( - verbose_name=_("Parameter"), - max_length=255, + + user = models.ForeignKey( + settings.AUTH_USER_MODEL, related_name="options", on_delete=models.CASCADE ) + name = models.CharField(verbose_name=_("Parameter"), max_length=255) objects = UserOptionManager() -- 2.51.2