############################ ## This goes in forms.py ## ############################ from django import forms from django.utils.translation import ugettext_lazy as _ from django.forms.util import ValidationError from django.forms.widgets import TextInput, PasswordInput class FixedCharField(forms.CharField): default_error_messages = { 'wrong_length': _(u'Ensure this value has exactly %(set_length)d characters (it has %(value_length)d).'), } def __init__(self, length, *args, **kwargs): self.length = length super(FixedCharField, self).__init__(*args, **kwargs) def clean(self, value): "Validates length. Returns a Unicode object." value = super(FixedCharField, self).clean(value) value_length = len(value) if value_length != self.length: raise ValidationError(self.error_messages['wrong_length'] % {'set_length': self.length, 'value_length': value_length}) return value def widget_attrs(self, widget): if isinstance(widget, (TextInput, PasswordInput)): # The HTML attribute is maxlength, not max_length. return {'maxlength': str(self.length)} ############################ ## This goes in models.py ## ############################ from myapp.forms import FixedCharField as FixedCharFormField from django.db import models from django.db import connection from django.conf import settings from django.utils.datastructures import DictWrapper class FixedCharField(models.Field): def __init__(self, verbose_name=None, name=None, primary_key=False, length=None, *args, **kwargs): if length == None: raise Exception("FixedCharField needs length attribute.") self.length = length # The kwargs will never include verbose_name nor name. (TODO: Really?) kwargs['verbose_name'] = verbose_name kwargs['name'] = name super(FixedCharField, self).__init__(*args, **kwargs) def db_type(self): data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") if settings.DATABASE_ENGINE == 'mysql': return 'char(%(length)s)' % data elif settings.DATABASE_ENGINE == 'sqlite3': return 'char(%(length)s)' % data elif settings.DATABASE_ENGINE == 'oracle': return 'VARCHAR2(%(length)s)' % data else: return models.CharField.db_type(self) # def get_internal_type(self): # return "FixedCharField" def to_python(self, value): return models.CharField(self, value) def formfield(self, **kwargs): defaults = {'form_class': FixedCharFormField, 'length': self.length} defaults.update(kwargs) return super(FixedCharField, self).formfield(**defaults)