Python حرفه‌ای: ساخت یک سرور با Flask

Professional Python: Build a Server with Flask

27 اسفند 1399
درسنامه درس 33 از سری پایتون حرفه‌ای
Python حرفه ای: ساخت یک سرور با Flask (قسمت 33)

ساخت سرور با Flask

در جلسه قبل یک محیط مجازی (virtual environment) را ایجاد کردیم و حالا باید کدنویسی و ساخت سرور را شروع کنیم. اگر یادتان باشد ما flask را برای این کار نصب کرده بودیم بنابراین ابتدا در پوشه پروژه خود (برای من پوشه portfolio) یک فایل به نام server.py را ایجاد کنید که محتویات زیر را داشته باشد:

from flask import Flask

app = Flask(__name__)







@app.route('/')

def hello_world():

    return 'Hello, World!'

ما ابتدا flask را وارد این اسکریپت کرده ایم و سپس تابع flask را صدا زده ایم. احتمالا می پرسید __name__ چیست؟ __name__ نام برنامه ما است. Flask از این نام برای پیدا کردن منابع مختلف مانند فایل های سیستمی و ارائه اطلاعات debug استفاده می کند. در زبان پایتون __name__ متغیری است که برابر با نام ماژول است بنابراین در هر فایلی، مقدار خاصی خواهد داشت و یکی نیست. به طور مثال اگر فایلی به نام test.py داشته باشیم، مقدار __name__ در آن برابر test خواهد بود. اگر این فایل test.py در پکیجی به نام my_package بود، مقدار __name__ برابر با my_package.test خواهد بود و الی آخر. البته اگر __name__ را در فایل خودمان (یعنی server.py در پروژه خودمان) print کنیم، مقدار __main__ را دریافت می کنید. چرا؟ به دلیل اینکه فایل اصلی پروژه یک استثناء است و همیشه مقدار __main__ را می گیرد. به زبان ساده تر __name__ به ما کمک می کند تا نام import را دریافت کنیم. در اکثر مواقع استفاده از __name__ کارتان را ساده تر می کند اما می توانید یک نام ساده را به شکل یک رشته ساده نیز پاس بدهید:

app = Flask('portfolio')

من برای راحت بودن کار از همان name استفاده می کنم. در خط بعدی یک decorator به نام app.route@ را داریم که مسیر خاصی از سرور را مشخص می کند. ما یک علامت اسلش (/) را در پرانتز های این decorator قرار داده ایم که یعنی آدرس اصلی سایت (مثلا google.com یا roxo.ir)، سپس گفته ایم در این مسیر تابعی به نام hello_world تعریف شود که رشته hello world را برگرداند. این یک سرور بسیار ساده است!

سوال بعدی اینجاست که چطور این سرور را اجرا کنیم؟ در مرحله اول بهتر است وارد virtual environment خود بشویم. ترمینال خود را باز کنید و بر اساس روشی که در جلسه قبل توضیح دادم وارد virtual environment خود بشوید. یادآوری برای کاربران لینوکس؛ ترمینال خود را در مسیر پروژه باز کرده و دستور زیر را در آن اجرا کنید:

. venv/bin/activate

در مرحله بعدی باید آدرس فایل سرور (server.py) را export کنیم بنابراین دستور زیر را در ترمینال اجرا می کنیم:

export FLASK_APP=server.py

اجرای این دستور هیچ نتیجه خاصی را نمایش نمی دهد. در صورتی که از کاربران ویندوز هستید به جای دستور بالا از دستور زیر در CMD استفاده کنید:

set FLASK_APP=server.py

در صورتی که از powershell استفاده می کنید نیز دستور زیر را اجرا کنید:

$env:FLASK_APP = "server.py"

تنها دستوری که باقی مانده است، دستور اجرای سرور است (در ویندوز و لینوکس یکی است):

flask run

با اجرای این دستور نتیجه زیر را می گیریم:

* Serving Flask app "server.py"

 * Environment: production

   WARNING: This is a development server. Do not use it in a production deployment.

   Use a production WSGI server instead.

 * Debug mode: off

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

کد بالا می گوید که سرور محلی ما در آدرس http://127.0.0.1:5000 در حال اجرا است و می توانیم با فشردن کلیدهای CTRL + C آن را متوقف کنیم. یادتان باشد که نباید این پنجره ترمینال را ببندید یا کلیدهای Ctrl + C را فشار دهید مگر آنکه کارتان تمام شده باشد و قصد متوقف کردن سرور را داشته باشید. در ضمن این نتیجه به ما هشدار می دهد که سرور موجود فقط برای اهداف تمرینی است و نباید از آن برای وب سایت های واقعی خود استفاده کنید، بلکه باید از سرورهای WSGI استفاده نمایید. ما مشکلی با این مسئله نداریم چرا که فعلا فقط در حال تمرین هستیم و وب سایتی واقعی نداریم.

برای مشاهده وب سایت محلی خودمان آدرس http://127.0.0.1:5000 را در نوار آدرس مرورگر تایپ کرده و اینتر بزنید (به این آدرس localhost می گوییم). با این کار باید رشته زیر را مشاهده کنید:

Hello, World!

اگر یادتان باشد یک اسلش (/) را به app.route@ داده بودیم و به شما گفتم که / به معنی آدرس اصلی یک سایت مانند google.com است. آدرس localhost یا سایت محلی ما چیست؟ بله  http://127.0.0.1:5000 آدرس ما اصلی ما یا همان / است. بیایید برای تست یک مسیر دیگر را نیز به فایل server.py اضافه کنیم:

from flask import Flask

app = Flask(__name__)







@app.route('/')

def hello_world():

    return 'Hello, World!'







@app.route('/test')

def test_function():

    return 'This is a test'

این بار آدرس test/ را به route داده ایم بنابراین باید به آدرس http://127.0.0.1:5000/test برویم. با رفتن به این آدرس در مرورگر، به خطای زیر برخورد می کنیم:

Not Found

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

این خطا به ما می گوید که چنین آدرسی وجود ندارد! چرا؟ دوباره به ترمینال خود نگاه کنید:

 * Serving Flask app "server.py"

 * Environment: production

   WARNING: This is a development server. Do not use it in a production deployment.

   Use a production WSGI server instead.

 * Debug mode: off

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

127.0.0.1 - - [02/Feb/2021 00:11:44] "GET / HTTP/1.1" 200 -

127.0.0.1 - - [02/Feb/2021 00:11:44] "GET /favicon.ico HTTP/1.1" 404 -

127.0.0.1 - - [02/Feb/2021 00:27:14] "GET /test HTTP/1.1" 404 -

همانطور که می بینید هر درخواست ما به سرور در ترمینال ثبت شده است و آخرین درخواست نیز خطای ۴۰۴ را داشته است که یعنی چنین آدرسی وجود ندارد. دلیل این مسئله این است که ما باید پس از انجام هر تغییر یک بار سرور را ریستارت کنیم بنابراین کلیدهای Ctrl + C را فشار دهید تا سرور متوقف شود و دوباره دستور flask run را اجرا نمایید. این بار اگر به آدرس http://127.0.0.1:5000/test در مرورگر خود برویم نتیجه زیر را مشاهده می کنیم:

This is a test

مسئله اینجاست که ریستارت کردن سرور به صورت دستی آزاردهنده است بنابراین باید سرور را در حالت debugger mode اجرا کنیم. برای این کار سرور را متوقف کنید (Ctrl + C) و سپس دستور زیر را در آن اجرا کنید:

export FLASK_ENV=development

در ضمن اگر از کاربران ویندوز هستید مثل همیشه به جای export در دستور بالا باید از SET یا env$ استفاده کنید (بالاتر برای FLASK_APP=server.py توضیح داده شد). حالا دوباره دستور flask run را اجرا کنید. این بار نتیجه زیر را می گیرید:

 * Serving Flask app "server.py" (lazy loading)

 * Environment: development

 * Debug mode: on

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

 * Restarting with stat

 * Debugger is active!

 * Debugger PIN: 132-389-626

یعنی در حالت debugger هستیم و اگر فایلی را تغییر بدهیم، سرور به صورت خودکار ریستارت می شود.

قالب های flask

زمانی که شما رشته ای را به سمت مرورگر ارسال می کنید (مثلا رشته 'This is a test') این رشته توسط flask به کد HTML تبدیل می شود چرا که مرورگرها به زبان HTML صحبت می کنند. مثلا اگر من به آدرس test/ بروم و کلید f12 را در مرورگر بزنم تا developer tools برایم باز شود، سورس کد صفحه را می بینم:

<html>

  <head></head>

  <body>

    This is a test

  </body>

</html>

همانطور که می بینید رشته ما درون تگ body قرار گرفته است. طبیعتا flask برای ما کدنویسی HTML را انجام نمی دهد بلکه تگ های اولیه و ساده را قرار می دهد بنابراین ارسال رشته های عادی اصلا کار جالبی نیست. وب سایت های واقعی از کدهای HTML و CSS و JavaScript واقعی استفاده می کنند نه اینکه تنها یک رشته را به سمت کاربر ارسال کنند. چطور می توانیم یک فایل HTML را ارسال کنیم؟

قالب ها یا template های flask در واقع فایل های HTML ساده ای هستند که به جای رشته عادی به مرورگر ارسال می شوند. من ابتدا در کنار فایل server.py خودم یک فایل به نام index.html ایجاد می کنم. از آنجایی که این دوره یک دوره HTML نیست من کدهای HTML را برایتان توضیح نمی دهم و اصلا کدنویسی نمی کنیم بلکه از قالب ساده و آماده W3Schools استفاده می کنیم بنابراین کدهای زیر را در فایل index.html خود کپی کنید:

<!DOCTYPE html>

<html>

<title>W3.CSS Template</title>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato">

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

<style>

body,h1,h2,h3,h4,h5,h6 {font-family: "Lato", sans-serif;}

body, html {

  height: 100%;

  color: #777;

  line-height: 1.8;

}




/* Create a Parallax Effect */

.bgimg-1, .bgimg-2, .bgimg-3 {

  background-attachment: fixed;

  background-position: center;

  background-repeat: no-repeat;

  background-size: cover;

}




/* First image (Logo. Full height) */

.bgimg-1 {

  background-image: url('/w3images/parallax1.jpg');

  min-height: 100%;

}




/* Second image (Portfolio) */

.bgimg-2 {

  background-image: url("/w3images/parallax2.jpg");

  min-height: 400px;

}




/* Third image (Contact) */

.bgimg-3 {

  background-image: url("/w3images/parallax3.jpg");

  min-height: 400px;

}




.w3-wide {letter-spacing: 10px;}

.w3-hover-opacity {cursor: pointer;}




/* Turn off parallax scrolling for tablets and phones */

@media only screen and (max-device-width: 1600px) {

  .bgimg-1, .bgimg-2, .bgimg-3 {

    background-attachment: scroll;

    min-height: 400px;

  }

}

</style>

<body>




<!-- Navbar (sit on top) -->

