Move modelform customisation logic to wagtail.admin.forms.models

pull/4727/merge
Matt Westcott 2018-09-12 19:17:33 +01:00
rodzic 5fabaeb8db
commit 36d56136e2
2 zmienionych plików z 83 dodań i 80 usunięć

Wyświetl plik

@ -1,97 +1,19 @@
import copy
from django import forms
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext as _
from modelcluster.forms import ClusterForm, ClusterFormMetaclass
from taggit.managers import TaggableManager
from wagtail.admin import widgets
from wagtail.core.models import Page
from .auth import * # NOQA
from .choosers import * # NOQA
from .collections import * # NOQA
from .models import * # NOQA
from .models import WagtailAdminModelForm
from .pages import * # NOQA
from .search import * # NOQA
from .view_restrictions import * # NOQA
# Form field properties to override whenever we encounter a model field
# that matches one of these types - including subclasses
FORM_FIELD_OVERRIDES = {
models.DateField: {'widget': widgets.AdminDateInput},
models.TimeField: {'widget': widgets.AdminTimeInput},
models.DateTimeField: {'widget': widgets.AdminDateTimeInput},
TaggableManager: {'widget': widgets.AdminTagWidget},
}
# Form field properties to override whenever we encounter a model field
# that matches one of these types exactly, ignoring subclasses.
# (This allows us to override the widget for models.TextField, but leave
# the RichTextField widget alone)
DIRECT_FORM_FIELD_OVERRIDES = {
models.TextField: {'widget': widgets.AdminAutoHeightTextInput},
}
# Callback to allow us to override the default form fields provided for each model field.
def formfield_for_dbfield(db_field, **kwargs):
# adapted from django/contrib/admin/options.py
overrides = None
# If we've got overrides for the formfield defined, use 'em. **kwargs
# passed to formfield_for_dbfield override the defaults.
if db_field.__class__ in DIRECT_FORM_FIELD_OVERRIDES:
overrides = DIRECT_FORM_FIELD_OVERRIDES[db_field.__class__]
else:
for klass in db_field.__class__.mro():
if klass in FORM_FIELD_OVERRIDES:
overrides = FORM_FIELD_OVERRIDES[klass]
break
if overrides:
kwargs = dict(copy.deepcopy(overrides), **kwargs)
return db_field.formfield(**kwargs)
class WagtailAdminModelFormMetaclass(ClusterFormMetaclass):
# Override the behaviour of the regular ModelForm metaclass -
# which handles the translation of model fields to form fields -
# to use our own formfield_for_dbfield function to do that translation.
# This is done by sneaking a formfield_callback property into the class
# being defined (unless the class already provides a formfield_callback
# of its own).
# while we're at it, we'll also set extra_form_count to 0, as we're creating
# extra forms in JS
extra_form_count = 0
def __new__(cls, name, bases, attrs):
if 'formfield_callback' not in attrs or attrs['formfield_callback'] is None:
attrs['formfield_callback'] = formfield_for_dbfield
new_class = super(WagtailAdminModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
return new_class
class WagtailAdminModelForm(ClusterForm, metaclass=WagtailAdminModelFormMetaclass):
@property
def media(self):
# Include media from formsets forms. This allow StreamField in InlinePanel for example.
media = super().media
for formset in self.formsets.values():
media += formset.media
return media
# Now, any model forms built off WagtailAdminModelForm instead of ModelForm should pick up
# the nice form fields defined in FORM_FIELD_OVERRIDES.
class WagtailAdminPageForm(WagtailAdminModelForm):
class Meta:

Wyświetl plik

@ -0,0 +1,81 @@
import copy
from django.db import models
from modelcluster.forms import ClusterForm, ClusterFormMetaclass
from taggit.managers import TaggableManager
from wagtail.admin import widgets
# Form field properties to override whenever we encounter a model field
# that matches one of these types - including subclasses
FORM_FIELD_OVERRIDES = {
models.DateField: {'widget': widgets.AdminDateInput},
models.TimeField: {'widget': widgets.AdminTimeInput},
models.DateTimeField: {'widget': widgets.AdminDateTimeInput},
TaggableManager: {'widget': widgets.AdminTagWidget},
}
# Form field properties to override whenever we encounter a model field
# that matches one of these types exactly, ignoring subclasses.
# (This allows us to override the widget for models.TextField, but leave
# the RichTextField widget alone)
DIRECT_FORM_FIELD_OVERRIDES = {
models.TextField: {'widget': widgets.AdminAutoHeightTextInput},
}
# Callback to allow us to override the default form fields provided for each model field.
def formfield_for_dbfield(db_field, **kwargs):
# adapted from django/contrib/admin/options.py
overrides = None
# If we've got overrides for the formfield defined, use 'em. **kwargs
# passed to formfield_for_dbfield override the defaults.
if db_field.__class__ in DIRECT_FORM_FIELD_OVERRIDES:
overrides = DIRECT_FORM_FIELD_OVERRIDES[db_field.__class__]
else:
for klass in db_field.__class__.mro():
if klass in FORM_FIELD_OVERRIDES:
overrides = FORM_FIELD_OVERRIDES[klass]
break
if overrides:
kwargs = dict(copy.deepcopy(overrides), **kwargs)
return db_field.formfield(**kwargs)
class WagtailAdminModelFormMetaclass(ClusterFormMetaclass):
# Override the behaviour of the regular ModelForm metaclass -
# which handles the translation of model fields to form fields -
# to use our own formfield_for_dbfield function to do that translation.
# This is done by sneaking a formfield_callback property into the class
# being defined (unless the class already provides a formfield_callback
# of its own).
# while we're at it, we'll also set extra_form_count to 0, as we're creating
# extra forms in JS
extra_form_count = 0
def __new__(cls, name, bases, attrs):
if 'formfield_callback' not in attrs or attrs['formfield_callback'] is None:
attrs['formfield_callback'] = formfield_for_dbfield
new_class = super(WagtailAdminModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
return new_class
class WagtailAdminModelForm(ClusterForm, metaclass=WagtailAdminModelFormMetaclass):
@property
def media(self):
# Include media from formsets forms. This allow StreamField in InlinePanel for example.
media = super().media
for formset in self.formsets.values():
media += formset.media
return media
# Now, any model forms built off WagtailAdminModelForm instead of ModelForm should pick up
# the nice form fields defined in FORM_FIELD_OVERRIDES.