Skip to content

Instantly share code, notes, and snippets.

@odoku
Last active December 26, 2015 20:59
Show Gist options
  • Save odoku/7212337 to your computer and use it in GitHub Desktop.
Save odoku/7212337 to your computer and use it in GitHub Desktop.
DjangoのModelForm拡張。 Metaにerror_messagsを書けるようにしたので、バリデーションエラー時に出力される文言を簡単に変更出来る。
#-*-coding=utf8-*-
from __future__ import absolute_import, unicode_literals
from django.utils import six
from django.forms.forms import get_declared_fields
from django.forms.models import BaseModelForm
from django.forms.widgets import media_property
from django.utils.datastructures import SortedDict
def fields_for_model(model, fields=None, exclude=None, widgets=None, error_messages=None, formfield_callback=None):
"""
django.forms.models.fields_for_model()の拡張。
error_messagesを引数に取れる。
"""
field_list = []
ignored = []
opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
kwargs = {}
if not f.editable:
continue
if fields is not None and not f.name in fields:
continue
if exclude and f.name in exclude:
continue
if widgets and f.name in widgets:
kwargs['widget'] = widgets[f.name]
if error_messages and f.name in error_messages:
kwargs['error_messages'] = error_messages[f.name]
if formfield_callback is None:
formfield = f.formfield(**kwargs)
elif not callable(formfield_callback):
raise TypeError('formfield_callback must be a function or callable')
else:
formfield = formfield_callback(f, **kwargs)
if formfield:
field_list.append((f.name, formfield))
else:
ignored.append(f.name)
field_dict = SortedDict(field_list)
if fields:
field_dict = SortedDict(
[(f, field_dict.get(f)) for f in fields
if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)]
)
return field_dict
class ModelFormOptions(object):
def __init__(self, options=None):
self.model = getattr(options, 'model', None)
self.fields = getattr(options, 'fields', None)
self.exclude = getattr(options, 'exclude', None)
self.widgets = getattr(options, 'widgets', None)
self.error_messages = getattr(options, 'error_messages', None)
class ModelFormMetaclass(type):
"""
django.forms.models.ModelFormMetaclassの拡張。
Metaクラスにerror_messagesを書けるように変更。
"""
def __new__(cls, name, bases, attrs):
formfield_callback = attrs.pop('formfield_callback', None)
try:
parents = [b for b in bases if issubclass(b, ModelForm)]
except NameError:
# We are defining ModelForm itself.
parents = None
declared_fields = get_declared_fields(bases, attrs, False)
new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
attrs)
if not parents:
return new_class
if 'media' not in attrs:
new_class.media = media_property(new_class)
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
if opts.model:
# If a model is defined, extract form fields from it.
fields = fields_for_model(opts.model, opts.fields,
opts.exclude, opts.widgets,
opts.error_messages, formfield_callback)
# make sure opts.fields doesn't specify an invalid field
none_model_fields = [k for k, v in six.iteritems(fields) if not v]
missing_fields = set(none_model_fields) - \
set(declared_fields.keys())
if missing_fields:
message = 'Unknown field(s) (%s) specified for %s'
message = message % (', '.join(missing_fields),
opts.model.__name__)
raise FieldError(message)
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
fields.update(declared_fields)
else:
fields = declared_fields
new_class.declared_fields = declared_fields
new_class.base_fields = fields
return new_class
class ModelForm(six.with_metaclass(ModelFormMetaclass, BaseModelForm)):
pass
@odoku
Copy link
Author

odoku commented Oct 29, 2013

こんな感じ。

class UserForm(ModelForm):
    class Meta:
        model = User
        'error_messages' = {
            'username': {
                'required': 'Username is required.'
            }
        }

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