Skip to content

Commit

Permalink
ver 1.3 updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jqb committed Nov 20, 2012
1 parent 1488bbd commit da24750
Show file tree
Hide file tree
Showing 18 changed files with 232 additions and 59 deletions.
9 changes: 9 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
graft .
include setup.py
include README.rst
include LICENSE
recursive-include django_settings/templates *
recursive-include django_settings/templates/admin *
recursive-include django_settings/templates/admin/django_settings *
exclude example*
exclude tests*
47 changes: 35 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
django-settings
===============

Current version: 1.2 beta
Current version: 1.3 beta


Django reusable application for storing global project settings in database.

By project settings I mean things like admin mail, some default values like
default_post_limit etc. Values are validated depending their type.
Begining with ver 1.2 you can register your own settings values.
Begining with ver 1.3 you can register your own settings values.


API
---

IMPORTANT: changed in version 1.2, old api still works but caching do not work
with it.
IMPORTANT: changed in version 1.3, old api still works but caching do not work with it.

::

import django_settings

# getting values
# this will raise django_settings.models.Setting.DoesNotExist
# exception if value not exists
# if value is not in cache it will be cached
django_settings.get('post_limit')

# if you not sure value exists you can pass "default" parameter,
# at this point default is NOT cached
django_settings.get('post_limit', default=10)

# set values - cache is updated for "get" and "exists" method
Expand Down Expand Up @@ -87,12 +92,8 @@ Remember to define model as abstract, this is important because of how django
treats model classes.


When ``register`` function will be invoked all your models will be available in
``django_settings.models`` module, so django can treat them as regular models.


There is ability to setup some defaults via project settings.py file.
Those settings will be setup ONLY if they not exists in db yet.
Those settings will be setup ONLY if they not already exists in db.

::

Expand All @@ -117,11 +118,33 @@ You can manipulate setting via your admin interface.
Changelog
---------

1.2 beta - two big things has been changed.
1.3 beta - several improvements has been made since ver 1.0

1) from now you can extend settings with your own types
2) new api with caching mechanism introduced
1) setting name need to be unique now (backward incompatiblity)
2) from now you can extend settings with your own types using
`django_settings.register` function
3) new api with caching mechanism introduced
4) admin interface has been improved, action to clear cache
keys only used by the package added

Some tests has been added for core functionality.


Backward incompatible changes

`django_settings.models.Setting` name need to be unique now, however
ver 1.3 still allows it to not to be unique. Just set `DJANGO_SETTINGS_UNIQUE_NAMES`
application setting to False (True is by default).


Author
------

* Kuba Janoszek (kuba.janoszek@gmail.com)


Contributors
------------

* `Trey Hunner <https://github.com/treyhunner/>`_

2 changes: 1 addition & 1 deletion django_settings/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
VERSION = '1.3beta'
VERSION = '1.3-beta'

__version__ = VERSION
__author__ = "Kuba Janoszek"
Expand Down
55 changes: 53 additions & 2 deletions django_settings/admin.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,74 @@
# -*- coding: utf-8 -*-
# framework
from django.contrib import admin
from django.utils.translation import ugettext as _
from django.contrib.admin.views import main as admin_views
from django.contrib.contenttypes import generic
from django.utils.translation import ugettext as _
from django.http import Http404


# module local
from . import models, forms, dataapi


class ChangeList(admin_views.ChangeList):
@property
def available_settings_models(self):
return dataapi.data.type_names()


# Additional columns
def get_setting_value(obj):
# return obj.setting_object.value
return dataapi.data.get(obj.name)
get_setting_value.short_description = _('Value')
# end


# Actions
def clear_cache(modeladmin, request, queryset):
data = dataapi.data
cache_keys = []
add_key = cache_keys.append
for name in queryset.values_list('name', flat=True):
add_key(data.get._cache_key(name))
add_key(data.exists._cache_key(name))
data.cache.delete_many(cache_keys)
clear_cache.short_description = _("Clear cache for settings")
# end