<div class="w3-top">

  <div class="w3-bar" id="myNavbar">

    <a class="w3-bar-item w3-button w3-hover-black w3-hide-medium w3-hide-large w3-right" href="javascript:void(0);" onclick="toggleFunction()" title="Toggle Navigation Menu">

      <i class="fa fa-bars"></i>

    </a>

    <a href="#home" class="w3-bar-item w3-button">HOME</a>

    <a href="#about" class="w3-bar-item w3-button w3-hide-small"><i class="fa fa-user"></i> ABOUT</a>

    <a href="#portfolio" class="w3-bar-item w3-button w3-hide-small"><i class="fa fa-th"></i> PORTFOLIO</a>

    <a href="#contact" class="w3-bar-item w3-button w3-hide-small"><i class="fa fa-envelope"></i> CONTACT</a>

    <a href="#" class="w3-bar-item w3-button w3-hide-small w3-right w3-hover-red">

      <i class="fa fa-search"></i>

    </a>

  </div>




  <!-- Navbar on small screens -->

  <div id="navDemo" class="w3-bar-block w3-white w3-hide w3-hide-large w3-hide-medium">

    <a href="#about" class="w3-bar-item w3-button" onclick="toggleFunction()">ABOUT</a>

    <a href="#portfolio" class="w3-bar-item w3-button" onclick="toggleFunction()">PORTFOLIO</a>

    <a href="#contact" class="w3-bar-item w3-button" onclick="toggleFunction()">CONTACT</a>

    <a href="#" class="w3-bar-item w3-button">SEARCH</a>

  </div>

</div>




<!-- First Parallax Image with Logo Text -->

<div class="bgimg-1 w3-display-container w3-opacity-min" id="home">

  <div class="w3-display-middle" style="white-space:nowrap;">

    <span class="w3-center w3-padding-large w3-black w3-xlarge w3-wide w3-animate-opacity">MY <span class="w3-hide-small">WEBSITE</span> LOGO</span>

  </div>

</div>




<!-- Container (About Section) -->

<div class="w3-content w3-container w3-padding-64" id="about">

  <h3 class="w3-center">ABOUT ME</h3>

  <p class="w3-center"><em>I love photography</em></p>

  <p>We have created a fictional "personal" website/blog, and our fictional character is a hobby photographer. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,

    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa

    qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

  <div class="w3-row">

    <div class="w3-col m6 w3-center w3-padding-large">

      <p><b><i class="fa fa-user w3-margin-right"></i>My Name</b></p><br>

      <img src="/w3images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

    </div>




    <!-- Hide this text on small devices -->

    <div class="w3-col m6 w3-hide-small w3-padding-large">

      <p>Welcome to my website. I am lorem ipsum consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure

        dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum consectetur adipiscing elit, sed do eiusmod tempor

        incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

    </div>

  </div>

  <p class="w3-large w3-center w3-padding-16">Im really good at:</p>

  <p class="w3-wide"><i class="fa fa-camera"></i>Photography</p>

  <div class="w3-light-grey">

    <div class="w3-container w3-padding-small w3-dark-grey w3-center" style="width:90%">90%</div>

  </div>

  <p class="w3-wide"><i class="fa fa-laptop"></i>Web Design</p>

  <div class="w3-light-grey">

    <div class="w3-container w3-padding-small w3-dark-grey w3-center" style="width:85%">85%</div>

  </div>

  <p class="w3-wide"><i class="fa fa-photo"></i>Photoshop</p>

  <div class="w3-light-grey">

    <div class="w3-container w3-padding-small w3-dark-grey w3-center" style="width:75%">75%</div>

  </div>

</div>




<div class="w3-row w3-center w3-dark-grey w3-padding-16">

  <div class="w3-quarter w3-section">

    <span class="w3-xlarge">14+</span><br>

    Partners

  </div>

  <div class="w3-quarter w3-section">

    <span class="w3-xlarge">55+</span><br>

    Projects Done

  </div>

  <div class="w3-quarter w3-section">

    <span class="w3-xlarge">89+</span><br>

    Happy Clients

  </div>

  <div class="w3-quarter w3-section">

    <span class="w3-xlarge">150+</span><br>

    Meetings

  </div>

</div>




<!-- Second Parallax Image with Portfolio Text -->

<div class="bgimg-2 w3-display-container w3-opacity-min">

  <div class="w3-display-middle">

    <span class="w3-xxlarge w3-text-white w3-wide">PORTFOLIO</span>

  </div>

</div>




<!-- Container (Portfolio Section) -->

<div class="w3-content w3-container w3-padding-64" id="portfolio">

  <h3 class="w3-center">MY WORK</h3>

  <p class="w3-center"><em>Here are some of my latest lorem work ipsum tipsum.<br> Click on the images to make them bigger</em></p><br>




  <!-- Responsive Grid. Four columns on tablets, laptops and desktops. Will stack on mobile devices/small screens (100% width) -->

  <div class="w3-row-padding w3-center">

    <div class="w3-col m3">

      <img src="/w3images/p1.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="The mist over the mountains">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p2.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="Coffee beans">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p3.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="Bear closeup">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p4.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="Quiet ocean">

    </div>

  </div>




  <div class="w3-row-padding w3-center w3-section">

    <div class="w3-col m3">

      <img src="/w3images/p5.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="The mist">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p6.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="My beloved typewriter">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p7.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="Empty ghost train">

    </div>




    <div class="w3-col m3">

      <img src="/w3images/p8.jpg" style="width:100%" onclick="onClick(this)" class="w3-hover-opacity" alt="Sailing">

    </div>

    <button class="w3-button w3-padding-large w3-light-grey" style="margin-top:64px">LOAD MORE</button>

  </div>

</div>




<!-- Modal for full size images on click-->

<div id="modal01" class="w3-modal w3-black" onclick="this.style.display='none'">

  <span class="w3-button w3-large w3-black w3-display-topright" title="Close Modal Image"><i class="fa fa-remove"></i></span>

  <div class="w3-modal-content w3-animate-zoom w3-center w3-transparent w3-padding-64">

    <img id="img01" class="w3-image">

    <p id="caption" class="w3-opacity w3-large"></p>

  </div>

</div>




<!-- Third Parallax Image with Portfolio Text -->

<div class="bgimg-3 w3-display-container w3-opacity-min">

  <div class="w3-display-middle">

     <span class="w3-xxlarge w3-text-white w3-wide">CONTACT</span>

  </div>

</div>




<!-- Container (Contact Section) -->

<div class="w3-content w3-container w3-padding-64" id="contact">

  <h3 class="w3-center">WHERE I WORK</h3>

  <p class="w3-center"><em>I'd love your feedback!</em></p>




  <div class="w3-row w3-padding-32 w3-section">

    <div class="w3-col m4 w3-container">

      <img src="/w3images/map.jpg" class="w3-image w3-round" style="width:100%">

    </div>

    <div class="w3-col m8 w3-panel">

      <div class="w3-large w3-margin-bottom">

        <i class="fa fa-map-marker fa-fw w3-hover-text-black w3-xlarge w3-margin-right"></i> Chicago, US<br>

        <i class="fa fa-phone fa-fw w3-hover-text-black w3-xlarge w3-margin-right"></i> Phone: +00 151515<br>

        <i class="fa fa-envelope fa-fw w3-hover-text-black w3-xlarge w3-margin-right"></i> Email: mail@mail.com<br>

      </div>

      <p>Swing by for a cup of <i class="fa fa-coffee"></i>, or leave me a note:</p>

      <form action="/action_page.php" target="_blank">

        <div class="w3-row-padding" style="margin:0 -16px 8px -16px">

          <div class="w3-half">

            <input class="w3-input w3-border" type="text" placeholder="Name" required name="Name">

          </div>

          <div class="w3-half">

            <input class="w3-input w3-border" type="text" placeholder="Email" required name="Email">

          </div>

        </div>

        <input class="w3-input w3-border" type="text" placeholder="Message" required name="Message">

        <button class="w3-button w3-black w3-right w3-section" type="submit">

          <i class="fa fa-paper-plane"></i> SEND MESSAGE

        </button>

      </form>

    </div>

  </div>

</div>




<!-- Footer -->

<footer class="w3-center w3-black w3-padding-64 w3-opacity w3-hover-opacity-off">

  <a href="#home" class="w3-button w3-light-grey"><i class="fa fa-arrow-up w3-margin-right"></i>To the top</a>

  <div class="w3-xlarge w3-section">

    <i class="fa fa-facebook-official w3-hover-opacity"></i>

    <i class="fa fa-instagram w3-hover-opacity"></i>

    <i class="fa fa-snapchat w3-hover-opacity"></i>

    <i class="fa fa-pinterest-p w3-hover-opacity"></i>

    <i class="fa fa-twitter w3-hover-opacity"></i>

    <i class="fa fa-linkedin w3-hover-opacity"></i>

  </div>

  <p>Powered by <a href="https://www.w3schools.com/w3css/default.asp" title="W3.CSS" target="_blank" class="w3-hover-text-green">w3.css</a></p>

</footer>




<script>

// Modal Image Gallery

function onClick(element) {

  document.getElementById("img01").src = element.src;

  document.getElementById("modal01").style.display = "block";

  var captionText = document.getElementById("caption");

  captionText.innerHTML = element.alt;

}




// Change style of navbar on scroll

window.onscroll = function() {myFunction()};

function myFunction() {

    var navbar = document.getElementById("myNavbar");

    if (document.body.scrollTop > 100 || document.documentElement.scrollTop > 100) {

        navbar.className = "w3-bar" + " w3-card" + " w3-animate-top" + " w3-white";

    } else {

        navbar.className = navbar.className.replace(" w3-card w3-animate-top w3-white", "");

    }

}




// Used to toggle the menu on small screens when clicking on the menu button

function toggleFunction() {

    var x = document.getElementById("navDemo");

    if (x.className.indexOf("w3-show") == -1) {

        x.className += " w3-show";

    } else {

        x.className = x.className.replace(" w3-show", "");

    }

}

</script>




</body>

</html>

این قالب یک قالب آماده HTML به همراه استایل های CSS و برخی کدهای JavaScript است و ظاهر زیبایی نیز دارد بنابراین بهترین گزینه برای ما است که می خواهیم فقط روی کدهای پایتون تمرکز کنیم. حالا به فایل سرور (server.py) برگشته و تابعی به نام render_template را از پکیج flask وارد اسکریپت خود کنید:

from flask import Flask, render_template

app = Flask(__name__)







@app.route('/')

def hello_world():

    return 'Hello, World!'







@app.route('/portfolio')

def portfolio():

    return render_template('./index.html')

همانطور که می بینید من مسیر http://127.0.0.1:5000/portfolio را برای این فایل HTML در نظر گرفته ام. همچنین به جای برگرداندن یک رشته ساده این بار تابع render_template را اجرا کرده ام و آدرس فایل index.html را به آن داده ام. توجه داشته باشید که باید حتما این تابع را return کنید. در حال حاضر اگر سرور در حال اجرا باشد و به آدرس http://127.0.0.1:5000/portfolio برویم، با خطای زیر روبرو می شویم:

jinja2.exceptions.TemplateNotFound

jinja2.exceptions.TemplateNotFound: ./index.html

این خطا می گوید قالب یا template ای به نام index.html پیدا نشده است. البته در ادامه این خطا یک traceback وجود دارد که تمام توابع اجرا شده تا لحظه رسیدن به خطا را لیست کرده است (شامل سورس کد flask نیز می شود) تا اگر مایل بودید آن را مطالعه کنید و بفهمید دقیقا کدام قسمت از کدها باعث خطا شده است. به نظر شما مشکل کجاست؟ اگر به documentation رسمی flask در قسمت template ها نگاهی بیندازید متوجه می شوید که flask به صورت خودکار در پوشه ای به نام templates به دنبال آدرس پاس داده شده می گردد یا به زبان ساده تر انتظار دارد فایل index.html ما درون پوشه ای به نام templates باشد به همین دلیل من یک پوشه به همین نام را در کنار فایل server.py ایجاد می کنم و سپس فایل index.html را درون آن قرار می دهم. حالا دوباره فایل server.py را ذخیره می کنیم تا سرور ریستارت شود:

from flask import Flask, render_template

app = Flask(__name__)







@app.route('/')

def hello_world():

    return 'Hello, World!'







@app.route('/portfolio')

def portfolio():

    return render_template('index.html')

