Closed hardiksondagar closed 1 year ago
Hi @hardiksondagar,
You could define a random
drop with properties that generate a different random number each time they are accessed. This is consistent with how the "standard" now
and today
objects are implemented (source).
import random
from typing import Iterator
from typing import Mapping
from typing import Union
from liquid import Environment
class RandomDrop(Mapping[str, Union[int, float]]):
"""Mapping-like object for generating random numbers."""
def __contains__(self, item: object) -> bool:
return item in ("random", "randint")
def __getitem__(self, key: str) -> Union[int, float]:
if key == "random":
return random.random()
if key == "randint":
return random.randint(1, 10)
raise KeyError(str(key))
def __iter__(self) -> Iterator[str]:
return iter(["random", "randint"])
def __len__(self) -> int:
return 2
def __str__(self) -> str:
return "RandomDrop"
env = Environment(globals={"random": RandomDrop()})
Usage would look like this...
{% assign rand_val = random.random -%}
{{ rand_val }}
{% for i in (1..random.randint) -%}
{{ random.random }}
{% endfor -%}
Output
0.5856242442692104
0.7310637087401849
0.4737098762489945
0.9324768766566306
0.358920961408541
0.703978047935022
If the leading random.
is undesirable, it is possible to add RandomDrop
directly to a render context's scope, by subclassing liquid.Context
, liquid.BoundTemplate
and liquid.Environment
.
Let me know if you'd like an example of that.
Thank you for the prompt reply @jg-rp. I would really appreciate an example to use random without random.
prefix.
import random
from typing import Iterator
from typing import List
from typing import Mapping
from typing import Optional
from typing import Union
from liquid import Context
from liquid import Environment
from liquid import BoundTemplate
from liquid.context import Namespace
class RandomDrop(Mapping[str, Union[int, float]]):
"""Mapping-like object for generating random numbers."""
def __contains__(self, item: object) -> bool:
return item in ("random", "randint")
def __getitem__(self, key: str) -> Union[int, float]:
if key == "random":
return random.random()
if key == "randint":
return random.randint(1, 10) # Arbitrary example
raise KeyError(str(key))
def __iter__(self) -> Iterator[str]:
return iter(["random", "randint"])
def __len__(self) -> int:
return 2
def __str__(self) -> str:
return "RandomDrop"
class MyContext(Context):
def __init__(
self,
env: Environment,
globals: Optional[Namespace] = None,
disabled_tags: Optional[List[str]] = None,
copy_depth: int = 0,
parent_context: Optional[Context] = None,
loop_iteration_carry: int = 1,
local_namespace_size_carry: int = 0,
template: Optional[BoundTemplate] = None,
):
super().__init__(
env,
globals,
disabled_tags,
copy_depth,
parent_context,
loop_iteration_carry,
local_namespace_size_carry,
template,
)
# Pushing RandomDrop to the front the the chain map means its
# properties take priority over any template variables set using
# `{% assign %}` and `{% capture %}`.
self.scope.push(RandomDrop())
class MyBoundTemplate(BoundTemplate):
context_class = MyContext
class MyEnvironment(Environment):
template_class = MyBoundTemplate
env = MyEnvironment()
The previous example would then become...
{% assign rand_val = random -%}
{{ rand_val }}
{% for i in (1..randint) -%}
{{ random }}
{% endfor -%}
Thanks a lot for this example. This example will allow me to add more such drop
objects. Thanks again.
We need python's random method exposed in liquid to assign a random value to a variable. Writing a simple custom filter solves the main thing, but in order to do that some variable is required.
Code
Usage
Is there any better way to do this without the need for piping? I looked at the
tags
but am unsure if it's a better solution.By the way thanks a lot for building this library, we are using it on a daily basis and helping us in a big way to deliver business values.