class SettingAdmin(admin.ModelAdmin):
model = models.Setting
form = forms.SettingForm
list_display = ('name', 'setting_type', get_setting_value)
search_fields = ('name', 'setting_type__name')
actions = [
clear_cache
]

def get_setting_model(self, obj, request):
if obj:
return obj.setting_object.__class__
try:
typename = request.REQUEST['typename'] # NOTE: both lines might
return dataapi.data.model_for_name(typename) # raise KeyError
except KeyError:
raise Http404

def get_form(self, request, obj=None, **kwargs):
Form = super(SettingAdmin, self).get_form(request, obj=obj, **kwargs)
Form.setting_model = self.get_setting_model(obj, request)
return Form

def get_changelist(self, request, **kwargs):
return ChangeList

def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
setting_model = self.get_setting_model(obj, request)
response = super(SettingAdmin, self).render_change_form(
request, context, add=add, change=change, form_url=form_url, obj=obj)
response.context_data['setting_model_name'] = setting_model.__name__
return response


admin.site.register(models.Setting, SettingAdmin)
Expand Down
4 changes: 2 additions & 2 deletions django_settings/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ def _kwargs_to_key(self, kwargs):
return ":".join(["%s:%s" % (k,v) for k, v in kwargs.items()])

def _cache_key_for_method(self, method_name, *args, **kwargs):
key = "%s:%s:%s:%s" % (
key = ":".join((
self.key_prefix,
method_name,
self._args_to_key(args),
self._kwargs_to_key(kwargs),
)
))
return key

def _cache_key(self, *args, **kwargs):
Expand Down
6 changes: 6 additions & 0 deletions django_settings/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from django.conf import settings

DJANGO_SETTINGS_UNIQUE_NAMES = getattr(settings, 'DJANGO_SETTINGS_UNIQUE_NAMES', True)
DJANGO_SETTINGS = getattr(settings, 'DJANGO_SETTINGS', None) or {}

19 changes: 17 additions & 2 deletions django_settings/dataapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
'Q' : 'django.db.models',
})
db = lazyimport({
'registry': 'django_settings.models',
'Setting': 'django_settings.models',
'registry' : 'django_settings.models',
'Setting' : 'django_settings.models',
})


Expand All @@ -29,10 +29,22 @@ def __call__(self, type_name, name, value, validate=True):
return cached_value


NIL = type('NIL', (object,), {})()


class dataapi_get_method_proxy(MethodProxy):
def _cache_key(self, name): # should accept only "name"
return self._cache_key_for_method('get', name)

def __call__(self, name, **kwargs):
default = kwargs.pop('default', NIL)
try:
return super(dataapi_get_method_proxy, self).__call__(name, **kwargs)
except db.Setting.DoesNotExist:
if default != NIL:
return default
raise


class DataAPIMetaclass(type):
registry = []
Expand Down Expand Up @@ -71,6 +83,9 @@ def contenttypes_queryset(self):
query = query | django.Q(name=name)
return django.ContentType.objects.filter(query)

def model_for_name(self, name):
return db.registry[name]

def type_names(self):
return db.registry.names()

Expand Down
44 changes: 15 additions & 29 deletions django_settings/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,24 @@
class SettingForm(forms.ModelForm):
class Meta:
model = django_settings.db.Setting
fields = ('setting_type', 'name')
fields = ('name', 'value')

# this might be predefined with admin interface
setting_model = None

value = forms.CharField()

def __init__(self, *a, **kw):
forms.ModelForm.__init__(self, *a, **kw)
self.fields['setting_type'].queryset = django_settings.data.contenttypes_queryset()
def __init__(self, *args, **kwargs):
super(SettingForm, self).__init__(*args, **kwargs)
self.fields['value'] = self.setting_model._meta.get_field('value').formfield()

instance = kw.get('instance')
instance = kwargs.get('instance')
if instance:
self.fields['value'].initial = getattr(instance.setting_object, 'value', '')

self._change_callback = kw.get(
'change_callback',
django_settings.DataAPI.setting_changed # classmethod
)

