Template inheritance is the most powerful part of Jinja. It means one template can inherit from another template.

It allows us to build a base skeleton template that contains all common elements of our websites. We can also define blocks that the child templates can override to serve their needs. It saves us a lot of time and also reduces work to not repeat the code.

Today we will create a settings.html skeleton template which will be extended by settings.config.html and setting.webhook.html.

setting.html

{% extends "layouts/base.html" %}

{% set active_page = "settings" %}

{% block title %}Settings{% endblock title %}

{% block main_content %}

{% set navbar_menus = [
    ('/settings/config', 'config', 'Config'),
    ('/settings/webhook', 'webhook', 'Webhook')
] -%}

{% set active_setting = active_setting|default('config') -%}

<div class="main-center">
  <div class="row">
    <div class="col-3">
      <ul class="nav flex-column nav-pills">
        {% for href, id, caption in navbar_menus %}
          <li class="nav-item">
            <a class="nav-link {% if id == active_setting %} active {% endif%}" href="{{ href }}">
              {{ caption }}
            </a>
          </li>
        {% endfor %}
      </ul>
    </div>
    <div class="col-8">
      <h2>{{ page_name }}</h2><hr/>
      {% block settings_content %}
      {% endblock settings_content %}
    </div>
  </div>
</div>
{% endblock main_content %}

settings.config.html

{% extends "layouts/settings.html" %}

{% set active_setting = "config" %}

{% block settings_content %}
<form action="" method="post">
  {% if message %}
  <div class="alert alert-{{ message["category"] }} alert-dismissible" role="alert">
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
    <strong>{{ message["title"] }}</strong> {{ message["text"] }}
  </div>
  {% endif %}
  <div class="form-group">
    <label for="telegram_bot_token">Telegram Bot Token:</label>
      <input type="text" class="form-control" name="telegram_bot_token" id="telegram_bot_token" value="{{ telegram_bot_token_value }}">
    </div>
  <button type="submit" class="btn btn-primary">Update</button>
</form>
{% endblock settings_content %}

settings.webhook.html

{% extends "layouts/settings.html" %}

{% set active_setting = "webhook" %}

{% block settings_content %}
<form action="" method="post">
  {% if message %}
  <div class="alert alert-{{ message["category"] }} alert-dismissible" role="alert">
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
    <strong>{{ message["title"] }}</strong> {{ message["text"] }}
  </div>
  {% endif %}
  <div class="form-group">
  <label for="webhook_url">Webhook Url:</label>
    <input type="url" class="form-control" name="webhook_url" id="webhook_url" value="{{ webhook_url_value }}">
  </div>
  <button type="submit" class="btn btn-primary">Update</button>
</form>
{% endblock settings_content %}

settings.py

import webapp2
import json

from google.appengine.api import urlfetch

from app.models.config import TelegramConfig
import app.handlers.base as base


class SettingsHandler(base.Handler):
    @base.admin_required
    def get(self):
        return webapp2.redirect_to("settings_config")


class ConfigHandler(base.Handler):
    @base.admin_required
    def get(self):
        kwargs = {
            'page_name': 'Config'
        }
        kwargs['telegram_bot_token_value'] = TelegramConfig.get_telgram_token()
        self.render_template('settings.config.html', **kwargs)

    @base.admin_required
    def post(self):
        kwargs = {
            'page_name': 'Config'
        }
        new_botToken = self.request.get('telegram_bot_token')
        TelegramConfig.set_telegram_token(new_botToken)
        kwargs['message'] = {
            'category': 'success',
            'title': 'Success!',
            'text': 'Update Successfully'
        }
        kwargs['telegram_bot_token_value'] = new_botToken
        self.render_template('settings.config.html', **kwargs)


