Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Created June 14, 2012 19:05
Show Gist options
  • Save zacharyvoase/2932253 to your computer and use it in GitHub Desktop.
Save zacharyvoase/2932253 to your computer and use it in GitHub Desktop.
hstore-based localized fields in Django

This could be the basis of a localized field implementation using hstore.

It mostly uses django-hstore, but then some simple JS in the admin replaces the default textarea with a richer key/value-oriented widget. The textarea is hidden, and changes in the keys and values of the form are just JSON-serialized back into the textarea—so it works with django-hstore as it is currently implemented, allows for easier programmatic access + manipulation, and doesn't require a lot of hacking.

The main change, code-wise, is that the hstore textareas need to be given an HTML class of 'hstore'. This requires more work than I think should be necessary—you need to wire the model field up to a form field, and the form field up to a widget, which replaces the class.

from django.utils.translation import ugettext_lazy as _
from django_hstore import hstore, forms
class LocalizedField(hstore.DictionaryField):
"""The Django model field."""
description = _("A field that can be localized on a per-object basis.")
def formfield(self, **params):
params.setdefault('form_class', LocalizedFormField)
return super(hstore.DictionaryField, self).formfield(**params)
class LocalizedFieldWidget(forms.DictionaryFieldWidget):
def __init__(self, *args, **kwargs):
kwargs.setdefault('attrs', {}).setdefault('class', 'hstore')
super(LocalizedFieldWidget, self).__init__(*args, **kwargs)
class Media:
# The hstore-localized.js file from this gist.
js = ('admin/js/hstore-localized.js',)
class LocalizedFormField(forms.DictionaryField):
def __init__(self, *args, **kwargs):
kwargs['widget'] = LocalizedFieldWidget
super(DictionaryField, self).__init__(*args, **kwargs)
(function ($) {
var makeKVDiv = function (fieldName, i, key, value) {
var div = $("<div/>").attr({"class": "row", "data-i": i});
div.append($("<input type=text class=key />").attr({"name": fieldName + "/" + i + "/" + "key"}).val(key))
.append($("<input type=text class=value />").attr({"name": fieldName + "/" + i + "/" + "value"}).val(value))
.append($("<button class=delete />").text("Delete"));
return div;
};
var makeFieldsetForHstoreField = function (fieldName, initialData) {
var fieldset = $("<fieldset/>").attr({"class": 'hstore', "data-for-field": fieldName}),
i = 0;
$.each(initialData, function (key, value) {
fieldset.append(makeKVDiv(fieldName, i, key, value));
i += 1;
});
fieldset.append($("<button class=add />").text("Add"));
return fieldset;
};
var getObjForHstoreFieldset = function (fieldset) {
var obj = {};
fieldset.find("div.row").each(function () {
var key = $(this).find("input.key").val(),
value = $(this).find("input.value").val();
if (!(key === "" && value === "")) {
obj[key] = value;
}
});
return obj;
};
var updateTextFieldForFieldset = function (fieldset) {
var fieldName = fieldset.attr("data-for-field");
$("textarea[name=" + fieldName + "]").html(JSON.stringify(getObjForHstoreFieldset(fieldset)));
};
$("fieldset.hstore button.delete").live("click", function (ev) {
ev.preventDefault();
var fieldset = $(this).parents("fieldset.hstore");
$(this).parent('div.row').remove();
updateTextFieldForFieldset(fieldset);
});
$("fieldset.hstore button.add").live("click", function (ev) {
ev.preventDefault();
var fieldset = $(this).parents("fieldset.hstore"),
fieldName = fieldset.attr("data-for-field"),
indices = fieldset.find("div.row").map(function () { return parseInt($(this).attr("data-i"), 10); }).get(),
maxI = Math.max.apply(Math, indices);
fieldset.find("button.add").before(makeKVDiv(fieldName, maxI + 1, "", ""));
});
$("fieldset.hstore input").live("change", function () {
var fieldset = $(this).parents("fieldset.hstore");
updateTextFieldForFieldset(fieldset);
});
$(function () {
$("textarea.hstore").each(function (i, elem) {
var field = $(elem),
fieldName = field.attr('name'),
initialData = $.parseJSON(field.val());
field.hide().after(makeFieldsetForHstoreField(fieldName, initialData));
});
});
})(django.jQuery);
@chrispaolini
Copy link

Line 30, DictionaryField isn't imported anywhere.
Also in the JS I'm getting "Uncaught TypeError: Cannnot read properly 'length' of null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment