Package: django-components
django-componentsA way to create simple reusable template components in Django.It lets you create "template components", that contains both the template, the Javascript and the CSS needed to generate the front end code you need for a modern app. Components look like this:{%component"calendar"date="2015-06-19"%}{%endcomponent%}And this is what gets rendered (plus the CSS and Javascript you've specified):<divclass="calendar-component">Today's date is<span>2015-06-19</span></div>Read on to learn about the details!Release notesVersion 0.5CHANGES THE SYNTAXfor components.component_blockis nowcomponent, andcomponentblocks need an endingendcomponenttag. The newpython manage.py upgradecomponentcommand can be used to upgrade a directory (use --path argument to point to each dir) of components to the new syntax automatically.This change is done to simplify the API in anticipation of a 1.0 release of django_components. After 1.0 we intend to be stricter with big changes like this in point releases.Version 0.34adds components as views, which allows you to handle requests and render responses from within a component. See thedocumentationfor more details.Version 0.28introduces 'implicit' slot filling and thedefaultoption forslottags.Version 0.27adds a second installable app:django_components.safer_staticfiles. It provides the same behavior asdjango.contrib.staticfilesbut with extra security guarantees (more info below in Security Notes).Version 0.26changes the syntax for{% slot %}tags. From now on, we separate defining a slot ({% slot %}) from filling a slot with content ({% fill %}). This means you will likely need to change a lot of slot tags to fill. We understand this is annoying, but it's the only way we can get support for nested slots that fill in other slots, which is a very nice featuPpre to have access to. Hoping that this will feel worth it!Version 0.22starts autoimporting all files inside components subdirectores, to simplify setup. An existing project might start to get AlreadyRegistered-errors because of this. To solve this, either remove your custom loading of components, or set "autodiscover": False in settings.COMPONENTS.Version 0.17renamesComponent.contextandComponent.templatetoget_context_dataandget_template_name. The old methods still work, but emit a deprecation warning. This change was done to sync naming with Django's class based views, and make using django-components more familiar to Django users.Component.contextandComponent.templatewill be removed when version 1.0 is released.Security notes 🚨You are advised to read this section before using django-components in production.Static filesComponents can be organized however you prefer.
That said, our prefered way is to keep the files of a component close together by bundling them in the same directory.
This means that files containing backend logic, such as Python modules and HTML templates, live in the same directory as static files, e.g. JS and CSS.If your are usingdjango.contrib.staticfilesto collect static files, no distinction is made between the different kinds of files.
As a result, your Python code and templates may inadvertently become available on your static file server.
You probably don't want this, as parts of your backend logic will be exposed, posing apotential security vulnerability.As ofv0.27, django-components ships with an additional installable appdjango_components.safer_staticfiles.
It is a drop-in replacement fordjango.contrib.staticfiles.
Its behavior is 100% identical except it ignores .py and .html files, meaning these will not end up on your static files server.
To use it, add it to INSTALLED_APPS and removedjango.contrib.staticfiles.INSTALLED_APPS=[# 'django.contrib.staticfiles',   # <-- REMOVE'django_components','django_components.safer_staticfiles'# <-- ADD]If you are on an older version of django-components, your alternatives are a) passing--ignore <pattern>options to thecollecstaticCLI command, or b) defining a subclass of StaticFilesConfig.
Both routes are described in the officialdocs of thestaticfilesapp.InstallationInstall the app into your environment:pip install django_componentsThen add the app into INSTALLED_APPS in settings.pyINSTALLED_APPS=[...,'django_components',]ModifyTEMPLATESsection of settings.py as follows:Remove'APP_DIRS': True,addloaderstoOPTIONSlist and set it to following value:TEMPLATES=[{...,'OPTIONS':{'context_processors':[...],'loaders':[('django.template.loaders.cached.Loader',['django.template.loaders.filesystem.Loader','django.template.loaders.app_directories.Loader','django_components.template_loader.Loader',])],},},]Modify STATICFILES_DIRS (or add it if you don't have it) so django can find your static JS and CSS files:STATICFILES_DIRS=[...,BASE_DIR/"components",]OptionalTo avoid loading the app in each template using{% load django_components %}, you can add the tag as a 'builtin' in settings.pyTEMPLATES=[{...,'OPTIONS':{'context_processors':[...],'builtins':['django_components.templatetags.component_tags',]},},]Read on to find out how to build your first component!CompatiblityDjango-components supports allofficially supported versionsof Django and Python.Python versionDjango version3.63.23.73.23.83.2, 4.0, 4.1, 4.23.93.2, 4.0, 4.1, 4.23.103.2, 4.0, 4.1, 4.2, 5.03.114.1, 4.2, 5.03.124.2, 5.0Create your first componentA component in django-components is the combination of four things: CSS, Javascript, a Django template, and some Python code to put them all together.Start by creating empty files in the structure above.First you need a CSS file. Be sure to prefix all rules with a unique class so they don't clash with other rules./* In a file called [project root]/components/calendar/style.css */.calendar-component{width:200px;background:pink;}.calendar-componentspan{font-weight:bold;}Then you need a javascript file that specifies how you interact with this component. You are free to use any javascript framework you want. A good way to make sure this component doesn't clash with other components is to define all code inside an anonymous function that calls itself. This makes all variables defined only be defined inside this component and not affect other components./* In a file called [project root]/components/calendar/script.js */(function(){if(document.querySelector(".calendar-component")){document.querySelector(".calendar-component").onclick=function(){alert("Clicked calendar!");};}})()Now you need a Django template for your component. Feel free to define more variables likedatein this example. When creating an instance of this component we will send in the values for these variables. The template will be rendered with whatever template backend you've specified in your Django settings file.{# In a file called [project root]/components/calendar/calendar.html #}<divclass="calendar-component">Today's date is<span>{{date}}</span></div>Finally, we use django-components to tie this together. Start by creating a file calledcalendar.pyin your component calendar directory. It will be auto-detected and loaded by the app.Inside this file we create a Component by inheriting from the Component class and specifying the context method. We also register the global component registry so that we easily can render it anywhere in our templates.# In a file called [project root]/components/calendar/calendar.pyfromdjango_componentsimportcomponent@component.register("calendar")classCalendar(component.Component):# Templates inside `[your apps]/components` dir and `[project root]/components` dir will be automatically found. To customize which template to use based on context# you can override def get_template_name() instead of specifying the below variable.template_name="calendar/calendar.html"# This component takes one parameter, a date string to show in the templatedefget_context_data(self,date):return{"date":date,}classMedia:css="calendar/style.css"js="calendar/script.js"And voilá!! We've created our first component.Use the component in a templateFirst load thecomponent_tagstag library, then use thecomponent_[js/css]_dependenciesandcomponenttags to render the component to the page.{%loadcomponent_tags%}<!DOCTYPE html><html><head><title>My example calendar</title>{%component_css_dependencies%}</head><body>{%component"calendar"date="2015-06-19"%}{%endcomponent%}{%component_js_dependencies%}</body><html>The output from the above template will be:<!DOCTYPE html><html><head><title>My example calendar</title><linkhref="style.css"type="text/css"media="all"rel="stylesheet"></head><body><divclass="calendar-component">Today's date is<span>2015-06-19</span></div><scriptsrc="script.js"></script></body><html>This makes it possible to organize your front-end around reusable components. Instead of relying on template tags and keeping your CSS and Javascript in the static directory.Using single-file componentsComponents can also be defined in a single file, which is useful for small components. To do this, you can use thetemplate,js, andcssclass attributes instead of thetemplate_nameandMedia. For example, here's the calendar component from above, defined in a single file:# In a file called [project root]/components/calendar.pyfromdjango_componentsimportcomponent@component.register("calendar")classCalendar(component.Component):defget_context_data(self,date):return{"date":date,}template="""<div class="calendar-component">Today's date is <span>{{ date }}</span></div>"""css=""".calendar-component { width: 200px; background: pink; }.calendar-component span { font-weight: bold; }"""js="""(function(){if (document.querySelector(".calendar-component")) {document.querySelector(".calendar-component").onclick = function(){ alert("Clicked calendar!"); };}})()"""This makes it easy to create small components without having to create a separate template, CSS, and JS file.Using slots in templatesNew in version 0.26:Theslottag now serves only to declare new slots inside the component template.To override the content of a declared slot, use the newly introducedfilltag instead.Whereas unfilled slots used to raise a warning, filling a slot is now optional by default.To indicate that a slot must be filled, the newrequiredoption should be added at the end of theslottag.Components support something called 'slots'.
When a component is used inside another template, slots allow the parent template to override specific parts of the child component by passing in different content.
This mechanism makes components more reusable and composable.In the example below we introduce two block tags that work hand in hand to make this work. These are...{% slot <name> %}/{% endslot %}: Declares a new slot in the component template.{% fill <name> %}/{% endfill %}: (Used inside acomponenttag pair.) Fills a declared slot with the specified content.Let's update our calendar component to support more customization. We'll addslottag pairs to its template,calendar.html.<divclass="calendar-component"><divclass="header">{%slot"header"%}Calendar header{%endslot%}</div><divclass="body">{%slot"body"%}Today's date is<span>{{date}}</span>{%endslot%}</div></div>When using the component, you specify which slots you want to fill and where you want to use the defaults from the template. It looks like this:{%component"calendar"date="2020-06-06"%}{%fill"body"%}Can you believe it's already<span>{{date}}</span>??{%endfill%}{%endcomponent%}Since the header block is unspecified, it's taken from the base template. If you put this in a template, and pass indate=2020-06-06, this is what gets rendered:<divclass="calendar-component"><divclass="header">Calendar header</div><divclass="body">Can you believe it's already<span>2020-06-06</span>??</div></div>As you can see, component slots lets you write reusable containers that you fill in when you use a component. This makes for highly reusable components that can be used in different circumstances.It can become tedious to usefilltags everywhere, especially when you're using a component that declares only one slot. To make things easier,slottags can be marked with an optional keyword:default. When added to the end of the tag (as shown below), this option lets you pass filling content directly in the body of acomponenttag pair – without using afilltag. Choose carefully, though: a component template may contain at most one slot that is marked asdefault. Thedefaultoption can be combined with other slot options, e.g.required.Here's the same example as before, except with default slots and implicit filling.The template:<divclass="calendar-component"><divclass="header">{%slot"header"%}Calendar header{%endslot%}</div><divclass="body">{%slot"body"default%}Today's date is<span>{{date}}</span>{%endslot%}</div></div>Including the component (notice how thefilltag is omitted):{%component"calendar"date="2020-06-06"%}Can you believe it's already<span>{{date}}</span>??{%endcomponent%}The rendered result (exactly the same as before):<divclass="calendar-component"><divclass="header">Calendar header</div><divclass="body">Can you believe it's already<span>2020-06-06</span>??</div></div>You may be tempted to combine implicit fills with explicitfilltags. This will not work. The following component template will raise an error when compiled.{# DON'T DO THIS #}{%component"calendar"date="2020-06-06"%}{%fill"header"%}Totally new header!{%endfill%}Can you believe it's already<span>{{date}}</span>??{%endcomponent%}By contrast, it is permitted to usefilltags in nested components, e.g.:{%component"calendar"date="2020-06-06"%}{%component"beautiful-box"%}{%fill"content"%}Can you believe it's already<span>{{date}}</span>??{%endfill%}{%endcomponent%}{%endcomponent%}This is fine too:{%component"calendar"date="2020-06-06"%}{%fill"header"%}{%component"calendar-header"%}Super Special Calendar Header{%endcomponent%}{%endfill%}{%endcomponent%}Components as viewsNew in version 0.34Components can now be used as views. To do this,Componentsubclasses Django'sViewclass. This means that you can use all of themethodsofViewin your component. For example, you can overridegetandpostto handle GET and POST requests, respectively.In addition,Componentnow has arender_to_responsemethod that renders the component template based on the provided context and slots' data and returns anHttpResponseobject.Here's an example of a calendar component defined as a view:# In a file called [project root]/components/calendar.pyfromdjango_componentsimportcomponent@component.register("calendar")classCalendar(component.Component):template="""<div class="calendar-component"><div class="header">{% slot "header" %}{% endslot %}</div><div class="body">Today's date is <span>{{ date }}</span></div></div>"""defget(self,request,*args,**kwargs):context={"date":request.GET.get("date","2020-06-06"),}slots={"header":"Calendar header",}returnself.render_to_response(context,slots)Then, to use this component as a view, you should create aurls.pyfile in your components directory, and add a path to the component's view:# In a file called [project root]/components/urls.pyfromdjango.urlsimportpathfromcalendarimportCalendarurlpatterns=[path("calendar/",Calendar.as_view()),]Remember to add__init__.pyto your components directory, so that Django can find theurls.pyfile.Finally, include the component's urls in your project'surls.pyfile:# In a file called [project root]/urls.pyfromdjango.urlsimportinclude,pathurlpatterns=[path("components/",include("components.urls")),]Note: slots content are automatically escaped by default to prevent XSS attacks. To disable escaping, setescape_slots_content=Falsein therender_to_responsemethod. If you do so, you should make sure that any content you pass to the slots is safe, especially if it comes from user input.If you're planning on passing an HTML string, check Django's use offormat_htmlandmark_safe.AdvancedRe-using content defined in the original slotCertain properties of a slot can be accessed from within a 'fill' context. They are provided as attributes on a user-defined alias of the targeted slot. For instance, let's say you're filling a slot called 'body'. To access properties of this slot, alias it using the 'as' keyword to a new name -- or keep the original name. With the new slot alias, you can call<alias>.defaultto insert the default content.{%component"calendar"date="2020-06-06"%}{%fill"body"as"body"%}{{body.default}}. Have a great day!{%endfill%}{%endcomponent%}Produces:<divclass="calendar-component"><divclass="header">Calendar header</div><divclass="body">Today's date is<span>2020-06-06</span>. Have a great day!</div></div>Conditional slotsAdded in version 0.26.In certain circumstances, you may want the behavior of slot filling to depend on
whether or not a particular slot is filled.For example, suppose we have the following component template:<divclass="frontmatter-component"><divclass="title">{%slot"title"%}Title{%endslot%}</div><divclass="subtitle">{%slot"subtitle"%}{# Optional subtitle #}{%endslot%}</div></div>By default the slot named 'subtitle' is empty. Yet when the component is used without
explicit fills, the div containing the slot is still rendered, as shown below:<divclass="frontmatter-component"><divclass="title">Title</div><divclass="subtitle"></div></div>This may not be what you want. What if instead the outer 'subtitle' div should only
be included when the inner slot is in fact filled?The answer is to use the{% if_filled <name> %}tag. Together with{% endif_filled %},
these define a block whose contents will be rendered only if the component slot with
the corresponding 'name' is filled.This is what our example looks like with an 'if_filled' tag.<divclass="frontmatter-component"><divclass="title">{%slot"title"%}Title{%endslot%}</div>{%if_filled"subtitle"%}<divclass="subtitle">{%slot"subtitle"%}{# Optional subtitle #}{%endslot%}</div>{%endif_filled%}</div>Just as Django's builtin 'if' tag has 'elif' and 'else' counterparts, so does 'if_filled'
include additional tags for more complex branching. These tags are 'elif_filled' and
'else_filled'. Here's what our example looks like with them.<divclass="frontmatter-component"><divclass="title">{%slot"title"%}Title{%endslot%}</div>{%if_filled"subtitle"%}<divclass="subtitle">{%slot"subtitle"%}{# Optional subtitle #}{%endslot%}</div>{%elif_filled"title"%}...{%else_filled%}...{%endif_filled%}</div>Sometimes you're not interested in whether a slot is filled, but rather that itisn't.
To negate the meaning of 'if_filled' in this way, an optional boolean can be passed to
the 'if_filled' and 'elif_filled' tags.In the example below we useFalseto indicate that the content should be rendered
only if the slot 'subtitle' isnotfilled.{%if_filledsubtitleFalse%}<divclass="subtitle">{%slot"subtitle"%}{%endslot%}</div>{%endif_filled%}Setting UpComponentDependencyMiddlewareComponentDependencyMiddlewareis a Django middleware designed to manage and inject CSS/JS dependencies for rendered components dynamically. It ensures that only the necessary stylesheets and scripts are loaded in your HTML responses, based on the components used in your Django templates.To set it up, add the middleware to yourMIDDLEWAREin settings.py:MIDDLEWARE=[# ... other middleware classes ...'django_components.middleware.ComponentDependencyMiddleware'# ... other middleware classes ...]Then, enableRENDER_DEPENDENCIESin setting.py:COMPONENTS={"RENDER_DEPENDENCIES":True,# ... other component settings ...}Component context and scopeBy default, components can access context variables from the parent template, just like templates that are included with the{% include %}tag. Just like with{% include %}, if you don't want the component template to have access to the parent context, addonlyto the end of the{% component %}tag):{%component"calendar"date="2015-06-19"only%}{%endcomponent%}NOTE:{% csrf_token %}tags need access to the top-level context, and they will not function properly if they are rendered in a component that is called with theonlymodifier.Components can also access the outer context in their context methods by accessing the propertyouter_context.You can also setcontext_behaviortoisolatedto make all components isolated by default. This is useful if you want to make sure that components don't accidentally access the outer context.Available settingsAll library settings are handled from a global COMPONENTS variable that is read from settings.py. By default you don't need it set, there are resonable defaults.Configure the module where components are loaded fromConfigure the location where components are loaded. To do this, add a COMPONENTS variable to you settings.py with a list of python paths to load. This allows you to build a structure of components that are independent from your apps.COMPONENTS={"libraries":["mysite.components.forms","mysite.components.buttons","mysite.components.cards",],}Disable autodiscoveryIf you specify all the component locations with the setting above and have a lot of apps, you can (very) slightly speed things up by disabling autodiscovery.COMPONENTS={"autodiscover":False,}Tune the template cacheEach time a template is rendered it is cached to a global in-memory cache (using Python's lru_cache decorator). This speeds up the next render of the component. As the same component is often used many times on the same page, these savings add up. By default the cache holds 128 component templates in memory, which should be enough for most sites. But if you have a lot of components, or if you are using thetemplatemethod of a component to render lots of dynamic templates, you can increase this number. To remove the cache limit altogether and cache everything, set template_cache_size toNone.COMPONENTS={"template_cache_size":256,}Isolate components' context by defaultIf you'd like to prevent components from accessing the outer context by default, you can set thecontext_behaviorsetting toisolated. This is useful if you want to make sure that components don't accidentally access the outer context.COMPONENTS={"context_behavior":"isolated",}Management CommandYou can use the built-in management commandstartcomponentto create a django component. The command accepts the following arguments and options:name: The name of the component to create. This is a required argument.--path: The path to the components directory. This is an optional argument. If not provided, the command will use theBASE_DIRsetting from your Django settings.--js: The name of the JavaScript file. This is an optional argument. The default value isscript.js.--css: The name of the CSS file. This is an optional argument. The default value isstyle.css.--template: The name of the template file. This is an optional argument. The default value istemplate.html.--force: This option allows you to overwrite existing files if they exist. This is an optional argument.--verbose: This option allows the command to print additional information during component creation. This is an optional argument.--dry-run: This option allows you to simulate component creation without actually creating any files. This is an optional argument. The default value isFalse.Management Command UsageTo use the command, run the following command in your terminal:pythonmanage.pystartcomponent<name>--path<path>--js<js_filename>--css<css_filename>--template<template_filename>--force--verbose--dry-runReplace<name>,<path>,<js_filename>,<css_filename>, and<template_filename>with your desired values.Management Command ExamplesHere are some examples of how you can use the command:Creating a Component with Default SettingsTo create a component with the default settings, you only need to provide the name of the component:pythonmanage.pystartcomponentmy_componentThis will create a new component namedmy_componentin thecomponentsdirectory of your Django project. The JavaScript, CSS, and template files will be namedscript.js,style.css, andtemplate.html, respectively.Creating a Component with Custom SettingsYou can also create a component with custom settings by providing additional arguments:pythonmanage.pystartcomponentnew_component--pathmy_components--jsmy_script.js--cssmy_style.css--templatemy_template.htmlThis will create a new component namednew_componentin themy_componentsdirectory. The JavaScript, CSS, and template files will be namedmy_script.js,my_style.css, andmy_template.html, respectively.Overwriting an Existing ComponentIf you want to overwrite an existing component, you can use the--forceoption:pythonmanage.pystartcomponentmy_component--forceThis will overwrite the existingmy_componentif it exists.Simulating Component CreationIf you want to simulate the creation of a component without actually creating any files, you can use the--dry-runoption:pythonmanage.pystartcomponentmy_component--dry-runThis will simulate the creation ofmy_componentwithout creating any files.Install locally and run the testsStart by forking the project by clicking theFork buttonup in the right corner in the GitHub . This makes a copy of the repository in your own name. Now you can clone this repository locally and start adding features:gitclonehttps://github.com/<yourGitHubusername>/django-components.gitTo quickly run the tests install the local dependencies by running:pipinstall-rrequirements-dev.txtNow you can run the tests to make sure everything works as expected:pytestThe library is also tested across many versions of Python and Django. To run tests that way:pyenvinstall-s3.6
pyenvinstall-s3.7
pyenvinstall-s3.8
pyenvinstall-s3.9
pyenvinstall-s3.10
pyenvinstall-s3.11
pyenvinstall-s3.12
pyenvlocal3.63.73.83.93.103.113.12
tox-p