Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ii:lab:laborator5 [2016/10/02 18:02]
alexandru.radovici created
ii:lab:laborator5 [2021/01/20 08:51] (current)
florin.stancu [Exerciții]
Line 1: Line 1:
-====== Laborator 5 - GDB ======+====== Laborator 5 - Python Flask ====== 
 + 
 +===== Obiective ===== 
 + 
 +În cadrul acestul laborator vom explora framework-ul web [[https://​flask.palletsprojects.com/​en/​1.1.x/​ | Flask]] și vom dezvolta o aplicație web minimală. 
 + 
 +===== Introducere ===== 
 + 
 +Un framework oferă unui programator o structură predefinită cu ajutorul căruia acesta poate dezvolta rapid aplicații. 
 +De exemplu, un framework web implementează logica necesară stabilirii conexiunilor dintre server și clienți, și îi permite astfel programatorului să se concentreze pe logica aplicației și conținutul pe care îl oferă clienților. 
 + 
 +Folosind **Flask** putem dezvolta o aplicație de tip server web folosind limbajul **Python**. 
 + 
 +===== Instalare ===== 
 + 
 +Pentru a instala **Flask** vom folosi managerul de pachete al limbajului **Python**, **pip**: 
 + 
 +<code bash> 
 + 
 +$ pip install flask 
 + 
 +</​code>​ 
 + 
 +Pentru a verifica instalarea, deschidem interpretorul **python**, și importăm modulul **flask**:​ 
 + 
 +<code bash> 
 + 
 +$ python 
 + 
 +>>>​ import flask 
 +>>>​ 
 + 
 +</​code>​ 
 + 
 +<note important>​ 
 +Dacă nu vă merge (diferite setup-uri), încercați una dintre comenzile echivalente:​ 
 +<code bash> 
 +pip3 install ...  # pip pentru python3, dacă este aliasat 
 +python3 -mpip install ...  # pentru python3 
 + 
 +python -mpip install ...  # pentru python2 
 +</​code>​ 
 +</​note>​ 
 + 
 +===== Hello World ===== 
 + 
 +Vom crea un director numit **hello-flask** în directorul nostru **HOME**: 
 + 
 +<code bash> 
 + 
 +$ mkdir ~/​hello-flask 
 +$ cd ~/​hello-flask 
 + 
 +</​code>​ 
 + 
 +În cadrul acestui director, creăm un fișier **hello.py** cu conținutul de mai jos: 
 + 
 +<code python hello.py>​ 
 + 
 +from flask import Flask 
 + 
 +app = Flask(__name__) 
 + 
 +@app.route("/"​) 
 +@app.route("/​index"​) 
 +def hello(): 
 +    return "Hello World from Flask"​ 
 + 
 +if __name__ == "​__main__":​ 
 +    # Only for debugging while developing 
 +    app.run(host='​0.0.0.0',​ debug=True, port=5000) 
 + 
 +</​code>​ 
 + 
 +Apoi pornim serverul web: 
 + 
 +<code bash> 
 + 
 +$ python hello.py 
 + * Serving Flask app "​hello"​ (lazy loading) 
 + * Environment:​ production 
 +   ​WARNING:​ This is a development server. Do not use it in a production deployment. 
 +   Use a production WSGI server instead. 
 + * Debug mode: on 
 + * Running on http://​0.0.0.0:​5000/​ (Press CTRL+C to quit) 
 + * Restarting with stat 
 + * Debugger is active! 
 + * Debugger PIN: 850-713-158 
 + 
 +</​code>​ 
 + 
 +Acum putem accesa adresele [[http://​127.0.0.1:​5000]] și [[http://​127.0.0.1:​5000/​index]]. 
 + 
 +Să trecem pas cu pas prin cod: 
 + 
 +<code python>​ 
 + 
 +from flask import Flask 
 + 
 +app = Flask(__name__) 
 + 
 +</​code>​ 
 + 
 +Snippetul de mai sus importă framework-ul **Flask** și îl inițializează. 
 + 
 +Partea cu adevărat interesantă se întâmplă în liniile următoare:​ 
 + 
 +<code python>​ 
 + 
 +@app.route("/"​) 
 +@app.route("/​index"​) 
 +def hello(): 
 +    return "Hello World from Flask"​ 
 + 
 +</​code>​ 
 + 
 +Serverul nostru este găzduit pe mașina noastră locală, și este accesibil la adresa **127.0.0.1** folosind portul **5000**; deci pagina noastră este accesibilă la adresa [[http://​127.0.0.1:​5000]]. 
 + 
 +Folosind sintaxa **@app.route** definim funcțiile care vor fi apelate în funcție de calea pe care a introdus-o utilizatorul în browser. 
 +Astfel: 
 +  * **@app.route("/"​)** corespunde căii principale către pagina noastră web, [[http://​127.0.0.1:​5000/​]] 
 +  * **@app.route("/​index"​)** corespunde căii [[http://​127.0.0.1:​5000/​index]] 
 + 
 +Pentru ambele căi, atunci când utilizatorul va accesa pagina, serverul va întoarce rezultatul definit de funcție, în cazul de față "Hello World from Flask"​. 
 + 
 +<note tip> 
 + 
 +Sintaxa **@app.route** poartă numele de **decorator** în Python. 
 + 
 +Decoratorii nu intră în scopul laboratorului,​ dar vă încurajăm să citiți mai multe despre aceștia [[https://​pythonguide.readthedocs.io/​en/​latest/​python/​decorator.html | aici]]. 
 + 
 +</​note>​ 
 + 
 +Observăm că putem adăuga mai multe căi pentru o funcție. 
 + 
 +<note warning>​ 
 + 
 +Numele asociat instanței framework-ului Flask este important pentru sintaxa **route**. 
 + 
 +Dacă instanțiam cu alt nume, ex. **myapp = Flask(__name__)**,​ atunci trebuie să folosim acest nume (**@myapp.route**) pentru definirea căilor. 
 + 
 +</​note>​ 
 + 
 +În final, pornim serverul: 
 + 
 +<code python>​ 
 + 
 +    app.run(host='​0.0.0.0',​ debug=True, port=5000) 
 + 
 +</​code>​ 
 + 
 +===== Templates ===== 
 + 
 +În secțiunea anterioară,​ am văzut cum putem face serverul să servească conținut clienților. 
 +Deocamdată conținutul este unul banal: textul "Hello World from Flask"​. 
 +În continuare vom vedea cum putem returna pagini întregi. 
 + 
 +Folosind template-uri putem servi pagini HTML complete, cu CSS și JavaScript. 
 + 
 +Creăm directorul **templates** în directorul **hello-flask**:​ 
 + 
 +<code bash> 
 + 
 +$ cd ~/​hello-flask 
 +$ mkdir templates 
 + 
 +</​code>​ 
 + 
 +Acum vom crea fișierul **templates/​index.html** cu următorul conținut:​ 
 + 
 +<code html index.html>​ 
 + 
 +<​html>​ 
 +    <​head>​ 
 +        <​title>​ {{ title }} </​title>​ 
 +    </​head>​ 
 +    <​body>​ 
 +        <​h1>​Hello,​ {{ user.username }}!</​h1>​ 
 +    </​body>​ 
 +</​html>​ 
 + 
 +</​code>​ 
 + 
 +Fișierul de mai sus este un template [[https://​jinja.palletsprojects.com/​en/​2.11.x/​|Jinja2]]. 
 +Template-urile **Jinja** ne oferă un mod simplu prin care putem să generăm conținut folosind cod python. 
 + 
 +În exemplul de mai sus, textul din interiorul ​ **{****{** ... **}****}** va fi înlocuit cu variabile pe care le vom trimite din codul nostru python. 
 +Pentru aceasta, modificăm funcția **hello()** în modul următor: 
 + 
 +<code python>​ 
 + 
 +@app.route("/"​) 
 +@app.route("/​index"​) 
 +def hello(): 
 +    user = {'​username':​ '​Student'​} 
 +    return render_template('​index.html',​ title='​Home',​ user=user) 
 + 
 +</​code>​ 
 + 
 +Funcția **render_template()** va prelucra template-ul Jinja2 definit mai sus, va înlocui variabilele din cadrul ​ **{****{** ... **}****}** și va oferi clientului pagina HTML validă. 
 + 
 +După ce am făcut modificările,​ accesăm pagina [[http://​127.0.0.1:​5000]]. 
 + 
 + 
 +==== Structuri de control ==== 
 + 
 +În cadrul template-urilor Jinja putem folosi și structuri de control precum **if** și **for**. 
 +Sintaxa pentru acestea este următoarea:​ 
 + 
 +<code html> 
 + 
 +<​html>​ 
 +    <​head>​ 
 +        {% if title %} 
 +        <​title>​{{ title }}</​title>​ 
 +        {% else %} 
 +        <​title>​Welcome</​title>​ 
 +        {% endif %} 
 +    </​head>​ 
 +    <​body>​ 
 +        {% for user in users %} 
 +        <​div><​p>​{{ user.username }}</​p></​div>​ 
 +        {% endfor %} 
 +    </​body>​ 
 +</​html>​ 
 + 
 +</​code>​ 
 + 
 +<note tip> 
 + 
 +În cadrul **{****{** ... **}****}** folosim cod python. 
 + 
 +</​note>​ 
 + 
 +==== Extinderea unor template-uri de bază ==== 
 + 
 +Majoritatea paginilor unei aplicații web conțin componente comune (cum ar fi bara de navigație). 
 +Pentru a evita scrierea repetată a componentelor comune în fiecare pagină, putem folosi **template inheritance**:​ componentele comune sunt scrise într-un template de bază care este apoi extins de fiecare pagină. 
 + 
 +Pentru aceasta, în directorul **~/​hello-flask/​templates** definim fișierul **base.html**,​ ca mai jos: 
 + 
 +<code html base.html>​ 
 + 
 +<​html>​ 
 +    <​head>​ 
 +      {% if title %} 
 +      <​title>​{{ title }}</​title>​ 
 +      {% else %} 
 +      <​title>​Welcome</​title>​ 
 +      {% endif %} 
 +    </​head>​ 
 +    <​body>​ 
 +        <​div><​a href="/​index">​Home</​a></​div>​ 
 +        <​hr>​ 
 +        {% block content %}{% endblock %} 
 +    </​body>​ 
 +</​html>​ 
 + 
 +</​code>​ 
 + 
 +Fișierul template de mai sus este acum baza noastră. 
 +El definește structura generală a paginii. 
 +Orice pagină nouă o să extindă această bază și apoi trebuie să definească doar blocul **content**. 
 + 
 +Astfel, modificăm fișierul **~/​hello-flask/​templates/​index.html** ca mai jos: 
 + 
 +<code html index.html>​ 
 + 
 +{% extends "​base.html"​ %} 
 + 
 +{% block content %} 
 +    <​h1>​Hello,​ {{ user.username }}!</​h1>​ 
 +{% endblock %} 
 + 
 +</​code>​ 
 + 
 +===== Bootstrap ===== 
 + 
 +[[https://​getbootstrap.com/​docs/​5.0/​getting-started/​introduction/​|Bootstrap]] este un framework foarte popular folosit pentru a crea interfețe cu utilizatorul (UI). 
 + 
 +Pentru a folosi **Bootstrap**,​ adăugăm următoarele linii în fișierul **base.html** 
 + 
 +<code html base.html>​ 
 + 
 +<​html>​ 
 +    <​head>​ 
 +        <meta charset="​utf-8">​ 
 +        <meta name="​viewport"​ content="​width=device-width,​ initial-scale=1">​ 
 + 
 +        <link href="​https://​cdn.jsdelivr.net/​npm/​bootstrap@5.0.0-beta1/​dist/​css/​bootstrap.min.css"​ rel="​stylesheet"​ crossorigin="​anonymous">​ 
 +        <link href="​https://​cdn.jsdelivr.net/​npm/​bootstrap-icons@1.3.0/​font/​bootstrap-icons.css"​ rel="​stylesheet">​ 
 + 
 +        <script src="​https://​cdn.jsdelivr.net/​npm/​@popperjs/​core@2.5.4/​dist/​umd/​popper.min.js"​ crossorigin="​anonymous"></​script>​ 
 +        <script src="​https://​cdn.jsdelivr.net/​npm/​bootstrap@5.0.0-beta1/​dist/​js/​bootstrap.min.js"​ crossorigin="​anonymous"></​script>​ 
 + 
 +        {% if title %} 
 +        <​title>​{{ title }}</​title>​ 
 +        {% else %} 
 +        <​title>​Welcome</​title>​ 
 +        {% endif %} 
 +    </​head>​ 
 +    <​body>​ 
 +        <​div><​a href="/​index">​Home</​a></​div>​ 
 +        <​hr>​ 
 +        {% block content %}{% endblock %} 
 +    </​body>​ 
 +</​html>​ 
 + 
 +</​code>​ 
 + 
 +Acum putem folosi Bootstrap pentru a ne înfrumuseța pagina. 
 +Începem prin a centra textul din fișierul **index.html**:​ 
 + 
 +<code html index.html>​ 
 + 
 +{% extends "​base.html"​ %} 
 + 
 +{% block content %} 
 +    <h1 class="​text-center">​Hello,​ {{ user.username }}!</​h1>​ 
 +{% endblock %} 
 + 
 +</​code>​ 
 + 
 +Observăm că a fost necesar doar să adăugăm clasa **text-center** elementului **h1**. 
 +Acesta este modul de funcționare Bootstrap: tot ce trebuie făcut este să includem elementele în clasa pe care ne-o dorim. 
 + 
 +Bootstrap împarte (imaginar) pagina în 12 [[https://​getbootstrap.com/​docs/​5.0/​layout/​grid/​|coloane]]. 
 +Noi apoi decidem, prin intermediul atributului **class**, [[https://​getbootstrap.com/​docs/​5.0/​layout/​grid/​|câte din aceste coloane vor ocupa elementele noastre]]. 
 + 
 +De exemplu: 
 + 
 +<code html> 
 + 
 +<div class="​container">​ 
 +  <div class="​row">​ 
 +    <div class="​col-sm">​ 
 +      One of three columns 
 +    </​div>​ 
 +    <div class="​col-sm">​ 
 +      One of three columns 
 +    </​div>​ 
 +    <div class="​col-sm">​ 
 +      One of three columns 
 +    </​div>​ 
 +  </​div>​ 
 +</​div>​ 
 + 
 +</​code>​ 
 + 
 +Exemplul de mai sus va împărți lățimea rândului în mod egal între cele trei coloane. 
 + 
 +==== Bootstrap Icons ==== 
 + 
 +Bootstrap ne oferă și un set de imagini SVG pe care le putem folosi pe post de [[https://​icons.getbootstrap.com/​|icons]]. 
 + 
 +Ca să înlocuim textul **Home** din fișierul **base.html** cu un icon, trebuie să: 
 +  * identificăm icon-ul unei [[https://​icons.getbootstrap.com/​icons/​house/​|case]] 
 +  * înlocuim textul **Home** cu textul din **Icon font**. 
 + 
 +Astfel, textul 
 +    
 +<code html> 
 +<​div><​a href="/​index">​Home</​a></​div>​ 
 +</​code>​ 
 +     
 +Devine 
 +     
 +<code html> 
 +<​div><​a href="/​index"><​i class="​bi bi-house"></​i></​a></​div>​ 
 +</​code>​ 
 + 
 + 
 +===== Exerciții ===== 
 + 
 +O să scrieți o aplicație web care are rolul unui CV. 
 +CV-ul trebuie să conțină secțiunile:​ Work Experience, Education, Projects și Languages and Technologies 
 + 
 +1. **[10]** Aplicația trebuie să aibă două pagini: Home (CV-ul) și Contact 
 + 
 +2. **[30]** Informațiile secțiunilor din CV sunt populate de către server, și nu sunt scrise în template-ul Jinja. 
 +Informațiile secțiunilor sunt stocate sub formă de dicționar. 
 +De exemplu: 
 +<code python>​ 
 +work_exp = [ { position: "​swe",​ company: "​xyz",​ period: "Jun 2020 - Oct 2020" } ] 
 +</​code>​ 
 + 
 +3. **[10]** Folosiți icon-uri pentru Home și Contact 
 + 
 +4. **[20]** Folosiți componenta Bootstrap [[https://​getbootstrap.com/​docs/​5.0/​components/​navbar/​|Navbar]] pentru butoanele Home și Contact 
 + 
 +5. **[30]** Pagina de contact trebuie să conțină și un formular prin intermediul căruia un utilizator să vă poată contacta. 
 +Formularul trebuie să conțină câmpurile: Nume, Adresă de e-mail și Mesaj. 
 +Atunci când utilizatorul apasă butonul **Trimite** al formularului,​ serverul flask vă va //[simula că]// trimite un e-mail cu informațiile formularului prin scrierea unui fișier pe server (e.g., ''​contact-mail.txt''​).
ii/lab/laborator5.1475420525.txt.gz · Last modified: 2016/10/02 18:02 by alexandru.radovici
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0