توجه داشته باشید که این بار به جای استفاده از /. در هنگام آدرس دهی به فایل index.html، فقط از نام این فایل استفاده کرده ام. چرا؟ به دلیل اینکه /. یک آدرس دهی نسبی و به معنی «همین آدرس» می باشد اما فایل index.html ما در همین آدرس (در کنار server.py) نیست بلکه در پوشه ای به نام templates قرار دارد. حالا دوباره به آدرس http://127.0.0.1:5000/portfolio در مرورگر خود بروید. تنها مشکلی که وجود دارد این است که تصاویر برایمان بارگذاری نمی شوند. چرا؟ به قسمت زیر از کدهای فایل index.html نگاهی بیندازید:

<img src="/w3images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

اگر با کدهای HTML آشنا باشید می دانید که src در تگ های img مشخص کننده آدرس تصویر است. ما در اینجا آدرس نسبی /w3images/avatar_hat.jpg را داریم که متعلق به سرورهای W3Schools است. شاید هنوز متوجه مشکل نشده باشید؛ مرورگر خود را باز کرده و دوباره صفحه http://127.0.0.1:5000/portfolio را باز کنید. حالا کلید f12 را بزنید و از developer tools، سربرگ console را باز کنید. در این قسمت باید خطا های زیر را مشاهده کنید:

portfolio:97 GET http://127.0.0.1:5000/w3images/avatar_hat.jpg 404 (NOT FOUND)

portfolio:156 GET http://127.0.0.1:5000/w3images/p1.jpg 404 (NOT FOUND)

portfolio:160 GET http://127.0.0.1:5000/w3images/p2.jpg 404 (NOT FOUND)

portfolio:164 GET http://127.0.0.1:5000/w3images/p3.jpg 404 (NOT FOUND)

portfolio:168 GET http://127.0.0.1:5000/w3images/p4.jpg 404 (NOT FOUND)

portfolio:174 GET http://127.0.0.1:5000/w3images/p5.jpg 404 (NOT FOUND)

portfolio:178 GET http://127.0.0.1:5000/w3images/p6.jpg 404 (NOT FOUND)

portfolio:182 GET http://127.0.0.1:5000/w3images/p7.jpg 404 (NOT FOUND)

portfolio:186 GET http://127.0.0.1:5000/w3images/p8.jpg 404 (NOT FOUND)

portfolio:215 GET http://127.0.0.1:5000/w3images/map.jpg 404 (NOT FOUND)

portfolio:1 GET http://127.0.0.1:5000/w3images/parallax1.jpg 404 (NOT FOUND)

portfolio:1 GET http://127.0.0.1:5000/w3images/parallax2.jpg 404 (NOT FOUND)

portfolio:1 GET http://127.0.0.1:5000/w3images/parallax3.jpg 404 (NOT FOUND)

سرور ما به صورت محلی و روی سیستم خودمان در حال اجرا است بنابراین این عکس ها را روی سیستم خودمان جست و جو می کند در حالی که چنین تصاویری در سیستم ما وجود ندارد (این تصاویر در اصل روی سرورهای W3Schools وجود داشتند). چطور می توان چنین مشکلی را حل کرد؟

ارسال فایل های استاتیک

حتما با خواندن این تیتر می پرسید فایل های استاتیک چه فایل هایی هستند؟ از نظر سرور ما فایل ها دو نوع می باشند:

  • فایل های پویا یا دینامیک (dynamic): این نوع فایل ها از قبل وجود ندارند بلکه توسط کدهای پایتون در لحظه ساخته می شوند بنابراین به آن ها پویا می گوییم.
  • فایل های ایستا یا استاتیک (static): این نوع فایل ها از قبل وجو دارند و آماده ارسال به مرورگر هستند و پس از ارسال شدن به مرورگر، هیچ نیازی به ویرایش نداشته و هیچ گاه تغییر نخواهند کرد.

چند مثال ساده از فایل های استاتیک:

  • تصاویر JPG یا PNG یا GIF و الی آخر.
  • ویدیو های MP4 و MKV و الی آخر.
  • فایل های PDF و DOCX و الی آخر.
  • فایل های CSS یا JavaScript و الی آخر.

احتمالا با این مثال متوجه منظور من شده اید. فریم ورک flask از ما می خواهد که تمام فایل های static را در پوشه ای به نام static قرار بدهیم. شاید با خودتان بگویید ما چنین کاری را انجام نداده ایم اما صفحه portfolio ما استایل های خودش را دارد! حرفتان درست است اما باید به کد زیر در فایل index.html توجه کنید تا مسئله برایتان روشن شود:

<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

ما از قالب آماده سایت W3Schools استفاده کرده بودیم بنابراین استایل های CSS آن نیز به صورت آماده در اختیار ما گذاشته شده بودند. مسئله اینجاست که ما کدهای CSS را از روی سرور خودمان به مرورگر ارسال نمی کنیم بلکه URL کامل آن را از سرورهای W3Schools به href داده ایم (در کد بالا مشخص است).  زمانی که صحبت از ارسال فایل های استاتیک است منظور ما ارسال این فایل ها از روی سرور خودمان است نه از یک سرور دیگر. اگر به آدرس https://www.w3schools.com/w3css/4/w3.css بروید (همان آدرسی که به href داده ایم) تمام کدهای CSS از قبل نوشته شده را مشاهده خواهید کرد. اگر این فایل CSS در همین پوشه portfolio و در آدرسی مثل static/styles.css قرار داشت می توانستیم در فایل index.html استایل ها را به شکل زیر آدرس دهی کنیم:

<link rel="stylesheet" href="static/styles.css">

البته در documentation رسمی flask روش پیشنهادی برای آدرس دهی فایل های استاتیک به شکل زیر است:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

