StreamField for native Django Admin or with Grappelli
Project description
Django StreamField
This is a simple realisation of StreamField's idea of Wagtail CMS for plain Django admin or with Grappelli skin.
Highlights
You can build your page with different kind of blocks. Sort them and sort the lists inside the blocks.
For editing content inside the blocks, it use native popup mechanism in Django admin interface. This allow you to use other field's widgets inside the blocks as is. For example, if you want to use in your blocks FileBrowseField from django-filebrowser, it will perfectly working without any additional settings.
Module also working with Grappelli Interface (Optional)
Contents
Installation
Requirements: django>=2.*
pip install django-streamfield
How to use
1. Create new app called streamblocks
Here will be your models that you will use as blocks
2. Put to streamblocks/models.py
some models
...that you want to use as blocks in your stream field. Add this models in STREAMBLOCKS_MODELS list. For example:
# streamblocks/models.py
# one object
class RichText(models.Model):
text = models.TextField(blank=True, null=True)
class Meta:
# This will use as name of block in admin
verbose_name="Text"
# list of objects
class ImageWithText(models.Model):
image = models.ImageField(upload_to="folder/")
text = models.TextField(null=True, blank=True)
# StreamField option for list of objects
as_list = True
class Meta:
verbose_name="Image with text"
verbose_name_plural="Images with text"
# Register blocks for StreamField as list of models
STREAMBLOCKS_MODELS = [
RichText,
ImageWithText
]
3. Add apps to settings.py
Add to INSTALLED_APPS
INSTALLED_APPS = [
...
'streamblocks',
'streamfield',
...
4. Add streamfield.urls to main urls.py
urlpatterns += [
path('streamfield/', include('streamfield.urls'))
]
5. Create templates for each models above, named as lowercase names of the models:
- streamblocks/templates/streamblocks/richtext.html
- streamblocks/templates/streamblocks/imagewithtext.html
And use block_content
as context.
Note: block_content will be single object if no 'as_list' property in your model, and will be a list of objects if there is.
<!--richtext.html-->
<div class="rich-text-block">
{{ block_content.text|safe }}
</div>
<!--imagewithtext.html-->
<ul class="image-with-text-block">
{% for block in block_content %}
<li>
<img src="{{ block.image.url }}" alt="">
<p>{{ block.text }}</p>
</li>
{% endfor %}
</ul>
Note: You may use also
block_template
option. For specify a block template file.
class RichText(models.Model):
...
block_template = "streamblocks/richtext.html"
...
Note: If you need unique string in block template, use
block_model
andblock_unique_id
Full list of variables in template context:
block_model
(lowercase of modelname - "richtext")block_unique_id
(unique string)block_content
(block data from db)as_list
(boolean)options
(block options)
6. Add StreamField to your model in your application
And add the models that you want to use in this stream as model_list
# models.py
from streamfield.fields import StreamField
from streamblocks.models import RichText, ImageWithText
class Page(models.Model):
stream = StreamField(
model_list=[
RichText,
ImageWithText
],
verbose_name="Page blocks"
)
Then if you have your 'page' in context, you can get content by field's cached property page.stream.render
...
<div class="content">
{{ page.stream.render }}
</div>
...
Or, if you need extra context in blocks, you may use template tag:
{% load streamfield_tags %}
...
<div class="content">
{% stream_render page.stream request=request %}
</div>
...
Admin
Custom admin class for block's models
Models will automaticaly register in admin.
If you want provide custom admin class,
first unregister models and register again, using StreamBlocksAdmin
class.
# streamblocks/admin.py
from django.contrib import admin
from streamfield.admin import StreamBlocksAdmin
from streamblocks.models import RichText
admin.site.unregister(RichText)
@admin.register(RichText)
class RichTextBlockAdmin(StreamBlocksAdmin, admin.ModelAdmin):
pass
Custom templates for render block models in admin
If you need to customize admin templates for block models wich you are using, you need to put templates named as described in section 3 (above). but put it inside "admin" folder.
For example for RichText block it will be:
streamblocks/templates/streamblocks/admin/richtext.html
As context use "form":
{{ form.text.value }}
You may also specify custom template as option:
class RichText(models.Model):
...
custom_admin_template = "streamblocks/admin/richtext.html"
...
Override how to render block's fields in admin
Create custom template for field with name as lowercased field widget name, and put it inside .../streamblocks/admin/fields/
folder.
For example for TextField widget (Textarea) of RichText block, it will be:
streamblocks/templates/streamblocks/admin/fields/textarea.html
As context use "field":
{{ field.value|default:""|safe }}
Override list of blocks for your StreamField in admin.py
Typicaly you set the blocks in your models as model_list
attribute of StreamField field.
But if you want to change blocks, for example depending on object, you can do it in admin site
of your model. Suppose you want to use only RichText
on page with id=1.
# admin.py
from streamfield.fields import StreamFieldWidget
from streamblocks.models import RichText
from .models import Page
class PageAdmin(models.Admin):
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if obj and obj.id == 1:
form.base_fields['stream'].widget = StreamFieldWidget(attrs={
'model_list': [ RichText ]
})
return form
Be careful with already existing blocks in db. If you remove them from admin, it produce error.
Block options
You may use options
property in your streamblocks models to add some additional options to your block.
This is useful with as_list
property when you need to add some options to whole block not separatly to each object of this list.
For example:
# streamblocks/models.py
# list of objects as slider
class Slide(models.Model):
image = models.ImageField(upload_to="folder/")
text = models.TextField(null=True, blank=True)
# StreamField option for list of objects
as_list = True
options = {
'autoplay': {
'label': 'Autoplay slider',
'type': 'checkbox',
'default': False
},
'width': {
'label': 'Slider size',
'type': 'select',
'default': 'wide',
'options': [
{'value': 'wide', 'name': 'Wide slider'},
{'value': 'narrow', 'name': 'Narrow slider'},
]
}
}
class Meta:
verbose_name="Slide"
verbose_name_plural="Slider"
In block template you can use this options as options.autoplay
In page admin you will see it on the bottom of this block.
Note: Now only "checkbox" and "select" type is working. You may apply options for all blocks with
STREAMFIELD_BLOCK_OPTIONS
(See Settings)
If you want to add block options to options, which was set in django settings, you may use extra_options
.
class Slide(models.Model):
...
extra_options = {
"autoplay": {
"label": "Autoplay",
"type": "checkbox",
"default": False
}
}
...
If you want to switch off options, which set in django settings, for current block. Set options={}
Special cases
Complex Blocks
You may use StreamField as part of blocks and create with that way complex structure
and use {{ block_content.<field_name>.render }}
Blocks without data in database. Only templates.
You may use it for widgets or separators or for whatever you want...
Just make the block model abstract
.
class EmptyBlock(models.Model):
class Meta:
abstract = True
verbose_name='Empty space'
and use streamblocks/templates/streamblocks/emptyblock.html
for your content.
Note: Don't forget to register a block in STREAMBLOCKS_MODELS
Add extra context to blocks
Supose, you need to add some data to blocks from global context.
Instead of using render property in template {{ page.stream.render }}
,
you need to use template tag stream_render
from streamfield_tags
with keywords arguments.
For example, if you have in page template request
and page
objects and want to use it in blocks:
{% load streamfield_tags %}
...
<div class="content">
{% stream_render page.stream request=request page=page %}
</div>
...
Cache for reduce the number of database requests
There is two ways of caching:
- Simple cache view with django cache
- Create additional field, for example: 'stream_rendered' and render to this field html in save method
def save(self, *args, **kwargs):
self.stream_rendered = self.stream.render
super().save(*args, **kwargs)
...and use this field in your html
Settings
# settings.py
STREAMFIELD_STREAMBLOCKS_APP_PATH
If your app streamblocks
located not in project root directory, you need to reflect it in settings.py
STREAMFIELD_STREAMBLOCKS_APP_PATH = 'yourapps.streamblocks'
STREAMFIELD_BASE_ADMIN_URL
If you change default admin site url ('/admin/'), reflect that in settings:
STREAMFIELD_BASE_ADMIN_URL = '/myadmin/'
STREAMFIELD_SHOW_ADMIN_HELP_TEXT
If you want to hide "Help" link in admin, above the "Add new block" link. Set:
STREAMFIELD_SHOW_ADMIN_HELP_TEXT = False
STREAMFIELD_DELETE_BLOCKS_FROM_DB
If you want to keep streamblock's instances in db, when you removing it from StreamField. Set:
STREAMFIELD_DELETE_BLOCKS_FROM_DB = False
It was default behavior in previous releases.
Note: If you delete entire object which contain StreamField, streamblock's instances will not be deleted. You should care about it by yourself.
STREAMFIELD_BLOCK_OPTIONS
You may use STREAMFIELD_BLOCK_OPTIONS
in settings.py to add some options to all blocks.
For example:
STREAMFIELD_BLOCK_OPTIONS = {
"margins": {
"label": "Margins",
"type": "checkbox",
"default": True
}
}
In block template use {{ options.margins }}
Note: Now only "checkbox" and "select" type is working.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_streamfield-1.3.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5cdaaa68a027ab2140472728dfce8131a47ce6391be201f750b316d85128f7af |
|
MD5 | 1c295076d2d8bda6864094770c5a2b3a |
|
BLAKE2b-256 | 32943a2a46257f2fb1bf8c3ac06f3c42062ab3d9cde3945afe24e631058711b8 |