diff --git a/pyproject.toml b/pyproject.toml index dd383c0e25..9b95e04ccf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,7 @@ optional = [ "django-filter", "django-guardian>=2.4.0,<3.4", "inflection==0.5.1", + "jinja2>=3.0", "legacy-cgi; python_version>='3.13'", "markdown>=3.3.7", "psycopg[binary]>=3.1.8", diff --git a/rest_framework/jinja2.py b/rest_framework/jinja2.py new file mode 100644 index 0000000000..6e01777edd --- /dev/null +++ b/rest_framework/jinja2.py @@ -0,0 +1,27 @@ +from jinja2.ext import Extension +from markupsafe import Markup + +from rest_framework.renderers import HTMLFormRenderer + + +class DRFExtension(Extension): + """Jinja2 extension exposing DRF template rendering functions.""" + + def __init__(self, environment): + super().__init__(environment) + environment.globals["render_form"] = self.render_form + environment.globals["render_field"] = self.render_field + + def render_form(self, serializer, template_pack=None): + """Render a complete HTML form for the given serializer.""" + style = {"template_pack": template_pack} if template_pack else {} + renderer = HTMLFormRenderer() + return Markup(renderer.render(serializer.data, None, {"style": style})) + + def render_field(self, field, style=None): + """Render an individual HTML field.""" + renderer = ( + style.get("renderer", HTMLFormRenderer()) if style else HTMLFormRenderer() + ) + html = renderer.render_field(field, style or {}) + return Markup(html) diff --git a/tests/test_jinja2_templates.py b/tests/test_jinja2_templates.py new file mode 100644 index 0000000000..5c30bd2fb0 --- /dev/null +++ b/tests/test_jinja2_templates.py @@ -0,0 +1,121 @@ +import importlib + +import pytest + +from rest_framework import serializers + + +def load_jinja(): + pytest.importorskip("jinja2") + jinja2 = importlib.import_module("jinja2") + DRFExtension = importlib.import_module("rest_framework.jinja2").DRFExtension + return jinja2.Environment, DRFExtension + + +class SimpleSerializer(serializers.Serializer): + name = serializers.CharField() + age = serializers.IntegerField() + + +def test_jinja2_render_form_with_extension(): + """ + Test that the DRF Jinja2 extension correctly exposes render_form + and render_field to the Jinja2 environment. + """ + Environment, DRFExtension = load_jinja() + + # Setup Jinja2 environment with the new DRF extension + env = Environment(extensions=[DRFExtension]) + + # Create a template that uses the DRF rendering functions + template_str = """ + {% set form_data = render_form(serializer) %} + {{ form_data }} + """ + template = env.from_string(template_str) + + serializer = SimpleSerializer(data={"name": "Mohammed", "age": 30}) + serializer.is_valid() + + # Render the template + output = template.render(serializer=serializer) + + # Assertions: Verify the output contains expected HTML form elements + assert ( + "