اما هنوز با این دستور آشنا نشده ایم بنابراین من توضیح زیادی در مورد آن نمی دهم تا زمانی که به این مبحث برسیم. با تمام این توضیحات متوجه می شویم که برای حل مشکل تصاویر دو راه حل ممکن داریم:

  • آدرس تصاویر را مستقیما از سرور W3Schools مشخص کنیم.
  • تصاویر مورد نظر را از وب سایت W3Schools دانلود کرده و در پوشه ای به نام static در سیستم خودمان ذخیره کنیم.

من هر دو راه حل را به شما نشان می دهم. در روش اول که آدرس دهی از طریق آدرس دهی کامل است باید آدرس دقیق و کامل تصویر را از سرورهای W3schools پیدا کنیم. به طور مثال ما در حال حاضر فایلی به نام index.html داریم که کدهای HTML ما را دارد. به این قسمت از کدها توجه کنید:

<img src="/w3images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

آدرس /w3images/avatar_hat.jpg یک آدرس نسبی است. ما برای تصحیح این آدرس باید دامنه سایت را نیز به ابتدایش اضافه کنیم:

<img src="https://www.w3schools.com/w3images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

حالا اگر به آدرس http://127.0.0.1:5000/portfolio رفته و مرورگر را refresh کنیم این عکس تصحیح می شود. طبیعتا انجام این کار برای تمام تصاویر کمی سخت است بنابراین به سراغ راه حل دوم می رویم. در راه حل دوم باید تمام این تصاویر را دانلود کرده و در پوشه static قرار بدهیم. من لینک تمام این تصاویر را برایتان قرار می دهم تا آن ها را دانلود کنید:

پس از اینکه این تصاویر را دانلود کردید، وارد پوشه static شده و پوشه جدیدی در آن ایجاد کنید که images نام دارد و این تصاویر را در آن قرار بدهید. حالا به جای آنکه چنین تگ های img داشته باشیم:

<img src="https://www.w3schools.com/w3images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

تگ هایی به شکل زیر را خواهیم داشت:

<img src="static/images/avatar_hat.jpg" class="w3-round w3-image w3-opacity w3-hover-opacity-off" alt="Photo of Me" width="500" height="333">

به عبارت دیگر باید قسمت w3images در آدرس تصاویر را با static/images تعویض کرده و نام فایل را نیز دست نخورده باقی بگذارید. من این کار را برای کل فایل index.html انجام داده ام:

<!DOCTYPE html>

