{"id":13415813,"url":"https://github.com/raagin/django-streamfield","last_synced_at":"2025-03-14T23:31:08.583Z","repository":{"id":34829488,"uuid":"184009451","full_name":"raagin/django-streamfield","owner":"raagin","description":"This is a simple realisation of StreamField's idea from Wagtail CMS for plain Django admin or with Grappelli skin.","archived":false,"fork":false,"pushed_at":"2024-09-18T13:56:27.000Z","size":1385,"stargazers_count":124,"open_issues_count":2,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-09-29T00:41:09.475Z","etag":null,"topics":["django-admin","django-streamfield"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raagin.png","metadata":{"files":{"readme":"README.md","changelog":"changes2.0.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-29T06:04:20.000Z","updated_at":"2024-09-18T13:54:49.000Z","dependencies_parsed_at":"2024-01-11T19:15:41.045Z","dependency_job_id":"7b5a331f-e664-442d-a13b-b3f9bca7f235","html_url":"https://github.com/raagin/django-streamfield","commit_stats":{"total_commits":149,"total_committers":6,"mean_commits":"24.833333333333332","dds":"0.046979865771812124","last_synced_commit":"ae7a05696f51c37ece2d95643adafde48d6774ce"},"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raagin%2Fdjango-streamfield","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raagin%2Fdjango-streamfield/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raagin%2Fdjango-streamfield/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raagin%2Fdjango-streamfield/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raagin","download_url":"https://codeload.github.com/raagin/django-streamfield/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243663456,"owners_count":20327299,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django-admin","django-streamfield"],"created_at":"2024-07-30T21:00:52.300Z","updated_at":"2025-03-14T23:31:08.567Z","avatar_url":"https://github.com/raagin.png","language":"Python","funding_links":[],"categories":["Third-Party Packages"],"sub_categories":["Model Fields","Models"],"readme":"# Django StreamField\n\nThis is a simple realisation of StreamField's idea of Wagtail CMS for plain Django admin or with Grappelli skin.\nStable version: 2.3.2\nDjango \u003c= 5.1\n\n[Major changes (1.4.5 \u003e 2)](changes2.0.md)\n\n## Highlights\nYou can build your page with different kind of blocks. \nSort them and sort the lists inside the blocks.\n\n**The blocks here are regular instances of Django models.** For editing content inside the blocks, it use native popup mechanism in Django admin interface.\nThis allow you to use other field's widgets inside the blocks as is.\nFor example, if you want to use in your blocks FileBrowseField\nfrom django-filebrowser, it will perfectly working \nwithout any additional settings.\n\nModule also working with [Grappelli Interface](https://github.com/sehmaschine/django-grappelli) (Optional)\n\n![django-streamfield demo screenshot](https://raagin.ru/media/uploads/django-streamfield-2.jpg)\n\n## Contents\n\n- [Installation](#installation)\n- [How to use](#how-to-use)\n- [Admin](#admin)\n  - [Custom admin class for block's models](#custom-admin-class-for-blocks-models)\n  - [Custom templates for render block models in admin](#custom-templates-for-render-block-models-in-admin)\n  - [Override how to render block's fields in admin](#override-how-to-render-blocks-fields-in-admin)\n  - [Override list of blocks for your StreamField in admin.py](#override-list-of-blocks-for-your-streamfield-in-adminpy)\n- [Block options](#block-options)\n- [Special cases](#special-cases)\n  - [Complex Blocks](#complex-blocks)    \n  - [Blocks without data in database. Only templates](#blocks-without-data-in-database-only-templates)\n  - [Add extra context to blocks](#add-extra-context-to-blocks)\n  - [Get field data as list](#get-field-data-as-list)\n  - [Cache for reduce the number of database requests](#cache-for-reduce-the-number-of-database-requests)\n  - [Create a copy](#create-a-copy)\n  - [Add block programarly](#add-block-programarly)\n  - [Set size of block's popup window](#set-size-of-blocks-popup-window)\n- [Settings](#settings)\n- [Migrations](#migrations)\n\n## Installation\n\nRequirements: `django\u003e=3.1`\n\n`pip install django-streamfield`\n\n## How to use\n- Create streamblocks app with your models\n- Add streamfield and streamblocks to INSTALLED_APPS\n- Add streamfield.urls\n- Create templates for streamblocks\n- Add StreamField to your model\n- Use it in templates\n\n**1. Create new app called `streamblocks` and put there some models**\n\n...that you want to use as blocks in your StreamField.  \nAdd them to the list `STREAMBLOCKS_MODELS`.\nFor example:\n\n```python\n# streamblocks/models.py\n\n# one object\nclass RichText(models.Model):\n    text = models.TextField(blank=True, null=True)   \n    \n    def __str__(self):\n        # This text will be added to block title name. \n        # For better navigation when block is collapsed.\n        return self.text[:30]\n\n    class Meta:\n        # This will use as name of block in admin\n        # See also STREAMFIELD_BLOCK_TITLE in settings\n        verbose_name=\"Text\"\n\n# list of objects\nclass ImageWithText(models.Model):\n    image = models.ImageField(upload_to=\"folder/\")\n    text = models.TextField(null=True, blank=True)\n    \n    # StreamField option for list of objects\n    as_list = True\n    \n    def __str__(self):\n        # This text will be added to block title name.\n        # For better navigation when block is collapsed.\n        return self.text[:30]\n\n    class Meta:\n        verbose_name=\"Image with text\"\n        verbose_name_plural=\"Images with text\"\n\n# Register blocks for StreamField as list of models\nSTREAMBLOCKS_MODELS = [\n    RichText,\n    ImageWithText\n]\n```\n\u003e Important!: Don't use 'as_list', 'options', 'extra_options' as models field names, because they are used by streamfield.\n\n**2. Add apps to settings.py and make migrations**\n\nAdd to INSTALLED_APPS\n\n```python\nINSTALLED_APPS = [\n    ...\n    'streamblocks',\n    'streamfield',\n    ...\n```\nRun `python manage.py makemigrations` and `python manage.py migrate`\n\n**3. Add streamfield.urls to main urls.py**\n```python\nurlpatterns += [\n    path('streamfield/', include('streamfield.urls'))\n]\n```\n\n**4. Create templates for each block model, named as lowercase names of the models:**\n\n1. streamblocks/templates/streamblocks/richtext.html\n2. streamblocks/templates/streamblocks/imagewithtext.html\n\nAnd use `block_content` as context.\n\n\u003e Note: block_content will be single object \nif no 'as_list' property in your model, \nand will be a list of objects if there is.\n\n```html\n\u003c!--richtext.html--\u003e\n\u003cdiv class=\"rich-text-block\"\u003e\n    {{ block_content.text|safe }}\n\u003c/div\u003e\n```\n```html\n\u003c!--imagewithtext.html--\u003e\n\u003cul class=\"image-with-text-block\"\u003e\n    {% for block in block_content %}\n    \u003cli\u003e\n        \u003cimg src=\"{{ block.image.url }}\" alt=\"\"\u003e\n        \u003cp\u003e{{ block.text }}\u003c/p\u003e\n    \u003c/li\u003e\n    {% endfor %}\n\u003c/ul\u003e\n```\n\n\u003e Note: You may use also `block_template` option. For specify a block template file.\n\n```python\nclass RichText(models.Model):\n    ...\n    block_template = \"streamblocks/richtext.html\"\n    ...\n```\n\u003e Note: If you need unique string in block template, use `block_model` and `block_unique_id`\n\n*Full list of variables in template context:*  \n- `block_model` (lowercase of modelname - \"richtext\")\n- `block_unique_id` (unique string)\n- `block_content` (block data from db)\n- `as_list` (boolean)\n- `options` ([block options](#block-options))\n\n\u003e Note: For unique identifier inside the lists you may use a combination of `block_unique_id` and `block.id` of subblock.\n\n**5. Add StreamField to your model in your application**\n\nAnd add the models that you want to use in this stream as model_list\n```python\n# models.py\nfrom streamfield.fields import StreamField\nfrom streamblocks.models import RichText, ImageWithText\n\nclass Page(models.Model):\n    stream = StreamField(\n        model_list=[ \n            RichText,\n            ImageWithText\n        ],\n        verbose_name=\"Page blocks\"\n        )\n```\n**6. Use it in template**\nIf you have your `page` in context, \nyou can get content by field's cached property page.stream.render\n```html\n...\n\u003cdiv class=\"content\"\u003e\n    {{ page.stream.render }}\n\u003c/div\u003e\n...\n```\n\nOr, if you need extra context in blocks, you may use template tag:\n```html\n{% load streamfield_tags %}\n...\n\u003cdiv class=\"content\"\u003e\n  {% stream_render page.stream request=request %}\n\u003c/div\u003e\n...\n```\nThird way it's to use list. [See bellow](#get-field-data-as-list)\n\n\n## Admin\n### Custom admin class for block's models\nModels will automaticaly register in admin.\nIf you want provide custom admin class, \nfirst unregister models and register again, using `StreamBlocksAdmin` class.\n\n```python\n# streamblocks/admin.py\n\nfrom django.contrib import admin\nfrom streamfield.admin import StreamBlocksAdmin\n\nfrom streamblocks.models import RichText\n\nadmin.site.unregister(RichText)\n@admin.register(RichText)\nclass RichTextBlockAdmin(StreamBlocksAdmin, admin.ModelAdmin):\n    pass\n```\n\n### Custom templates for render block models in admin\nIf you need to customize admin templates for block models wich you are using, you need to put templates named as \ndescribed in section 4 (above). but put it inside \"admin\" folder.\n\nFor example for RichText block it will be:\n\n`streamblocks/templates/streamblocks/admin/richtext.html`\n\nAs context use \"form\" and/or \"object\" (Not working for abstract blocks):\n```html\n{{ form.text.value }}\n{{ object }}\n```\n\nThe default admin template is: `streamfield/admin/change_form_render_template.html`  \nYou can extend it\n```html\n{% extends \"streamfield/admin/change_form_render_template.html\" %}\n{% block streamblock_form %}\n{{ block.super }}\nOriginal object is: {{ object }}\n{% endblock streamblock_form %}\n```\n\n\nYou may also specify custom template as option:\n```python\nclass RichText(models.Model):\n    ...\n    custom_admin_template = \"streamblocks/admin/richtext.html\"\n    ...\n```\n\n### Override how to render block's fields in admin\nCreate custom template for field with name as generated by `django.utils.text.camel_case_to_spaces` from field widget name, and put it inside `.../streamblocks/admin/fields/` folder.\n\nFor example for TextField widget (Textarea) of RichText block, it will be:\n`streamblocks/templates/streamblocks/admin/fields/textarea.html`\n\nAnd `MyCustomWidget`:\n`streamblocks/templates/streamblocks/admin/fields/my_custom_widget.html`\n\nAs context use \"field\":\n```html\n{{ field.value|default:\"\"|safe }}\n```\n\n### Override list of blocks for your StreamField in admin.py\nTypicaly you set the blocks in your models as `model_list` attribute of StreamField field.\nBut if you want to change the blocks, for example depending on the object, you can do it in the admin site\nof your model. Suppose you want to use only `RichText` on page with id=1.\n\n```python\n# admin.py\nfrom streamfield.fields import StreamFieldWidget\nfrom streamblocks.models import RichText\nfrom .models import Page\n\n@admin.register(Page)\nclass PageAdmin(models.Admin):\n\n    def get_form(self, request, obj=None, **kwargs):\n        form = super().get_form(request, obj, **kwargs)\n        if obj and obj.id == 1:\n            form.base_fields['stream'].widget = StreamFieldWidget(attrs={\n                'model_list': [ RichText ]\n                })\n        return form\n```\nBe careful with already existing blocks in db. If you remove them from admin, it produce error.\n\n## Block options\nYou may use `options` property in your streamblocks models to add some additional options to your block.\nThis is useful with `as_list` property when you need to add some options to whole block not separatly to each object of this list.\n\nFor example:\n```python\n# streamblocks/models.py\n\n# list of objects as slider\nclass Slide(models.Model):\n    image = models.ImageField(upload_to=\"folder/\")\n    text = models.TextField(null=True, blank=True)\n    \n    # StreamField option for list of objects\n    as_list = True\n    \n    options = {\n        'autoplay': {\n            'label': 'Autoplay slider',\n            'type': 'checkbox',\n            'default': False\n        },\n        'width': {\n            'label': 'Slider size',\n            'type': 'select',\n            'default': 'wide',\n            'options': [\n                {'value': 'wide', 'name': 'Wide slider'},\n                {'value': 'narrow', 'name': 'Narrow slider'},\n            ]\n        },\n        'class_name': {\n          'label': 'Class Name',\n          'type': 'text',\n          'default': ''\n        }\n    }\n\n    class Meta:\n        verbose_name=\"Slide\"\n        verbose_name_plural=\"Slider\"\n```\nIn block template you can use this options as `options.autoplay`\nIn page admin you will see it on the bottom of this block.\n\u003e Note: Now only \"checkbox\", \"text\" and \"select\" type is working.\nYou may apply options for all blocks with `STREAMFIELD_BLOCK_OPTIONS` (See [Settings](#settings))\n\nIf you want to add block options to options, which was set in django settings, you may use `extra_options`.\n```python\nclass Slide(models.Model):\n    ...\n    extra_options = {\n        \"autoplay\": {\n            \"label\": \"Autoplay\",\n            \"type\": \"checkbox\",\n            \"default\": False\n        }\n    }\n    ...\n```\n\nIf you want to switch off options, which set in django settings, for current block. Set `options={}`\n\n## Special cases\n### Complex Blocks\nYou may use StreamField as part of blocks and create with that way complex structure\nand use `{{ block_content.\u003cfield_name\u003e.render }}`\n\n### Blocks without data in database. Only templates.\nYou may use it for widgets or separators or for whatever you want...\nJust make the block model `abstract`.\n```python\nclass EmptyBlock(models.Model):\n    class Meta:\n        abstract = True\n        verbose_name='Empty space'\n```\nand use `streamblocks/templates/streamblocks/emptyblock.html` for your content.\nFor admin `streamblocks/templates/streamblocks/admin/emptyblock.html`\n\u003e Note: Don't forget to register a block in STREAMBLOCKS_MODELS\n\n### Add extra context to blocks\nSupose, you need to add some data to blocks from global context.\nInstead of using render property in template `{{ page.stream.render }}`,\nyou need to use template tag `stream_render` from `streamfield_tags` with keywords arguments.\n\nFor example, if you have in page template `request` and `page` objects and want to use it in blocks:\n```html\n{% load streamfield_tags %}\n...\n\u003cdiv class=\"content\"\u003e\n  {% stream_render page.stream request=request page=page %}\n\u003c/div\u003e\n...\n```\n\n### Get field data as list\nIf you have special case, you can get data as list.\n\n```python\n# views.py\nstream_list = page.stream.as_list()\n# You will get list of dictionaries \n# print(stream_list)\n[{\n    'data': {\n        'block_model': '.....', \n        'block_unique_id': '....', \n        'block_content': [...], \n        'as_list': True, \n        'options': {}\n    }, \n    'template': '....'\n    },\n    ...\n]\n```\n\n```html\n\u003c!-- template.html --\u003e\n{% for b in page.stream.as_list %}\n    {% include b.template with block_content=b.data.block_content %}\n{% endfor %}\n```\n\n\n\n### Cache for reduce the number of database requests\nThere is two ways of caching:\n- Simple cache view with django cache \n- Create additional field, for example: 'stream_rendered'\nand render to this field html in save method\n\n```python\ndef save(self, *args, **kwargs):\n    self.stream_rendered = self.stream.render\n    super().save(*args, **kwargs)\n```\n...and use this field in your html\n\n### Create a copy\nStreamObject have a method 'copy', which create a copies of all the instances that used in this field.   \nFor example, if you have object 'page' with the field 'stream', and you need a copy of this object. You can do this:\n\n```python\npage.pk = None\npage.stream = page.stream.copy()\npage.save()\n```\n\u003e Note: If you will not use `page.stream.copy()` instances will be the same as in the original object\n\n### Add block programarly\n```python\nr = RichText(text='\u003cp\u003eLorem ipsum\u003c/p\u003e')\nim1 = ImageWithText.objects.create(image='...')\nim2 = ImageWithText.objects.create(image='...')\npage.stream.add(r)\npage.stream.add([im1, im2])\npage.save()\n```\n\u003e Note: If you create a new instance of page in shell, before using `add` method, you need to call instance form db. Because field `stream` should be wrapped in StreamObject\n```python\npage = Page() \npage.save()\npage.refresh_from_db()\n```\n\n### Set size of block's popup window\nAdd `popup_size` attribute to StreamField\n```python\n    ...\n    stream = StreamField(\n        model_list=[...],\n        popup_size=(1000, 500) # default value. Width: 1000px, Height: 500px\n        )\n    ...\n```\n\n\n## Settings\n```python\n# settings.py\n```\n\n### STREAMFIELD_SHOW_ADMIN_HELP_TEXT\nIf you want to show \"Help\" link in admin.  \nSet: \n```python\nSTREAMFIELD_SHOW_ADMIN_HELP_TEXT = True\n```\n\n### STREAMFIELD_ADMIN_HELP_TEXT\nYou can setup custom help text in settings\n```python\nSTREAMFIELD_ADMIN_HELP_TEXT = '\u003cp\u003eText\u003c/p\u003e'\n```\n\n### STREAMFIELD_DELETE_BLOCKS_FROM_DB\nIf you want to keep streamblock's instances in db, when you removing it from StreamField. Set: \n```python\nSTREAMFIELD_DELETE_BLOCKS_FROM_DB = False\n```\nIt was default behavior in previous releases. \n\u003e Note: If you delete entire object which contain StreamField, streamblock's instances will not be deleted. You should care about it by yourself.\n\n### STREAMFIELD_BLOCK_TITLE (\u003e 2.0.1)\nThe default block name uses the verbose_name from the model. Plus the name for each object is taken from `__str__` method. For \"as_list\" blocks, from the first block. You can use STREAMFIELD_BLOCK_TITLE to change it to another method or property. If you want disable this, set to False. If some blocks will not have setuped method, they will be ignored.\n\n### STREAMFIELD_BLOCK_OPTIONS\n\nYou may use `STREAMFIELD_BLOCK_OPTIONS` in settings.py to add some options to all blocks.\n\nFor example:\n```python\nSTREAMFIELD_BLOCK_OPTIONS = {\n    \"margins\": {\n        \"label\": \"Margins\",\n        \"type\": \"checkbox\",\n        \"default\": True\n    }\n}\n```\nIn block template use `{{ options.margins }}`\n\n\u003e Note: Now only \"checkbox\", \"text\",  and \"select\" type is working.\n\n## Migrations\nIf you add new options to Block with already existed data, you need to migrate options for adding default values to stored json.  \nCreate empty migration and use `migrate_stream_options` function from `streamfield.base`.  \nAt the moment this only works with unique streamblocks class names.  \n\nExample:\n```python\n# migration\nfrom django.db import migrations\nfrom streamfield.base import migrate_stream_options\n\ndef migrate_options(apps, schema_editor):\n    Page = apps.get_model(\"main\", \"Page\")\n    for page in Page.objects.all():\n        page.stream = migrate_stream_options(page.stream)\n        page.save()\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        '...'\n    ]\n\n    operations = [\n        migrations.RunPython(migrate_options),\n    ]\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraagin%2Fdjango-streamfield","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraagin%2Fdjango-streamfield","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraagin%2Fdjango-streamfield/lists"}