def test_blank_false_is_implied_by_default(self): # passing a block list field = StreamField([("paragraph", blocks.CharBlock())], use_json_field=self.use_json_field) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) class MyStreamBlock(blocks.StreamBlock): paragraph = blocks.CharBlock() class Meta: required = False # passing a block instance field = StreamField(MyStreamBlock(), use_json_field=self.use_json_field) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) field = StreamField(MyStreamBlock(required=False), use_json_field=self.use_json_field) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([]) # passing a block class field = StreamField(MyStreamBlock, use_json_field=self.use_json_field) self.assertTrue(field.stream_block.required) with self.assertRaises(StreamBlockValidationError): field.stream_block.clean([])
def test_blank_field_is_not_required(self): # passing a block list field = StreamField( [("paragraph", blocks.CharBlock())], blank=True, use_json_field=self.use_json_field, ) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream class MyStreamBlock(blocks.StreamBlock): paragraph = blocks.CharBlock() class Meta: required = True # passing a block instance field = StreamField(MyStreamBlock(), blank=True, use_json_field=self.use_json_field) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream field = StreamField(MyStreamBlock(required=True), blank=True, use_json_field=self.use_json_field) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream # passing a block class field = StreamField(MyStreamBlock, blank=True, use_json_field=self.use_json_field) self.assertFalse(field.stream_block.required) field.stream_block.clean([]) # no validation error on empty stream
def test_internal_type(self): text = StreamField([("paragraph", blocks.CharBlock())], use_json_field=False) json = StreamField([("paragraph", blocks.CharBlock())], use_json_field=True) self.assertEqual(text.get_internal_type(), "TextField") self.assertEqual(json.get_internal_type(), "JSONField")
class ResourcesPage(Page): resources = StreamField( [ ( "resource", blocks.StreamBlock( [ ("resource_name", blocks.CharBlock()), ("information", blocks.RichTextBlock()), ( "structured_info_block", blocks.StreamBlock( [ ("heading", blocks.CharBlock()), ("body", blocks.RichTextBlock()), ] ), ), ] ), ) ], use_json_field=True, ) content_panels = Page.content_panels + [ FieldPanel("resources"), ]
class DeprecatedStreamModel(models.Model): body = StreamField( [ ("heading", blocks.CharBlock()), ("rich text", blocks.RichTextBlock()), ], )
class DocumentPage(Page): date_published = models.DateField("Last Updated", blank=True, null=True) body = StreamField( [( "section", blocks.StreamBlock([ ("header", CharBlock()), ("text", blocks.RichTextBlock()), ("quote", BlockQuoteBlock()), ( "subsection", blocks.StreamBlock([ ("header", CharBlock()), ("text", blocks.TextBlock()), ( "subsubsection", blocks.StreamBlock([ ("header", CharBlock()), ("text", blocks.TextBlock()), ]), ), ]), ), ]), )], null=True, blank=True, use_json_field=True, ) content_panels = Page.content_panels + [ FieldPanel("date_published"), FieldPanel("body"), ]
class StreamPage(Page): body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ExtendedImageChooserBlock()), ( "product", StructBlock([ ("name", CharBlock()), ("price", CharBlock()), ]), ), ("raw_html", RawHTMLBlock()), ( "books", StreamBlock([ ("title", CharBlock()), ("author", CharBlock()), ]), ), ], use_json_field=False, ) api_fields = ("body", ) content_panels = [ FieldPanel("title"), FieldPanel("body"), ] preview_modes = []
class InvalidStreamModel(models.Model): body = StreamField( [ ("heading", blocks.CharBlock()), ("rich text", blocks.RichTextBlock()), ], use_json_field=self.use_json_field, )
class DefaultRichBlockFieldPage(Page): body = StreamField( [ ("rich_text", RichTextBlock()), ], use_json_field=False, ) content_panels = Page.content_panels + [FieldPanel("body")]
class JSONStreamModel(models.Model): body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], use_json_field=True, )
def test_streamfield_count_argument_precedence(self): class TestStreamBlock(blocks.StreamBlock): heading = blocks.CharBlock() paragraph = blocks.RichTextBlock() class Meta: min_num = 2 max_num = 5 block_counts = {"heading": {"max_num": 1}} # args being picked up from the class definition field = StreamField(TestStreamBlock, use_json_field=self.use_json_field) self.assertEqual(field.stream_block.meta.min_num, 2) self.assertEqual(field.stream_block.meta.max_num, 5) self.assertEqual( field.stream_block.meta.block_counts["heading"]["max_num"], 1) # args being overridden by StreamField field = StreamField( TestStreamBlock, min_num=3, max_num=6, block_counts={"heading": { "max_num": 2 }}, use_json_field=self.use_json_field, ) self.assertEqual(field.stream_block.meta.min_num, 3) self.assertEqual(field.stream_block.meta.max_num, 6) self.assertEqual( field.stream_block.meta.block_counts["heading"]["max_num"], 2) # passing None from StreamField should cancel limits set at the block level field = StreamField( TestStreamBlock, min_num=None, max_num=None, block_counts=None, use_json_field=self.use_json_field, ) self.assertIsNone(field.stream_block.meta.min_num) self.assertIsNone(field.stream_block.meta.max_num) self.assertIsNone(field.stream_block.meta.block_counts)
class DeadlyStreamPage(Page): body = StreamField( [ ("title", DeadlyCharBlock()), ], use_json_field=False, ) content_panels = Page.content_panels + [ FieldPanel("body"), ]
class JSONMinMaxCountStreamModel(models.Model): body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], min_num=2, max_num=5, use_json_field=True, )
class CustomRichBlockFieldPage(Page): body = StreamField( [ ("rich_text", RichTextBlock(editor="custom")), ], use_json_field=False, ) content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("body"), ]
class InlineStreamPageSection(Orderable): page = ParentalKey("tests.InlineStreamPage", related_name="sections", on_delete=models.CASCADE) body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], use_json_field=False, ) panels = [FieldPanel("body")]
class DefaultStreamPage(Page): body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], default="", use_json_field=False, ) content_panels = [ FieldPanel("title"), FieldPanel("body"), ]
class InfoPage(Page): body = RichTextField(blank=True) additional_content = StreamField( [("embed", blocks.RawHTMLBlock())], null=True, blank=True, use_json_field=True, ) search_fields = Page.search_fields + [ index.SearchField("body"), ] content_panels = Page.content_panels + [ FieldPanel("body", classname="full"), FieldPanel("additional_content"), ]
class JSONBlockCountsStreamModel(models.Model): body = StreamField( [ ("text", CharBlock()), ("rich_text", RichTextBlock()), ("image", ImageChooserBlock()), ], block_counts={ "text": { "min_num": 1 }, "rich_text": { "max_num": 1 }, "image": { "min_num": 1, "max_num": 1 }, }, use_json_field=True, )
class StreamPage(Page): body = StreamField([ ("map", GoogleMapsBlock()), ("map_leaflet", LeafletBlock()), ( "map_struct", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("map", GoogleMapsBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_with_deprecated_geopanel", blocks.StructBlock( [ ("address", blocks.CharBlock(required=True)), ("map", GeoBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_leaflet", blocks.StructBlock( [ ( "address", GeoAddressBlock(required=True, geocoder=geocoders.MAPBOX), ), ("map", LeafletBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_with_zoom", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("zoom", GeoZoomBlock(required=False)), ( "map", GoogleMapsBlock(address_field="address", zoom_field="zoom"), ), ], icon="user", ), ), ( "map_struct_leaflet_with_zoom", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("zoom", GeoZoomBlock(required=False)), ( "map", LeafletBlock(address_field="address", zoom_field="zoom"), ), ], icon="user", ), ), ]) content_panels = Page.content_panels + [ StreamFieldPanel("body"), ]
class PageWithStreamField(Page): body = StreamField(BaseStreamBlock(), verbose_name="Page body", blank=True)
class BlogPage(HeadlessPreviewMixin, Page): date = models.DateField("Post date") advert = models.ForeignKey( "home.Advert", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) hero_image = models.ForeignKey( "images.CustomImage", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) book_file = models.ForeignKey( document_model_string, null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) featured_media = models.ForeignKey( "wagtailmedia.Media", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) author = models.ForeignKey( AuthorPage, null=True, blank=True, on_delete=models.SET_NULL, related_name="+" ) body = StreamField(StreamFieldBlock()) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) content_panels = Page.content_panels + [ FieldPanel("date"), ImageChooserPanel("hero_image"), StreamFieldPanel("body"), FieldPanel("tags"), InlinePanel("related_links", label="Related links"), InlinePanel("authors", label="Authors"), FieldPanel("author"), SnippetChooserPanel("advert"), DocumentChooserPanel("book_file"), MediaChooserPanel("featured_media"), ] @property def copy(self): return self def paginated_authors(self, info, **kwargs): return resolve_paginated_queryset(self.authors, info, **kwargs) graphql_fields = [ GraphQLString("date", required=True), GraphQLStreamfield("body"), GraphQLTag("tags"), GraphQLCollection( GraphQLForeignKey, "related_links", "home.blogpagerelatedlink", required=True, item_required=True, ), GraphQLCollection(GraphQLString, "related_urls", source="related_links.url"), GraphQLCollection(GraphQLString, "authors", source="authors.person.name"), GraphQLCollection( GraphQLForeignKey, "paginated_authors", "home.Author", is_paginated_queryset=True, ), GraphQLSnippet("advert", "home.Advert"), GraphQLImage("hero_image"), GraphQLDocument("book_file"), GraphQLMedia("featured_media"), GraphQLForeignKey("copy", "home.BlogPage"), GraphQLPage("author"), ]
class NewsPage(Page): main_story_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) main_story_heading = models.CharField(max_length=500, null=True, blank=True) main_story_copy = RichTextField(blank=True) action_network_href = models.URLField(blank=True, null=True) related_stories = StreamField( [("related_story", RelatedStoryBlock())], null=True, blank=True, collapsed=False, default=upcoming_events_as_related_stories, use_json_field=True, ) parent_page_type = ["news.NewsIndexPage"] # appname.ModelName search_fields = Page.search_fields + [ index.SearchField("main_story_copy"), ] content_panels = Page.content_panels + [ FieldPanel("main_story_image", ), FieldPanel("main_story_heading"), FieldPanel("main_story_copy", classname="full"), FieldPanel("related_stories"), ] def save(self, *args, **kwargs): if self.action_network_href: email.edit( self.action_network_href, { "subject": self.title, "body": render_block_to_string("news/news_page.html", "content", context={"page": self}), "from": "STL DSA", "reply_to": "*****@*****.**", }, settings.ACTIONNETWORK_API_KEYS["main"], ) else: response = email.create( self.title, # main_story_html + "".join(related_stories_html), render_block_to_string("news/news_page.html", "content", context={"page": self}), "STL DSA", "*****@*****.**", settings.ACTIONNETWORK_API_KEYS["main"], ) action_network_href = response.json()["_links"]["self"]["href"] self.action_network_href = action_network_href if self.go_live_at and self.go_live_at > timezone.now(): polling.poll( lambda: requests.get( self.action_network_href, headers={ "OSDI-API-Token": settings.ACTIONNETWORK_API_KEYS[ "main"] }, ).json().get("total_targeted", 0) > 0, step=1, timeout=10, step_function=lambda step: step + 2, ) email.schedule( f"{self.action_network_href}/schedule", self.go_live_at, settings.ACTIONNETWORK_API_KEYS["main"], ) super().save(*args, **kwargs) # Call the "real" save() method.
class AddedStreamFieldWithEmptyListDefaultPage(Page): body = StreamField([("title", CharBlock())], default=[], use_json_field=False)
def test_use_json_field_warning(self): message = "StreamField must explicitly set use_json_field argument to True/False instead of None." with self.assertWarnsMessage(RemovedInWagtail50Warning, message): StreamField([("paragraph", blocks.CharBlock())])
class TableBlockStreamPage(Page): table = StreamField([("table", TableBlock())], use_json_field=False) content_panels = [FieldPanel("table")]
class GeoStreamPage(Page): body = StreamField([ ("map", GoogleMapsBlock()), ("map_with_leaflet", LeafletBlock()), ( "map_struct", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("map", GoogleMapsBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_with_deprecated_geopanel", blocks.StructBlock( [ ("address", blocks.CharBlock(required=True)), ("map", GeoBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_with_leaflet", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("map", LeafletBlock(address_field="address")), ], icon="user", ), ), ( "map_struct_with_zoom", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("zoom", GeoZoomBlock(required=False)), ( "map", GoogleMapsBlock(address_field="address", zoom_field="zoom"), ), ], icon="user", ), ), ( "map_struct_leaflet_with_zoom", blocks.StructBlock( [ ("address", GeoAddressBlock(required=True)), ("zoom", GeoZoomBlock(required=False)), ( "map", LeafletBlock(address_field="address", zoom_field="zoom"), ), ], icon="user", ), ), ]) content_panels = Page.content_panels + [ StreamFieldPanel("body"), ] def get_context(self, request): data = super(GeoStreamPage, self).get_context(request) return data