Package: paper-streamfield
paper-streamfieldImplementation of the Wagtail's StreamField block picker for paper-admin.Compatibilitypython>= 3.9django>= 3.1paper-admin>= 6.0InstallationInstall the latest release with pip:pipinstallpaper-streamfieldAddstreamfieldto your INSTALLED_APPS in django'ssettings.py:INSTALLED_APPS=(# other apps"streamfield",)Addstreamfield.urlsto your URLconf:urlpatterns=patterns('',...path("streamfields/",include("streamfield.urls")),)How to useCreate some models that you want to use as blocks:# blocks/models.pyfromdjango.core.validatorsimportMaxValueValidator,MinValueValidatorfromdjango.dbimportmodelsfromdjango.utils.textimportTruncatorclassHeadingBlock(models.Model):text=models.TextField()rank=models.PositiveSmallIntegerField(default=1,validators=[MinValueValidator(1),MaxValueValidator(6)])classMeta:verbose_name="Heading"def__str__(self):returnTruncator(self.text).chars(128)classTextBlock(models.Model):text=models.TextField()classMeta:verbose_name="Text"def__str__(self):returnTruncator(self.text).chars(128)Register your models usingStreamBlockModelAdminclass.# blocks/admin.pyfromdjango.contribimportadminfromstreamfield.adminimportStreamBlockModelAdminfrom.modelsimportHeadingBlock,TextBlock@admin.register(HeadingBlock)classHeadingBlockAdmin(StreamBlockModelAdmin):list_display=["__str__","rank"]@admin.register(TextBlock)classTextBlockAdmin(StreamBlockModelAdmin):passCreate templates for each block model, named as lowercase
model name orsnake_casedmodel name.<!-- blocks/templates/blocks/headingblock.html --><!-- or --><!-- blocks/templates/blocks/heading_block.html --><h{{block.rank}}>{{ block.text }}</h{{ block.rank }}><!-- blocks/templates/blocks/textblock.html --><!-- or --><!-- blocks/templates/blocks/text_block.html --><div>{{ block.text|linebreaks }}</div>Add aStreamFieldto your model:# app/models.pyfromdjango.dbimportmodelsfromdjango.utils.translationimportgettext_lazyas_fromstreamfield.field.modelsimportStreamFieldclassPage(models.Model):stream=StreamField(_("stream"),models=["blocks.HeaderBlock","blocks.TextBlock",])classMeta:verbose_name="Page"Result:Now you can create some blocks:Userender_streamtemplate tag to render the stream field.<!-- app/templates/index.html -->{% load streamfield %}

{% render_stream page.stream %}Result:When working with block templates, it's important to note that
you have access to all variables from the parent context.Special casesUse custom template name or template engineYou can specify a template name or engine to render a specific block
withStreamBlockMetaclass in your block model:classHeadingBlock(models.Model):# ...classStreamBlockMeta:template_engine="jinja2"template_name="blocks/heading.html"Caching the rendered HTML of a blockYou can enable caching for specific blocks to optimize rendering.classHeadingBlock(models.Model):# ...classStreamBlockMeta:cache=Truecache_ttl=3600Once caching is enabled for the block, the rendered HTML will be stored
in cache, and subsequent requests will retrieve the cached content,
reducing the need for re-rendering.Note that the specified block willnotbe invalidated
when something changes in it.Adding context variables to all blocksYou can add context variables to all blocks in your StreamField by providing them
through therender_streamtemplate tag. This allows you to pass common context data
to customize the rendering of all content blocks consistently:<!-- app/templates/index.html -->{% load streamfield %}

{% render_stream page.stream classes="text text--small" %}<!-- blocks/templates/blocks/textblock.html --><divclass="{{ classes }}">{{ block.text|linebreaks }}</div>Adding context variables to a specific blockTo add context variables to a specific content block,
you must create a custom processor. A processor provides a mechanism for
customizing the context data and the rendering process of an individual block.Create a custom processor class that inherits fromstreamfield.processors.DefaultProcessor.fromstreamfield.processorsimportDefaultProcessorfromreviews.modelsimportReviewclassReviewsBlockProcessor(DefaultProcessor):defget_context(self,block):context=super().get_context(block)context["reviews"]=Review.objects.all()[:5]returncontextIn your block's model, specify the processor to use:classReviewsBlock(models.Model):# ...classStreamBlockMeta:processor="your_app.processors.ReviewsBlockProcessor"You can utilize theexceptions.SkipBlockfeature to conditionally skip the rendering
of a block. This can be useful, for example, when dealing with a block like "Articles"
that should only render when there are articles available. Example:fromstreamfield.processorsimportDefaultProcessorfromstreamfield.exceptionsimportSkipBlockfromarticles.modelsimportArticleclassArticlesBlockProcessor(DefaultProcessor):defget_context(self,block):context=super().get_context(block)articles=Article.object.all()[:3]iflen(articles)<3:# Skip block if not enough article instancesraiseSkipBlockcontext["articles"]=articlesreturncontextUsingrender_blocktemplate tagIn some cases, you may have a page that references a specific block through
aForeignKeyrelationship, and you want to render that referenced block on the page.
You can achieve this using the render_block template tag. Here's an example:# page/models.pyfromdjango.dbimportmodelsfromblocks.modelsimportTextBlockclassPage(models.Model):text_block=models.ForeignKey(TextBlock,on_delete=models.SET_NULL,blank=True,null=True)classMeta:verbose_name="Page"<!-- app/templates/page.html -->{% load streamfield %}<div><h1>Page Title</h1><div><h2>Text Block:</h2>{% render_block page.text_block %}</div></div>Customize block in admin interfaceYou can customize how a block is rendered in the admin interface
by specifyingstream_block_templatefield in theStreamBlockModelAdminclass:fromdjango.contribimportadminfromstreamfield.adminimportStreamBlockModelAdminfrom.modelsimportImageBlock@admin.register(ImageBlock)classImageBlockAdmin(StreamBlockModelAdmin):stream_block_template="blocks/admin/image.html"list_display=["__str__","title","alt"]<!-- blocks/admin/image.html -->{% extends "streamfield/admin/block.html" %}

{% block content %}<divclass="d-flex"><divclass="flex-grow-0 mr-2"><imgclass="preview"src="{{ instance.image }}"width="48"height="36"title="{{ instance.title }}"alt="{{ instance.alt }}"style="object-fit: cover"></div>{{ block.super }}</div>{% endblock content %}SettingsPAPER_STREAMFIELD_DEFAULT_PROCESSORDefault processor for content blocks.Default:"streamfield.processors.DefaultProcessor"PAPER_STREAMFIELD_DEFAULT_TEMPLATE_ENGINEDefault template engine forrender_streamtemplate tag.Default:None