def clean(self):
cd = self.cleaned_data
SettingClass = cd['setting_type'].model_class()
SettingClassForm = modelform_factory(SettingClass)

value = cd.get('value')
if not value:
self._errors['value'] = self.error_class(['Value field cannot be empty.'])
else:
setting_form = SettingClassForm({'value': cd['value']})
if not setting_form.is_valid():
del cd['value']
self._errors['value'] = self.error_class(['Value is not valid.'])
return cd
def setting_changed(self, instance):
django_settings.DataAPI.setting_changed(instance)
return instance

def save(self, *args, **kwargs):
cd = self.clean()
Expand All @@ -48,12 +35,11 @@ def save(self, *args, **kwargs):
setting_object = self.instance.setting_object
setting_object.delete()

SettingClass = cd['setting_type'].model_class()
setting_object = SettingClass.objects.create(value=cd['value'])
setting_object = self.setting_model.objects.create(value=cd['value'])

kwargs['commit'] = False
kwargs.update(commit=False)
instance = forms.ModelForm.save(self, *args, **kwargs)
instance.setting_object = setting_object
instance.save()
self._change_callback(instance)
return instance
return self.setting_changed(instance)

11 changes: 5 additions & 6 deletions django_settings/management.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
# -*- coding: utf-8 -*-
from django.db.models import signals
from django.conf import settings
from django.contrib.contenttypes.models import ContentType

import django_settings
from . import dataapi, conf, models


DEFAULT_SETTINGS = getattr(settings, 'DJANGO_SETTINGS', {})
DEFAULT_SETTINGS = getattr(conf, 'DJANGO_SETTINGS', {})


def initialize_data(sender, **kwargs):
for name, type_name_and_value in DEFAULT_SETTINGS.items():
type_name, value = type_name_and_value

if not django_settings.exists(type_name):
django_settings.set(type_name, name, value)
if not dataapi.data.exists(type_name):
dataapi.data.set(type_name, name, value)

signals.post_syncdb.connect(initialize_data, sender=django_settings.models)
signals.post_syncdb.connect(initialize_data, sender=models)
5 changes: 4 additions & 1 deletion django_settings/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from django.contrib.contenttypes import generic
from django.utils.translation import ugettext_lazy as _

# app local
from . import conf


class Model(models.Model): # Base class for db setting
class Meta:
Expand Down Expand Up @@ -56,7 +59,7 @@ class Meta:
setting_id = models.PositiveIntegerField()
setting_object = generic.GenericForeignKey('setting_type', 'setting_id')

name = models.CharField(max_length=255)
name = models.CharField(max_length=255, unique=conf.DJANGO_SETTINGS_UNIQUE_NAMES)



Expand Down
10 changes: 10 additions & 0 deletions django_settings/templates/admin/django_settings/change_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "admin/change_form.html" %}
{% load i18n %}

{% block content_title %}
<h1>
{% if add %}{% trans "Add" %}{% endif %}
{% if change %}{% trans "Change" %}{% endif %}
{{ setting_model_name }}
</h1>
{% endblock %}
26 changes: 26 additions & 0 deletions django_settings/templates/admin/django_settings/change_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "admin/change_list.html" %}
{% load i18n settings_admin_urls %}

{% block object-tools %}
{% if has_add_permission %}
<div class="object-tools">
<select class="select-content-type">
<option>
-- {% blocktrans with cl.opts.verbose_name|lower as name %}Create new {{ name }}{% endblocktrans %} --
</option>
{% for type_name in cl.available_settings_models %}
<option data-url="{{ cl|add_url_for_setting_type:type_name }}">
{{ type_name }}
</option>
{% endfor %}
</select>
<script type="text/javascript">
(function($) {
$('select.select-content-type').change(function() {
window.location = $('select.select-content-type option:selected').attr('data-url');
});
})(django.jQuery);
</script>
</div>
{% endif %}
{% endblock %}
1 change: 1 addition & 0 deletions django_settings/templatetags/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
Loading

0 comments on commit da24750

Please sign in to comment.