Render template based on the name of the endpoint in Flask? - python

I'm looking to make the code more maintainable. Right now "thing1" is repeated three times. Is there a way to make this
#app.route("/thing1")
def thing1():
return render_template("thing1.ejs")
#app.route("/thing2")
def thing2():
return render_template("thing2.ejs")
#app.route("/thing3")
def thing3():
return render_template("thing3.ejs")
More like...
#app.route("/thing1")
def thing1():
return render_template_name_of_function() # should render thing1
#app.route("/thing2")
def thing2():
return render_template_name_of_function() # should render thing2
#app.route("/thing3")
def thing3():
return render_template_name_of_function() # should render thing3

This is how it would be done.
#app.route("/thing1")
def thing1():
return render()
#app.route("/another_thing1")
def another_thing1():
return render()
#app.route("/yet_anther_thing1")
def yet_antoher_thing1():
return render()
def render():
return render_template("thing1.ejs")
Although, unless you think it is absolutely necessary, I would argue it should be done by using redirect("thing1").

You can try to read function infos by using inspect module, to get the currently function name:
import inspect
#app.route("/thing1")
def thing1():
return render_template(inspect.stack()[0][3])
#app.route("/thing2")
def thing2():
return render_template(inspect.stack()[0][3])
#app.route("/thing3")
def thing3():
return render_template(inspect.stack()[0][3])
Then, you can specify a extension to template file, after the inspect.stack() call, such as inspect.stack()[0][3] + '.html'

Related

Can python decorators be used to change behavior of a function returned by a function?

I have functions, that return validator functions, simple example:
def check_len(n):
return lambda s: len(s) == n
Is it possible to add a decorator, that prints out a message, in case the check evaluates to false?
Something like this:
#log_false_but_how
def check_len(n):
return lambda s: len(s) == n
check_one = check_len(1)
print(check_one('a')) # returns True
print(check_one('abc')) # return False
Expected output:
True
validator evaluated to False
False
I've tried creating an annotation, but can only access the function creation with it.
One way would be to define the functions like this:
def log_false(fn):
def inner(*args):
res = fn(*args)
if not res:
print("validation failed for {}".format(fn.__name__))
return res
return inner
#log_false
def check_one(s):
return check_len(1)(s)
But this way we lose the dynamic creation of validation functions.
You're doing the validation in the wrong place. check_len is a function factory, so res is not a boolean - it's a function. Your #log_false decorator has to wrap a validator function around each lambda returned by check_len. Basically you need to write a decorator that decorates the returned functions.
def log_false(validator_factory):
# We'll create a wrapper for the validator_factory
# that applies a decorator to each function returned
# by the factory
def check_result(validator):
#functools.wraps(validator)
def wrapper(*args, **kwargs):
result = validator(*args, **kwargs)
if not result:
name = validator_factory.__name__
print('validation failed for {}'.format(name))
return result
return wrapper
#functools.wraps(validator_factory)
def wrapper(*args, **kwargs):
validator = validator_factory(*args, **kwargs)
return check_result(validator)
return wrapper
Result:
#log_false
def check_len(n):
return lambda s: len(s) == n
check_one = check_len(1)
print(check_one('a')) # prints nothing
print(check_one('abc')) # prints "validation failed for check_len"

How Can I Run a Function Inside of Views.py and Print Result? -- Django

The problem is that I am trying to call a function from my views.py and print the result in my template through a context variable. The function prints in the terminal and does not work in the template. I posted a question a few days ago regarding doing this with subprocess, but I still could not figure it out.
How can I print "This is a test function for AAPL" in my template instead of in the terminal?
Views.py
from django.shortcuts import render
from backtests.scripts import Backtests
def index(request):
if 'symbol' in request.GET:
symbol = request.GET.get('symbol','Invalid Symbol')
request.session['symbol'] = symbol
else:
symbol = request.session['symbol']
earnings_object = Backtests("AAPL")
test = earnings_object.print_discrep()
return render(request, 'backtests/earnings.html', {'symbol':symbol, 'test':test})
scripts.py
class Backtests:
def __init__(self, symbol):
self.symbol = symbol
def print_discrep(self):
print('This is a test function for the graph ' + self.symbol)
It is returning None in my template.
You're calling the function in the view. To call it in the template, assign the function object, not the result of the function call:
test = earnings_object.print_discrep # no ()
Update:
print prints to the command line, not into your template.
The template displays the return value of functions you call. Your print_discrep doesn't have a return value, defaulting to None.
If you want to display data in the template, return that data in your function:
def get_discrep(self):
return 'This is a test function for the graph ' + self.symbol
Then you can call the function either in the view or in the template.

Is it possible to check if a function is decorated inside another function?