<html>

  <title>W3.CSS Template</title>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css" />

  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato" />

  <link

    rel="stylesheet"

    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"

  />

  <style>

    body,

    h1,

    h2,

    h3,

    h4,

    h5,

    h6 {

      font-family: "Lato", sans-serif;

    }

    body,

    html {

      height: 100%;

      color: #777;

      line-height: 1.8;

    }




    /* Create a Parallax Effect */

    .bgimg-1,

    .bgimg-2,

    .bgimg-3 {

      background-attachment: fixed;

      background-position: center;

      background-repeat: no-repeat;

      background-size: cover;

    }




    /* First image (Logo. Full height) */

    .bgimg-1 {

      background-image: url("/static/images/parallax1.jpg");

      min-height: 100%;

    }




    /* Second image (Portfolio) */

    .bgimg-2 {

      background-image: url("/static/images/parallax2.jpg");

      min-height: 400px;

    }




    /* Third image (Contact) */

    .bgimg-3 {

      background-image: url("/static/images/parallax3.jpg");

      min-height: 400px;

    }




    .w3-wide {

      letter-spacing: 10px;

    }

    .w3-hover-opacity {

      cursor: pointer;

    }




    /* Turn off parallax scrolling for tablets and phones */

    @media only screen and (max-device-width: 1600px) {

      .bgimg-1,

      .bgimg-2,

      .bgimg-3 {

        background-attachment: scroll;

        min-height: 400px;

      }

    }

  </style>

  <body>

    <!-- Navbar (sit on top) -->

    <div class="w3-top">

      <div class="w3-bar" id="myNavbar">

        <a

          class="w3-bar-item w3-button w3-hover-black w3-hide-medium w3-hide-large w3-right"

          href="javascript:void(0);"

          onclick="toggleFunction()"

          title="Toggle Navigation Menu"

        >

          <i class="fa fa-bars"></i>

        </a>

        <a href="#home" class="w3-bar-item w3-button">HOME</a>

        <a href="#about" class="w3-bar-item w3-button w3-hide-small"

          ><i class="fa fa-user"></i> ABOUT</a

        >

        <a href="#portfolio" class="w3-bar-item w3-button w3-hide-small"

          ><i class="fa fa-th"></i> PORTFOLIO</a

        >

        <a href="#contact" class="w3-bar-item w3-button w3-hide-small"

          ><i class="fa fa-envelope"></i> CONTACT</a

        >

        <a

          href="#"

          class="w3-bar-item w3-button w3-hide-small w3-right w3-hover-red"

        >

          <i class="fa fa-search"></i>

        </a>

      </div>




      <!-- Navbar on small screens -->

      <div

        id="navDemo"

        class="w3-bar-block w3-white w3-hide w3-hide-large w3-hide-medium"

      >

        <a

          href="#about"

          class="w3-bar-item w3-button"

          onclick="toggleFunction()"

          >ABOUT</a

        >

        <a

          href="#portfolio"

          class="w3-bar-item w3-button"

          onclick="toggleFunction()"

          >PORTFOLIO</a

        >

        <a

          href="#contact"

          class="w3-bar-item w3-button"

          onclick="toggleFunction()"

          >CONTACT</a

        >

        <a href="#" class="w3-bar-item w3-button">SEARCH</a>

      </div>

    </div>




    <!-- First Parallax Image with Logo Text -->

    <div class="bgimg-1 w3-display-container w3-opacity-min" id="home">

      <div class="w3-display-middle" style="white-space: nowrap">

        <span

          class="w3-center w3-padding-large w3-black w3-xlarge w3-wide w3-animate-opacity"

          >MY <span class="w3-hide-small">WEBSITE</span> LOGO</span

        >

      </div>

    </div>




    <!-- Container (About Section) -->

    <div class="w3-content w3-container w3-padding-64" id="about">

      <h3 class="w3-center">ABOUT ME</h3>

      <p class="w3-center"><em>I love photography</em></p>

      <p>

        We have created a fictional "personal" website/blog, and our fictional

        character is a hobby photographer. Lorem ipsum dolor sit amet,

        consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore

        et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

        exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum

        dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non

        proident, sunt in culpa qui officia deserunt mollit anim id est laborum

        consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore

        et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

        exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

      </p>

      <div class="w3-row">

        <div class="w3-col m6 w3-center w3-padding-large">

          <p>

            <b><i class="fa fa-user w3-margin-right"></i>My Name</b>

          </p>

          <br />

          <img

            src="static/images/avatar_hat.jpg"

            class="w3-round w3-image w3-opacity w3-hover-opacity-off"

            alt="Photo of Me"

            width="500"

            height="333"

          />

        </div>




        <!-- Hide this text on small devices -->

        <div class="w3-col m6 w3-hide-small w3-padding-large">

          <p>

            Welcome to my website. I am lorem ipsum consectetur adipiscing elit,

            sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

            Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris

            nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in

            reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla

            pariatur. Excepteur sint occaecat cupidatat non proident, sunt in

            culpa qui officia deserunt mollit anim id est laborum consectetur

            adipiscing elit, sed do eiusmod tempor incididunt ut labore et

            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud

            exercitation ullamco laboris nisi ut aliquip ex ea commodo

            consequat.

          </p>

        </div>

      </div>

      <p class="w3-large w3-center w3-padding-16">Im really good at:</p>

      <p class="w3-wide"><i class="fa fa-camera"></i>Photography</p>

      <div class="w3-light-grey">

        <div

          class="w3-container w3-padding-small w3-dark-grey w3-center"

          style="width: 90%"

        >

          90%

        </div>

      </div>

      <p class="w3-wide"><i class="fa fa-laptop"></i>Web Design</p>

      <div class="w3-light-grey">

        <div

          class="w3-container w3-padding-small w3-dark-grey w3-center"

          style="width: 85%"

        >

          85%

        </div>

      </div>

      <p class="w3-wide"><i class="fa fa-photo"></i>Photoshop</p>

      <div class="w3-light-grey">

        <div

          class="w3-container w3-padding-small w3-dark-grey w3-center"

          style="width: 75%"

        >

          75%

        </div>

      </div>

    </div>




    <div class="w3-row w3-center w3-dark-grey w3-padding-16">

      <div class="w3-quarter w3-section">

        <span class="w3-xlarge">14+</span><br />

        Partners

      </div>

      <div class="w3-quarter w3-section">

        <span class="w3-xlarge">55+</span><br />

        Projects Done

      </div>

      <div class="w3-quarter w3-section">

        <span class="w3-xlarge">89+</span><br />

        Happy Clients

      </div>

      <div class="w3-quarter w3-section">

        <span class="w3-xlarge">150+</span><br />

        Meetings

      </div>

    </div>




    <!-- Second Parallax Image with Portfolio Text -->

    <div class="bgimg-2 w3-display-container w3-opacity-min">

      <div class="w3-display-middle">

        <span class="w3-xxlarge w3-text-white w3-wide">PORTFOLIO</span>

      </div>

    </div>




    <!-- Container (Portfolio Section) -->

    <div class="w3-content w3-container w3-padding-64" id="portfolio">

      <h3 class="w3-center">MY WORK</h3>

      <p class="w3-center">

        <em

          >Here are some of my latest lorem work ipsum tipsum.<br />

          Click on the images to make them bigger</em

        >

      </p>

      <br />




      <!-- Responsive Grid. Four columns on tablets, laptops and desktops. Will stack on mobile devices/small screens (100% width) -->

      <div class="w3-row-padding w3-center">

        <div class="w3-col m3">

          <img

            src="/static/images/p1.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="The mist over the mountains"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p2.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="Coffee beans"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p3.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="Bear closeup"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p4.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="Quiet ocean"

          />

        </div>

      </div>




      <div class="w3-row-padding w3-center w3-section">

        <div class="w3-col m3">

          <img

            src="/static/images/p5.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="The mist"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p6.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="My beloved typewriter"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p7.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="Empty ghost train"

          />

        </div>




        <div class="w3-col m3">

          <img

            src="/static/images/p8.jpg"

            style="width: 100%"

            onclick="onClick(this)"

            class="w3-hover-opacity"

            alt="Sailing"

          />

        </div>

        <button

          class="w3-button w3-padding-large w3-light-grey"

          style="margin-top: 64px"

        >

          LOAD MORE

        </button>

      </div>

    </div>




    <!-- Modal for full size images on click-->

    <div

      id="modal01"

      class="w3-modal w3-black"

      onclick="this.style.display='none'"

    >

      <span

        class="w3-button w3-large w3-black w3-display-topright"

        title="Close Modal Image"

        ><i class="fa fa-remove"></i

      ></span>

      <div

        class="w3-modal-content w3-animate-zoom w3-center w3-transparent w3-padding-64"

      >

        <img id="img01" class="w3-image" />

        <p id="caption" class="w3-opacity w3-large"></p>

      </div>

    </div>




    <!-- Third Parallax Image with Portfolio Text -->

    <div class="bgimg-3 w3-display-container w3-opacity-min">

      <div class="w3-display-middle">

        <span class="w3-xxlarge w3-text-white w3-wide">CONTACT</span>

      </div>

    </div>




    <!-- Container (Contact Section) -->

    <div class="w3-content w3-container w3-padding-64" id="contact">

      <h3 class="w3-center">WHERE I WORK</h3>

      <p class="w3-center"><em>I'd love your feedback!</em></p>




      <div class="w3-row w3-padding-32 w3-section">

        <div class="w3-col m4 w3-container">

          <img

            src="/static/images/map.jpg"

            class="w3-image w3-round"

            style="width: 100%"

          />

        </div>

        <div class="w3-col m8 w3-panel">

          <div class="w3-large w3-margin-bottom">

            <i

              class="fa fa-map-marker fa-fw w3-hover-text-black w3-xlarge w3-margin-right"

            ></i>

            Chicago, US<br />

            <i

              class="fa fa-phone fa-fw w3-hover-text-black w3-xlarge w3-margin-right"

            ></i>

            Phone: +00 151515<br />

            <i

              class="fa fa-envelope fa-fw w3-hover-text-black w3-xlarge w3-margin-right"

            ></i>

            Email: mail@mail.com<br />

          </div>

          <p>

            Swing by for a cup of <i class="fa fa-coffee"></i>, or leave me a

            note:

          </p>

          <form action="/action_page.php" target="_blank">

            <div class="w3-row-padding" style="margin: 0 -16px 8px -16px">

              <div class="w3-half">

                <input

                  class="w3-input w3-border"

                  type="text"

                  placeholder="Name"

                  required

                  name="Name"

                />

              </div>

              <div class="w3-half">

                <input

                  class="w3-input w3-border"

                  type="text"

                  placeholder="Email"

                  required

                  name="Email"

                />

              </div>

            </div>

            <input

              class="w3-input w3-border"

              type="text"

              placeholder="Message"

              required

              name="Message"

            />

            <button

              class="w3-button w3-black w3-right w3-section"

              type="submit"

            >

              <i class="fa fa-paper-plane"></i> SEND MESSAGE

            </button>

          </form>

        </div>

      </div>

    </div>




    <!-- Footer -->

    <footer

      class="w3-center w3-black w3-padding-64 w3-opacity w3-hover-opacity-off"

    >

      <a href="#home" class="w3-button w3-light-grey"

        ><i class="fa fa-arrow-up w3-margin-right"></i>To the top</a

      >

      <div class="w3-xlarge w3-section">

        <i class="fa fa-facebook-official w3-hover-opacity"></i>

        <i class="fa fa-instagram w3-hover-opacity"></i>

        <i class="fa fa-snapchat w3-hover-opacity"></i>

        <i class="fa fa-pinterest-p w3-hover-opacity"></i>

        <i class="fa fa-twitter w3-hover-opacity"></i>

        <i class="fa fa-linkedin w3-hover-opacity"></i>

      </div>

      <p>

        Powered by

        <a

          href="https://www.w3schools.com/w3css/default.asp"

          title="W3.CSS"

          target="_blank"

          class="w3-hover-text-green"

          >w3.css</a

        >

      </p>

    </footer>




    <script>

      // Modal Image Gallery

      function onClick(element) {

        document.getElementById("img01").src = element.src;

        document.getElementById("modal01").style.display = "block";

        var captionText = document.getElementById("caption");

        captionText.innerHTML = element.alt;

      }




      // Change style of navbar on scroll

      window.onscroll = function () {

        myFunction();

      };

      function myFunction() {

        var navbar = document.getElementById("myNavbar");

        if (

          document.body.scrollTop > 100 ||

          document.documentElement.scrollTop > 100

        ) {

          navbar.className =

            "w3-bar" + " w3-card" + " w3-animate-top" + " w3-white";

        } else {

          navbar.className = navbar.className.replace(

            " w3-card w3-animate-top w3-white",

            ""

          );

        }

      }




      // Used to toggle the menu on small screens when clicking on the menu button

      function toggleFunction() {

        var x = document.getElementById("navDemo");

        if (x.className.indexOf("w3-show") == -1) {

          x.className += " w3-show";

        } else {

          x.className = x.className.replace(" w3-show", "");

        }

      }

    </script>

  </body>

