ما در جلسه قبل قالب جدیدمان را از وب سایت www.mashup-template.com دانلود کردیم و برایتان توضیح دادم که من قالب Univers را از این وب سایت دانلود کرده ام. در ادامه نیز برخی از فایل های اضافی را حذف کردیم اما حالا نوبت به شکل دهی به پروژه جدید است. در پوشه portfolio خود (یا هر نامی که برای پوشه اصلی گذاشته اید) مثل همیشه یک پوشه به نام templates ایجاد کنید و تمام فایل های HTML را در آن قرار دهید. در مرحله بعدی فایل های CSS و JS را به همراه پوشه assets به پوشه جدیدی به نام static انتقال دهید.
با انجام این کار ساختاراولیه پروژه را ایجاد کرده ایم اما هنوز مشکلاتی وجود دارد. به طور مثال در حال حاضر آدرس منابع مختلف خراب شده است. برای نمونه فایل index.html خود را باز کنید و به یکی از آدرس های آن نگاهی بیندازید. من فقط قسمت <head> از این فایل را برایتان می آورم:
<head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width,initial-scale=1" name="viewport"> <meta content="description" name="description"> <meta name="google" content="notranslate" /> <meta content="Mashup templates have been developped by Orson.io team" name="author"> <!-- Disable tap highlight on IE --> <meta name="msapplication-tap-highlight" content="no"> <link rel="apple-touch-icon" sizes="180x180" href="./assets/apple-icon-180x180.png"> <link href="./assets/favicon.ico" rel="icon"> <title>Title page</title> <link href="./main.3f6952e4.css" rel="stylesheet"> </head>
ما کاری کردیم که فایل index.html در پوشه templates باشد اما فایل هایی مثل فایل css ما درون پوشه static هستند بنابراین آدرس main.3f6952e4.css/. برایشان صحیح نیست. برای راحتی کارتان می توانید با فشردن کلید های Ctrl + F وارد حالت جست و جو در ویرایشگر خود شده و به دنبال رشته /assets/ بگردید. هر جایی که این مقدار وجود داشته باشد باید قبل از آن static را نیز اضافه کنیم. مثال:
<link href="static/assets/favicon.ico" rel="icon">
اما از آنجایی که ما عکسی برای favicon نداریم من از تصویر png موجود در این پروژه استفاده می کنم:
<link href="static/assets/apple-icon-180x180.png" rel="icon">
این کار را برای آدرس های دیگر نیز انجام بدهید تا قالب به صورت صحیح تنظیم شود. به طور مثال قسمت head به شکل زیر در می آید:
<head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width,initial-scale=1" name="viewport"> <meta content="description" name="description"> <meta name="google" content="notranslate" /> <meta content="Mashup templates have been developped by Orson.io team" name="author"> <!-- Disable tap highlight on IE --> <meta name="msapplication-tap-highlight" content="no"> <link rel="apple-touch-icon" sizes="180x180" href="static/assets/apple-icon-180x180.png"> <link href="static/assets/apple-icon-180x180.png" rel="icon"> <title>Title page</title> <link href="static/main.3f6952e4.css" rel="stylesheet"> </head>
همچنین در انتهای فایل index.html نیز باید آدرس فایل جاوا اسکریپت را اصلاح کنیم:
<script type="text/javascript" src="/static/main.70a66962.js"></script>
در هنگام آدرس دهی در فریم ورک Flask، آدرس ها همیشه از آدرس اصلی یا root پروژه محاسبه می شوند. به همین دلیل است که در هنگام آدرس دهی به فایل جاوا اسکریپتی بالا به شکل زیر عمل نکرده ایم:
<script type="text/javascript" src="../static/main.70a66962.js"></script>
با این حال هر دو روش صحیح است اما معمولا از روش اول استفاده می شود. در نهایت آدرس لینک تصویر پس زمینه را نیز تصحیح می کنیم:
<div class="hero-full-container background-image-container white-text-container" style="background-image: url('static/assets/images/space.jpg')">
در پوشه portfolio (یا هر نامی که برای پوشه اصلی پروژه انتخاب کرده اید) یک فایل جدید به نام server.py ایجاد کنید. محتویات این فایل مانند همیشه به شکل زیر خواهد بود:
from flask import Flask, render_template app = Flask(__name__) @app.route('/') def home(): return render_template('index.html')
حالا مثل همیشه دستور flask run را در ترمینال خود اجرا می کنیم تا سرور شروع به کار کند. حالا به آدرس http://127.0.0.1:5000 در مرورگر می رویم. در صورتی که همه چیز را درست انجام داده باشید صفحه با ظاهری مناسب برایتان نمایش داده خواهد شد اما اگر فایل های CSS یا JavaScript را به اشتباه آدرس دهی کرده باشید ظاهر صفحه بهم خواهد ریخت. اگر به نوار navigation در بالای صفحه اصلی توجه کرده باشید چند لینک مختلف برای صفحات مختلف را مشاهده می کنید (works و about me و contact و الی آخر). ما زمانی که در صفحه اصلی (آدرس /) باشیم فایل index.html را نمایش می دهیم (این چیزی است که در server.py نوشته ایم) اما اگر روی لینک Home کلیک کنیم به آدرس http://127.0.0.1:5000/index.html منتقل می شویم و از آنجایی که چنین مسیری (index.html/) را برای server.py تعریف نکرده ایم، با خطای Not Found مواجه خواهیم شد.
برای حل این مشکل دو راه وجود دارد. راه حل اول این است که وارد فایل index.html شده و سپس src را برای این لینک تغییر بدهیم. در حال حاضر این لینک ها به شکل زیر هستند:
<div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav "> <li><a href="./index.html" title="">01 : Home</a></li> <li><a href="./works.html" title="">02 : Works</a></li> <li><a href="./about.html" title="">03 : About me</a></li> <li><a href="./contact.html" title="">04 : Contact</a></li> <li><a href="./components.html" title="">05 : Components</a></li> </ul> </div>
بنابراین می توان به راحتی آن را به شکل زیر تغییر داد:
<div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav "> <li><a href="/" title="">01 : Home</a></li> <li><a href="./works.html" title="">02 : Works</a></li> <li><a href="./about.html" title="">03 : About me</a></li> <li><a href="./contact.html" title="">04 : Contact</a></li> <li><a href="./components.html" title="">05 : Components</a></li> </ul> </div>
طبیعتا این روش، بهترین و ساده ترین روش است اما من برای اینکه نکته جدیدی را به شما آموزش داده باشم از redirect استفاده می کنم. به زبان ساده redirect کردن یک کاربر یعنی انتقال آن کاربر به صفحه ای دیگر:
from flask import Flask, render_template, redirect app = Flask(__name__) @app.route('/') def home(): return render_template('index.html') @app.route('/index.html') def home_redirect(): return redirect('/', code=302)
من در این کد مسیر جدیدی (index/) را تعریف کرده ام تا اگر کاربر روی آن کلیک کند، یک redirect برایش برگردانده شود. این redirect دو آرگومان می گیرد: آرگومان اول آدرسی است که کاربر باید به آن منتقل شود و آرگومان دوم یک HTTP Status Code می باشد. HTTP Status Code ها مجموعه ای از کد های وضعیت برای درخواست های HTTP می باشند و به کاربر می گویند که هر اتفاق به چه دلیل رخ داده است. به طور مثال کد ۳۰۲ که در بالا استفاده شده است یعنی resource یا داده مورد نظر او به آدرس جدیدی منتقل شده است. پاس دادن کد های وضعیت HTTP همیشه یک استاندارد بوده و باید انجام شود اما از نظر فنی اگر آرگومان دوم را اصلا پاس ندهید باز هم کد هایتان کار خواهند کرد.
حالا اگر در مرورگر خود به آدرس http://127.0.0.1:5000/index.html بروید، سریعا به آدرس http://127.0.0.1:5000 منتقل خواهید شد. این عملیات آنچنان سریع انجام می شود که شما اصلا متوجه آن نخواهید شد و تصور می کنید مستقیما روی لینک http://127.0.0.1:5000 کلیک کرده اید.
من به عنوان تمرین از شما می خواهم که لینک های دیگر navigation را نیز به route های خودشان متصل کنید تا خطای Not Found را دریافت نکنیم. قبلا این موضوع (تعریف route) را به شما یاد داده ام.
برای جلوگیری از دریافت خطا باید ابتدا route های مورد نظرشان را تعریف کنیم. من در ابتدا لینک های navigation را در فایل index.html تصحیح می کنم:
<div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav "> <li><a href="/index" title="">01 : Home</a></li> <li><a href="/works" title="">02 : Works</a></li> <li><a href="/about" title="">03 : About me</a></li> <li><a href="/contact" title="">04 : Contact</a></li> <li><a href="/components" title="">05 : Components</a></li> </ul> </div>
حضور پسوند html. در انتهای آدرس از نظر من ظاهر جالبی ندارد به همین دلیل آن را از تمام لینک ها حذف کرده ام اما شما می توانید آن ها را باقی بگذارید. اگر این کار را انجام دادید باید route هایتان را نیز بر اساس همین آدرس ها تعریف کنید.
حالا تنها کاری که باقی مانده است تعریف کردن route ها در فایل server.py است:
from flask import Flask, render_template, redirect app = Flask(__name__) @app.route('/') def home(): return render_template('index.html') @app.route('/index') def home_redirect(): return redirect('/', code=302) @app.route('/works') def works(): return render_template('works.html') @app.route('/work') def work(): return render_template('work.html') @app.route('/about') def about(): return render_template('about.html') @app.route('/contact') def contact(): return render_template('contact.html') @app.route('/components') def components(): return render_template('components.html')
حالا با کلیک روی هر کدام از لینک های navigation به آن آدرس منتقل می شوید. به طور مثال http://127.0.0.1:5000/works صفحه works را برایتان نمایش می دهد اما مشکل اینجاست که فایل های CSS و JavaScript آن به شکل صحیح بارگذاری نمی شوند بنابراین ظاهر صفحه درهم ریخته خواهد بود. برای تصحیح این مشکل به تک تک فایل های HTML می رویم و آدرس این فایل ها را تصحیح می کنیم.
من ابتدا از فایل about.html شروع می کنم. مثل همیشه قسمت <head> را به شکل زیر ویرایش می کنیم:
<head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width,initial-scale=1" name="viewport"> <meta content="description" name="description"> <meta name="google" content="notranslate" /> <meta content="Mashup templates have been developped by Orson.io team" name="author"> <!-- Disable tap highlight on IE --> <meta name="msapplication-tap-highlight" content="no"> <link rel="apple-touch-icon" sizes="180x180" href="/static/assets/apple-icon-180x180.png"> <link href="/static/assets/apple-icon-180x180.png" rel="icon"> <title>Title page</title> <link href="./static/main.3f6952e4.css" rel="stylesheet"> </head>
تنها کاری که انجام داده ام اضافه کردن static به قبل از آدرس های این فایل ها بوده است. در مرحله بعدی پسوند html را از لینک های navigation حذف می کنیم:
<div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav "> <li><a href="./index" title="">01 : Home</a></li> <li><a href="./works" title="">02 : Works</a></li> <li><a href="./about" title="">03 : About me</a></li> <li><a href="./contact" title="">04 : Contact</a></li> <li><a href="./components" title="">05 : Components</a></li> </ul> </div>
سپس یک تصویر را داریم:
<img src="./static/assets/images/profil.jpg" class="img-responsive" alt="">
و نهایتا فایل جاوا اسکریپت:
<script type="text/javascript" src="./static/main.70a66962.js"></script>
از شما می خواهم که این کار را برای فایل های دیگر HTML نیز انجام بدهید. من فقط مثالی از فایل ویرایش شده works.html را برایتان قرار می دهم:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width,initial-scale=1" name="viewport"> <meta content="description" name="description"> <meta name="google" content="notranslate" /> <meta content="Mashup templates have been developped by Orson.io team" name="author"> <!-- Disable tap highlight on IE --> <meta name="msapplication-tap-highlight" content="no"> <link rel="apple-touch-icon" sizes="180x180" href="./static/assets/apple-icon-180x180.png"> <link href="./static/assets/apple-icon-180x180.png" rel="icon"> <title>Title page</title> <link href="./static/main.3f6952e4.css" rel="stylesheet"> </head> <body class=""> <div id="site-border-left"></div> <div id="site-border-right"></div> <div id="site-border-top"></div> <div id="site-border-bottom"></div> <!-- Add your content of header --> <header> <nav class="navbar navbar-fixed-top navbar-default"> <div class="container"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav "> <li><a href="./index" title="">01 : Home</a></li> <li><a href="./works" title="">02 : Works</a></li> <li><a href="./about" title="">03 : About me</a></li> <li><a href="./contact" title="">04 : Contact</a></li> <li><a href="./components" title="">05 : Components</a></li> </ul> </div> </div> </nav> </header> <div class="section-container"> <div class="container"> <div class="row"> <div class="col-sm-8 col-sm-offset-2 section-container-spacer"> <div class="text-center"> <h1 class="h2">02 : Works</h1> <p>Nulla facilisi. Vivamus vestibulum, elit in scelerisque ultricies, nisl nunc pulvinar ligula, id sodales arcu sapien in nisi. Quisque libero enim, mattis non augue posuere, venenatis dapibus urna.</p> </div> </div> <div class="col-md-12"> <div id="myCarousel" class="carousel slide projects-carousel"> <!-- Carousel items --> <div class="carousel-inner"> <div class="item active"> <div class="row"> <div class="col-sm-4"> <a href="./work" title="" class="black-image-project-hover"> <img src="./static/assets/images/work01-hover.jpg" alt="" class="img-responsive"> </a> <div class="card-container card-container-lg"> <h4>001/006</h4> <h3>Fringilla sit amet</h3> <p>Nulla facilisi. Vivamus vestibulum, elit in scelerisque ultricies.</p> <a href="./work" title="" class="btn btn-default">Discover</a> </div> </div> <div class="col-sm-4"> <a href="./work" title="" class="black-image-project-hover"> <img src="./static/assets/images/work02-hover.jpg" alt="" class="img-responsive"> </a> <div class="card-container card-container-lg"> <h4>002/006</h4> <h3>Nulla scelerisque</h3> <p>Proin pharetra metus id iaculis dignissim. In aliquet lorem ut ex ullamcorper.</p> <a href="./work" title="" class="btn btn-default">Discover</a> </div> </div> <div class="col-sm-4"> <a href="./work" title="" class="black-image-project-hover"> <img src="./static/assets/images/work03-hover.jpg" alt="" class="img-responsive"> </a> <div class="card-container card-container-lg"> <h4>003/006</h4> <h3>Vivamus vestibulum</h3> <p>Fusce sed hendrerit augue, a rhoncus velit. In non lorem mattis, tempor sem sit amet.</p> <a href="./work" title="" class="btn btn-default">Discover</a> </div> </div> </div> <!--/row--> </div> <!--/item--> <div class="item"> <div class="row"> <div class="col-sm-4"> <a href="./work" class="black-image-project-hover"> <img src="./static/assets/images/work02-hover.jpg" alt="Image" class="img-responsive"> </a> <div class="card-container"> <h4>004/006</h4> <h3>Nulla scelerisque</h3> <p>Proin pharetra metus id iaculis dignissim. In aliquet lorem ut ex ullamcorper.</p> <a href="./work" class="btn btn-default">Discover</a> </div> </div> <div class="col-sm-4"> <a href="./work" class="black-image-project-hover"> <img src="./static/assets/images/work01-hover.jpg" alt="Image" class="img-responsive"> </a> <div class="card-container"> <h4>005/006</h4> <h3>Fringilla sit amet</h3> <p>Nulla facilisi. Vivamus vestibulum, elit in scelerisque ultricies.</p> <a href="./work" class="btn btn-default">Discover</a> </div> </div> <div class="col-sm-4"> <a href="./work" class="black-image-project-hover"> <img src="./static/assets/images/work03-hover.jpg" alt="Image" class="img-responsive"> </a> <div class="card-container"> <h4>006/006</h4> <h3>Vivamus vestibulum</h3> <p>Fusce sed hendrerit augue, a rhoncus velit. In non lorem mattis, tempor sem sit amet.</p> <a href="./work" class="btn btn-default">Discover</a> </div> </div> </div> <!--/row--> </div> <!--/item--> </div> <!--/carousel-inner--> <a class="left carousel-control" href="#myCarousel" data-slide="prev">‹</a> <a class="right carousel-control" href="#myCarousel" data-slide="next">›</a> </div> <!--/myCarousel--> </div> </div> </div> </div> <footer class="footer-container text-center"> <div class="container"> <div class="row"> <div class="col-xs-12"> <p>© UNTITLED | Website created with <a href="http://www.mashup-template.com/" title="Create website with free html template">Mashup Template</a>/<a href="https://www.unsplash.com/" title="Beautiful Free Images">Unsplash</a></p> </div> </div> </div> </footer> <script> document.addEventListener("DOMContentLoaded", function (event) { navActivePage(); }); </script> <!-- Google Analytics: change UA-XXXXX-X to be your site's ID <script> (function (i, s, o, g, r, a, m) { i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { (i[r].q = i[r].q || []).push(arguments) }, i[r].l = 1 * new Date(); a = s.createElement(o), m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m) })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); ga('create', 'UA-XXXXX-X', 'auto'); ga('send', 'pageview'); </script> --> <script type="text/javascript" src="./static/main.70a66962.js"></script> </body> </html>
از آنجایی که این فرآیند تکراری و بسیار آسان است من این کار را برای فایل های دیگر انجام نمی دهم و آن را به شما می سپارم. پس از ویرایش این فایل ها یک بار سرور را به صورت دستی ریستارت کنید و دوباره به مرورگر بروید. حالا باید تمام صفحات ظاهری مناسب و بدون خطا داشته باشند.
حالا که ظاهر وب سایت خود را تصحیح کرده ایم باید به سراغ دریافت داده ها از فرم برویم. ما آدرس http://127.0.0.1:5000/contact در مرورگر برویم یک فرم تماس با ما (contact me) را خواهیم داشت تا مردم بتوانند سوالات یا پیشنهادات خود را برای ما ارسال کنند. برای انجام این کار باید درخواست ارسال شده از مرورگر را دریافت کنیم اما چطور؟ در documentation رسمی flask مثال زیر را پیدا خواهید کرد:
@app.route('/login', methods=['POST', 'GET']) def login(): error = None if request.method == 'POST': if valid_login(request.form['username'], request.form['password']): return log_the_user_in(request.form['username']) else: error = 'Invalid username/password' # the code below is executed if the request method # was GET or the credentials were invalid return render_template('login.html', error=error)
در flask شیء ای خاص به نام request داریم که می توانیم با استفاده از خصوصیت form آن، به تمام فیلد های فرم دسترسی داشته باشیم. اگر توسعه دهنده ای با کمی تجربه باشید با یک نگاه ساده به کد بالا متوجه فرآیند کلی آن خواهید شد اما اگر بخواهیم از چنین چیزی در کد خودمان استفاده کنیم باید یک route جدید را ایجاد کنیم این کد هنوز پیچیده است و بهتر است خودمان قدم به قدم شروع کنیم اما یک نکته در آن جلب توجه می کند: در کد بالا methods (آرگومان دوم) مشخص می کند که این route چه نوع درخواست هایی را قبول می کند. در اینجا هر دو نوع درخواست POST و GET را قبول کرده ایم که همان مقدار پیش فرض است. برای انجام این کار به فایل server.py می رویم و مسیر زیر را به این فایل اضافه می کنیم:
@app.route('/submit_form', methods=['POST', 'GET']) def submit_form(): return 'form submitted!'
من فعلا می خواهم نحوه استفاده از این route را به شما نشان بدهم بنابراین فقط رشته ای با مقدار form submitted را برمی گردانیم. در حال حاضر فقط این مسیر را تعریف کرده ایم اما از آن استفاده نمی کنیم. حالا چه کار کنیم؟ طبیعتا باید به فایل contact.html برویم و به تگ <form> نگاهی می اندازیم:
<form action="" class="reveal-content"> <div class="row"> <div class="col-md-7"> <div class="form-group"> <input type="email" class="form-control" id="email" placeholder="Email"> </div> <div class="form-group"> <input type="text" class="form-control" id="subject" placeholder="Subject"> </div> <div class="form-group"> <textarea class="form-control" rows="5" placeholder="Enter your message"></textarea> </div> <button type="submit" class="btn btn-default btn-lg">Send</button> </div> <div class="col-md-5 address-container"> <ul class="list-unstyled"> <li> <span class="fa-icon"> <i class="fa fa-phone" aria-hidden="true"></i> </span> + 33 9 07 45 12 65 </li> <li> <span class="fa-icon"> <i class="fa fa-at" aria-hidden="true"></i> </span> e-mail@mail.io </li> <li> <span class="fa-icon"> <i class="fa fa fa-map-marker" aria-hidden="true"></i> </span> 42 rue Moulbert 75016 Paris </li> </ul> <h3>Follow me on social networks</h3> <a href="http://www.facebook.com" title="" class="fa-icon"> <i class="fa fa-facebook"></i> </a> <a href="http://www.twitter.com" title="" class="fa-icon"> <i class="fa fa-twitter"></i> </a> <a href="http://www.linkedin.com" title="" class="fa-icon"> <i class="fa fa-linkedin"></i> </a> </div> </div> </form>
یکی از attribute ها یا خصوصیت های تگ form، خصوصیت action است که مشخص می کند اطلاعات این فرم پس از ثبت شدن توسط کاربر به کجا ارسال شوند. ما می توانیم به action همان آدرس route خودمان را بدهیم تا به آنجا ارسال شود:
<form action="submit_form" method="POST" class="reveal-content"> // بقیه کد ها
دقت کنید که من متد POST را نیز به این فرم اضافه کرده ام. در صورتی که نمی دانید به صورت خیلی خلاصه برایتان توضیح می دهم که دستورات ارسالی بین مرورگر و سرور چندین نوع دارند که دو نوع از مهم ترین آن ها GET و POST می باشند:
این دو متد جزئیات متفاوت دیگری نیز دارند و علاوه بر آن ها متد های دیگری را نیز داریم (PUT و DELETE و غیره) اما این دوره مخصوص توسعه وب نیست بنابراین نمی توانیم وارد تک تک جزئیات بشویم. حالا در مرورگر خود به آدرس http://127.0.0.1:5000/contact رفته و فرم را پر کنید (داده هایی که می نویسید مهم نیستند) و در نهایت دکمه send را فشار بدهید. با فشار دادن این دکمه باید به آدرس http://127.0.0.1:5000/submit_form منتقل شوید و رشته form submitted! نیز برایتان نمایش داده شود.
من این کار را به صورت بسیار ساده و فقط با یک رشته (form submitted!) انجام داده ام تا شما کلیت کار را درک کنید اما حالا باید به فکری برای دریافت واقعی این داده ها باشیم. در جلسه بعدی این کار را انجام خواهیم داد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.