same function but 2 different route and render 2 different templates - flask

i want to know how use same function but 2 different route and render 2 different templates, the way i see is to do like this:
#app.route('/analysis_equipment_overview_approvalviewv2/<prelim_uid>', methods=['GET', 'POST'])
def analysis_equipment_overview_approvalviewv2(prelim_uid):
#same logic here
return render_template('template_1.html')
app.route('/analysis_equipment_overview_approvalview/<prelim_uid>', methods=['GET', 'POST'])
def analysis_equipment_overview_approvalview(prelim_uid):
#same logic here
return render_template('template_2.html')
is there any way to reduce the lines, especially when the logic is too long and avoid to write it twice?

I find this way, but need clean code at pattern regex with constant.
#app.route('/analysis_equipment_overview_approvalviewv2/<prelim_uid>', methods=['GET', 'POST'])
#app.route('/analysis_equipment_overview_approvalview/<prelim_uid>', methods=['GET', 'POST'])
def analysis_equipment_overview_approvalview(prelim_uid):
#same logic here
if re.search(r'/analysis_equipment_overview_approvalviewv2/', request.full_path):
return render_template('template_2.html')
return render_template('template_1.html')

Related

Flask reroute always using default function argument

Given a route such as the one below, I have found that if I have a default value for the argument test it won't change when passing a value via url_for with a redirect. What I'd like is to change the parameter test when rendering the template, depending on whether a form was just submitted or not.
#app.route('/view_story/<story_id>', methods=['GET', 'POST'])
def view_story(story_id, test='no'):
...
if current_user.is_authenticated:
if request.method == 'POST':
#Add stuff to database from the form.
return redirect(url_for('view_story', story_id=story_id, test='yes'))
return render_template('story.html', test=test)
Try the below code:
#app.route('/view_story/<story_id>', defaults={"test": "no"})
#app.route('/view_story/<story_id>/<test>', methods=['GET', 'POST'])
def view_story(story_id, test):
...
if current_user.is_authenticated:
if request.method == 'POST':
#Add stuff to database from the form.
return redirect(url_for('view_story', story_id=story_id, test='yes'))
return render_template('story.html', test=test)

How to hide variables from flask url routing?

I'm making API and looking for a way to hide the extra information from the url. I have a function index:
#app.route('/', methods=['GET', 'POST'])
def index():
count = foo()
return redirect(url_for("result", count=count))
and a function result
#app.route("/done/<count>")
def result(count):
count = count
return jsonify(count=count)
Inner function count allwase return different values. At the end I get a result like
http://127.0.0.1:5000/done/43
But I need more common url view for universal API like
http://127.0.0.1:5000/done
The problem is that if I remove <count> from endpoint, i get error
TypeError: result() missing 1 required positional argument: 'count'
Is there a way to override this?
This task solving by session variable
from flask import session
#app.route('/', methods=['GET', 'POST'])
def index():
count = foo()
session['count'] = count
return redirect(url_for("result"))
#app.route("/done/")
def result(count):
count = session['count']
return jsonify(count=count)

Can Function based views be reused in several apps without duplicating the code?

