From cb8dedc7511fe24ad3e0c51108392dd9ca9d209d Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Fri, 20 Mar 2026 22:14:11 +0700 Subject: [PATCH 1/4] Add comprehensive storefront template documentation Addresses gaps in template tags, objects, filters, and URL references for theme development. Changes include: Tags: cart_form, core_js, add_query_param, render_field, annotate_form_field, purchase_info_for_line Objects: cart, line, session, variant_form, filters (faceting), subscription_group, user, cart template context variables Filters: currency (price formatting), split URLs: homepage, cart actions, customer/auth, localization endpoints, GraphQL API endpoint, typo fix (support:category-list) Structural: per-template context table showing what variables each view provides, dashboard cross-reference table mapping template variables to their dashboard configuration paths Fixes: broken /docs/storefront/api link, filters.md missing from meta.json sidebar Co-Authored-By: Claude Opus 4.6 --- .../storefront/themes/cdn-and-caching.mdx | 2 +- .../storefront/themes/templates/filters.md | 30 ++ .../storefront/themes/templates/meta.json | 2 +- .../storefront/themes/templates/objects.mdx | 333 +++++++++++++++++- .../docs/storefront/themes/templates/tags.mdx | 130 +++++++ .../templates/urls-and-template-paths.mdx | 38 +- 6 files changed, 529 insertions(+), 6 deletions(-) diff --git a/content/docs/storefront/themes/cdn-and-caching.mdx b/content/docs/storefront/themes/cdn-and-caching.mdx index 2f2989a..659b875 100644 --- a/content/docs/storefront/themes/cdn-and-caching.mdx +++ b/content/docs/storefront/themes/cdn-and-caching.mdx @@ -39,5 +39,5 @@ Updating a template through the dashboard or [Theme Kit](/docs/storefront/themes There are a few cases wherein a form on the frontend needs to use a `{% csrf_token %}` field to secure submission to the backend. The platform core JS will automatically replace `{% csrf_token %}` that are in cached versions of pages to ensure the forms still work. -**It is advisable to not implement custom templates that require `{% csrf_token %}`, we recommend to [Storefront API](/docs/storefront/api) instead.** +**It is advisable to not implement custom templates that require `{% csrf_token %}`, we recommend the [Storefront GraphQL API](/docs/storefront/graphql) instead.** \ No newline at end of file diff --git a/content/docs/storefront/themes/templates/filters.md b/content/docs/storefront/themes/templates/filters.md index a42f782..b350161 100644 --- a/content/docs/storefront/themes/templates/filters.md +++ b/content/docs/storefront/themes/templates/filters.md @@ -300,6 +300,13 @@ Returns a plural suffix if the value is not 1, '1', or an object of length 1. By You have { num_messages }} message {{ num_messages|pluralize }} ``` +### split +Splits a string by the given delimiter and returns a list. +```django title="split" +{{ value|split:"," }} +``` +For example, if value is `"red,green,blue"`, the output will be the list `['red', 'green', 'blue']`. + ### slugify Converts to ASCII. Converts spaces to hyphens. Removes characters that aren’t alphanumerics, underscores, or hyphens. Converts to lowercase. Also strips @@ -355,6 +362,29 @@ Returns the number of words. For example, if value is "Joel is a slug", the output will be 4. +## Currency + +### currency +Formats a decimal value as a currency string using the provided currency code. This is the primary filter for displaying prices throughout a theme. +```django title="currency" +{{ session.price.price|currency:session.price.currency }} +``` +For example, if the price is `29.99` and the currency is `USD`, the output will be `$29.99`. The filter handles currency symbol placement and formatting based on the currency code. + +```django title="Example Product Price Display" +{% purchase_info_for_product request product as session %} +{% if session.price.exists %} + {% if session.price.price_retail %} + + {{ session.price.price_retail|currency:session.price.currency }} + + {% endif %} + + {{ session.price.price|currency:session.price.currency }} + +{% endif %} +``` + ## Files ### asset_url diff --git a/content/docs/storefront/themes/templates/meta.json b/content/docs/storefront/themes/templates/meta.json index 5e474d2..18b68ca 100644 --- a/content/docs/storefront/themes/templates/meta.json +++ b/content/docs/storefront/themes/templates/meta.json @@ -1,4 +1,4 @@ { "title": "Templates", - "pages": ["index", "tags", "objects", "urls-and-template-paths"] + "pages": ["index", "tags", "objects", "filters", "urls-and-template-paths"] } diff --git a/content/docs/storefront/themes/templates/objects.mdx b/content/docs/storefront/themes/templates/objects.mdx index f38f115..39b4464 100644 --- a/content/docs/storefront/themes/templates/objects.mdx +++ b/content/docs/storefront/themes/templates/objects.mdx @@ -57,8 +57,7 @@ Returns a list of active storefront languages you can iterate over, see [languag ### menus - -Allows you to access a menu's items by it's code to iterate over to generate a menu from the backend, see [menu items](#items-menu). +Allows you to access a menu's items by its code to iterate over to generate a menu from the backend, see [menu items](#items-menu). Menus are configured in the dashboard at **Storefront > Navigation**. ```django title="Example Dynamic Menu" @@ -204,6 +203,9 @@ Returns a list of post categories you can iterate over, see [post_category](#pos Content from store Privacy Policy settings, typically used in a "Privacy Policy" page to automatically pull content in from settings. + +Store policies are configured in the dashboard at **Settings > Policies**. The content entered there is automatically available as global template variables. + ```django title="privacy_policy" {{ privacy_policy }} @@ -340,7 +342,7 @@ Object have many properties that can be accessed in templates. ### branding -Store branding properties accessed through the [store](#store) object to leverage within templates. +Store branding properties accessed through the [store](#store) object to leverage within templates. Branding values are configured in the dashboard at **Settings > Branding**. | Property | Type | Description | | ----- | ------ | ------ | @@ -599,6 +601,24 @@ Product review object. | `user` | Object | The customer that created the review, see [user](#user). | +### user + +The current user/customer object available when a customer is authenticated. + +| Property | Type | Description | +| ----- | ------ | ------ | +| `is_authenticated` | Boolean | Whether the user is logged in. | +| `is_staff` | Boolean | Whether the user is an admin/staff member. | + +```django title="Example Conditional Auth Links" +{% if user.is_authenticated %} + My Account + Logout +{% else %} + Login +{% endif %} +``` + ### voucher Voucher object. @@ -607,3 +627,310 @@ Voucher object. | ----- | ------ | ------ | | `title` | String | Voucher title. | | `code` | String | Voucher code. | + + +## View-Specific Objects + +View-specific objects are available in the templates rendered by their corresponding views. See [Template Contexts](#template-contexts) below for which objects are available in each template. + + +### cart + +The cart object represents the current user's shopping cart and is available in cart templates. + +```django title="Example Cart Summary" +{% if not cart.is_empty %} +
+ {{ cart.num_items }} item{{ cart.num_items|pluralize }} + {{ cart.total_incl_tax|currency:cart.currency }} +
+{% endif %} +``` + +| Property | Type | Description | +| ----- | ------ | ------ | +| `is_empty` | Boolean | Whether the cart has any items. | +| `num_items` | Integer | Total number of items in the cart. | +| `total_incl_tax` | Decimal | Cart total including tax. | +| `total_excl_tax` | Decimal | Cart total excluding tax. | +| `total_excl_tax_excl_discounts` | Decimal | Cart subtotal before discounts and tax. | +| `currency` | String | The cart's currency code, eg `USD`. | +| `is_tax_known` | Boolean | Whether tax has been calculated. | +| `display_taxes` | String | Tax display label. | +| `offer_discounts` | List | List of applied offer discounts. | +| `grouped_voucher_discounts` | List | List of applied voucher discounts. | +| `subscription_rebill_quantity` | Integer | Number of subscription items in the cart. | + + +### line + +A cart line item object representing a single product entry in the cart. Available when iterating over cart lines in cart templates. + +```django title="Example Cart Line Items" +{% for form in formset %} + {% with line=form.instance %} +
+ {{ line.description }} + {{ line.unit_price_incl_tax|currency:line.price_currency }} + Qty: {{ line.quantity }} + {% if line.discount_value %} + -{{ line.discount_value|currency:line.price_currency }} + {% endif %} + {{ line.line_price_incl_tax_incl_discounts|currency:line.price_currency }} +
+ {% endwith %} +{% endfor %} +``` + +| Property | Type | Description | +| ----- | ------ | ------ | +| `description` | String | Product title/description in the cart. | +| `product` | Object | The [product](#product) object for this line. | +| `quantity` | Integer | Quantity of this item in the cart. | +| `unit_price_incl_tax` | Decimal | Unit price including tax. | +| `unit_price_excl_tax` | Decimal | Unit price excluding tax. | +| `is_tax_known` | Boolean | Whether tax is calculated for this line. | +| `price_currency` | String | Currency code for this line item. | +| `discount_value` | Decimal | Discount amount applied to this line. | +| `line_price_incl_tax_incl_discounts` | Decimal | Total line price after discounts and tax. | +| `subscription_range` | Field | Subscription frequency selector field, if applicable. | + + +### session + +The session object is returned by the [`purchase_info_for_product`](/docs/storefront/themes/templates/tags#purchase_info_for_product) and [`purchase_info_for_line`](/docs/storefront/themes/templates/tags#purchase_info_for_line) template tags. It contains pricing and availability information for a product in the current user's currency. + +```django title="Example Product Price with Session" +{% purchase_info_for_product request product as session %} +{% if session.price.exists %} + {% if session.price.price_retail %} + + {{ session.price.price_retail|currency:session.price.currency }} + + {% endif %} + {{ session.price.price|currency:session.price.currency }} +{% endif %} + +{% if not session.availability.is_available_to_buy %} + Out of Stock +{% endif %} +``` + +**session.price** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `exists` | Boolean | Whether a price exists for this product. | +| `price` | Decimal | The current selling price. | +| `price_retail` | Decimal | The retail/compare-at price, if set. | +| `currency` | String | The currency code for this price. | +| `excl_tax` | Decimal | The price excluding tax. | + +**session.availability** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `is_available_to_buy` | Boolean | Whether the product can be purchased. | + + +### variant_form + +The variant selection form object available on product detail pages (`catalogue/product.html`). Used to render variant attribute selectors (size, color, etc.) for products with variants. + +```django title="Example Variant Selection" +{% if variant_form %} +
+ {% for field in variant_form %} +
+ + {% render_field field class+="form-select" %} +
+ {% endfor %} +
+{% endif %} +``` + +### filters + +The `filters` object is a list of product filter/facet objects available on category pages (`catalogue/category.html`). Filters allow customers to narrow product listings by attributes like price, color, size, etc. The `has_active_filter` boolean indicates whether any filter is currently applied. + +There are three filter types: `price_range`, `boolean`, and `list`. Each type has different properties for rendering the appropriate UI. + +```django title="Example Category Filters" +{% if filters %} +
+ {% for filter in filters %} +
+
{{ filter.label }}
+ + {% if filter.type == 'price_range' %} + + + + {% elif filter.type == 'boolean' %} + + + {% else %} + {% for value in filter.values %} + + {% endfor %} + {% endif %} +
+ {% endfor %} + + {% if has_active_filter %} + {% for filter in filters %} + {% for active in filter.active_values %} + Remove {{ active }} + {% endfor %} + {% endfor %} + {% endif %} +
+{% endif %} +``` + +**Common filter properties:** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `type` | String | Filter type: `'price_range'`, `'boolean'`, or `'list'`. | +| `label` | String | Display label for the filter. | +| `active_values` | List | List of currently selected filter values. | +| `url_to_remove` | String | URL to remove this active filter. | + +**Price range filter properties:** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `min_value.value` | Decimal | Current minimum price value. | +| `min_value.param_name` | String | Query parameter name for the minimum. | +| `min_value.label` | String | Display label for the minimum input. | +| `max_value.value` | Decimal | Current maximum price value. | +| `max_value.param_name` | String | Query parameter name for the maximum. | +| `max_value.label` | String | Display label for the maximum input. | +| `range_max` | Decimal | Maximum possible range value. | + +**Boolean filter properties:** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `true_value.param_name` | String | Query parameter name for true selection. | +| `true_value.active` | Boolean | Whether true is currently selected. | +| `true_value.count` | Integer | Number of results matching true. | +| `false_value.param_name` | String | Query parameter name for false selection. | +| `false_value.active` | Boolean | Whether false is currently selected. | +| `false_value.count` | Integer | Number of results matching false. | + +**List filter value properties:** + +| Property | Type | Description | +| ----- | ------ | ------ | +| `param_name` | String | Query parameter name for this value. | +| `active` | Boolean | Whether this value is currently selected. | +| `count` | Integer | Number of results matching this value. | +| `value` | String | The filter value. | +| `label` | String | Display label for this value. | + + +### subscription_group + +Subscription group object available in cart summary templates when the cart contains subscription products. Accessed through the `subscription_groups` list. + +```django title="Example Subscription Summary" +{% if subscription_groups %} +{% for group in subscription_groups %} +
+
Subscription - {{ group.frequency }}
+ {% for line in group.lines %} +
{{ line.description }} - {{ line.unit_price_incl_tax|currency:group.currency }}
+ {% endfor %} +
Shipping: {{ group.shipping_price.excl_tax|currency:group.currency }}
+
Tax: {{ group.total_tax|currency:group.currency }}
+
Total: {{ group.total_incl_tax|currency:group.currency }}
+
+{% endfor %} +{% endif %} +``` + +| Property | Type | Description | +| ----- | ------ | ------ | +| `lines` | List | List of cart [line](#line) items in this subscription group. | +| `currency` | String | Currency code for this group. | +| `frequency` | String | Subscription frequency label (eg "Monthly"). | +| `shipping_price.excl_tax` | Decimal | Shipping cost before tax. | +| `total_tax` | Decimal | Total tax for this group. | +| `display_taxes` | String | Tax display label. | +| `total_incl_tax` | Decimal | Group total including tax. | + + +## Cart Template Context + +The following additional context variables are available in cart templates (`templates/cart.html`). + +| Variable | Type | Description | +| ----- | ------ | ------ | +| `cart` | Object | The [cart](#cart) object. | +| `formset` | FormSet | Form set for editing cart line quantities and removing items. | +| `saved_formset` | FormSet | Form set for saved/wishlist items. | +| `cart_warnings` | List | List of warning messages about cart state. | +| `upsell_messages` | List | List of upsell offer messages. | +| `voucher_form` | Form | Coupon/voucher code entry form. | +| `max_cart_quantity_per_line` | Integer | Maximum allowed quantity per line item. | +| `subscription_groups` | List | List of [subscription_group](#subscription_group) objects. | +| `subscription_total_incl_tax` | Decimal | Total for all subscriptions including tax. | +| `subscription_total_excl_tax` | Decimal | Total for all subscriptions excluding tax. | +| `interval_count_choices` | List | Subscription frequency choices for product forms. | +| `editable` | Boolean | Whether the cart is in editable mode. | +| `modal_open` | Boolean | Whether the side cart modal should be open. | + + +## Template Contexts + +All templates receive the [Global Objects](#global-objects) (`store`, `settings`, `currencies`, `languages_active_storefront`, `menus`, `products`, `product_categories`, `posts`, `post_categories`, `privacy_policy`, `terms_and_conditions`, `subscription_terms_and_conditions`, `request`, `storefront_geos`). The table below lists additional view-specific context variables passed to each template. + +| Template | View-Specific Context | +| ----- | ------ | +| `templates/index.html` | Global objects only. Use the [`where`](/docs/storefront/themes/templates/tags#where) tag to query products and categories. | +| `templates/cart.html` | `cart`, `formset`, `saved_formset`, `cart_warnings`, `upsell_messages`, `voucher_form`, `max_cart_quantity_per_line`, `subscription_groups`, `editable`, `modal_open` | +| `templates/catalogue/product.html` | `product`, `variant_form`, `interval_count_choices` | +| `templates/catalogue/index.html` | `products` (paginated), `paginator`, `page_obj` | +| `templates/catalogue/category.html` | `category`, `products` (paginated), `filters`, `has_active_filter`, `paginator`, `page_obj` | +| `templates/search.html` | `query`, `products` (paginated), `paginator`, `page_obj` | +| `templates/blog/index.html` | `posts` (paginated), `paginator`, `page_obj` | +| `templates/blog/post.html` | `post` | +| `templates/pages/page.html` | `page` | +| `templates/support/index.html` | `categories` | +| `templates/support/category.html` | `category`, `articles` | +| `templates/support/article.html` | `article` | +| `templates/reviews/index.html` | `product`, `reviews` (paginated), `paginator`, `page_obj` | +| `templates/reviews/form.html` | `product`, `form` | +| `templates/reviews/review.html` | `product`, `review` | + + +Use the [`purchase_info_for_product`](/docs/storefront/themes/templates/tags#purchase_info_for_product) tag in any template to get pricing and availability for a product. Use [`cart_form`](/docs/storefront/themes/templates/tags#cart_form) on product pages to generate add-to-cart forms. + + + +## Dashboard Cross-Reference + +Some template variables are populated from dashboard settings. Use this reference to understand where data originates when building or debugging templates. + +| Template Variable | Dashboard Path | Description | +| ----- | ------ | ------ | +| `store.branding.logo` | Settings > Branding | Store logo image. | +| `store.branding.icon` | Settings > Branding | Store icon/favicon image. | +| `store.branding.primary_color` | Settings > Branding | Primary brand color (HEX). | +| `store.branding.accent_color` | Settings > Branding | Accent brand color (HEX). | +| `store.name`, `store.tagline` | Settings > General | Store name and tagline. | +| `store.legal_name`, `store.address` | Settings > General | Legal details and address. | +| `menus.{menu_key}.items` | Storefront > Navigation | Navigation menu items (up to 3 levels). | +| `privacy_policy` | Settings > Policies | Privacy policy content. | +| `terms_and_conditions` | Settings > Policies | Terms and conditions content. | +| `subscription_terms_and_conditions` | Settings > Policies | Subscription T&C content. | +| `settings.*` | Storefront > Themes > Customize | Theme settings from `settings_schema.json`. | diff --git a/content/docs/storefront/themes/templates/tags.mdx b/content/docs/storefront/themes/templates/tags.mdx index d450426..1657263 100644 --- a/content/docs/storefront/themes/templates/tags.mdx +++ b/content/docs/storefront/themes/templates/tags.mdx @@ -32,6 +32,47 @@ import AppHookLocations from '../../../../_snippets/_app-hook-locations.mdx'; +### add_query_param + +The `add_query_param` tag appends or updates a query parameter on the current URL. Commonly used for building pagination links and filter URLs while preserving existing query parameters. + +```django title="add_query_param" +{% add_query_param request 'page' page_obj.next_page_number %} +``` + +```django title="Example Pagination with add_query_param" +{% if paginator.num_pages > 1 %} + +{% endif %} +``` + +| Argument | Description | +| --- | --- | +| request | The current `request` context object. | +| param_name | The query parameter name to set, eg `'page'`. | +| value | The value to assign to the parameter. | + +### annotate_form_field + +The `annotate_form_field` tag adds HTML attributes to a form field based on its Django form field properties (required, type, etc.). Useful for adding client-side validation and accessibility attributes. + +```django title="annotate_form_field" +{% annotate_form_field field %} +{{ field }} +``` + ### boolean operators If tags may be used in combination with boolean operators for conditional control flow. @@ -51,6 +92,32 @@ If tags may be used in combination with boolean operators for conditional contro | `<=` | less than or equal to | | `>=` | greater than or equal to | +### cart_form + +The `cart_form` tag generates an add-to-cart form for a product. Required on every product page to enable purchasing. + +```django title="cart_form" +{% cart_form request product 'single' as cart_form %} +``` + +```django title="Example Product Add to Cart Form" +{% cart_form request product 'single' as cart_form %} +
+ {% csrf_token %} + {{ cart_form }} + +
+``` + +| Argument | Description | +| --- | --- | +| request | The current `request` context object. | +| product | The `product` context object. | +| form_type | The form type, typically `'single'`. | +| variable | Assigned template variable name for the form. | + ### comment Ignores everything between `{% comment %}` and `{% endcomment %}`. An optional note may be inserted in the first tag. For example, this is useful when commenting out code for documenting why the code was disabled. @@ -62,6 +129,27 @@ Ignores everything between `{% comment %}` and `{% endcomment %}`. An optional n {% endcomment %} ``` +### core_js + +The `core_js` tag outputs the platform's core JavaScript bundle. This is required in every theme's base layout and powers cart functionality, AJAX form submissions, CSRF token handling, and other platform features. + +```django title="core_js" +{% core_js %} +``` + +```django title="Example Placement in Base Layout" + {# jQuery must be loaded before core_js #} + + {% core_js %} + + + +``` + + +jQuery must be loaded before `{% core_js %}`. The platform's core JavaScript depends on jQuery being available in the global scope. + + ### csrf_token This tag is used for CSRF protection and required on any template with a form that sends a POST request to the back end. @@ -211,6 +299,48 @@ The `purchase_info_for_product` tag is used to retrieve the price of a product i | product | Must pass the current `product` context object. | +### purchase_info_for_line + +The `purchase_info_for_line` tag retrieves the price and availability of a cart line item in the current session's currency. Works the same as `purchase_info_for_product` but accepts a cart line object. + +```django title="purchase_info_for_line" +{% purchase_info_for_line request line as session %} +{% if session.price.exists %} + {{ session.price.price|currency:session.price.currency }} +{% endif %} +``` + +| Argument | Description | +| --- | --- | +| request | Must pass the current `request` context object. | +| line | Must pass a cart `line` context object. | + +### render_field + +The `render_field` tag renders a form field with additional HTML attributes. Use it to add CSS classes, placeholders, and other attributes to Django form fields in templates. + +```django title="render_field" +{% render_field field class+="form-control" placeholder="Enter your email" %} +``` + +```django title="Example Form with render_field" +
+ {% csrf_token %} +
+ + {% render_field field class+="form-control" %} + {% if field.errors %} +
{{ field.errors.0 }}
+ {% endif %} +
+
+``` + +| Argument | Description | +| --- | --- | +| field | The form field object to render. | +| attributes | HTML attributes to add, using `attr="value"` or `attr+="value"` (append) syntax. | + ### seo The `seo` tag generates SEO meta data for products in standardized format for consumption by 3rd party systems. diff --git a/content/docs/storefront/themes/templates/urls-and-template-paths.mdx b/content/docs/storefront/themes/templates/urls-and-template-paths.mdx index 51e23bc..f7f1fde 100644 --- a/content/docs/storefront/themes/templates/urls-and-template-paths.mdx +++ b/content/docs/storefront/themes/templates/urls-and-template-paths.mdx @@ -15,6 +15,13 @@ import IntroTheme from '../../../../_snippets/_view-intro-theme.mdx'; Ensure your template paths match with expected template paths for built-in storefront views. Use the public themes on [Github](https://github.com/NextCommerceCo/) as a reference guide and starting point. All URL paths are automatically localized to the users language following your store's Localization settings. + +### Homepage + +| URL Name | URL Path | Template Path | +| --- | --- | --- | +| N/A | / | templates/index.html | + ### Blog | URL Name | URL Path & Arguments | Template Path | @@ -27,6 +34,10 @@ Ensure your template paths match with expected template paths for built-in store | URL Name | URL Path | Template Path | | --- | --- | --- | | cart:summary | /cart/ | templates/cart.html | +| cart:add | POST /cart/add/:product_slug/ | N/A (action endpoint) | +| cart:saved | /cart/saved/ | templates/cart.html (saved items) | +| cart:vouchers-add | POST /cart/vouchers/add/ | N/A (action endpoint) | +| cart:vouchers-remove | POST /cart/vouchers/remove/:voucher_id/ | N/A (action endpoint) | ### Catalogue @@ -68,10 +79,35 @@ Ensure your template paths match with expected template paths for built-in store | URL Name | URL Path | Template Path | | --- | --- | --- | -| support:cateogry-list | /support/categories/ | templates/support/index.html | +| support:category-list | /support/categories/ | templates/support/index.html | | support:article-list | /support/categories/:category_slug/ | templates/support/category.html | | support:article-detail | /support/articles/:article_slug/ | templates/support/article.html | +### Customer / Authentication + +| URL Name | URL Path | Template Path | +| --- | --- | --- | +| customer:login | /accounts/login/ | N/A (platform-managed) | +| customer:logout | /accounts/logout/ | N/A (platform-managed) | +| customer:summary | /accounts/ | N/A (platform-managed) | +| customer:support-ticket-create | /accounts/support/create/ | N/A (platform-managed) | + +### Localization + +These are POST action endpoints used in forms for switching language, currency, or storefront geo. + +| URL Name | Method | Description | +| --- | --- | --- | +| set_language | POST | Change the active language. | +| core:set-currency | POST | Change the active currency. | +| core:set-storefront | POST | Change the active storefront geo (country, language, currency). | + +### API + +| URL Name | URL Path | Description | +| --- | --- | --- | +| storefrontapi:graphql | /api/graphql/ | [Storefront GraphQL API](/docs/storefront/graphql) endpoint. | + ### Error Pages | URL Name | URL Path | Template Path | From a4b738260cd21b3f20dc78e55c6bb5f78ec820bd Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 23 Mar 2026 14:36:47 +0700 Subject: [PATCH 2/4] Remove per-user objects, add GraphQL requirement callout Addresses review feedback: cart, line, subscription_group, and user objects removed from template documentation. These represent per-user data that is incompatible with full-page caching (keyed by URL + language + currency). All cart and user interactions must use the Storefront GraphQL API via client-side JavaScript. Kept cache-safe objects: session (product pricing), variant_form, filters (URL-param driven, public data). Added prominent callout directing developers to GraphQL API for cart and user data. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../storefront/themes/templates/objects.mdx | 139 +----------------- 1 file changed, 5 insertions(+), 134 deletions(-) diff --git a/content/docs/storefront/themes/templates/objects.mdx b/content/docs/storefront/themes/templates/objects.mdx index 39b4464..aa3c45e 100644 --- a/content/docs/storefront/themes/templates/objects.mdx +++ b/content/docs/storefront/themes/templates/objects.mdx @@ -598,27 +598,9 @@ Product review object. | `id` | String | Review ID. | | `title` | String | Review title. | | `score` | Integer | Review score. | -| `user` | Object | The customer that created the review, see [user](#user). | +| `user` | Object | The customer that created the review. | -### user - -The current user/customer object available when a customer is authenticated. - -| Property | Type | Description | -| ----- | ------ | ------ | -| `is_authenticated` | Boolean | Whether the user is logged in. | -| `is_staff` | Boolean | Whether the user is an admin/staff member. | - -```django title="Example Conditional Auth Links" -{% if user.is_authenticated %} - My Account - Logout -{% else %} - Login -{% endif %} -``` - ### voucher Voucher object. @@ -634,68 +616,6 @@ Voucher object. View-specific objects are available in the templates rendered by their corresponding views. See [Template Contexts](#template-contexts) below for which objects are available in each template. -### cart - -The cart object represents the current user's shopping cart and is available in cart templates. - -```django title="Example Cart Summary" -{% if not cart.is_empty %} -
- {{ cart.num_items }} item{{ cart.num_items|pluralize }} - {{ cart.total_incl_tax|currency:cart.currency }} -
-{% endif %} -``` - -| Property | Type | Description | -| ----- | ------ | ------ | -| `is_empty` | Boolean | Whether the cart has any items. | -| `num_items` | Integer | Total number of items in the cart. | -| `total_incl_tax` | Decimal | Cart total including tax. | -| `total_excl_tax` | Decimal | Cart total excluding tax. | -| `total_excl_tax_excl_discounts` | Decimal | Cart subtotal before discounts and tax. | -| `currency` | String | The cart's currency code, eg `USD`. | -| `is_tax_known` | Boolean | Whether tax has been calculated. | -| `display_taxes` | String | Tax display label. | -| `offer_discounts` | List | List of applied offer discounts. | -| `grouped_voucher_discounts` | List | List of applied voucher discounts. | -| `subscription_rebill_quantity` | Integer | Number of subscription items in the cart. | - - -### line - -A cart line item object representing a single product entry in the cart. Available when iterating over cart lines in cart templates. - -```django title="Example Cart Line Items" -{% for form in formset %} - {% with line=form.instance %} -
- {{ line.description }} - {{ line.unit_price_incl_tax|currency:line.price_currency }} - Qty: {{ line.quantity }} - {% if line.discount_value %} - -{{ line.discount_value|currency:line.price_currency }} - {% endif %} - {{ line.line_price_incl_tax_incl_discounts|currency:line.price_currency }} -
- {% endwith %} -{% endfor %} -``` - -| Property | Type | Description | -| ----- | ------ | ------ | -| `description` | String | Product title/description in the cart. | -| `product` | Object | The [product](#product) object for this line. | -| `quantity` | Integer | Quantity of this item in the cart. | -| `unit_price_incl_tax` | Decimal | Unit price including tax. | -| `unit_price_excl_tax` | Decimal | Unit price excluding tax. | -| `is_tax_known` | Boolean | Whether tax is calculated for this line. | -| `price_currency` | String | Currency code for this line item. | -| `discount_value` | Decimal | Discount amount applied to this line. | -| `line_price_incl_tax_incl_discounts` | Decimal | Total line price after discounts and tax. | -| `subscription_range` | Field | Subscription frequency selector field, if applicable. | - - ### session The session object is returned by the [`purchase_info_for_product`](/docs/storefront/themes/templates/tags#purchase_info_for_product) and [`purchase_info_for_line`](/docs/storefront/themes/templates/tags#purchase_info_for_line) template tags. It contains pricing and availability information for a product in the current user's currency. @@ -838,58 +758,6 @@ There are three filter types: `price_range`, `boolean`, and `list`. Each type ha | `label` | String | Display label for this value. | -### subscription_group - -Subscription group object available in cart summary templates when the cart contains subscription products. Accessed through the `subscription_groups` list. - -```django title="Example Subscription Summary" -{% if subscription_groups %} -{% for group in subscription_groups %} -
-
Subscription - {{ group.frequency }}
- {% for line in group.lines %} -
{{ line.description }} - {{ line.unit_price_incl_tax|currency:group.currency }}
- {% endfor %} -
Shipping: {{ group.shipping_price.excl_tax|currency:group.currency }}
-
Tax: {{ group.total_tax|currency:group.currency }}
-
Total: {{ group.total_incl_tax|currency:group.currency }}
-
-{% endfor %} -{% endif %} -``` - -| Property | Type | Description | -| ----- | ------ | ------ | -| `lines` | List | List of cart [line](#line) items in this subscription group. | -| `currency` | String | Currency code for this group. | -| `frequency` | String | Subscription frequency label (eg "Monthly"). | -| `shipping_price.excl_tax` | Decimal | Shipping cost before tax. | -| `total_tax` | Decimal | Total tax for this group. | -| `display_taxes` | String | Tax display label. | -| `total_incl_tax` | Decimal | Group total including tax. | - - -## Cart Template Context - -The following additional context variables are available in cart templates (`templates/cart.html`). - -| Variable | Type | Description | -| ----- | ------ | ------ | -| `cart` | Object | The [cart](#cart) object. | -| `formset` | FormSet | Form set for editing cart line quantities and removing items. | -| `saved_formset` | FormSet | Form set for saved/wishlist items. | -| `cart_warnings` | List | List of warning messages about cart state. | -| `upsell_messages` | List | List of upsell offer messages. | -| `voucher_form` | Form | Coupon/voucher code entry form. | -| `max_cart_quantity_per_line` | Integer | Maximum allowed quantity per line item. | -| `subscription_groups` | List | List of [subscription_group](#subscription_group) objects. | -| `subscription_total_incl_tax` | Decimal | Total for all subscriptions including tax. | -| `subscription_total_excl_tax` | Decimal | Total for all subscriptions excluding tax. | -| `interval_count_choices` | List | Subscription frequency choices for product forms. | -| `editable` | Boolean | Whether the cart is in editable mode. | -| `modal_open` | Boolean | Whether the side cart modal should be open. | - - ## Template Contexts All templates receive the [Global Objects](#global-objects) (`store`, `settings`, `currencies`, `languages_active_storefront`, `menus`, `products`, `product_categories`, `posts`, `post_categories`, `privacy_policy`, `terms_and_conditions`, `subscription_terms_and_conditions`, `request`, `storefront_geos`). The table below lists additional view-specific context variables passed to each template. @@ -897,7 +765,6 @@ All templates receive the [Global Objects](#global-objects) (`store`, `settings` | Template | View-Specific Context | | ----- | ------ | | `templates/index.html` | Global objects only. Use the [`where`](/docs/storefront/themes/templates/tags#where) tag to query products and categories. | -| `templates/cart.html` | `cart`, `formset`, `saved_formset`, `cart_warnings`, `upsell_messages`, `voucher_form`, `max_cart_quantity_per_line`, `subscription_groups`, `editable`, `modal_open` | | `templates/catalogue/product.html` | `product`, `variant_form`, `interval_count_choices` | | `templates/catalogue/index.html` | `products` (paginated), `paginator`, `page_obj` | | `templates/catalogue/category.html` | `category`, `products` (paginated), `filters`, `has_active_filter`, `paginator`, `page_obj` | @@ -916,6 +783,10 @@ All templates receive the [Global Objects](#global-objects) (`store`, `settings` Use the [`purchase_info_for_product`](/docs/storefront/themes/templates/tags#purchase_info_for_product) tag in any template to get pricing and availability for a product. Use [`cart_form`](/docs/storefront/themes/templates/tags#cart_form) on product pages to generate add-to-cart forms. + +**Cart and user data must use the [Storefront GraphQL API](/docs/storefront/graphql).** All storefront pages are fully cached per language and currency combination. Per-user data (cart contents, authentication state, wishlists) rendered in server-side templates would be cached and served to other visitors. Use client-side JavaScript with the GraphQL API for all cart and user interactions. + + ## Dashboard Cross-Reference From 65254ffcd26bdd331c8e39a3938f9246e6cfebd2 Mon Sep 17 00:00:00 2001 From: Alex Phelps Date: Tue, 24 Mar 2026 11:02:01 +0700 Subject: [PATCH 3/4] fixed example --- content/docs/storefront/themes/templates/filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/storefront/themes/templates/filters.md b/content/docs/storefront/themes/templates/filters.md index b350161..ae58d26 100644 --- a/content/docs/storefront/themes/templates/filters.md +++ b/content/docs/storefront/themes/templates/filters.md @@ -297,7 +297,7 @@ For example, if value is the string "Sandy", the output would be the list ['S', Returns a plural suffix if the value is not 1, '1', or an object of length 1. By default, this suffix is 's'. ```jinja title="pluralize" -You have { num_messages }} message {{ num_messages|pluralize }} +You have {{ num_messages }} message{{ num_messages|pluralize }} ``` ### split From 9ff9906785286e752f455d6536eccf3e5a154c93 Mon Sep 17 00:00:00 2001 From: Alex Phelps Date: Tue, 24 Mar 2026 11:07:19 +0700 Subject: [PATCH 4/4] fix typos --- content/docs/apps/guides/dispute-service.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/apps/guides/dispute-service.mdx b/content/docs/apps/guides/dispute-service.mdx index c6560c2..d214cc4 100644 --- a/content/docs/apps/guides/dispute-service.mdx +++ b/content/docs/apps/guides/dispute-service.mdx @@ -137,7 +137,7 @@ It's desirable to cancel fulfillment for orders that have not shipped yet when t To retrieve a list of all fulfillment orders and their status, use the [ordersFulfillmentOrdersRetrieve](/docs/admin-api/reference/orders/ordersFulfillmentOrdersRetrieve) endpoint. - + If order `fulfilmlent_status` is `unfulfilled`, your dispute service can stop fulfillment using the [fulfillmentOrdersHold](/docs/admin-api/reference/fulfillment/fulfillmentOrdersHold) endpoint. @@ -151,7 +151,7 @@ If order `fulfilmlent_status` is `unfulfilled`, your dispute service can stop fu ``` - + If order `fulfilmlent_status` is `processing` your dispute service can request processing fulfillment orders be canceled with the fulfillment locations with the [cancellationRequestSend](/docs/admin-api/reference/fulfillment/cancellationRequestSend) endpoint.