refactor: rename template_name to template_file (#878)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Juro Oravec 2025-01-01 17:06:14 +01:00 committed by GitHub
parent b99e32e9d5
commit d94a459c8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 251 additions and 138 deletions

View file

@ -14,7 +14,7 @@ from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "template.html"
template_file = "template.html"
# This component takes one parameter, a date string to show in the template
def get_context_data(self, date):
@ -127,6 +127,7 @@ NOTE: The Library instance can be accessed under `library` attribute of `Compone
When you are creating an instance of `ComponentRegistry`, you can define the components' behavior within the template.
The registry accepts these settings:
- `context_behavior`
- `tag_formatter`

View file

@ -10,7 +10,7 @@ HTML / JS / CSS with a component:
[`Component.css`](../../reference/api.md#django_components.Component.css) and
[`Component.js`](../../reference/api.md#django_components.Component.js) to define the main HTML / CSS / JS for a component
as inlined code.
- You can set [`Component.template_name`](../../reference/api.md#django_components.Component.template_name),
- You can set [`Component.template_file`](../../reference/api.md#django_components.Component.template_file),
[`Component.css_file`](../../reference/api.md#django_components.Component.css_file) and
[`Component.js_file`](../../reference/api.md#django_components.Component.js_file) to define the main HTML / CSS / JS
for a component in separate files.
@ -22,7 +22,7 @@ HTML / JS / CSS with a component:
You **cannot** use both inlined code **and** separate file for a single language type:
- You can only either set `Component.template` or `Component.template_name`
- You can only either set `Component.template` or `Component.template_file`
- You can only either set `Component.css` or `Component.css_file`
- You can only either set `Component.js` or `Component.js_file`
@ -51,7 +51,7 @@ HTML / JS / CSS with a component:
As seen in the [getting started example](../getting_started/your_first_component.md), to associate HTML / JS / CSS
files with a component, you can set them as
[`Component.template_name`](../../reference/api.md#django_components.Component.template_name),
[`Component.template_file`](../../reference/api.md#django_components.Component.template_file),
[`Component.js_file`](../../reference/api.md#django_components.Component.js_file)
and
[`Component.css_file`](../../reference/api.md#django_components.Component.css_file) respectively:
@ -61,7 +61,7 @@ from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "template.html"
template_file = "template.html"
css_file = "style.css"
js_file = "script.js"
```
@ -70,7 +70,7 @@ In the example above, we defined the files relative to the directory where the c
Alternatively, you can specify the file paths relative to the directories set in
[`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
or
or
[`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs).
If you specify the paths relative to component's directory, django-componenents does the conversion automatically
@ -85,7 +85,7 @@ from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "calendar/template.html"
template_file = "calendar/template.html"
css_file = "calendar/style.css"
js_file = "calendar/script.js"
```
@ -94,59 +94,59 @@ class Calendar(Component):
**File path resolution in-depth**
At component class creation, django-components checks all file paths defined on the component (e.g. `Component.template_name`).
At component class creation, django-components checks all file paths defined on the component (e.g. `Component.template_file`).
For each file path, it checks if the file path is relative to the component's directory.
And such file exists, the component's file path is re-written to be defined relative to a first matching directory
in [`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
or
or
[`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs).
**Example:**
```py title="[root]/components/mytable/mytable.py"
class MyTable(Component):
template_name = "mytable.html"
template_file = "mytable.html"
```
1. Component `MyTable` is defined in file `[root]/components/mytable/mytable.py`.
2. The component's directory is thus `[root]/components/mytable/`.
3. Because `MyTable.template_name` is `mytable.html`, django-components tries to
3. Because `MyTable.template_file` is `mytable.html`, django-components tries to
resolve it as `[root]/components/mytable/mytable.html`.
4. django-components checks the filesystem. If there's no such file, nothing happens.
5. If there IS such file, django-components tries to rewrite the path.
6. django-components searches `COMPONENTS.dirs` and `COMPONENTS.app_dirs` for a first
directory that contains `[root]/components/mytable/mytable.html`.
7. It comes across `[root]/components/`, which DOES contain the path to `mytable.html`.
8. Thus, it rewrites `template_name` from `mytable.html` to `mytable/mytable.html`.
8. Thus, it rewrites `template_file` from `mytable.html` to `mytable/mytable.html`.
NOTE: In case of ambiguity, the preference goes to resolving the files relative to the component's directory.
## Defining additional JS and CSS files
Each component can have only a single template, and single main JS and CSS. However, you can define additional JS or CSS
Each component can have only a single template, and single main JS and CSS. However, you can define additional JS or CSS
using the nested [`Component.Media` class](../../../reference/api#django_components.Component.Media).
This `Media` class behaves similarly to
[Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/#assets-as-a-static-definition):
- Paths are generally handled as static file paths, and resolved URLs are rendered to HTML with
`media_class.render_js()` or `media_class.render_css()`.
`media_class.render_js()` or `media_class.render_css()`.
- A path that starts with `http`, `https`, or `/` is considered a URL, skipping the static file resolution.
This path is still rendered to HTML with `media_class.render_js()` or `media_class.render_css()`.
This path is still rendered to HTML with `media_class.render_js()` or `media_class.render_css()`.
- A [`SafeString`](https://docs.djangoproject.com/en/5.1/ref/utils/#django.utils.safestring.SafeString),
or a function (with `__html__` method) is considered an already-formatted HTML tag, skipping both static file
resolution and rendering with `media_class.render_js()` or `media_class.render_css()`.
or a function (with `__html__` method) is considered an already-formatted HTML tag, skipping both static file
resolution and rendering with `media_class.render_js()` or `media_class.render_css()`.
However, there's a few differences from Django's Media class:
1. Our Media class accepts various formats for the JS and CSS files: either a single file, a list,
or (CSS-only) a dictonary (See [`ComponentMediaInput`](../../../reference/api#django_components.ComponentMediaInput)).
or (CSS-only) a dictonary (See [`ComponentMediaInput`](../../../reference/api#django_components.ComponentMediaInput)).
2. Individual JS / CSS files can be any of `str`, `bytes`, `Path`,
[`SafeString`](https://docs.djangoproject.com/en/5.1/ref/utils/#django.utils.safestring.SafeString), or a function
(See [`ComponentMediaInputPath`](../../../reference/api#django_components.ComponentMediaInputPath)).
[`SafeString`](https://docs.djangoproject.com/en/5.1/ref/utils/#django.utils.safestring.SafeString), or a function
(See [`ComponentMediaInputPath`](../../../reference/api#django_components.ComponentMediaInputPath)).
3. Our Media class does NOT support
[Django's `extend` keyword](https://docs.djangoproject.com/en/5.1/topics/forms/media/#extend)
[Django's `extend` keyword](https://docs.djangoproject.com/en/5.1/topics/forms/media/#extend)
```py
class MyTable(Component):
@ -268,7 +268,7 @@ class ModuleJsPath:
@register("calendar")
class Calendar(Component):
template_name = "calendar/template.html"
template_file = "calendar/template.html"
def get_context_data(self, date):
return {
@ -314,7 +314,7 @@ class MyMedia(Media):
@register("calendar")
class Calendar(Component):
template_name = "calendar/template.html"
template_file = "calendar/template.html"
css_file = "calendar/style.css"
js_file = "calendar/script.js"
@ -331,27 +331,28 @@ class Calendar(Component):
Component's HTML / CSS / JS is resolved and loaded lazily.
This means that, when you specify any of
[`template_name`](../../reference/api.md#django_components.Component.template_name),
[`template_file`](../../reference/api.md#django_components.Component.template_file),
[`js_file`](../../reference/api.md#django_components.Component.js_file),
[`css_file`](../../reference/api.md#django_components.Component.css_file),
or [`Media.js/css`](../../reference/api.md#django_components.Component.Media),
these file paths will be resolved only once you either:
1. Access any of the following attributes on the component:
- [`media`](../../reference/api.md#django_components.Component.media),
[`template`](../../reference/api.md#django_components.Component.template),
[`template_name`](../../reference/api.md#django_components.Component.template_name),
[`js`](../../reference/api.md#django_components.Component.js),
[`js_file`](../../reference/api.md#django_components.Component.js_file),
[`css`](../../reference/api.md#django_components.Component.css),
[`css_file`](../../reference/api.md#django_components.Component.css_file)
- [`media`](../../reference/api.md#django_components.Component.media),
[`template`](../../reference/api.md#django_components.Component.template),
[`template_file`](../../reference/api.md#django_components.Component.template_file),
[`js`](../../reference/api.md#django_components.Component.js),
[`js_file`](../../reference/api.md#django_components.Component.js_file),
[`css`](../../reference/api.md#django_components.Component.css),
[`css_file`](../../reference/api.md#django_components.Component.css_file)
2. Render the component.
Once the component's media files have been loaded once, they will remain in-memory
on the Component class:
- HTML from [`Component.template_name`](../../reference/api.md#django_components.Component.template_name)
- HTML from [`Component.template_file`](../../reference/api.md#django_components.Component.template_file)
will be available under [`Component.template`](../../reference/api.md#django_components.Component.template)
- CSS from [`Component.css_file`](../../reference/api.md#django_components.Component.css_file)
will be available under [`Component.css`](../../reference/api.md#django_components.Component.css)
@ -359,7 +360,7 @@ on the Component class:
will be available under [`Component.js`](../../reference/api.md#django_components.Component.js)
Thus, whether you define HTML via
[`Component.template_name`](../../reference/api.md#django_components.Component.template_name)
[`Component.template_file`](../../reference/api.md#django_components.Component.template_file)
or [`Component.template`](../../reference/api.md#django_components.Component.template),
you can always access the HTML content under [`Component.template`](../../reference/api.md#django_components.Component.template).
And the same applies for JS and CSS.
@ -371,7 +372,7 @@ And the same applies for JS and CSS.
# are not yet loaded!
@register("calendar")
class Calendar(Component):
template_name = "calendar/template.html"
template_file = "calendar/template.html"
css_file = "calendar/style.css"
js_file = "calendar/script.js"
@ -395,7 +396,7 @@ print(Calendar.css)
django-components assumes that the component's media files like `js_file` or `Media.js/css` are static.
If you need to dynamically change these media files, consider instead defining multiple Components.
Modifying these files AFTER the component has been loaded at best does nothing. However, this is
an untested behavior.

View file

@ -3,10 +3,13 @@ title: Single-file components
weight: 1
---
Components can be defined in a single file, which is useful for small components. To do this, you can use the `template`, `js`, and `css` class attributes instead of the `template_name` and `Media`. For example, here's the calendar component from above, defined in a single file:
Components can be defined in a single file, which is useful for small components. To do this, you can use the `template`, `js`, and `css` class attributes instead of the `template_file`, `js_file`, and `css_file`.
For example, here's the calendar component from
the [Getting started](../getting_started/your_first_component.md) tutorial,
defined in a single file:
```python title="[project root]/components/calendar.py"
# In a file called [project root]/components/calendar.py
from django_components import Component, register, types
@register("calendar")
@ -17,18 +20,27 @@ class Calendar(Component):
}
template: types.django_html = """
<div class="calendar-component">Today's date is <span>{{ date }}</span></div>
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
"""
css: types.css = """
.calendar-component { width: 200px; background: pink; }
.calendar-component span { font-weight: bold; }
.calendar {
width: 200px;
background: pink;
}
.calendar span {
font-weight: bold;
}
"""
js: types.js = """
(function(){
if (document.querySelector(".calendar-component")) {
document.querySelector(".calendar-component").onclick = function(){ alert("Clicked calendar!"); };
if (document.querySelector(".calendar")) {
document.querySelector(".calendar").onclick = () => {
alert("Clicked calendar!");
};
}
})()
"""

View file

@ -49,6 +49,7 @@ Inside `calendar.css`, write:
Be sure to prefix your rules with unique CSS class like `calendar`, so the CSS doesn't clash with other rules.
<!-- TODO: UPDATE AFTER SCOPED CSS ADDED -->
!!! note
Soon, django-components will automatically scope your CSS by default, so you won't have to worry
@ -99,6 +100,7 @@ an [anonymous self-invoking function](https://developer.mozilla.org/en-US/docs/G
This makes all variables defined only be defined inside this component and not affect other components.
<!-- TODO: UPDATE AFTER FUNCTIONS WRAPPED -->
!!! note
Soon, django-components will automatically wrap your JS in a self-invoking function by default
@ -172,7 +174,6 @@ So in your HTML, you may see something like this:
</html>
```
### 4. Link JS and CSS to a component
Finally, we return to our Python component in `calendar.py` to tie this together.
@ -184,7 +185,7 @@ and [`css_file`](../../../reference/api#django_components.Component.css_file) at
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
js_file = "calendar.js" # <--- new
css_file = "calendar.css" # <--- new
@ -208,7 +209,6 @@ automatically embed the associated JS and CSS.
(e.g. `[your apps]/components` dir and `[project root]/components`)
3. Relative to any of the directories defined by `STATICFILES_DIRS`.
<!-- TODO: UPDATE AFTER AT LEAST ONE IMPLEMENTED
!!! info
@ -243,7 +243,7 @@ with a few differences:
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
js_file = "calendar.js"
css_file = "calendar.css"
@ -273,7 +273,6 @@ class Calendar(Component):
and/or [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs)
(e.g. `[your apps]/components` dir and `[project root]/components`)
!!! info
The `Media` nested class is shaped based on [Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/).
@ -312,7 +311,7 @@ Additionally to `Media.js` applies that:
1. JS in `Media.js` is executed **before** the component's primary JS.
2. JS in `Media.js` is executed **in the same order** as it was defined.
3. If there is multiple components that specify the same JS path or URL in `Media.js`,
3. If there is multiple components that specify the same JS path or URL in `Media.js`,
this JS will be still loaded and executed only once.
Putting all of this together, our `Calendar` component above would render HTML like so:
@ -322,8 +321,12 @@ Putting all of this together, our `Calendar` component above would render HTML l
<head>
...
<!-- CSS from Media.css -->
<link href="/static/path/to/shared.css" media="all" rel="stylesheet">
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" media="all" rel="stylesheet">
<link href="/static/path/to/shared.css" media="all" rel="stylesheet" />
<link
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
media="all"
rel="stylesheet"
/>
<!-- CSS from Component.css_file -->
<style>
.calendar {

View file

@ -8,7 +8,7 @@ our colleague - The calendar date needs to be shown on 3 different pages:
1. On one page, it needs to be shown as is
2. On the second, the date needs to be **bold**
3. On the third, the date needs to be in *italics*
3. On the third, the date needs to be in _italics_
As a reminder, this is what the component's template looks like:
@ -43,7 +43,7 @@ In the example below we introduce two tags that work hand in hand to make this w
- `{% slot <name> %}`/`{% endslot %}`: Declares a new slot in the component template.
- `{% fill <name> %}`/`{% endfill %}`: (Used inside a [`{% component %}`](../../reference/template_tags.md#component)
tag pair.) Fills a declared slot with the specified content.
tag pair.) Fills a declared slot with the specified content.
### 2. Add a slot tag
@ -122,7 +122,7 @@ Which will render as:
{% endcomponent %}
```
2. Implicitly as the [default slot](../fundamentals/slots.md#default-slot) (Omitting the
2. Implicitly as the [default slot](../fundamentals/slots.md#default-slot) (Omitting the
[`{% fill %}`](../../reference/template_tags.md#fill) tag)
```htmldjango
{% component "calendar" date="2024-12-13" %}
@ -177,7 +177,7 @@ def to_workweek_date(d: date):
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
workweek_date = to_workweek_date(date)
@ -187,7 +187,7 @@ class Calendar(Component):
}
```
And the issue is that in our template, we used the `date` value that we used *as input*,
And the issue is that in our template, we used the `date` value that we used _as input_,
which is NOT the same as the `date` variable used inside Calendar's template.
### 5. Adding data to slots

View file

@ -31,7 +31,7 @@ from django_components import Component, register # <--- new
@register("calendar") # <--- new
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
js_file = "calendar.js"
css_file = "calendar.css"

View file

@ -23,7 +23,7 @@ from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
...
def get_context_data(self):
return {
@ -71,7 +71,7 @@ from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
return {
@ -135,7 +135,7 @@ We need to group the list items by size into following buckets by population:
- 0-10,000,000
- 10,000,001-20,000,000
- 20,000,001-30,000,000
- 20,000,001-30,000,000
- +30,000,001
So we want to end up with following data:
@ -179,7 +179,7 @@ def group_by_pop(data):
@register("population_table")
class PopulationTable(Component):
template_name = "population_table.html"
template_file = "population_table.html"
def get_context_data(self, data):
return {
@ -200,7 +200,7 @@ def to_workweek_date(d: date):
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
workweek_date = to_workweek_date(date) # <--- new

View file

@ -15,7 +15,7 @@ A component in django-components can be as simple as a Django template and Pytho
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
```
Or a combination of Django template, Python, CSS, and Javascript:
@ -43,7 +43,7 @@ document.querySelector(".calendar").onclick = function () {
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
js_file = "calendar.js"
css_file = "calendar.css"
```
@ -80,7 +80,6 @@ class Calendar(Component):
[syntax highlighting](../../guides/setup/syntax_highlight.md) for better experience.
However, autocompletion / intellisense does not work with syntax highlighting.
We'll start by creating a component that defines only a Django template:
### 1. Create project structure
@ -124,14 +123,14 @@ when creating an instance of this component.
In `calendar.py`, create a subclass of [Component](../../../reference/api#django_components.Component)
to create a new component.
To link the HTML template with our component, set [`template_name`](../../../reference/api#django_components.Component.template_name)
To link the HTML template with our component, set [`template_file`](../../../reference/api#django_components.Component.template_file)
to the name of the HTML file.
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
```
!!! note
@ -156,7 +155,7 @@ will become available within the template as variables, e.g. as `{{ date }}`.
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
template_file = "calendar.html"
def get_context_data(self):
return {