Last active
December 26, 2015 20:59
-
-
Save odoku/7212337 to your computer and use it in GitHub Desktop.
DjangoのModelForm拡張。
Metaにerror_messagsを書けるようにしたので、バリデーションエラー時に出力される文言を簡単に変更出来る。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#-*-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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
こんな感じ。