Is there any way to check inside function f1 in my example if calling a function (here decorated or not_decorated) has a specific decorator (in code #out)? Is such information passed to a function?
def out(fun):
def inner(*args, **kwargs):
fun(*args, **kwargs)
return inner
#out
def decorated():
f1()
def not_decorated():
f1()
def f1():
if is_decorated_by_out: # here I want to check it
print('I am')
else:
print('I am not')
decorated()
not_decorated()
Expected output:
I am
I am not
To be clear, this is egregious hackery, so I don't recommend it, but since you've ruled out additional parameters, and f1 will be the same whether wrapped or not, you've left hacks as your only option. The solution is to add a local variable to the wrapper function for the sole purpose of being found by means of stack inspection:
import inspect
def out(fun):
def inner(*args, **kwargs):
__wrapped_by__ = out
fun(*args, **kwargs)
return inner
def is_wrapped_by(func):
try:
return inspect.currentframe().f_back.f_back.f_back.f_locals.get('__wrapped_by__') is func
except AttributeError:
return False
#out
def decorated():
f1()
def not_decorated():
f1()
def f1():
if is_wrapped_by(out):
print('I am')
else:
print('I am not')
decorated()
not_decorated()
Try it online!
This assumes a specific degree of nesting (the manual back-tracking via f_back to account for is_wrapped_by itself, f1, decorated and finally to inner (from out). If you want to determine if out was involved anywhere in the call stack, make is_wrapped_by loop until the stack is exhausted:
def is_wrapped_by(func):
frame = None
try:
# Skip is_wrapped_by and caller
frame = inspect.currentframe().f_back.f_back
while True:
if frame.f_locals.get('__wrapped_by__') is func:
return True
frame = frame.f_back
except AttributeError:
pass
finally:
# Leaving frame on the call stack can cause cycle involving locals
# which delays cleanup until cycle collector runs;
# explicitly break cycle to save yourself the headache
del frame
return False
If you are open to creating an additional parameter in f1 (you could also use a default parameter), you can use functools.wraps and check for the existence of the __wrapped__ attribute. To do so, pass the wrapper function to f:
import functools
def out(fun):
#functools.wraps(fun)
def inner(*args, **kwargs):
fun(*args, **kwargs)
return inner
#out
def decorated():
f1(decorated)
def not_decorated():
f1(not_decorated)
def f1(_func):
if getattr(_func, '__wrapped__', False):
print('I am')
else:
print('I am not')
decorated()
not_decorated()
Output:
I am
I am not
Suppose you have a function decoration like this one
def double_arg(fun):
def inner(x):
return fun(x*2)
return inner
however you can't access it (it's inside a 3rd party lib or something). In this case you can wrap it into another function that adds the name of the decoration to the resulting function
def keep_decoration(decoration):
def f(g):
h = decoration(g)
h.decorated_by = decoration.__name__
return h
return f
and replace the old decoration by the wrapper.
double_arg = keep_decoration(double_arg)
You can even write a helper function that checks whether a function is decorated or not.
def is_decorated_by(f, decoration_name):
try:
return f.decorated_by == decoration_name
except AttributeError:
return False
Example of use...
#double_arg
def inc_v1(x):
return x + 1
def inc_v2(x):
return x + 1
print(inc_v1(5))
print(inc_v2(5))
print(is_decorated_by(inc_v1, 'double_arg'))
print(is_decorated_by(inc_v2, 'double_arg'))
Output
11
6
True
False

Can I return a response in a function within a view function?

Here's the view function:
def bar(request):
...
record = get_record_from_model(model, **kwargs)
...
return JsonResponse(data_to_response)
and below there is the function used in view function:
def get_record_from_model(model, **kwargs):
try:
return model.objects.get(**kwargs)
except model.DoesNotExist:
error_data = copy.copy(settings.ERROR["NOT_EXIST_ERR"])
return JsonResponse(error_data)
Can I return JsonResponse(error_data) to the client in get_record_from_model function when exception occur?
Something like raise Http404
The short answer is that you can't do it there directly because the calling function still has to do something with the return value from get_record_for_model. That said, I would recommend that you do something like the below, which sends data as well as a found/not found boolean back to the calling function:
def get_record_from_model(model, **kwargs):
try:
return model.objects.get(**kwargs), True
except model.DoesNotExist:
error_data = copy.copy(settings.ERROR["NOT_EXIST_ERR"])
return error_data, False
...
def bar(request):
...
data, found = get_record_from_model(model, **kwargs)
if not found:
return JsonResponse(data, status=404)
...
return JsonResponse(response_data)
Use django's built-in shortcut get_object_or_404
from django.shortcuts import get_object_or_404
def bar(request):
...
record = get_object_or_404(model, **kwargs)
...
return JsonResponse(data_to_response)

How to define instance methods from wrap

I am working on a django site and want to define some instance methods like below.
Class Auth(models.Model):
def wrap_has_perm(self, perm):
def wrap():
if self.is_staff and self.has_perm(perm):
return True
else:
return False
can_add_order = wrap_has_perm('finance.normal')
can_review_order = wrap_has_perm('finance.review')
is_leader = wrap_has_perm('finance.leader')
is_finance = wrap_has_perm('finance.finance')
I want to use can_add_order, can_review_order, is_leader, is_finance as django admin site's list_display element. But now these instance methods is illegal.(TypeError: wrap_has_perm() takes exactly 2 arguments (1 given))
How can I achieve these methods?
If I use partial:
def staff_has_perm(self, perm):
return self.is_staff and self.has_perm(perm)
can_add_order = partial(staff_has_perm, perm='finance.normal')
can_review_order = partial(staff_has_perm, perm='finance.review')
is_leader = partial(staff_has_perm, perm='finance.leader')
is_finance = partial(staff_has_perm, perm='finance.finance')
It raises (* TypeError: staff_has_perm() takes exactly 2 arguments (1 given));
Should I pass self to and how to ?
Move the self to wrap()'s definition:
def wrap_has_perm(perm):
def wrap(self):
However, a more Pythonic way to do this might be to use functools.partial:
from functools import partial
class Auth(models.Model):
def has_perm(self, perm):
# ...
can_add_order = partial(has_perm, perm='finance.normal')
can_review_order = partial(has_perm, perm='finance.review')
is_leader = partial(has_perm, perm='finance.leader')
is_finance = partial(has_perm, perm='finance.finance')

Categories