</html>

حالا اگر به آدرس http://127.0.0.1:5000/portfolio در مرورگر خود بروید تمام تصاویر را نیز مشاهده می کنید.

اضافه کردن favicon

احتمالا می دانید که favicon همان آیکون کوچک در تب مرورگر شما است که معمولا لوگوی سایت شما را دارد. این تصویر همیشه فرمت ico را دارد بنابراین تصویر دلخواهی با این فرمت را برای خودتان پیدا کنید. من شخصا از این تصویر رایگان استفاده می کنم چرا که تمیز است و راحت تر به چشم می آید (هرچند ربطی به پروژه ما نداشته باشد). حالا به documentation رسمی flask می رویم تا نحوه اضافه کردن favicon را یاد بگیریم. این documentation توضیح می دهد که برای اضافه کردن favicon دو راه وجود دارد. راه اول همان راه ساده ای است که قبلا هم داشتیم:

<link rel="shortcut icon" href="{{ url_for('static/images', filename='images/favicon.ico') }}">

همانطور که گفتم هنوز در مورد {{‌ }} صحبت نکرده ایم بنابراین نگران نباشید (در جلسه بعدی در مورد آن صحبت خواهیم کرد). کد بالا دقیقا معادل کد زیر است:

<link rel="shortcut icon" href="static/images/favicon.ico">

روش دوم انجام این کار برای وب سایت هایی است که باید از مرورگرهای بسیار بسیار قدیمی پشتیبانی کنند:

app.add_url_rule('/favicon.ico', redirect_to=url_for('static', filename='favicon.ico'))

ما با این روش کاری نداریم چرا که مخصوص مرورگرهای بسیار بسیار قدیمی است و کاربردی برای ما ندارد. کد زیر را به قسمت <head> فایل index.html خود اضافه کنید:

<link rel="shortcut icon" href="static/images/favicon.ico">

سپس دوباره به آدرس http://127.0.0.1:5000/portfolio در مرورگر بروید. این بار باید در کنار نام سایت (روی تب یا همان سربرگ مرورگر) آیکون مورد نظرتان را مشاهده کنید. اگر قسمتی را اشتباه انجام داده باشید خطایی را در قسمت console مرورگر دریافت خواهید کرد (بخش develop tools).

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری پایتون حرفه‌ای توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.