I have bunch of function based views that have similar functionality
For example create, list, edit view and search companies and contacts of: Customers , Vendors and Manufactures.
I can reuse the same model with few boolean flags for all 3 of them.
But I was wondering how to deal with view and templates in a best way to avoid code duplications. (currently I am thinking but massive usage of control-H but it doesn't feel right)
(I regret now not using the Class based views but it is too late)
There is a lot you can do by passing in url parameters to achieve reuse. Your imagination and consistency will help a lot here:
For a simple example:
urlpatterns = [
url(r'^add-customer/$', create, {'template':'customer.html'}, name='create_customer'),
url(r'^add-vendor/$', create, {'template':'vendor.html'}, name='create_vendor'),
url(r'^add-manufacturer/$', create, {'template':'manufacturer.html'}, name='create_manufacturer'),
...
and the view:
def create(request, template):
#use template as you like, coming from url conf.
return render(request, template)
...
That's probably normal code when you think about it. A little more interesting example:
from someapp.forms import *
from someapp.models import *
urlpatterns = [
url(r'^customer/$', create, {'template':'customer.html', 'form_class':customer_form}, name='create_customer'),
url(r'^vendor/$', create, {'template':'vendor.html', 'form_class':vendor_form}, name='create_vendor'),
url(r'^manufacturer/$', create, {'template':'manufacturer.html', 'form_class':manufacturer_form}, name='create_manufacturer'),
...
and the view:
def create(request, template, form_class):
#use template as you like, coming from url conf.
form = form_class()#
return render(request, template)
...
Still pretty much normal code. You can get funky and generate the urls dynamically:
In your urls.py:
for form_class in (CustomerForm, VendorForm, ManufacturerForm): #model forms
model = form_class._meta.model
model_name = model._meta.module_name
urlpatterns += url(r'^create/$', create, {'template':'%s.html' % model_name, 'form_class':form_class, 'model':model}, name='create_%s' % model_name),
and in the view:
def create(request, template, form_class, model):
#use template as you like, coming from url conf.
form = form_class()#
return render(request, template)
You can pass in pks to models and write codes like:
def create(request, form_class, model, id=None):
instance = get_object_or_404(model, pk=id) if id else None
edited = True if instance else False
if request.method == 'POST':
form = form_class(data=request.POST, files=request.FILES, instance=instance)
if form.is_valid():
instance = form.save()
...
else:
form = form_class(instance=instance)
return render(request, template_name, {'form': form, 'instance': instance})
Hopefully you have the stomach for it.
The answer, of course, depends on what actually is different between the models. But if you can abstract your view functions "with a few boolean flags" I would suggest to put them in a central place, probably a common file in your app or project, e.g. abstract_views.py.
Then you will only need to dispatch the view functions in a meaningful way:
import my_app.abstract_views
from .models import Model1, Model2
def model1_create_view(request):
abstract_views.create(request, model=Model1)
def model2_create_view(request):
abstract_views.create(request, model=Model2)
In this hypthetical example create would be the abstract view function for creating an instance and it would require a parameter model with the actual model to operate on.
ADDED:
Alternatively, use boolean flags, as requested:
import my_app.abstract_views
def model1_create_view(request):
abstract_views.create(request, is_customer=True)
def model2_create_view(request):
abstract_views.create(request, is_vendor=True)
When using boolean flags, remember to define what happens if someone is both, a customer and a vendor...
The definition of the abstract view then reads something like:
def abstract_view(request, is_customer=False, is_vendor=False):
context = do_something (is_customer, is_vendor)
return(render(request, 'app/template.html', context))
Hope that helps.

Do I need to use token_loader to implmement the "remember_me" feautre with Flask-login?

My current code seems to be working for "remember_me" to be working.
In views.py I have
#app.route('/login', methods=['GET', 'POST'])
def login():
form = SigninForm()
if form.validate_on_submit():
user = User.query.filter_by(email = form.email.data.lower()).first()
remember_me = form.remember_me.data
if user and check_password_hash(user.pwdhash, form.password.data):
login_user(user, remember=remember_me)
return redirect(url_for('index'))
else:
return redirect(url_for('login'))
else:
return render_template('login.html', form=form)
This is similar to Step 10 here. But I also see tutorials such as this where a lot of work is done using get_auth_token and token_loader. Is one of these methods better? As I mentioned up top, my code seems to be working without these tokens, so what is going on?
Your code is good enough and you don't need to use token_loader if you just want "remember me"

How to write a basic try/except in a Django Generic Class View

I'd like to write an except clause that redirects the user if there isn't something in a queryset. Any suggestions welcome. I'm a Python noob, which I get is the issue here.
Here is my current code:
def get_queryset(self):
try:
var = Model.objects.filter(user=self.request.user, done=False)
except:
pass
return var
I want to do something like this:
def get_queryset(self):
try:
var = Model.objects.filter(user=self.request.user, done=False)
except:
redirect('add_view')
return var
A try except block in the get_queryset method isn't really appropriate. Firstly, Model.objects.filter() won't raise an exception if the queryset is empty - it just returns an empty queryset. Secondly, the get_queryset method is meant to return a queryset, not an HttpResponse, so if you try to redirect inside that method, you'll run into problems.
I think you might find it easier to write a function based view. A first attempt might look like this:
from django.shortcuts import render
def my_view(request):
"""
Display all the objects belonging to the user
that are not done, or redirect if there are not any,
"""
objects = Model.objects.filter(user=self.request.user, done=False)
if not objects:
return HttpResponseRedirect("/empty-queryset-url/")
return render(request, 'myapp/template.html', {"objects": objects})
The advantage is that the flow of your function is pretty straight forward. This doesn't have as many features as the ListView generic class based view (it's missing pagination for example), but it is pretty clear to anyone reading your code what the view is doing.
If you really want to use the class based view, you have to dig into the CBV documentation for multiple object mixins and the source code, and find a suitable method to override.
In this case, you'll find that the ListView behaviour is quite different to what you want, because it never redirects. It displays an empty page by default, or a 404 page if you set allow_empty = False. I think you would have to override the get method to look something like this (untested).
class MyView(ListView):
def get_queryset(self):
return Model.objects.filter(user=self.request.user, done=False)
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
if len(self.object_list == 0):
return HttpResponseRedirect("/empty-queryset-url/")
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
This is purely supplemental to #Alasdair's answer. It should really be a comment, but couldn't be formatted properly that way. Instead of actually redefining get on the ListView, you could override simply with:
class MyView(ListView):
allow_empty = False # Causes 404 to be raised if queryset is empty
def get(self, request, *args, **kwargs):
try:
return super(MyView, self).get(request, *args, **kwargs)
except Http404:
return HttpResponseRedirect("/empty-queryset-url/")
That way, you're not responsible for the entire implementation of get. If Django changes it in the future, you're still good to go.