class WebhookHandler(base.Handler):
    def __init__(self, *args, **kwargs):
        super(WebhookHandler, self).__init__(*args, **kwargs)
        self.telegramBaseUrl = TelegramConfig.get_base_url()

    def __get_webhook_url(self):
        url = '{0}/getWebhookInfo'.format(self.telegramBaseUrl)
        try:
            result = urlfetch.fetch(url)
            if result.status_code == 200:
                jsonstr = result.content
                jsonobj = json.loads(jsonstr)
                return jsonobj['result']['url']
            else:
                return None
        except:
            return None

    def __set_webhook_url(self, new_url):
        url = '{0}/setWebhook?url={1}'.format(self.telegramBaseUrl, new_url)
        try:
            result = urlfetch.fetch(url)
            if result.status_code == 200:
                jsonstr = result.content
                jsonobj = json.loads(jsonstr)
                message = jsonobj['description']
                if 'error_code' in jsonobj:
                    success = False
                    error_code = jsonobj['error_code']
                    message = 'Code({0}) {1}'.format(error_code, message)
                else:
                    success = True
                return success, message
            else:
                return False, None
        except:
            return False, None

    @base.admin_required
    def get(self):
        kwargs = {
            'page_name': 'Webhook'
        }
        kwargs['webhook_url_value'] = self.__get_webhook_url()
        self.render_template('settings.webhook.html', **kwargs)

    @base.admin_required
    def post(self):
        kwargs = {
            'page_name': 'Webhook'
        }
        new_url = self.request.get('webhook_url')
        success, message = self.__set_webhook_url(new_url)
        if success:
            kwargs['message'] = {
                'category': 'success',
                'title': 'Success!',
                'text': message
            }
            kwargs['webhook_url_value'] = new_url
        else:
            kwargs['message'] = {
                'category': 'danger',
                'title': 'Error!',
                'text': message
            }
        self.render_template('settings.webhook.html', **kwargs)

config.py

import logging

from google.appengine.ext import ndb
from google.appengine.ext.ndb import model


class Config(ndb.Model):
    value = ndb.StringProperty()

    @classmethod
    def set(self, name, inputValue):
        entity = self(key=model.Key(self, name))
        entity.populate(value=inputValue)
        entity.put()

    @classmethod
    def get(self, name):
        NOT_SET_VALUE = u'!!!__ NOT SET __!!!'

        entity = self(key=model.Key(self, name))
        entity.populate(value=NOT_SET_VALUE)
        txn = lambda: entity.put() if not entity.key.get() else entity.key
        retval = model.transaction(txn).get()

        if retval.value == NOT_SET_VALUE:
            logging.error((
                '%s %s not found in the database. A placeholder ' +
                'record has been created. Go to the Developers Console '
                'for your app in App Engine, look up the Settings record '
                'with name=%s and enter its value in that record\'s value '
                'field.'
                ) % (
                self.__name__,
                name,
                name)
                )
        return retval.value


TELEGRAM_BOT_TOKEN_KEY = "TELEGRAM_BOT_TOKEN_KEY"


class TelegramConfig(Config):
    @classmethod
    def get_telgram_token(self):
        return self.get(TELEGRAM_BOT_TOKEN_KEY)

    @classmethod
    def set_telegram_token(self, inputValue):
        self.set(TELEGRAM_BOT_TOKEN_KEY, inputValue)

    @classmethod
    def get_base_url(self):
        token = self.get(TELEGRAM_BOT_TOKEN_KEY)
        return 'https://api.telegram.org/bot{}'.format(token)

main.py

import webapp2

from app.handlers import base, bot, settings

app = webapp2.WSGIApplication([
    webapp2.Route('/login', base.LoginHandler, name='login'),
    webapp2.Route('/logout', base.LogoutHandler, name='logout'),
    webapp2.Route('/403', base.Page403, name='403'),
    ('/webhook', bot.WebhookHandler),
    ('/', base.MainPage),
    ('/about', base.AboutPage),
    webapp2.Route
    (
        '/settings',
        settings.SettingsHandler,
        name='settings'
    ),
    webapp2.Route
    (
        '/settings/config',
        settings.ConfigHandler,
        name='settings_config'
    ),
    webapp2.Route
    (
        '/settings/webhook',
        settings.WebhookHandler,
        name='settings_webhook'
    ),
], debug=True)

Result

Settings Page