Max
Max

Reputation: 2505

dynamic render_template() in flask

I have several views in my flask app. and each view has its own sub view, constantly each sub view has their small templates. the visual demonstration and url structure is like:

|-settings ___________ [@app.route(/manage)]
|--menu_settings _____ [@app.route(/manage?current=menu_settings)]
|--main_settings _____ [@app.route(/manage?current=main_settings)]
|--additional_settings [@app.route(/manage?current=additional_settings)]

|-online_store [@app.route(/online_store)]
|--delivery __ [@app.route(/online_store?current=delivery)]
|--payment ___ [@app.route(/online_store?current=payment)]
|--store _____ [@app.route(/online_store?current=store)]

what I want to do is to use if or switch conditions. for example:

@admin.route('/manage', methods = ['GET', 'POST'])  
@login_required
def manage(current = ''):

    current = request.args.get('current') 

    if current == 'menu_settings':
        return render_template('admin/manage/site_figuration.html',
            title = 'Site figuration',
            current = current)
    elif current == 'internet_market':
        return render_template('admin/manage/internet_market.html',
        title = 'Internet market',
        current = current)
    else:
        return '404 - Template not found'

My question is that am I doing it right? or is there any another easier way to overcome this issue? is it reasonable to use this method or I must use JQuery load() method?

Upvotes: 0

Views: 6871

Answers (2)

Andrejs Cainikovs
Andrejs Cainikovs

Reputation: 28474

It looks like you are making things too complicated. How about:

@admin.route('/manage')
@admin.route('/manage/<current>')
@login_required
def manage(current = None):
    if current == 'menu_settings':
        template = 'admin/manage/site_configuration.html'
        title = 'Site configuration'
    elif current == 'internet_market':
        template = 'admin/manage/internet_market.html'
        title = 'Internet market'
    elif current is None:
        template = 'admin/manage/default.html'
        title = 'Default page for manage'
    else:
        flask.abort(404)

    return render_template(template, title=title, current=current)

But I also find having multiple routes as a more correct approach. Why reinvent the wheel?

@admin.route('/manage')
@login_required
def manage():
    return render_template('admin/manage/default.html',
                           title='Default page for manage',
                           current=None)

@admin.route('/manage/menu_settings')
@login_required
def manage_menu_settings():
    return render_template('admin/manage/site_configuration.html',
                           title='Site configuration',
                           current='menu_settings')

@admin.route('/manage/internet_market')
@login_required
def manage_internet_market():
    return render_template('admin/manage/internet_market.html',
                           title='Internet market',
                           current='internet_market')

NB: Please note, this code is untested, be aware of bugs and/or typos.

Upvotes: 3

Tyler Eaves
Tyler Eaves

Reputation: 13133

No, I don't think you're doming it right.

Why not just create multiple views?

@admin.route('/manage/menu', methods = ['GET', 'POST'])  
@login_required
def manage_menu():
  return render_template('admin/manage/site_figuration.html',
            title = 'Site figuration',
            current = request.args.get('current') )

@admin.route('/manage/internet_market', methods = ['GET', 'POST'])  
@login_required
def manage_market():
  return render_template('admin/manage/internet_market.html',
        title = 'Internet market',
        current = request.args.get('current') )

This approach is much more scalable/maintainable.

If you really wanted to your could probably wrap all this up into a decorator or class-based view (If flask supports those).

Edit

I primarily work in Pyramid rather than Flask (and honestly you might want to consider switching as it is similar but overall more powerful).

In Pyramid I would use a class-based view that would look something like this.

@view_defaults(permission='admin')
class admin_views:
  def __init__(self, request):
    self.request = request
    # Assign anything else you want to precalculate
    # for all views to a self variable

  @view("manage_menu", renderer='manage_menu.jinja2')
  def menu(self):
    return {'title':'Manage Menu'} #These are your template variables

  @view("internet_market", renderer='internet_market.jinja2')
  def market(self):
    return {'title': 'Internet Market"}

Then your route setup will be something like:

config.add_route('manage_menu', '/manage/menu')
config.add_route('internet_market', '/manage/market')

Where it gets really powerful is when you start using parameters

# Now the request.params dict will have a key of 'product'
# with whatever that path segment is.
config.add_route('product_view','/products/{product}')

You can also easily generate links with

route_path('view_name', request_obj, #any variables here)

e.g.

# That'll generate a proper link.
# If the client is using SSL it'll be an https link, etc.
route_path('product_view', self.request, product=5).

Upvotes: 2

Related Questions