I have n number of def blocks in my inheriting template which shows one of them in the inherited template depending on a variable value which is passed from the view.
Currently, I do the computing in the View file, then there decide which def to be visible, then pass this in a dictionary as a key-value along with other to-be-shown data to the inheriting template and there, using if-elif-endif clause evaluate this key's value to see which def to show and then manually call that def with **${self.val_of_key_nn()}**
I want to know if there is a way to get rid of this if-elif-endif bock and simply pass the self.def_to_call() command to the template directly from the view?
example code:
#defs is a key in the passed-dictionary.
#I want to reduce this block to a single-line code
% if defs == '1':
${self.block_1()}
% elif defs == '2':
${self.block_2()}
% else:
${self.block_default()}
% endif
Thank you.
Phil, your question is most timely... as of June 2012 they are working on making this ability more explicit for a future Pyramid version.
There is a way to do it currently, but it's undocumented, and if you use it you should consider it liable to change in the future. Instead of returning only a dict from your view callable, return a tuple:
('defname', {...}) where 'defname' is the (quoted) name of the specific def that you wish to render, and {...} is the dict as usual.
If you're interested in the proposed future method for this, which puts the defname in the template path (asset spec) instead:
https://github.com/Pylons/pyramid/commit/ea009a6d4a1ffa8585faa85581848f6e74a57dfc
FYI for other interested readers: there's an excellent post on use cases for this technique on zzzeek's blog here.
For example:
<%
defdict = {'1': block_1, '2': block_2}
%>
${defdict.get(defs, block_default)()}
Related
I am working on a Django Project, and I want the User to be able to add the code in the .py file. Well, I have .py file that contains some code, and in the mid of code I have some lines of code that specify some rules for Fuzzy Logic Algorithm, such as below:
...
rule1 = scikit-fuzzy.control.Rule(input1["bad"] & input2["bad"] & input3["bad"] & input4["bad"] & input5["average"], output["poor"])
...
So, I have total 243 lines of codes (in other words rules) like the above centered between some code (that is above it and below it there some other code). Now, I want Admin to be able to edit and update these lines of codes in a way that, using Forms I will take the inputs from the Admin (such as: 'good', 'bad', 'average', 'bad', 'good', 'poor', and I want the system to create a new line of code taking these strings as inputs.
If admin enters: 'good', 'bad', 'average', 'bad', 'good', 'poor', I want the .py to have a new line after the 243r line which should be like this:
rule243 = #
rule244 = scikit-fuzzy.control.Rule(input1["good"] & input2["bad"] & input3["average"] & input4["bad"] & input5["good"], output["poor"]
Can anyone help me in this?
Actually this should be doable, I've a solution in mind, Let's implement it
Take caution
This requires HIGH verification and because this is python code, You don't want to give the admin all the rights to execute ANY python code from the admin page. If the admin details gets hijacked, You're ##!$#ED.
Let's analyze the problem into smaller problems.
Big point
We want to execute code from admin page
Smaller points
We need to put multiple rules, and they are uncountable, A perfect job for a database model
We will think of the code as Rule object. we will think of the dictionary key as a RuleKey object. we will think of input or output as RuleType
In your case, input1['good'] is the same as RuleTypeX[RuleKey] where x is an Integer starting from 1.
We will think of the RuleKey as many-to-many relation with the Rule, The Rule can has many RuleKey objects and those RuleKey objects can exist in different Rule objects.
THING TO CONSIDER
Verification is very important, YOU DON'T WANT SOMEONE PASSING eval() IN YOUR CODE AND EXECUTE WHATEVER HE WANTS.
I've created this REGEXP to match your code
scikit-fuzzy\.control\.Rule\(\s?((input[0-9]+\[("|')(poor|good|average|bad|whatever)("|')\])\s?&?\s?)+,?\s?(output[0-9]*\[("|')(poor|good|average|bad|whatever)("|')\])+\s?\)
Took a lot of time lol, Tested on Regexr, This matches your code well, paste your code there and test to see, feel free to improve it, this is not the main focus here.
class RuleKey(models.Model):
class TYPES(models.IntegerChoices):
INPUT = 0
OUTPUT = 1
content = models.CharField(max_length=10)
type = models.IntegerField(choices=TYPES.choices) # choices are of type Integer, the field must also be Integer
def __str__(self):
return self.content
content will contain any of ['poor', 'good', 'whatever'] and type will be 0 for Input and 1 for Output and I MEANT TO TYPE IT CAPITALIZED because we need to solve the caps problem too.
We have a function to check if the final code is valid before evaluating it.
def valid_python_code(code):
if re.match(valid_code_pattern, code):
return True
return False
Don't use the stupid RegexValidator that django provides, It raises an Exception if it didn't match but IT RETURNS NONE IF IT MATCHES, makes no sense for me, or use it, whatever.
When we run code, We're 'executing' it and this is the formal word for running code.
class Rule(models.Model):
keys = models.ManyToManyField('RuleKey', related_name='rules')
wasExecutedBefore = models.BooleanField(default=False) # ran before or no
def execute(self):
if self.wasExecutedBefore:
return None
input_keys = self.keys.filter(type=0)
output_keys = self.keys.filter(type=1)
"""
Can't use f-strings as we're evaluating genexps here, note the key.type, In a production environment
This should be RuleKey.TYPES.INPUT or RuleKey.TYPES.OUTPUT, just swap the places of the classes, I'm lazy to do that
"""
code = "scikit-fuzzy.control.Rule({inputs}, {outputs})".format(
inputs=''.join(
f"{key.get_type_display().lower()}{i + 1}['{key.content}']" for i, key in enumerate(input_keys) if
key.type == 0),
outputs=''.join(
f"{key.get_type_display().lower()}{j + 1}['{key.content}']" for j, key in enumerate(output_keys) if
key.type == 1))
if valid_python_code(code):
return eval(code)
else:
raise ValueError('Invalid code or missing libs or whatever.')
Lemme comment here the problems I tackled when implementing this.
RuleKey.TYPES is an IntegerChoices class, Django takes the attributes and capitalizes the first letter and returns it, Your code needs input and not Input , fortunately we have str.lower()
The second problem is that a regular for-in loop doesn't have a counter when looping objects other than range objects, We will use enumerate instead.
The third problem is that f-strings introduced in Python-3.6 (Not sure of 3.6) don't support GENEXPS inside, You have to use .format()
The fourth problem is that when using a GENEXP directly inside format instead of using ''.join(GENEXP), You'll print the lazy version of it i.e <generator object at 0x214312>
Don't try to use keys = keys.objects.all() and do this in one query, The problem you'll face will be related to the foor loop counter
Don't use RuleKey.type, this returns the integer, You need to call django's get_foo_display and in this case it's get_type_display()
Let's illustrate
I'll name my variables on the following format, class_type_content
keys = [key_1_poor, key_0_good, key_1_average]
let's loop and filter
for key in keys: # no count
pass
let's enumerate
for i, item in enumerate(keys):
print(i) # prints index
print(item) # prints object
now for the .all() part, If you tried to iterate .all(), You'll succeed, however, If your input started with input1, Your output will start with output2 and this is because both are in the same Queryset object.
Now, Add this to your admin page, perhaps as a button.
# admin.py
class RuleAdmin(admin.ModelAdmin):
def response_change(self, request, rule_obj):
if "execute" in request.POST:
try:
obj.execute() # execute (DONT IGNORE THE NOTES BELOW)
obj.wasExecutedBefore = True
obj.save()
except (ValueError, TypeError): # eval failed or execute
pass
return redirect(".")
return super().response_change(request, obj)
admin.site.register(Rule, RuleAdmin) # register with admin
NOTE You need to import re and all the required libs like scikit and the ones needed for your code in your models.py.
Now in your app, I named my app core , create templates/admin/core/rule, this is on the following format. <app_name/templates/admin/app_name/model_name> all lowercase.
add a file called change_form.html
add this
{% extends 'admin/change_form.html' %}
{% block submit_buttons_bottom %}
{{ block.super }}
<div class="submit-row">
<input type="submit" value="Execute the rule" name="execute">
</div>
{% endblock %}
This creates a form that posts execute then we capture it in the model admin and if it exists, we execute the code.
I've written this dev.to article based on this topic
I'm trying to create a Jinja2 environment with the intention of only using it on JSON templates. Thus, I would like to apply the tojson filter on every rendered value. I'm aware that each individual template can use filter sections but would like to avoid this.
Is there any way to set a global filter? If not, would the next best thing be a custom loader that inserted the filter section at the beginning and end of the loaded data?
Apply a recursive function to your context dict before you pass it to Jinja2.
For example, this function escape every string inside a dict prepending a \ to specific characters.
def escape_markdown(data):
if isinstance(data, dict):
return {key: escape_markdown(val) for key, val in data.items()}
elif isinstance(data, str):
return re.sub(r'([\\*_])', r'\\\1', data)
else:
return data
Personally, I use a helper function to render my templates, which inserts some handy variables as well as being usable for what you need. I understand it's not exactly what you're asking for, but hopefully it will still be useful.
def render_response(self, _template, **context):
# Renders a template and writes the result to the response.
import json, time
context['now']=int(time.time())
context['anything_you_want']=self.session['something']
for k in context:
context[k] = json.dumps(context[k])
rv = self.jinja2.render_template(_template, **context)
self.response.write(rv)
I have a base template for when a user is logged in, and on that base template, I need to add user specific options in a drop down menu. This drop down menu with options must be constant across all handlers, i.e., any time the base template is invoked (extended) with a child template.
Other than performing the necessary DB query, assigning the query results to a variable, and passing that variable to every handler (there are many), how can I consolidate this into one query and one variable, which gets passed directly to the base template? I am using jinja2 templates as well.
I would hate to do something so cumbersome in exchange for something far more simple and maintainable.
Any ideas? Thanks.
EDIT
So I still haven't found anything that's exactly what I'm looking for; however, I decided to at least make some headway in the interim. So, I made a custom decorator that takes a view's returned dict() and appends the appropriate data to it. For example:
def get_base_data(func):
def wrapper(request):
d = func(request)
user_id = request.user.id # used in query
contact_group_data = ContactGroups.query.filter(...criteria...).all()
d['contact_group_data'] = contact_group_data
return d
return wrapper
Now, I can at least decorate each method very concisely and simply by putting:
#view_config(...)
#get_base_data
def my_handler(request):
pass # rest of code...
This is one of most inobvious things in Pyramid and took a while to find for me, too.
You can modify the global template context in BeforeRender event.
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/hooks.html#using-the-before-render-event
Alternatively, you could use class based views, inherit all your views from one base view class which has get_base_data(), then the class instance is passed to the template context to all your views and then you could extract the data with {{ view.get_base_data }}.
http://ruslanspivak.com/2012/03/02/class-based-views-in-pyramid/
I vouch for the latter approach as it is more beautiful, predictable and easier to maintain engineering wise.
Is there a simple django tag to get the first x characters of a string in a template?
In a list of modelinstances, I would like to give a different symbol per objectinstance, depending on the status of the objectinstance. Status could be 'waiting', 'success' or 'failed XXXX', with XXXX being the errorcode.
I would like to check if the first 5 characters of objectinstance.status == 'error', then the symbol will be red. However, how can I do this? In Python I could use objectinstance.status[:5].
Using https://docs.djangoproject.com/en/dev/ref/templates/builtins/ I managed to do this with following 'monstruous' concatenation, but is there something simple as .left() or .right()?
{% if run.status|make_list|slice:":5"|join:"" == 'error' %}
You could try:
{% if run.status|truncatechars:5 == 'error...' %}
(See truncatechars in the Django docs)
Although I might say, as an overall point, you shouldn't be putting this kind of logic in your Django templates (views in other frameworks). You want to put this into the Django view (controller in other framerworks). Meaning, you would something like this in your view:
has_error = run.status.startswith('error')
Ensure has_error is passed to the template and:
{% if has_error %}
It may be more work, but the logic to detect error conditions could be shared between multiple views and templates, and you separate control logic from view logic.
If you use Django 1.4+ you can use the truncatechars tag but it will only solve partially your answer and will add ellipsis at the end.
The only viable way, a part from concatenating many filters as you already did, is to write a custom filter. Here is a first draft you can customize:
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
#register.filter
#stringfilter
def slicestring(value, arg):
"""usage: "mylongstring"|slicestring:"2:4" """
els = map(int, arg.split(':'))
return value[els[0]:els[1]]
as a bonus this filter allows you to mimic almost completely the slice notation by providing a "slicing string" as the argument. The only exception seems the syntax [:9] that has to be replaced with [0:9], thus with this argument: yourvariable|slicestring:"0:9"
A side note: Since your question is "getting the first part of a string" I believe a custom filter may be the correct answer, however if the only reason to get a sliced string is to check for a part of it inside an if statement, then I have to agree with Anton: you should place your checks inside the view, not inside the template, when possible.
I have two types of users. After a user logs in, I take the user to their user profile at /profile
Based on the type of user, their profile may contain different navigation items and forms. Is there an easier way to construct the navigation based on the user type instead of putting {% if %} tags everywhere in the template.
Not sure why #dm03514 deleted his answer, but I'll post something like it, since it would've been my thoughts as well.
If the templates are different enough, then you're right, branching all over the place is a bad idea. It'll just make your template a confusing mess. So just create a separate template for each user type. Then, in your view, you can choose one template or another to show based on the user type. For example:
Old-style function-based views:
def profile_view(request):
if request.user.get_profile().type == 'foo':
template = 'path/to/foo_profile.html'
elif request.user.get_profile().type == 'bar':
template = 'path/to/bar_profile.html'
else:
template = 'path/to/generic_profile.html' # if you want a default
return render_to_response(template, {'data': 'data'}, context_instance=RequestContext(request))
New-style class-based views:
class MyView(View):
def get_template_names(self):
if self.request.user.get_profile().type == 'foo':
return ['path/to/foo_profile.html']
elif self.request.user.get_profile().type == 'bar':
return ['path/to/bar_profile.html']
else:
return ['path/to/generic_profile.html']
I'm not sure how your user types are set up, but if it's based on a string value, you can even create something much more automated, like:
template = 'path/to/%s_profile.html' % request.user.get_profile().type
Then, just create a template for each type based on that naming scheme.
And, of course, each template can extend a base profile.html template, allowing you factor out some common functionality.
Create a profile model and link the user to this profile model. (via a foreign key)
The profile model will then have different attributes for different behaviours.
These different behaviours would then be exposed in the web application either by branching the template (as you say using if in the template) or returning different data to the template or being sent to javascript functions in the font end to modify behaviour
Another alternative is to direct users to different pages based of there profile. The challenge with this is different urls will be needed to different users. This may lead to lots of code duplication.
Have you thought about using permissions for that?
I do same with custom permissions. Easy part is that auth provides anything you need, and it's always there in request.
Very similar code to what Chris Patt proposed would be used:
def profile_view(request):
if request.user.has_perm('permission_codename_foo'):
template = 'path/to/foo_profile.html'
elif request.user.has_perm('permission_codename_bar'):
template = 'path/to/bar_profile.html'
else:
template = 'path/to/generic_profile.html' # if you want a default
return render_to_response(template, {'data': 'data'}, context_instance=RequestContext(request))
what's useful, is that you can protect your views using same permissions with decorators:
#permission_required('permission_codename_foo')
def foo():
#your view here
or if you want to check for permission alternatives:
#user_passes_test(lambda u: u.has_perm('permission_codename_foo') or u.has_perm('permission_codename_bar'))
def foo_or_bar():
#your view here
django creates whole bunch of default permissions as well named: app_name.add_modelname, app_name.change_modelname, and app_name.delete_modelname if you need to limit user's possibilities
If you need to change parts of templates, try {% include %} to load those parts from different files.