Simple app to add configuration options to a Django project.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #1 from aaloy/master

added validation on types before saving to avoid "the big crash"

authored by

Marcos Gabarda and committed by
GitHub
35fac175 537ed368

+46 -51
+3 -8
options/__init__.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 3 2 4 3 from django import get_version 5 4 from django.utils.translation import ugettext_lazy as _ 6 5 7 6 8 7 FLOAT, INT, STRING = (0, 1, 2) 9 - TYPE_CHOICES = ( 10 - (FLOAT, _("Float")), 11 - (INT, _("Integer")), 12 - (STRING, _("String")), 13 - ) 8 + TYPE_CHOICES = ((FLOAT, _("Float")), (INT, _("Integer")), (STRING, _("String"))) 14 9 15 - default_app_config = 'options.apps.ConfigurationsConfig' 10 + default_app_config = "options.apps.ConfigurationsConfig" 16 11 17 - VERSION = (1, 0, 0, 'final', 0) 12 + VERSION = (1, 1, 0, "final", 0) 18 13 19 14 __version__ = get_version(VERSION)
+2 -4
options/admin.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, absolute_import 3 2 4 3 from django.contrib import admin 5 - 6 4 from options.models import Option 7 5 8 6 ··· 10 8 class OptionAdmin(admin.ModelAdmin): 11 9 """Manage configuration options.""" 12 10 13 - list_display = ['public_name', 'value'] 14 - search_fields = ['public_name', 'name'] 11 + list_display = ["public_name", "value"] 12 + search_fields = ["public_name", "name"]
+1 -2
options/apps.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 3 - 4 2 import logging 5 3 import six 6 4 from django.apps import AppConfig ··· 15 13 def create_default_options(sender, **kwargs): 16 14 """Creates the defaults configuration options if they don't exists.""" 17 15 from options.models import Option 16 + 18 17 for key, data in six.iteritems(DEFAULT_OPTIONS): 19 18 if not Option.objects.filter(name=key).exists(): 20 19 try:
-2
options/context_processors.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, absolute_import 3 - 4 2 from options.models import Option 5 3 6 4
+1 -3
options/management/commands/export_options.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 3 - 4 2 import json 5 3 6 4 from django.core.management import BaseCommand ··· 17 15 export[option.name] = { 18 16 "value": option.value, 19 17 "type": option.type, 20 - "public_name": option.public_name 18 + "public_name": option.public_name, 21 19 } 22 20 self.stdout.write(json.dumps(export, indent=4, sort_keys=True))
+2 -1
options/managers.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 2 + 3 3 4 4 from django.db import models 5 5 from options.settings import DEFAULT_EXCLUDE_USER_OPTIONS 6 + 6 7 7 8 class OptionManager(models.Manager): 8 9 """Manager for options."""
+37 -31
options/models.py
··· 1 1 # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 2 + 3 3 4 4 import six 5 5 from django.conf import settings 6 6 from django.db import models 7 - from django.utils.encoding import python_2_unicode_compatible 8 7 from django.utils.translation import ugettext_lazy as _ 9 8 10 9 from options import STRING, TYPE_CHOICES, INT, FLOAT 11 10 from options.managers import OptionManager, UserOptionManager 12 11 13 - @python_2_unicode_compatible 12 + 14 13 class BaseOption(models.Model): 15 14 """Base model for system options and configurations.""" 16 15 17 16 name = models.CharField( 18 - verbose_name=_("Parameter"), 19 - max_length=255, 20 - unique=True, 21 - db_index=True 17 + verbose_name=_("Parameter"), max_length=255, unique=True, db_index=True 22 18 ) 23 19 public_name = models.CharField( 24 20 verbose_name=_("Public name of the parameter"), 25 21 max_length=255, 26 22 unique=False, 27 - db_index=True 28 - ) 29 - type = models.PositiveIntegerField( 30 - choices=TYPE_CHOICES, 31 - default=STRING 23 + db_index=True, 32 24 ) 25 + type = models.PositiveIntegerField(choices=TYPE_CHOICES, default=STRING) 33 26 value = models.CharField( 34 - null=True, 35 - blank=True, 36 - default=None, 37 - max_length=256, 38 - verbose_name=_("Value") 27 + null=True, blank=True, default=None, max_length=256, verbose_name=_("Value") 39 28 ) 40 29 is_list = models.BooleanField(default=False) 41 30 ··· 45 34 def __str__(self): 46 35 return "%s" % self.public_name 47 36 37 + def _convert_value(self, value, type): 38 + converter = {INT: int, FLOAT: float, STRING: six.text_type} 39 + default_values = {INT: 0, FLOAT: 1.0, STRING: ""} 40 + try: 41 + option_value = converter.get(self.type, six.text_type)(self.value) 42 + except ValueError: 43 + option_value = default_values.get(self.type) 44 + return option_value 45 + 48 46 def get_value(self): 49 - """Gets the value with the proper type.""" 50 - converter = { 51 - INT: int, 52 - FLOAT: float, 53 - STRING: six.text_type 54 - } 47 + """Gets the value with the proper type. If the type is not 48 + valid it would return the default value for the field, to avoid 49 + problems with manual database modifications""" 50 + 55 51 if not self.is_list: 56 - return converter.get(self.type, six.text_type)(self.value) 52 + return self._convert_value(self.value, self.type) 57 53 else: 58 54 values = self.value.split(",") 59 - return list(map(lambda item: converter.get(self.type, six.text_type)(item), values)) 55 + return [self._convert_value(self.type, item) for item in values] 60 56 57 + def clean(self): 58 + from django.core.exceptions import ValidationError 59 + 60 + converter = {INT: int, FLOAT: float, STRING: six.text_type} 61 + try: 62 + converter.get(self.type, six.text_type)(self.value) 63 + except ValueError: 64 + raise ValidationError(_("Invalid value for this type.")) 65 + 66 + def save(self, *args, **kwargs): 67 + self.clean() 68 + super().save(*args, **kwargs) 61 69 62 70 63 71 class Option(BaseOption): ··· 69 77 ordering = ["public_name"] 70 78 71 79 72 - 73 80 class UserOption(BaseOption): 74 81 """Custom option for a user.""" 75 - 76 - user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="options", on_delete=models.CASCADE) 77 - name = models.CharField( 78 - verbose_name=_("Parameter"), 79 - max_length=255, 82 + 83 + user = models.ForeignKey( 84 + settings.AUTH_USER_MODEL, related_name="options", on_delete=models.CASCADE 80 85 ) 86 + name = models.CharField(verbose_name=_("Parameter"), max_length=255) 81 87 82 88 objects = UserOptionManager() 83 89