<style> html, body, .ui-content { background-color: #333; color: #ddd; } body > .ui-infobar { display: none; } .ui-view-area > .ui-infobar { display: block; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #ddd; } .markdown-body h1, .markdown-body h2 { border-bottom-color: #ffffff69; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: #fff; } .markdown-body img { background-color: transparent; } .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: white; } .ui-toc-dropdown { background-color: #333; } .ui-toc-label.btn { background-color: #191919; color: white; } .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: white; border-left: 1px solid white; } .markdown-body blockquote { color: #bcbcbc; } .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #4f4f4f; } .markdown-body code, .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } a, .open-files-container li.selected a { color: #5EB7E0; } </style>} # Python + Flask 虛擬美國股票交易網站 Part3 (模板/ 模板上下文) ###### tags: `CS50` `Python` `Flask` ## 前言 網站首頁左側2/3為主體,顯示用戶個資,持股清單。右側1/3為邊欄,顯示用戶的觀察清單。因為不是每個頁面都會顯示觀察清單,因此將觀察清單放在局部模板__sidebar.html中。其餘的寫在基模板base.html中 ### vfinance/templates/base.html 程式碼 ``` {% from 'bootstrap/nav.html' import render_nav_item %} <!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- documentation at http://getbootstrap.com/docs/4.1/, alternative themes at https://bootswatch.com/ --> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"> <!-- https://favicon.io/emoji-favicons/money-mouth-face/ --> <link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}"> <!-- css sheet --> <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}" type ='text/css'> <title>C$50 Finance: {% block title %}{% endblock %}</title> </head> <body> {% block nav %} <nav class="navbar navbar-expand-md navbar-light bg-light border"> <div class="container"> <!-- navbar brand --> <a class="navbar-brand" href="/"><span class="blue">C</span><span class="red">$</span><span class="yellow">5</span><span class="green">0</span> <span class="red">Finance</span></a> <!-- Hambuger Button --> <button aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler" data-target="#navbar" data-toggle="collapse" type="button"> <span class="navbar-toggler-icon"></span> </button> {% if current_user.is_authenticated %} <div class="collapse navbar-collapse"> <ul class= 'nav navbar-nav mr-auto'> <li class = 'nav-item dropdown'> <a href="#" class='nav-link dropdown-toggle' data-toggle='dropdown' role = 'button' aria-haspopup='true' area-expanded='false'> My Account <span class='caret'></span> </a> <div class='dropdown-menu' aria-labelledby='navbarDropdown'> <a class='dropdown-item' href = "/">Portifolio</a> <a class='dropdown-item' href = "{{ url_for('admin.trade_history')}}">History</a> <a class='dropdown-item' href = "{{ url_for('admin.show_watchlist')}}">Watchlist</a> </div> </li> <li class = 'nav-item dropdown'> <a href="#" class='nav-link dropdown-toggle' data-toggle='dropdown' role = 'button' aria-haspopup='true' area-expanded='false'> Trade <span class='caret'></span> </a> <div class='dropdown-menu' aria-labelledby='navbarDropdown'> <a class='dropdown-item' href = "{{ url_for('admin.get_quote')}}">Buy</a> <a class='dropdown-item' href = "#">Sell</a> </div> </li> <li class = 'nav-item dropdown'> <a href="#" class='nav-link dropdown-toggle' data-toggle='dropdown' role = 'button' aria-haspopup='true' area-expanded='false'> BackTest <span class='caret'></span> </a> </li> </ul> </div> <div class="navbar-collapse collapse "> <ul class="navbar-nav ml-auto"> <form class="form-inline my-2 my-lg-0" method = 'post' action="{{ url_for('admin.get_quote') }}"> {% if form %} {{form.csrf_token}} {% endif %} <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" id ='symbol' name = 'symbol'> <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Quote</button> </form> <li class="nav-item"> <a class="nav-link" href="#">{{ current_user.username }}</a> </li> <li class="nav-item"> <a class="nav-link" href="{{ url_for('auth.logout', next = request.full_path) }}">logout</a> </li> </ul> </div> {% else %} <div class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> {{ render_nav_item('auth.register', "Register") }} {{ render_nav_item('auth.login', "Login") }} </ul> </div> {% endif %} </div> </nav> {% endblock nav %} <main class="container p-5"> {% for message in get_flashed_messages(with_categories=True) %} <div class="alert alert-{{ message[0]}}" role='alert'> <button type="button" class="close" data-dismiss="alert">&times;</button> {{ message[1]}} </div> {% endfor %} {% block content %}{% endblock %} <footer class="small text-center text-muted"> Data provided for free by <a href="https://iextrading.com/developer">IEX</a>. View <a href="https://iextrading.com/api-exhibit-a/">IEX’s Terms of Use</a>. </footer> </main> {% block scripts %} <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.2.1.slim.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/popper.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/script.js') }}"></script> {{ moment.include_moment(local_js=url_for('static', filename='js/moment-with-locales.min.js')) }} {% endblock scripts %} </body> </html> ``` ## 註冊模板上下文 __init __.py 將template中會需要用到的模板註冊到工廠函數中 ``` def register_template_context(app): @app.context_processor def make_template_context(): user = User.query.first() if current_user.is_authenticated: watchlist = Watchlist.query.with_parent(current_user).order_by(Watchlist.symbol.asc()).all() priceInWatchlist = [] for stock in watchlist: quote = lookup(stock.symbol) price = quote['price'] priceInWatchlist.append(price) return dict( user = user, watchlist = watchlist, priceInWatchlist = priceInWatchlist ) else: return dict(user = user,) ``` ## 願望清單模板 vfinance/templates/home/__sidebar.html ``` {% if watchlist %} <div class='row'> <div class="card mb-6"> <div class="card-header">Watchlist</div> <ul class="list-group list-group-flush"> {% for n in range(watchlist|length) %} <li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"> <a href="{{ url_for('admin.show_quote', symbol = watchlist[n].symbol)}}"> {{ watchlist[n].symbol}} </a> &nbsp;&nbsp;&nbsp;&nbsp; <a> {{ priceInWatchlist[n] }} </a> </li> {% endfor %} </ul> </div> </div> {% endif %} ```