Repository: lektor/lektor (python)

Issue:
how can use databags as source for checkbox field
hi, i try to access databags from modeling file of a page, and below is the way i do:

[fields.label]
label = labels
type = checkboxes
source = site.databags.get_bag('categories')


and i got a error: TypeError: string indices must be integers

how can i use databags as a source of checkbox field?

thanks



Pull request description:
fix: `bag()` should get `site` from jinja context
Jinja global functions should obtain `site` from the Jinja context rather than the Lektor build Context.

Sometimes `site` is explicitly set in the jinja context. In that case, that is the value that should be used.  Furthermore, outside of an artifact build, a Lektor build Context is not available.

This mostly fixes #614, allowing the use of databags as the `source` for a `checkboxes` or `select` field.
Note that one will likely still have to specify a non-default value for `item_key`.  The default is `{{ this._id }}`, so unless the items from the databag have an `_id`, that won't work.


## Notes

We may want to consider deprecating `site_proxy` and `config_proxy`.  (Instead, compute defaults for `site` and `config` from the current Lektor context in `Environment.make_default_tmpl_values`.)


<!---
Remember to please check that your code passes tests locally. Test with the Makefile
commands `make test`, or if you only need to test just Python / JS `make test-python`
or `make test-js`.
--->

### Issue(s) Resolved

<!--- List the issue(s) below, in the form "Fixes #1234"; one per line --->

Fixes #614

### Related Issues / Links

<!---
Are there any similar or related issues or pull requests?
Did you make a pull request to update the docs?
--->

### Description of Changes

- [x] Added unit test(s) covering the changes (if testable)


<!--- Explain what you've done and why --->

<!--- Thanks for your help making Lektor better for everyone! --->


Patch:
diff --git a/lektor/environment/__init__.py b/lektor/environment/__init__.py
index 9e4ad35..6f99c10 100644
--- a/lektor/environment/__init__.py
+++ b/lektor/environment/__init__.py
@@ -182,9 +182,11 @@ class CustomJinjaEnvironment(jinja2.Environment):
             raise
 
 
-def lookup_from_bag(*args):
+@jinja2.pass_context
+def lookup_from_bag(jinja_ctx, *args):
     pieces = ".".join(x for x in args if x)
-    return site_proxy.databags.lookup(pieces)
+    site = jinja_ctx.get("site", default=site_proxy)
+    return site.databags.lookup(pieces)
 
 
 class Environment:


Test patch:
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 2e66e2f..31ecf41 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -7,6 +7,7 @@ from pathlib import Path
 import pytest
 
 import lektor.context
+from lektor.db import Pad
 from lektor.environment import Environment
 
 
@@ -17,6 +18,11 @@ def scratch_project_data(scratch_project_data):
     subpage_lr = scratch_project_data / "content/sub-page/contents.lr"
     subpage_lr.parent.mkdir()
     subpage_lr.write_text("".join(lektor.metaformat.serialize(data.items())))
+
+    testbag_ini = scratch_project_data / "databags/testbag.ini"
+    testbag_ini.parent.mkdir()
+    testbag_ini.write_text("foo = bar")
+
     return scratch_project_data
 
 
@@ -185,3 +191,18 @@ def test_dates_format_filter_raises_type_error_on_bad_kwarg(
     template = env.jinja_env.from_string("{{ now | %s(%s=42) }}" % (dates_filter, arg))
     with pytest.raises(TypeError):
         template.render(now=datetime.datetime.now())
+
+
+def test_bag_gets_site_from_jinja_context(
+    scratch_env: Environment, scratch_pad: Pad
+) -> None:
+    template = scratch_env.jinja_env.from_string("{{ bag('testbag.foo') }}")
+    assert template.render(site=scratch_pad) == "bar"
+
+
+def test_bag_gets_site_from_lektor_context(
+    scratch_env: Environment, scratch_pad: Pad
+) -> None:
+    template = scratch_env.jinja_env.from_string("{{ bag('testbag.foo') }}")
+    with lektor.context.Context(pad=scratch_pad):
+        assert template.render() == "bar"