From 34bd4bd974562ee35e6cf7cdc600c7a05dd022a1 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Tue, 10 Feb 2015 23:29:20 +0000 Subject: [PATCH 1/4] first pass at to_python and get_prep_value on PageChooserBlock --- wagtail/wagtailadmin/blocks.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/wagtail/wagtailadmin/blocks.py b/wagtail/wagtailadmin/blocks.py index fa2b779bc..f529b369d 100644 --- a/wagtail/wagtailadmin/blocks.py +++ b/wagtail/wagtailadmin/blocks.py @@ -356,6 +356,23 @@ class PageChooserBlock(FieldBlock): from wagtail.wagtailadmin.widgets import AdminPageChooser return ModelChoiceField(queryset=Page.objects.all(), widget=AdminPageChooser) + def to_python(self, value): + from wagtail.wagtailcore.models import Page + + if value is None or isinstance(value, Page): + return value + else: + try: + return Page.objects.get(pk=value) # TODO: use the specific class in place of Page where appropriate + except Page.DoesNotExist: + return None + + def get_prep_value(self, value): + if isinstance(value, Page): + return value.id + else: + return value + # ======= # Chooser # ======= From d11205454beae6bf436d09e01d544656d3bbb533 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Wed, 11 Feb 2015 01:21:24 +0000 Subject: [PATCH 2/4] fix PageChooserBlock to return a page object rather than an ID. AdminPageChooser has been tweaked to accept either an ID or an instance as its input value, and FieldBlock now passes the result of value_from_datadict through to_python to deal with the case where the widget doesn't return our expected type. --- wagtail/wagtailadmin/blocks.py | 7 ++++++- wagtail/wagtailadmin/widgets.py | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/wagtail/wagtailadmin/blocks.py b/wagtail/wagtailadmin/blocks.py index f529b369d..c80f87bf0 100644 --- a/wagtail/wagtailadmin/blocks.py +++ b/wagtail/wagtailadmin/blocks.py @@ -330,7 +330,7 @@ class FieldBlock(Block): }) def value_from_datadict(self, data, files, prefix): - return self.field.widget.value_from_datadict(data, files, prefix) + return self.to_python(self.field.widget.value_from_datadict(data, files, prefix)) def clean(self, value): return self.field.clean(value) @@ -368,11 +368,16 @@ class PageChooserBlock(FieldBlock): return None def get_prep_value(self, value): + from wagtail.wagtailcore.models import Page if isinstance(value, Page): return value.id else: return value + def render_basic(self, value): + if value: + return format_html('{1}', value.url, value.title) + # ======= # Chooser # ======= diff --git a/wagtail/wagtailadmin/widgets.py b/wagtail/wagtailadmin/widgets.py index 946f5987e..2f445cc98 100644 --- a/wagtail/wagtailadmin/widgets.py +++ b/wagtail/wagtailadmin/widgets.py @@ -82,10 +82,14 @@ class AdminPageChooser(AdminChooser): self.target_content_type = content_type or ContentType.objects.get_for_model(Page) def render_html(self, name, value, attrs): - original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs) - model_class = self.target_content_type.model_class() - instance = self.get_instance(model_class, value) + if isinstance(value, model_class): + instance = value + value = value.pk + else: + instance = self.get_instance(model_class, value) + + original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs) return render_to_string("wagtailadmin/widgets/page_chooser.html", { 'widget': self, @@ -96,7 +100,11 @@ class AdminPageChooser(AdminChooser): }) def render_js_init(self, id_, name, value): - page = Page.objects.get(pk=value) if value else None + model_class = self.target_content_type.model_class() + if isinstance(value, model_class): + page = value + else: + page = self.get_instance(model_class, value) parent = page.get_parent() if page else None content_type = self.target_content_type From e01167ff1f3d7de995c770d67dd73b7aad8763f4 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Wed, 11 Feb 2015 10:34:11 +0000 Subject: [PATCH 3/4] pull re-usable methods of PageChooserBlock into an abstract superclass --- wagtail/wagtailadmin/blocks.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/wagtail/wagtailadmin/blocks.py b/wagtail/wagtailadmin/blocks.py index c80f87bf0..ec05e121b 100644 --- a/wagtail/wagtailadmin/blocks.py +++ b/wagtail/wagtailadmin/blocks.py @@ -349,31 +349,39 @@ class RichTextBlock(FieldBlock): def render_basic(self, value): return mark_safe('
' + expand_db_html(value) + '
') -class PageChooserBlock(FieldBlock): + +class ChooserBlock(FieldBlock): + """Abstract superclass for fields that implement a chooser interface (page, image, snippet etc)""" @cached_property def field(self): - from wagtail.wagtailcore.models import Page # TODO: allow limiting to specific page types - from wagtail.wagtailadmin.widgets import AdminPageChooser - return ModelChoiceField(queryset=Page.objects.all(), widget=AdminPageChooser) + return ModelChoiceField(queryset=self.target_model.objects.all(), widget=self.widget) def to_python(self, value): - from wagtail.wagtailcore.models import Page - - if value is None or isinstance(value, Page): + if value is None or isinstance(value, self.target_model): return value else: try: - return Page.objects.get(pk=value) # TODO: use the specific class in place of Page where appropriate - except Page.DoesNotExist: + return self.target_model.objects.get(pk=value) + except self.target_model.DoesNotExist: return None def get_prep_value(self, value): - from wagtail.wagtailcore.models import Page - if isinstance(value, Page): + if isinstance(value, self.target_model): return value.id else: return value +class PageChooserBlock(ChooserBlock): + @cached_property + def target_model(self): + from wagtail.wagtailcore.models import Page # TODO: allow limiting to specific page types + return Page + + @cached_property + def widget(self): + from wagtail.wagtailadmin.widgets import AdminPageChooser + return AdminPageChooser + def render_basic(self, value): if value: return format_html('{1}', value.url, value.title) From 628b7947dcbf6e7ea598d4e8351425cd6027bec7 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Wed, 11 Feb 2015 11:56:43 +0000 Subject: [PATCH 4/4] implement ImageChooserBlock --- wagtail/wagtailadmin/blocks.py | 30 +----------------------------- wagtail/wagtailadmin/widgets.py | 17 ++++++++++++----- wagtail/wagtailimages/blocks.py | 20 ++++++++++++++++++++ wagtail/wagtailimages/widgets.py | 3 +-- 4 files changed, 34 insertions(+), 36 deletions(-) create mode 100644 wagtail/wagtailimages/blocks.py diff --git a/wagtail/wagtailadmin/blocks.py b/wagtail/wagtailadmin/blocks.py index ec05e121b..1ab589833 100644 --- a/wagtail/wagtailadmin/blocks.py +++ b/wagtail/wagtailadmin/blocks.py @@ -385,36 +385,8 @@ class PageChooserBlock(ChooserBlock): def render_basic(self, value): if value: return format_html('{1}', value.url, value.title) - -# ======= -# Chooser -# ======= - -class ChooserBlock(Block): - class Meta: - default = None - - @property - def media(self): - return Media(js=['wagtailadmin/js/blocks/chooser.js']) - - def js_initializer(self): - return "Chooser('%s')" % self.definition_prefix - - def render_form(self, value, prefix='', error=None): - if self.label: - return format_html( - """ """, - label=self.label, prefix=prefix - ) else: - return format_html( - """""", - prefix=prefix - ) - - def value_from_datadict(self, data, files, prefix): - return 123 + return '' # =========== diff --git a/wagtail/wagtailadmin/widgets.py b/wagtail/wagtailadmin/widgets.py index 2f445cc98..5c283b26e 100644 --- a/wagtail/wagtailadmin/widgets.py +++ b/wagtail/wagtailadmin/widgets.py @@ -53,6 +53,17 @@ class AdminChooser(WidgetWithScript, widgets.Input): except model_class.DoesNotExist: return None + def get_instance_and_id(self, model_class, value): + if value is None: + return (None, None) + elif isinstance(value, model_class): + return (value, value.pk) + else: + try: + return (model_class.objects.get(pk=value), value) + except model_class.DoesNotExist: + return (None, None) + def value_from_datadict(self, data, files, name): # treat the empty string as None result = super(AdminChooser, self).value_from_datadict(data, files, name) @@ -83,11 +94,7 @@ class AdminPageChooser(AdminChooser): def render_html(self, name, value, attrs): model_class = self.target_content_type.model_class() - if isinstance(value, model_class): - instance = value - value = value.pk - else: - instance = self.get_instance(model_class, value) + instance, value = self.get_instance_and_id(model_class, value) original_field_html = super(AdminPageChooser, self).render_html(name, value, attrs) diff --git a/wagtail/wagtailimages/blocks.py b/wagtail/wagtailimages/blocks.py new file mode 100644 index 000000000..9e06ef28d --- /dev/null +++ b/wagtail/wagtailimages/blocks.py @@ -0,0 +1,20 @@ +from django.utils.functional import cached_property + +from wagtail.wagtailadmin.blocks import ChooserBlock + +class ImageChooserBlock(ChooserBlock): + @cached_property + def target_model(self): + from wagtail.wagtailimages.models import get_image_model + return get_image_model() + + @cached_property + def widget(self): + from wagtail.wagtailimages.widgets import AdminImageChooser + return AdminImageChooser + + def render_basic(self, value): + if value: + return value.get_rendition('original').img_tag() + else: + return '' diff --git a/wagtail/wagtailimages/widgets.py b/wagtail/wagtailimages/widgets.py index e628ed4e9..e2e104d26 100644 --- a/wagtail/wagtailimages/widgets.py +++ b/wagtail/wagtailimages/widgets.py @@ -19,10 +19,9 @@ class AdminImageChooser(AdminChooser): self.image_model = get_image_model() def render_html(self, name, value, attrs): + instance, value = self.get_instance_and_id(self.image_model, value) original_field_html = super(AdminImageChooser, self).render_html(name, value, attrs) - instance = self.get_instance(self.image_model, value) - return render_to_string("wagtailimages/widgets/image_chooser.html", { 'widget': self, 'original_field_html': original_field_html,