0% au considerat acest document util (0 voturi)
2 vizualizări123 pagini

06 Web Application Building

Documentul oferă o prezentare generală a protocoalelor de nivel aplicație, cu un accent special pe FTP și HTTP. FTP permite transferul de fișiere între mașini printr-o conexiune de control și una de date, utilizând porturile 21 și 20, respectiv. De asemenea, se discută despre alte protocoale de nivel aplicație, cum ar fi SMTP și DNS, și funcțiile acestora în comunicarea pe internet.

Încărcat de

Sparrow Jack
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
2 vizualizări123 pagini

06 Web Application Building

Documentul oferă o prezentare generală a protocoalelor de nivel aplicație, cu un accent special pe FTP și HTTP. FTP permite transferul de fișiere între mașini printr-o conexiune de control și una de date, utilizând porturile 21 și 20, respectiv. De asemenea, se discută despre alte protocoale de nivel aplicație, cum ar fi SMTP și DNS, și funcțiile acestora în comunicarea pe internet.

Încărcat de

Sparrow Jack
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd

Contents

1.1. Protocoale de nivel aplicaţie .......................................................................................................... 4


Protocolul FTP ........................................................................................................................................... 6
Metodele FTP ............................................................................................................................................. 7
Coduri şi mesaje de stare FTP ................................................................................................................. 7
FTP-ul anonim (Anonymous FTP) .......................................................................................................... 8
Citirea directorului FTP folosind Python ............................................................................................. 8
Încărcarea fişierului pe serverul FTP (upload) ................................................................................. 10
1.2. Parametrizarea HTTP ..................................................................................................................... 11
Versiunea HTTP ....................................................................................................................................... 11
Identificatorul uniform de resurse – URI (Uniform Resource Identifier) .................................... 12
Data şi ora ................................................................................................................................................. 12
Tipuri de conţinut ................................................................................................................................... 14
Marcaje lingvistice .................................................................................................................................. 14
1.3. Cookie-uri şi sesiuni ....................................................................................................................... 15
Cookie-urile (Cookies) ........................................................................................................................... 15
Sesiunea .................................................................................................................................................... 16
Setarea cookie-urilor folosind biblioteca requests .......................................................................... 16
CookieJar ................................................................................................................................................... 17
Sesiuni cu ajutorul bibliotecii requests .............................................................................................. 17
1.4. Gestionarea bazei de date ............................................................................................................ 19
Teoria bazelor de date ........................................................................................................................... 19
Caracteristicile SQLite ............................................................................................................................ 20
Adăugarea datelor în baza de date ...................................................................................................... 21
Citirea din baza de date .......................................................................................................................... 23
1.5. Şablonul MVC .................................................................................................................................. 25
Conexiunea ............................................................................................................................................... 25
Componentele .......................................................................................................................................... 25
Modelul...................................................................................................................................................... 25
Vizualizarea .............................................................................................................................................. 26
Controlerul ............................................................................................................................................... 26
Avantajele modelului MVC: ................................................................................................................... 27
Dezavantajele utilizării MVC: ................................................................................................................ 27
1.6. Localizarea ........................................................................................................................................ 30
Avantajele localizării conţinutului ...................................................................................................... 30
Localizarea pe partea de server ........................................................................................................... 31
2.1. Limbajul de programare JavaScript............................................................................................ 33
Iniţializarea funcţiilor şi a variabilelor .............................................................................................. 33
Parametrii de intrare în funcţii ............................................................................................................ 35
JavaScript şi HTML .................................................................................................................................. 35
Accesul la variabilele externe şi la funcţiile încorporate ................................................................ 36
2.2. Document Object Model ............................................................................................................... 38
Ce este DOM? ............................................................................................................................................ 38
Adăugarea clasei în elementul HTML .................................................................................................. 40
Alăturarea unui eveniment la elementul HTML ................................................................................ 41
2.3. AJAX ................................................................................................................................................... 43
Trimiterea cererilor GET ....................................................................................................................... 46
Trimiterea cererilor POST ..................................................................................................................... 48
2.4. Biblioteca Bootstrap ....................................................................................................................... 49
Ce este Bootstrap? ................................................................................................................................... 49
Avantajele utilizării Bootstrap ............................................................................................................. 50
Structura Bootstrap ................................................................................................................................ 50
Configurarea proiectului Bootstrap .................................................................................................... 52
Verificarea funcţionării Bootstrap....................................................................................................... 52
Grila Bootstrap ........................................................................................................................................ 53
Cum funcţionează grila Bootstrap? ...................................................................................................... 53
Setarea grilei Bootstrap ......................................................................................................................... 57
Alinierea orizontală ................................................................................................................................ 57
Alinierea verticală a coloanelor ........................................................................................................... 58
Mutarea coloanelor în cadrul rândului ............................................................................................... 59
Influenţarea vizibilităţii coloanelor .................................................................................................... 60
2.5. Biblioteca jQuery............................................................................................................................. 66
Importarea bibliotecii jQuery ............................................................................................................... 66
Sintaxa jQuery – selectorii ..................................................................................................................... 67
jQuery – gestionarea evenimentelor ................................................................................................... 69
Crearea elementelor HTML noi ............................................................................................................ 71
Eliminarea elementului întreg – remove() ........................................................................................ 74
POST request ............................................................................................................................................ 78
3.1. Mediul şi ciclul de viaţă ................................................................................................................. 80
Avantajele Django ................................................................................................................................... 81
Crearea primului proiect Django ......................................................................................................... 82
Intermediarii Django .............................................................................................................................. 84
Nivelurile aplicaţiei Django şi ciclul de viaţă al fluxului de cerere-răspuns ............................... 86
3.2. Lucrul cu vizualizări şi şabloane .................................................................................................. 87
Vizualizările ca clase .............................................................................................................................. 87
Vizualizările ca funcţii ............................................................................................................................ 87
Implementarea vizualizării ................................................................................................................... 87

Page 2 of 123
Şabloanele Django (templates) ............................................................................................................ 91
Variabilele ................................................................................................................................................ 93
Tagurile/logica de control ..................................................................................................................... 93
Filtrele ....................................................................................................................................................... 93
Comentariile ............................................................................................................................................. 93
3.3. Maparea cererilor............................................................................................................................ 95
Funcţiile pentru lucrul cu maparea URL-urilor................................................................................. 96
Exemplu de utilizare a convertorului de cale .................................................................................... 96
3.4. Procesarea formularelor .............................................................................................................. 100
Formularele Django .............................................................................................................................. 101
4.1. Publicarea aplicaţiei web Python .............................................................................................. 104
4.2. Securizarea, testarea şi echilibrarea încărcării aplicaţiei ..................................................... 111
Versiunea ................................................................................................................................................ 111
Autentificarea ........................................................................................................................................ 111
Codul sursă al site-ului ......................................................................................................................... 112
Folosirea antetului (headerului)........................................................................................................ 112
Încărcarea (upload-ul) fişierelor ....................................................................................................... 112
Comanda Check...................................................................................................................................... 112
Testarea aplicaţiei ................................................................................................................................ 115
Testarea în Django ................................................................................................................................ 115
Echilibrarea încărcărilor (Load Balancer) ....................................................................................... 119
Durata sesiunii ....................................................................................................................................... 120
Modul de funcţionare a echilibrării ................................................................................................... 120
Tipuri de echilibrare ............................................................................................................................ 120
Echilibrarea software şi hardware .................................................................................................... 121
Avantajele şi dezavantajele echilibrării ........................................................................................... 121
Rolul componentei de echilibrare ..................................................................................................... 121
Niveluri de reţea şi partajarea încărcărilor ..................................................................................... 121
Algoritmi de echilibrare ...................................................................................................................... 122

Page 3 of 123
1.1. Protocoale de nivel aplicaţie

Rezumat
• Nivelul aplicaţie este cel mai înalt nivel al modelului OSI.
• Cel mai utilizat protocol al nivelului aplicaţie este HTTP, care se bazează pe protocolul de
transport TCP.
• Protocolul de nivel aplicaţie defineşte tipul mesajului, sintaxa mesajului, semnificaţia câmpului,
regulile şi condiţiile din timpul comunicării.
• Protocolul FTP se ocupă cu trimiterea şi primirea fişierelor online.
• Protocolul FTP se bazează pe protocolul TCP.
• Pentru a trimite fişiere prin FTP, sunt create două conexiuni: o conexiune de control şi o
conexiune pentru transferul fişierului în sine.
• Cu conexiunea de control autorizăm clientul pentru activarea conexiunii FTP.
• Conexiunea de control are loc pe portul 20, iar conexiunea de date pe portul 21.
• FTP-ul anonim este utilizat, în principal, pentru directoare şi fişiere care sunt disponibile public
şi în domeniul public.

Nivelul aplicaţie este al şaptelea, adică cel mai înalt nivel al modelului OSI. Toată manipularea directă a
datelor şi a informaţiilor are loc la acest nivel şi, astfel, permite accesul la software şi utilizator. Câteva
dintre serviciile furnizate de acest nivel sunt: e-mail, trimiterea şi primirea de fişiere, resurse de reţea,
distribuirea datelor către utilizator etc.

Acest nivel constă din diferite tipuri de protocoale. Unul dintre acestea este răspânditul HTTP (HyperText
Transfer Protocol), care stă la baza World Wide Web-ului. Ori de câte ori deschidem o pagină în
browserul nostru, acesta trimite numele paginii dorite şi locaţia acesteia către server, care utilizează
HTTP, iar serverul o trimite înapoi. În această lecţie, pe lângă faptul că ne vom familiariza cu protocoalele
de nivel aplicaţie, ne vom familiariza şi cu FTP-ul prin lucrări practice. Folosind biblioteca Python ftplib,
vom lista folderele şi fişierele de pe un server FTP extern, vom naviga în structură şi vom descărca şi
încărca fişiere.

Notă:

World Wide Web şi HTTP sunt strâns legate. Anume, World Wide Web este un sistem informaţional în care
documentele şi alte resurse sunt adresate folosind URL-uri (Uniform Resource Locators), care sunt livrate şi
trimise prin HTTP. Astfel, World Wide Web nu este internetul în sine, ci doar o parte a acestuia, deşi cei doi
termeni sunt adesea echivalaţi.

Alte protocoale de nivel aplicaţie sunt:

• FTP (File Transfer Protocol) - îi permite utilizatorului să trimită şi să recupereze fişiere de pe o


maşină la distanţă. Foloseşte protocolul TCP. La conectarea la un dispozitiv la distanţă, se
realizează două conexiuni: conexiunea de date şi conexiunea de control. Conexiunea de date
utilizează portul 20, iar conexiunea de control utilizează portul 21. O conexiune (de date) este
utilizată exclusiv pentru transferul de date, în timp ce cealaltă (de control) este utilizată pentru
a trimite comenzi şi a obţine răspunsuri.

• SMTP (Simple Mail Transfer Protocol) - îi permite utilizatorului să trimită şi să primească e-


mailuri. Foloseşte protocolul TCP. Protocolul SMTP are două părţi – client şi server şi foloseşte
portul 25.

• TELNET (remote login) - acces la distanţă care îi permite utilizatorului să trimită comenzi de la
o maşină la alta - printr-un terminal. Foloseşte protocolul TCP. Face acest lucru folosind

Page 4 of 123
sistemul NVT (Network Virtual Terminal) care codifică comenzile trimise pe partea de client şi
le decodează pe partea de server. Portul pe care îl foloseşte este 23.

• DNS (Domain Name System) - acest sistem ne permite să mapăm adresele IP cu numele site-
urilor către care se adresează acele adrese. DNS este clasificat în trei categorii:

o domeniul generic - acest domeniu se referă la comportamentul site-ului - indiferent


dacă este un site guvernamental (.gov), un site al unei organizaţii (.org) sau al unei
instituţii de învăţământ (.edu); în acest domeniu sunt utilizate trei caractere;
o domeniul ţării - acest domeniu implică o abreviere din două litere a ţării căreia îi
aparţine site-ul (.md, .ro, .us); în acest domeniu sunt utilizate două caractere;
o domeniul invers - acest domeniu este utilizat pentru maparea adresei IP la numele site-
ului.

Unele dintre aceste protocoale utilizează UDP, iar altele utilizează TCP ca protocol de transport.
Clasificarea protocoalelor de nivel aplicaţie, în funcţie de protocoalele de transport pe care le utilizează,
se poate vedea în imaginea de mai jos:

Imaginea 1.1. Clasificarea protocoalelor de nivel aplicaţie conform protocoalelor de transport pe care
sunt bazate

Funcţiile nivelului aplicaţie:

• e-mailul - acest nivel oferă baza pentru trimiterea, primirea şi stocarea e-mailurilor;
• terminalele virtuale de reţea - îi permit utilizatorului să se conecteze la un dispozitiv la distanţă;
aplicaţia emulează un terminal pe acel dispozitiv la distanţă astfel încât comunicarea între
utilizator şi acel dispozitiv final să se realizeze prin terminal;
• transferul de fişiere - utilizatorul are posibilitatea să acceseze şi să gestioneze un fişier la distanţă,
precum şi să îl poată adăuga;
• procesele de la ambele părţi (dispozitive iniţiale şi de destinaţie) sunt ultimele din lanţul de
comunicare.

Un protocol de nivel aplicaţie defineşte modul în care procesele de pe un anumit dispozitiv (sau două
dispozitive) îşi transferă informaţiile reciproc. Astfel, protocolul nivelului aplicaţie defineşte:

• tipul de mesaj - indiferent dacă sunt mesaje ca cereri sau mesaje ca răspunsuri;
• sintaxa mesajului - care sunt câmpurile din mesaje şi cum sunt delimitate aceste câmpuri;
• sensul câmpului – semnificaţia şi selecţia informaţiilor pe care le va conţine câmpul;
• termeni şi condiţii – cum şi când va trimite procesul mesaje şi va răspunde la mesaje.

Page 5 of 123
Protocolul FTP

Protocolul FTP este un protocol pentru transferul de fişiere într-un director dorit de la o maşină la alta.
Bazele acestui protocol datează din anul 1971.

O sesiune FTP normală începe cu un utilizator care doreşte să-i trimită altui utilizator un fișier. Pentru a
trimite fişiere, utilizatorul trebuie să trimită către server identificarea şi parola pentru autorizare. După
autorizarea cu succes, utilizatorul poate trimite fişiere către server şi invers.

HTTP şi FTP sunt destul de similare, deoarece ambele se bazează pe un transfer securizat de date şi
ambele se bazează pe protocolul TCP. Diferenţele dintre aceste două protocoale se reflectă în faptul că
FTP necesită două conexiuni paralele pentru transferul de fişiere - o conexiune de control (control
connection) şi o conexiune de date (data connection). Conexiunea de control este utilizată pentru a
identifica şi autoriza clientul cu serverul, pentru a trimite o metodă FTP care permite numai trimiterea
sau recuperarea unui fişier, schimbarea unui director la distanţă etc. Conexiunea de date este, de fapt,
utilizată pentru a trimite fişierul în sine, mai precis conţinutul acestuia.

Imaginea 1.2. Diagrama conexiunii FTP1

Page 6 of 123
Când începe o sesiune FTP cu serverul, clientul stabileşte mai întâi o conexiune de control TCP pe portul
21. Imediat după aceea, clientul trimite identificarea şi parola prin conexiunea de control. După ce a fost
autorizat cu succes de server, clientul trimite comenzi pentru a prelua fişierul dorit sau pentru a schimba
directorul la distanţă pentru a accesa fişierul dorit, pentru a crea un director nou şi aşa mai departe.
După ce utilizatorul (clientul) solicită transferul de fişiere (fie că este trimis la server sau de la server),
protocolul FTP deschide o conexiune TCP pentru transferul de fişiere, care are loc pe portul 20. Pe o
conexiune de transfer poate fi trimis doar un fişier. Dacă în aceeaşi sesiune utilizatorul doreşte să trimită
sau să livreze un alt fişier, se deschide o nouă conexiune de date TCP, astfel încât conexiunea de control
rămâne deschisă pe tot parcursul sesiunii. De asemenea, pentru întreaga sesiune, serverul trebuie să
ţină cont de clientul căruia i se atribuie conexiunea de control.

Metodele FTP

Metodele (comenzile) FTP trimise către server constau, de obicei, din patru litere, unde numele fiecăreia
dintre aceste metode este de şapte biţi în format ASCII. Câteva dintre aceste metode sunt:
METODĂ DESCRIERE
metodă prin care îi trimitem numele utilizatorului care doreşte să se conecteze la server şi
USER
să iniţializeze o conexiune FTP
metodă prin care îi trimitem parola utilizatorului care doreşte să se conecteze la server şi
PASS
să iniţializeze o conexiune FTP
metodă prin care clientul trimite informaţii către server (trimiterea unui formular de
autentificare/login)
CWD metodă care îi permite utilizatorului să schimbe directorul la distanţă
RMD metodă care îi permite utilizatorului să şteargă directorul dorit
metodă care îi permite utilizatorului să iniţializeze o conexiune de date pentru a primi (sau
RETR
trimite) un fişier
STOR metodă care îi permite utilizatorului să stocheze fişierul în directorul dorit
LIST metodă care îi permite utilizatorului să listeze fişiere în directorul curent
metodă prin care clientul instruieşte serverul să termine conexiunea curentă pentru a
ABOR
trimite date
QUIT metodă prin care clientul termină conexiunea de control cu serverul

Tabelul 1.1. Reprezentare tabelară a metodelor FTP

Coduri şi mesaje de stare FTP


Fiecare cerere către server trimite un cod specific căruia îi corespunde un anumit mesaj de stare. Câteva
dintre aceste coduri şi mesaje care le aparţin sunt:
Cod de stare Mesaj de stare
200 Command okay
221 Service closing control connection
225 Data connection open; no transfer in progress
331 Username okay, need a password
502 Command not implemented
503 Bad sequence of commands
504 Command not implemented for that parameter
530 Not logged in
551 Requested action aborted: page type unknown

Tabelul 1.2. Reprezentare tabelară a codurilor şi a mesajelor de stare FTP

Page 7 of 123
FTP-ul anonim (Anonymous FTP)

FTP-ul anonim este un subtip al protocolului FTP care nu necesită autorizarea clientului, deoarece
numele de utilizator şi parola sunt setate la anonimul implicit (default anonymous). FTP-ul anonim este
utilizat, în principal, pentru directoare şi fişiere care sunt disponibile public şi în domeniul public.

Citirea directorului FTP folosind Python

Conectarea la un server FTP în Python se face folosind biblioteca ftplib încorporată. Pentru o mai mare
simplitate, vom folosi un server disponibil public (FTP-ul anonim) [Link], al cărui nume de
utilizator pentru autentificare este anonymous, în timp ce parola se transmite ca un câmp gol.
Conectarea la acest server se face, pur şi simplu, utilizând clasa FTP a acestei biblioteci:

Lucrul cu serverul FTP ([Link]):


1from ftplib import FTP
2ftp = FTP('[Link]')
[Link]('anonymous','')
[Link]()
[Link]('upload')
6print('Content of folder: upload')
[Link]()
[Link]()

Imaginea 1.3. Lista de fişiere ale directorului rădăcină şi ale directorului upload

Page 8 of 123
Prin crearea unei instanţe a clasei FTP, am activat conexiunea la [Link] folosind
anonymous ca nume de utilizator prin metoda login() a acestei clase. În cazul în care am fi transmis
acreditările greşite, această bibliotecă ne-ar fi listat o eroare getaddrinfo. După o conexiune reuşită, în
domeniul autorizării contului cu care ne-am autentificat (în acest caz, numele contului este anonymous),
putem să citim şi să descărcăm fişiere şi să accesăm foldere. Modificarea fişierelor existente pe server
sau trimiterea fişierelor noi către server, nu este permisă atunci când utilizaţi acest server. În
liniile/rândurile listate ale metodei dir() vedem două listări diferite: una legată de fişiere (de exemplu: -
rw-r--r-- 1 0 0 524288 Feb 19 2016 [Link], -rw-r --r-- 1 0 0 1048576 Feb 19 2016 [Link]) şi altele
legate de directoare: drwxr-xr-x 2 105 108 77824 Aug 24 01:51 upload (în mod specific, această linie se
referă la folderul upload). Dacă vrem să ne poziţionăm şi să listăm exact acel folder upload, folosim
metoda cwd(‘upload’), căreia îi transmitem calea către folderul pe care dorim să-l accesăm, care îi
corespunde uneia dintre metodele de protocol FTP discutate mai devreme în lecţie. După ce aţi terminat
de lucrat cu clasa FTP, este o practică bună să închideţi manual conexiunea la serverul FTP folosind
comanda [Link] ().

Pentru mai multe informaţii despre metodele disponibile, consultaţi documentaţia oficială.

Descărcarea unui fişier prin protocolul FTP (download)

Prin biblioteca FTP putem descărca fişiere conform cerinţelor specificaţiilor protocolului FTP. Folosim
comanda RETR a protocolului FTP pentru a descărca fişiere. Folosim această comandă împreună cu
metoda obiectului de sesiune FTP retrbinary, căruia îi transmitem un string în formatul fişierului RETR
numele_fişierului_pe_care_îl_descărcăm, precum şi metoda write asupra obiectului file, de care avem
nevoie pentru a stoca conţinutul fişierului de pe server într-un fişier local: [Link], care este
fişierul pentru descărcarea fişierului de pe server:

Fişierul [Link] pentru descărcarea fişierului de pe server:


1from ftplib import FTP
2ftp = FTP('[Link]')
[Link]('anonymous','')
4local_filename = open('[Link]', "wb")
[Link]("RETR {}".format('[Link]'), local_filename.write)
6local_filename.close()
[Link]()

În exemplul anterior (exemplu de navigare prin director) am văzut ce fişiere sunt disponibile pentru noi
în directorul rădăcină al serverului. Din această listă putem selecta orice fişier de preluare. Vom selecta
un fişier numit [Link].

Acest exemplu funcţionează deschizând mai întâi fişierul local (un fişier pe discul nostru) în care dorim
să stocăm conţinutul de pe server. L-am denumit la fel ca fişierul pe care dorim să-l descărcăm - [Link].
Îl deschidem în modul binar - wb. Acum, folosim metoda retrbinary a obiectului de sesiune FTP către
care transmitem stringul RETR [Link], precum şi metoda write a fişierului deschis. În acest fel,
transmiterea funcţiei ca parametru se numeşte funcţie callback. După execuţia liniei [Link](..),
fişierul se află acum pe discul nostru şi putem închide în siguranţă şi fişierul nostru local_filename deschis.

Este important să reţineţi că, cu acest server FTP special – [Link] – fişierele stocate
sunt goale şi că orice încercare de a le deschide şi citi după ce le-am furnizat, va duce la o eroare. În
general, acesta nu este cazul, iar fişierele descărcate în acest fel pot fi deschise. Acest lucru se aplică
numai acestui server.

Page 9 of 123
Încărcarea fişierului pe serverul FTP (upload)

Încărcarea (upload-ul) fişierului se face în conformitate cu un principiu similar cu cel al descărcării


(download). Comanda protocolului FTP pentru încărcarea unui fişier este STOR, deci metoda obiectului
sesiunii FTP pe care o vom folosi este storbinary; îi redirecţionăm un string în următorul format: STOR
numele_fişierului_pe_care_îl_încărcăm, precum şi un obiect de fişier deschis:

Fişierul [Link] pentru încărcarea fişierului pe server


1from ftplib import FTP
2ftp = FTP('[Link]')
[Link]('anonymous','')
[Link]('upload')
5to_upload = open('[Link]','rb') # file to send
[Link]('STOR [Link]', to_upload) # send the file
7to_upload.close()
[Link]()
Configurarea serverului [Link] este astfel încât fişierele pot fi încărcate numai în
folderul upload situat în directorul rădăcină al serverului. După încărcarea fişierelor, acestea sunt şterse
imediat, iar acest lucru se poate citi în instrucţiunile oficiale ale acestui serviciu.

Prin urmare, mai întâi trebuie să ne poziţionăm în folderul upload folosind comanda [Link](‘upload’).
După aceea, deschidem pentru citire fişierul pe care vrem să-l trimitem (mai întâi trebuie să-l citim).
Fişierul pe care l-am deschis aici este, de fapt, scriptul nostru numit [Link]. Pentru a trimite acest
fişier în directorul upload, folosim metoda storbinary, căreia îi transmitem un string care reprezintă
comanda FTP: STOR [Link], precum şi obiectul fişierului deschis to_upload. Executând această
linie, am încărcat cu succes fişierul pe server. Deoarece configuraţia serverului necesită acest lucru, acest
fişier va fi şters, dar în condiţii reale fişierul va fi păstrat pe serverul pe care a fost plasat. Dacă încărcarea
fişierului eşuează, vom primi o eroare, deci, dacă, de exemplu, nu suntem poziţionaţi în singurul folder
de pe server unde pot fi încărcate fişierele de utilizator – folderul upload, vom primi eroarea
ftplib.error_perm: 553 Could not create file, ceea ce ne spune că nu avem permisiuni adecvate pentru
încărcare. Mai multe detalii despre codurile de stare şi erorile care pot apărea, puteţi găsi la acest link.

1. [Link]

Page 10 of 123
1.2. Parametrizarea HTTP

Rezumat
• Parametrii HTTP şi sintaxa acestora sunt definiţi clar în documentaţia protocolului în sine.

• Sintaxa pentru transmiterea versiunii HTTP este de aşa natură încât versiunea protocolului este
transmisă prima, apoi subversiunea (majoră şi minoră) a protocolului, separată printr-un punct.

• Un identificator uniform de resurse identifică o pagină după locaţie, nume sau ambele criterii
împreună.
• URI-ul constă dintr-un URL (identificator unic de locaţie) şi un URN (identificator unic de nume).
• Standardul HTTP acceptă trei formate de date diferite:

o Sun, 26 Jul 2020 08:49:37 GMT


o Sunday, 26-Jul-20 08:49:37 GMT
o Sun Jul 26 08:49:37 2020

• URI-urile pot fi afişate ca nişte căi absolute sau relative, conform standardului HTTP.
• Informaţiile despre setul de caractere sunt trimise prin câmpul de antet Accept-Charset.
• Codificarea conţinutului ne arată ce algoritm de codificare a fost utilizat pentru a codifica
conţinutul înainte ca acesta să fie trimis şi transmis prin câmpul de antet Accept-Encoding.
Scopul acestui proces este de a comprima întregul conţinut (al documentului) care este trimis,
fără a pierde datele originale.
• La tipurile de conţinut se referă câmpul de antet, care indică formatul conţinutului trimis.
Câmpul de antet în care sunt trimise informaţii despre tipul de conţinut este Content-Type.
• Marcajele lingvistice sunt trimise în câmpurile de antet Accept-Language sau Content-
Language.

Parametrii HTTP şi sintaxa lor sunt definiţi clar în documentaţia protocolului în sine, deci, există un set
special de reguli pe care parametrii trebuie să le urmeze pentru a fi transmişi cu succes. Aceşti
parametri, care sunt trimişi în antetul de răspuns, sunt importanţi pentru noi pentru a standardiza
fluxul şi formatul informaţiilor în timpul cererii/răspunsului.

Versiunea HTTP

Sintaxa pentru transmiterea versiunii HTTP este de aşa natură încât prima versiune a protocolului este
transmisă, iar apoi subversiunea (majoră şi minoră) protocolului este separată cu un punct (elementele
dintre ghilimele sunt părţi ale sintaxei; spaţiile dintre elemente dintre ghilimele nu fac parte din sintaxă):

Sintaxa valorii versiunii HTTP:

1"HTTP" "/" 1* "." 1*

Exemplu:

1HTTP/2.4
2HTTP/2.13

În acest exemplu, se poate observa că numerele de versiune şi subversiune sunt tratate ca numere
separate şi aceste numere pot fi mărite astfel încât, în loc de o cifră, să avem două (ceea ce se poate

Page 11 of 123
vedea şi în exemplu). De asemenea, conform standardului HTTP, zerourile din numerele de versiune (01,
02...) nu sunt trimise.

O aplicaţie care trimite o cerere sau un răspuns care include o valoare a antetului versiunii HTTP, cum
ar fi HTTP/1.1, trebuie să fie proiectată pentru a suporta acea versiune la maximum.

Identificatorul uniform de resurse – URI (Uniform Resource Identifier)

Identificatorul uniform de resurse identifică pagina în funcţie de locaţie, nume sau ambele criterii
împreună, deci se poate spune că URI constă dintr-un URL (identificator unic de locaţie) şi un URN
(identificator unic de nume). URI-urile pot fi afişate ca nişte căi absolute sau relative, conform
standardului HTTP. Aceste două forme diferă prin faptul că aceste căi URI absolute încep întotdeauna
cu numele schemei (http, https, ftp), urmate de două puncte. Protocolul HTTP în sine nu impune o limită
a lungimii URI-urilor, iar serverele trebuie să fie proiectate pentru a funcţiona cu astfel de scenarii. De
asemenea, serverele trebuie să poată returna eroarea 414 - URI Too Long dacă lungimea URI este mai
mare decât cea pentru care serverele sunt proiectate să funcţioneze.

Schema http sau https ne spune că este vorba de un protocol HTTP. Sintaxa adresei URL arată astfel
(elementele din paranteze sunt opţionale):

1"http:" "//" host [ ":" număr port ] [ cale_absolută [ "?" interogare ]]

Conform specificaţiei protocolului HTTP, dacă numărul portului nu este transmis, se utilizează portul
implicit 80. Deşi este posibil să se utilizeze o adresă IP ca host, se practică evitarea unor astfel de scenarii
ori de câte ori este posibil. În cazul în care calea absolută este omisă, aceasta trebuie înlocuită cu
caracterul / din URL-ul în sine. Interogările sunt transmise în conformitate cu regulile prin care
funcţionează stringurile de interogare, care au fost deja discutate în cursul Python Net Programming.

Data şi ora

Standardul HTTP acceptă trei formate de date diferite:

• Sun, 26 Jul 2020 08:49:37 GMT


• Sunday, 26-Jul-20 08:49:37 GMT
• Sun Jul 26 08:49:37 2020

Primul format se preferă ca un fel de standard de internet, acesta fiind un format cu lungime fixă. Al
doilea format este, de asemenea, utilizat pe scară largă, dar dezavantajul său este că anii sunt transmişi
ca numere din două cifre. Clienţii şi serverele bazate pe versiunea HTTP/1.1 a protocolului trebuie să
accepte analiza corectă a tuturor celor trei formate pentru compatibilitatea cu HTTP/1.0, dar trebuie să
genereze primul format doar când vine vorba de data şi ora din antet. De asemenea, toate marcajele de
timp din HTTP trebuie să fie prezentate în Greenwich Mean Time. Numele zilelor săptămânii, precum şi
numele lunilor, sunt sensibile la majuscule şi minuscule.

Sintaxa pentru datele preluate direct din documentaţia protocolului arată astfel:

Sintaxa valorii câmpului Content-Type:


1HTTP-date = rfc1123-date | rfc850-date | asctime-date
2rfc1123-date = wkday "," SP date1 SP time SP "GMT"
3rfc850-date = weekday "," SP date2 SP time SP "GMT"
4asctime-date = wkday SP date3 SP time SP 4DIGIT

Page 12 of 123
5date1 = 2DIGIT SP month SP 4DIGIT
6 ; day month year (e.g., 02 Jun 1982)
7date2 = 2DIGIT "-" month "-" 2DIGIT
8 ; day-month-year (e.g., 02-Jun-82)
9date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
10 ; month day (e.g., Jun 2)
11time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
12 ; 00:00:00 - 23:59:59
13wkday = "Mon" | "Tue" | "Wed"
14 | "Thu" | "Fri" | "Sat" | "Sun"
15weekday = "Monday" | "Tuesday" | "Wednesday"
16 | "Thursday" | "Friday" | "Saturday" | "Sunday"
17month = "Jan" | "Feb" | "Mar" | "Apr"
18 | "May" | "Jun" | "Jul" | "Aug"
19 | "Sep" | "Oct" | "Nov" | "Dec"

Exemplu:

1Sun, 10 Oct 2021 02:03:00 GMT = wkday "," SP date1 SP time SP "GMT"
2Sunday, 10-Oct-21 02:03:00 GMT = weekday "," SP date2 SP time SP "GMT"
3Sun Oct 10 02:03:00 2021 = wkday SP date3 SP time SP 4DIGIT

Seturi de caractere (character sets)

Acest parametru HTTP determină ce set de caractere este susţinut de client/server. Informaţiile despre
setul de caractere sunt trimise prin câmpul de antet Accept-Charset. Mai multe dintre aceste seturi pot
fi transmise prin parametru, dar apoi trebuie separate prin virgule. Dacă acest câmp nu este transmis,
se presupune că este un set de caractere US-ASCII. Lista de seturi de caractere şi caracterele care le
aparţin se poate găsi la acest link. Câteva dintre seturile de caractere existente sunt:

• US-ASCII
• ISO-8859-1
• ISO-8859-7

Codificarea conţinutului (content encoding)

Acest câmp ne arată ce algoritm de codificare a fost utilizat pentru a codifica conţinutul înainte ca acesta
să fie trimis. Scopul acestui proces este de a comprima întregul conţinut (al documentului) care este
trimis, fără a pierde datele originale. Datele codate în acest mod sunt trimise în câmpurile de antet:
Accept-Encoding şi Content-Encoding, deci acest câmp arată numele algoritmului utilizat în compresie,
care poate fi utilizat pentru decodarea şi convertirea documentului în forma sa originală. Urmează câţiva
dintre cei mai utilizaţi algoritmi de compresie conform IANA (Internet Assigned Numbers Authority):

• gzip - un format de compresie dezvoltat pentru GNU zip; se bazează pe algoritmul Lampev-Ziv
(LZ77);
• compress - format de compresie bazat pe algoritmul Lemper-Ziv-Welch (LZW);
• deflate - format de compresie bazat pe algoritmul de compresie cu acelaşi nume;
• identity - prezenţa acestei valori indică faptul că nu a fost aplicată compresia.

Page 13 of 123
Exemplu de valoare pentru câmpul Accept-Encoding:
1Accept-Encoding: gzip
2Accept-Encoding: deflate, gzip

Tipuri de conţinut

La tipurile de conţinut se referă câmpul de antet, care indică formatul conţinutului trimis. Content-Type
este un câmp de antet în care sunt trimise informaţii despre tipul de conţinut. Sintaxa pentru valoarea
acestui câmp arată astfel (elementul de după asterisc este opţional):

Sintaxa valorii câmpului Content-Type:

1tip "/" subtip *( ";" parametru )

Exemplu:

1Content-Type: text/richtext; charset=UTF-8

Toate elementele din sintaxă sunt sensibile la majuscule şi minuscule, cu excepţia valorii care merge în
locul parametrului element. Lista cu numele tipurilor şi subtipurilor care pot fi trimise poate fi găsită la
acest link.

Marcaje lingvistice

Protocolul HTTP ne permite să trimitem informaţii în antet care indică în ce limbă sau pentru ce limbă a
fost creat conţinutul. De asemenea, dacă este vorba de o cerere - cu marcajul lingvistic trimitem
informaţii către server în limba în care dorim feedbackul şi altele. Marcajele lingvistice sunt trimise în
câmpurile de antet Accept-Language sau Content-Language. Sintaxa pentru transmiterea valorilor la
acest antet este următoarea (elementul de după asterisc este opţional):

Sintaxa valorii pentru câmpul Accept-Language:


1marcaj_principal *( "-" sub_marcaj )

Exemplu:

1Accept-Language: en
2Accept-Language: en-US

Aceste elemente sunt, de asemenea, sensibile la majuscule şi minuscule, deci trebuie acordată o atenţie
deosebită. O listă a marcajelor şi a submarcajelor lingvistice disponibile, definite de IANA, poate fi găsită
la acest link.

Mai multe detalii despre parametrii şi antetele HTTP pot fi găsite pe această pagină.

Page 14 of 123
1.3. Cookie-uri şi sesiuni

Rezumat
• Un cookie este o mică bucată de text care este stocată pe partea de client, cel mai des, de un
browser de internet şi trimisă de partea de server.
• Utilizarea obişnuită a cookie-urilor poate fi găsită în autentificarea clientului cu serverul,
stocarea preferinţelor utilizatorului, cum ar fi, de exemplu, produsele plasate în coş în timpul
cumpărăturilor electronice, identificarea clientului de către server, paginile vizitate recent etc.
• Cookie-urile sunt o modalitate bună de a conecta o pagină la alta atunci când un utilizator
interacţionează cu un site sau o aplicaţie.
• O sesiune poate fi definită ca un spaţiu stocare pe server a informaţiilor despre client, care există
până când clientul părăseşte serverul sau aplicaţia server.
• Pentru a trimite cookie-uri folosind biblioteca requests, atunci când trimitem cererea (get, post,
path...) folosim argumentul-cheie cookies, către care transmitem dicţionarul cu perechile de
cheie:valoare către cookie-uri dorite.
• CookieJar este o funcţionalitate a bibliotecii requsts prin care putem gestiona cookie-urile mai
detaliat şi le putem aranja în funcţie de domeniile şi paginile către care dorim să le trimitem.
• Sesiunile sunt create folosind biblioteca requests prin intermediul constructorului de clase
Session() al bibliotecii requests.
• Sesiunile din biblioteca requests sunt implementate astfel încât aceeaşi conexiune TCP să fie
utilizată pentru a trimite toate cererile către acel domeniu.

Cookie-urile şi sesiunile sunt termeni care sunt adesea utilizaţi împreună atunci când vine vorba de site-
uri şi aplicaţii internet, cookie-urile fiind stocate pe partea de client, iar datele de sesiune pot fi stocate
atât pe server, cât şi pe partea de client. Aceşti doi termeni sunt în cea mai mare parte legaţi de
protocoalele de nivel aplicaţie şi, în principal, de HTTP.

Cookie-urile (Cookies)

Un cookie este o mică bucată de text care este stocată pe partea de client, cel mai des de un browser
de internet şi trimis de un server. Cookie-ul ca atare, stocat pe dispozitivul client, poate fi trimis la server
după necesitate. Utilizarea obişnuită a cookie-urilor poate fi găsită în autentificarea clientului cu serverul,
stocarea preferinţelor de utilizator - cum ar fi, de exemplu, produsele plasate în coş în timpul
cumpărăturilor electronice; la identificarea clientului de către server, pagini recent vizitate etc. De
asemenea, nu toate cookie-urile trimise de server către client sunt stocate - ci aceasta se face după
necesitate. Unele cookie-uri durează atâta timp cât există o căutare activă pentru un anumit site sau
aplicaţie de către client în momentul curent. Cookie-urile sunt create în formatul nume cookie - valoare
cookie.

Ori de câte ori are loc comunicarea între client şi server - browserul îi va trimite serverului, în general,
informaţii despre cookie-urile existente pe dispozitivul nostru. Vor fi trimise doar cookie-urile stocate
de acel browser (sau de orice alt client) care se referă la domeniul serverului. Aceasta înseamnă că, dacă
un utilizator foloseşte un browser pentru a accesa [Link], acelaşi browser va trimite doar
cookie-uri pe care le-a asociat cu acel site. Practic, cookie-urile sunt o modalitate bună de a conecta o
pagină la alta atunci când un utilizator interacţionează cu un site sau cu o aplicaţie.

Un alt aspect important al cookie-urilor este securitatea. Deşi cookie-urile nu sunt dăunătoare prin
natura lor, nu este neobişnuit ca cookie-urile să fie utilizate pentru a transmite viruşi şi alte programe
şi coduri maliţioase. Odată cu dezvoltarea browserelor, un astfel de scenariu este mai rar întâlnit în
ziua de astăzi.

Page 15 of 123
Sesiunea

O sesiune poate fi definită ca un spaţiu de stocare pe server a informaţiilor despre client, care există
până când clientul părăseşte serverul sau aplicaţia server. Ideea din spatele sesiunilor este că, în loc să
stocheze o cantitate mare de date cărora deseori li se face schimbul prin intermediul cookie-urilor în
client, stochează doar un identificator unic pe partea de client (un astfel de identificator este numit, de
obicei, session id). Acest identificator este transmis către server ori de câte ori clientul trimite o cerere
către server. În acest fel, aplicaţia web asociază acest număr de identificare a sesiunii cu baza sa de
date internă pentru a găsi date referitoare la clientul în cauză (dacă există).

Setarea cookie-urilor folosind biblioteca requests

Vom demonstra gestionarea cookie-urilor în Python folosind, în locul unui browser, biblioteca requests
cu care ne-am familiarizat în cursul Python Net Programming, şi care trebuie mai întâi instalată folosind
instrumentul pip, deoarece nu face parte din bibliotecile încorporate, în timp ce noi vom folosi
[Link] ca site de testare. Acest site acceptă setarea, citirea şi ştergerea cookie-urilor la următoarele
linkuri:

• [Link] – citirea cookie-urilor existente;


• [Link] – setarea cookie-urilor noi (datorită modului în care este
definit site-ul [Link], cookie-urile pot fi redirecţionate prin linkul propriu-
zis: [Link] cookie/valoare cookie);
• [Link] – ştergerea cookie-urilor existente.

Dacă am trimite o cerere GET către [Link] am primi un răspuns necompletat de la


server, deoarece în prezent nu există cookie-uri pe care serverul ni le-ar putea returna. Deci, vom folosi
pagina [Link] către care vom trimite un set de cookie-uri. Cookie-urile în sine,
care sunt trimise prin biblioteca requests, sunt de tip dicţionar şi sunt redirecţionate către argumentul
cookie al apelării get().

Exemplu de trimitere a cookie-urilor cu o cerere către server

Import requests r = [Link]('[Link] cookies={'cookie':'is_working'}) print([Link])

Listare:

1{ "cookies": {"cookie": "is_working" }}

Deoarece site-ul [Link] este setat astfel încât valoarea sa returnată să fie, de fapt, valoarea cererii
pe care o trimitem, acesta ne-a returnat valoarea care reprezintă, de fapt, cookie-urile pe care le-am
trimis noi în calitate de client. În scenariul real, dacă am trimite o cerere către site-ul dorit, cookie-urile
pe care ni le-ar trimite acel server ar fi accesate folosind metoda cookies, care returnează obiectul
RequestsCookieJar[], adică [Link]. Acest obiect se bazează pe dicţionare şi reprezintă o listă de
chei:valori ale parametrilor, unde cheia este numele cookie-ului, iar valoarea este valoarea acelui cookie.
Deci, dacă ştim în prealabil numele cookie-ului pe care îl returnează serverul, îl putem accesa şi după
nume: "[Link]['cookie'] ".

După apelarea noastră [Link]('[Link]


am trimis cookie-uri către server. Dacă ar fi să folosim pagina [Link] pentru a citi
paginile existente cu comanda:

Page 16 of 123
1r = [Link]("[Link]
2print([Link])

vom primi din nou un răspuns necompletat. Acest lucru se întâmplă din cauza faptului că acum creăm
o nouă sesiune cu această apelare (sesiunea anterioară a început prin apelarea metodei .get() asupra
linkului [Link] şi s-a încheiat după acea execuţie). Deoarece nu am salvat
nicio urmă a sesiunii anterioare, aceasta încetează să existe şi la fiecare cerere ulterioară creăm o nouă
sesiune. Acest principiu al creării de sesiuni noi, imediat după execuţia cererii, este analog cu utilizarea
unui browser atunci când un utilizator deschide un anumit site, caută anumite lucruri pe el şi apoi
închide browserul. În exemplul nostru, pentru a salva informaţiile despre cookie-urile trimise – trebuie
să utilizăm o sesiune.

CookieJar

Biblioteca requests ne oferă un alt mod convenabil de a lucra cu cookie-urile: Cookie Jar - o bază de
date cu cookie-uri. Creăm o astfel de bază de date folosind comanda
[Link](), după care obţinem un obiect de tip <RequestsCookieJar[]> cu
care putem gestiona cookie-urile mai detaliat. Pentru a plasa un cookie într-o astfel de bază de date
cookie predefinită, folosim metoda .set() a acestui obiect, căreia îi transmitem patru parametri, unde
primii doi reprezintă numele şi valoarea cookie-ului, în timp ce al treilea parametru reprezintă
domeniul către care va fi trimis cookie-ul nostru, iar al patrulea parametru reprezintă calea pentru
domeniul respectiv. Crearea unui obiect CookieJar, configurarea şi trimiterea către server arată astfel:

Exemplu de utilizare a obiectului CookieJar


1Import requests
2jar = [Link]()
[Link]('cookie', 'one', domain='[Link]', path='/cookies')
[Link]('cookie', 'two', domain='[Link]', path='/unknown')
5
6r = [Link]('[Link] cookies=jar)
7print([Link])

Listare:

1{ "cookies":{"cookie":"one" }}

Acest lucru facilitează urmărirea cookie-urilor şi gestionarea cookie-urilor atunci când trebuie să
trimitem mai multe cereri diferite pentru domenii sau pagini diferite. În exemplul nostru, am plasat două
cookie-uri în baza de date cookie: unul care va fi transmis atunci când clientul nostru se conectează la
[Link]/cookies şi celălalt care va fi transmis atunci când clientul nostru se conectează la
[Link]/unknown. Această a doua variantă cu calea /unknown nu există pe server şi a fost folosită
ca exemplu.

Sesiuni cu ajutorul bibliotecii requests

Biblioteca requests ne oferă o modalitate uşoară de a gestiona sesiunile şi de a crea un obiect de sesiune.
Sesiunile din biblioteca requests sunt implementate astfel încât aceeaşi conexiune TCP să fie utilizată
pentru a trimite toate cererile către acel domeniu. Când schimbaţi domeniul, se creează o nouă
conexiune TCP.

Prin utilizarea sesiunilor menţinem durabilitatea anumitor parametri, care, pe lângă cookie-uri, se referă
şi la alţi parametri din cerere (de exemplu: la antet). Folosim metoda Session() a acestei biblioteci pentru

Page 17 of 123
a crea un obiect de sesiune. Metodele pe care le-am folosit înainte, se aplică şi obiectului de sesiune
(get, post, cookies...).

În exemplul anterior, am trimis cookie-urile dorite cu o apelare get() prin


linkul [Link] şi cu o altă apelare din metoda get() prin
linkul [Link] am încercat să le citim. Deoarece nu s-a vorbit despre o conexiune
de sesiune, am primit un răspuns necompletat în timpul citirii. Exemplul anterior, în cazul utilizării unei
sesiuni, arată astfel:

Exemplu de trimitere a cererii GET cu argumente:


1Import requests
2first_session = [Link]()
3first_session.[Link]({'default_cookie': 'is_default'})
4req_one = first_session.get('[Link]
5req_two = first_session.get('[Link]
6print("Second request response: ", req_two.text)

Listare:

1Second request response: {


2 "cookies": {
3 "cookie": "is_working",
4 "default_cookie": "is_default" }}

Aici putem vedea foarte uşor diferenţa în raport cu varianta anterioară. Şi anume, pentru a menţine
durabilitatea parametrilor prin apelări în cadrul unei sesiuni, folosim comanda
first_session.[Link](), căreia îi transmite tipul de dicţionar cu perechile de cheie:valoare a
cookie-urilor pe care dorim să le transmitem în timpul fiecărei apelări. Apoi, în prima apelare, setăm un
cookie folosind linkul [Link]/cookies/set. După această apelare, obiectul nostru de sesiune ne
permite să menţinem aceeaşi conexiune cu serverul. Deci, după ce am primit o apelare prin
linkul [Link] şi am analizat răspunsul serverului, vedem că cookie-urile au fost într-
adevăr menţinute şi salvate chiar şi după a doua cerere. În acest fel, anteturile pot fi trimise şi memorate
în cadrul unei sesiuni.

Page 18 of 123
1.4. Gestionarea bazei de date

Rezumat
• Biblioteca sqlite3 se bazează pe software-ul SQLite, care a fost scris iniţial în C. SQLite în sine se
bazează pe SQL şi a fost dezvoltat pentru a-l îmbunătăţi.
• Bazele de date ne permit stocarea structurată şi organizată a datelor, precum şi lucrul rapid şi
fiabil cu datele stocate.
• Baza de date este formată din elemente care sunt prezentate sub formă de tabele cu rânduri şi
coloane. Aceste tabele sunt utilizate pentru a stoca datele pe care dorim să le stocăm în baza
de date. Pentru fiecare coloană din tabel, este predeterminat ce tip de date vor fi stocate, în
timp ce rândurile din tabel pot fi definite ca și colecţii de valori conexe. Fiecare rând din tabel
poate fi marcat cu un identificator unic – cu o cheie primară.
• Pentru a crea o bază de date, mai întâi trebuie să creăm un obiect de conexiune, care va
reprezenta conexiunea la baza de date.
• Obiectul cursor ne permite să apelăm metode care pot executa comenzi în limbajul SQLite, pot
prelua date returnate de astfel de interogări şi aşa mai departe.
• Metoda .execute() primeşte o comandă de tip string, care este, de fapt, scrisă în limbajul SQL.
• Adăugarea datelor la baza de date se face, de asemenea, folosind un obiect cursor, căruia de
data aceasta i se transmite o instrucţiune SQL de tip string şi un n-tuplu sau dicţionar, în funcţie
de faptul dacă este vorba de o adăugare parametrizată sau de o adăugare parametrizată cu
câmpuri denumite.
• Dacă dorim să inserăm mai multe rânduri simultan, în loc de [Link]() vom folosi doar
cursor.
• Metodele fetchone(), fetchmany() şi fetchall() sunt utilizate pentru obţinerea rezultatelor
interogării în sine:

o fetchone() - returnează un singur rând ca rezultat, deşi pot exista mai multe rânduri în
tabel; valoarea returată a acestei metode este n-tuplu;
o fetchmany(n) - parametrul n indică câte rânduri dorim, de fapt, să returnăm; valoarea
returnată a acestei metode este o listă de n-tupluri care reprezintă toate rândurile care
se potrivesc condiţiilor specificate de comanda noastră SQLite;
o fetchall() - returnează toate rândurile care corespund condiţiei specificate din comanda
SQLite.

Există multe sisteme diferite de gestionare a bazelor de date (MySQL, PostgreSQL...), dar din moment
ce Python acceptă SQLite şi biblioteca pentru lucrul cu acest sistem face parte din bibliotecile sale
încorporate, în această lecţie vom folosi SQLite. În această lecţie vom aborda câteva dintre noţiunile de
bază ale lucrului cu bazele de date SQLite, iar instrucţiuni şi informaţii mai detaliate despre baze de date
şi concepte conexe vor fi date în unul din cursurile următoare.
Biblioteca sqlite3 se bazează pe software-ul SQLite scris inițial în C. SQLite în sine se bazează pe SQL şi
a fost dezvoltat pentru a-l îmbunătăţi. SQL, prescurtat de la Structured Query Language, este un limbaj
utilizat pentru accesarea şi gestionarea bazelor de date. În mod sintactic, acest limbaj oferă comenzi
pentru citirea bazei de date, pentru ştergere, modificare şi multe altele.

Teoria bazelor de date


Bazele de date ne permit stocarea structurată şi organizată a datelor, precum şi lucrul rapid şi fiabil cu
datele stocate. Baza de date este formată din elemente care sunt prezentate sub formă de tabele cu
rânduri şi coloane. Aceste tabele sunt utilizate pentru a stoca datele pe care dorim să le stocăm în
baza de date. Pentru fiecare coloană din tabel, este predeterminat ce tip de date vor fi stocate, în timp
ce rândurile din tabel pot fi definite ca și colecţii de valori conexe. Fiecare rând din tabel poate fi
marcat cu un identificator unic – cu o cheie principală.

Page 19 of 123
Caracteristicile SQLite

SQLite în sine, independent de implementarea sa Python, oferă anumite avantaje faţă de alte sisteme
de baze de date bazate pe SQL. Aceste avantaje sunt:

• SQLite nu necesită un proces special pentru a permite accesul la fişier, dar oferă acces direct la
fişierul bazei de date;
• SQLite nu necesită o configuraţie suplimentară, deci, este uşor şi rapid să creaţi o bază de date,
spre deosebire de alte sisteme de baze de date (de exemplu, MySQL), care necesită utilizarea
serverului lor;
• compatibilitate - întrucât întreaga bază de date se află într-un singur fişier, iar implementarea
SQLite este open-source, acest sistem de gestionare a bazei de date poate fi utilizat pe alte
platforme şi sisteme de operare şi, în ultima vreme, poate fi văzut tot mai des în dezvoltarea
aplicaţiilor Android;
• Lite în SQLite ne spune că este vorba de un sistem de baze de date foarte mic, care nu necesită
prea multe resurse pentru a fi rulat şi întreţinut.

Crearea unei baze de date

Înainte de a începe să lucrăm cu baza de date, dacă fişierul bazei de date în sine nu există - mai întâi
trebuie să îl creăm. Acest lucru se poate face din Python şi din biblioteca sqlite3. De asemenea, pentru
a lucra cu baze de date SQLite, putem utiliza un instrument cu o interfaţă grafică – SQLite Browser.

Pentru a crea o bază de date, mai întâi creăm un obiect de conexiune, care va reprezenta o conexiune
la baza de date:

1import sqlite3
2conn = [Link]("[Link]")

După execuţia acestei linii, în variabila conn avem obiectul [Link], care în codul ulterior va
reprezenta conexiunea noastră cu baza de date şi cu aceasta putem gestiona în continuare baza de date.
De asemenea, dacă în momentul execuţiei acelei linii fişierul [Link] nu există - va fi creat unul
nou, iar dacă există un fişier cu acelaşi nume - va fi deschis cel existent.

Pe lângă utilizarea unei baze de date care este stocată permanent într-un fişier, există şi o modalitate
de a crea o bază de date SQLite care va exista doar în memoria RAM. Avantajul acestei abordări este că
face mai uşoară şi mai rapidă testarea anumitor funcţionalităţi dorite, fără a fi nevoie să lucraţi pe fişierul
propriu-zis. Dezavantajul este că după terminarea programului nostru, pierdem întreaga bază de date
cu toate datele. Vom crea baza de date stocată în memorie folosind linia:

1conn = [Link](:memory:)

În exemplul nostru, vom folosi baza de date stocată în fişierul [Link].

După ce am stabilit conexiunea la baza de date, următorul pas este să permitem programului nostru să
gestioneze datele şi tabelele din aceasta. Această funcţionalitate este realizată de un obiect cursor (parte
a clasei [Link]). Obiectul cursor ne permite să apelăm metode care pot executa comenzi în
limbajul SQLite, pot prelua date returnate de astfel de interogări şi aşa mai departe. Un obiect de tip
[Link] este creat folosind metoda .cursor() a obiectului de conexiune:

Page 20 of 123
1cursor = [Link]().

Acum, apelând metoda .execute() peste obiectul cursor, putem crea mai întâi primul tabel din baza de
date goală, cu următoarea comandă:

Exemplu de creare a unui tabel în baza de date [Link]


1import sqlite3
2conn = [Link]("[Link]")
3cursor = [Link]()
[Link]("""CREATE TABLE IF NOT EXISTS Students(
5 studentId INT PRIMARY KEY,
6 name TEXT,
7 age INT,
8 grade INT);
9""")
[Link]()

Metoda .execute() primeşte o comandă de tip string, care este, de fapt, scrisă în limbajul SQL. O vom
descompune pentru a o înţelege mai uşor:

• CREATE TABLE - comanda pentru crearea unui tabel; parte obligatorie;


• IF NOT EXIST - o condiţie care ne spune că un tabel este creat numai dacă nu există deja în baza
de date; utilizarea condiţiilor este opţională, dar dacă încercăm să creăm un tabel cu un nume
care există deja în baza de date - vom primi eroarea [Link]:table Students
already exists;
• Students - numele tabelului pe care îl creăm.

Apoi, în paranteze urmează numele coloanelor pe care dorim să le creăm, precum şi tipul de date pe
care îl va conţine tipul de coloană. Mai multe despre tipurile susţinute puteţi citi în documentaţia
oficială.

• (studentId INT PRIMARY KEY, - numele coloanei studentId care va reprezenta identificatorul
numeric al studentului, de tip int, în timp ce această coloană va fi şi cheia primară a tabelului
Students;
• name TEXT, - numele coloanei name, care va stoca datele despre nume, de tip text;
• age INT, - numele coloanei age, care va stoca date despre generaţie/an, de tip int;
• gradle INT); - numele coloanei gradle, care va stoca date despre gen, de tip int;
• întreaga instrucţiune SQL se încheie cu semnul punct şi virgulă (;).

În cele din urmă, cu comanda [Link]() am confirmat şi salvat aceste modificări în baza de date,
adică am executat o interogare SQL peste baza de date. Fără această comandă, dacă oprim executarea
programului după comanda [Link](), modificările pe care le-am făcut nu ar fi făcute, adică
tabelul nu ar fi fost creat.

Adăugarea datelor în baza de date

Adăugarea datelor în baza de date se face, de asemenea, folosind un obiect cursor, căruia de data
aceasta i se transmite o instrucţiune SQL de tip string şi n-tuplu sau dicţionar, în funcţie de faptul dacă
este o adăugare parametrizată sau o adăugare parametrizată cu câmpuri denumite. Comanda pentru
inserarea elementelor în baza de date, folosind adăugarea parametrizată, arată astfel:

Page 21 of 123
1student = ('01', 'Karen', 25, 9)
[Link]("INSERT INTO Students VALUES (?, ?, ?, ?)", student)
[Link]()

Semnificaţia comenzii pentru adăugarea unui rând nou este:

• INSERT INTO Students - cu această comandă de limbaj SQL stabilim în ce tabel din baza noastră
de date dorim să inserăm date;
• VALUES (?, ?, ?, ?) - cu această parte a comenzii, folosind semnele întrebării ?, care servesc drept
semne prin care le spunem comenzilor SQL că elementele din n-tuplul nostru se transmit în
acele locuri de plasare ([Link]). Deci, aceste placeholdere (semnele întrebării)
corespund fiecărui element al n-tuplului nostru şi al coloanei din tabel. Dacă am transmite un
număr mai mare de placeholdere decât numărul de coloane din tabel sau decât numărul de
elemente din n-tuplu, am primi o eroare.

Pentru a insera date în baza de date, putem folosi, de asemenea, adăugarea parametrizată cu nume de
câmpuri împreună cu dicţionare în loc de n-tupluri:

student = {"studentId":'02', 'name':'Steven', "age":'27', "grade":'7'}


[Link]("INSERT INTO Students VALUES (:studentId, :name, :age, :grade)",
2student)
[Link]()

Această comandă este puţin mai diferită decât cea precedentă, mai ales în modul prin care determinăm
coloanele cărora le vom transmite datele. În acest caz, totuşi, trebuie să specificăm numele coloanelor
împreună cu două puncte în faţa numelor lor (:), spre deosebire de modul anterior, unde în loc de nume
am folosit placeholdere.

Metoda executemany()

Prin aceste exemple, am văzut cum se introduce un rând în baza de date. Dacă dorim să inserăm mai
multe rânduri simultan, vom folosi metoda [Link]() în loc de [Link](). Această
metodă este, de asemenea, transmisă ca prim parametru stringului care reprezintă instrucţiunea SQL,
iar pentru al doilea parametru, în loc de n-tuplu sau de dicţionar (în funcţie de varianta pe care o
folosim), transmitem fie lista cu n-tupluri, fie lista de dicţionare:

Exemplu de adăugare parametrizată


1student = [('01', 'Karen', 25, 9), ('02', 'Steven', 27, 9)]
[Link]("INSERT INTO Students VALUES (?, ?, ?, ?)", student)

Exemplu de adăugare parametrizată cu nume de câmpuri


1student = [{"studentId":'2', 'name':'Steven', "age":'27', "grade":'7'},
2 {"studentId":'1', 'name':'Karen', "age":'25', "grade":'9'}]
[Link]("INSERT INTO Students VALUES (:studentId, :name, :age, :grade)", student)

Page 22 of 123
Citirea din baza de date

Până acum, am învăţat cum să stocăm date într-un tabel; cu toate acestea, pe lângă scrierea în baza de
date, trebuie să ştim şi să citim din baza de date. Citirea se face, de asemenea, folosind metoda execute()
asupra obiectului cursor:

Exemplu de citire a datelor din baza de date


[Link]("SELECT * FROM Students;")
2all_results = [Link]()
3print(all_results)
4[(2, 'Steven', 27, 7), (1, 'Karen', 25, 9)]

Comanda de citire SQL începe întotdeauna cu cuvântul-cheie SELECT, urmată de numele exacte ale
coloanelor pe care dorim să le citim sau, în cazul nostru, de asterisc (*), care specifică faptul că dorim
toate coloanele. Partea FROM Students a comenzii ne spune din ce tabel să preluăm coloanele
anterioare. Deci, cu această comandă, am spus literalmente: „marchează toate coloanele din tabelul
Students şi returnează datele în program”. Dacă există rânduri care îndeplinesc această condiţie - acea
comandă va returna rândurile respective.

După execuţia comenzii, rezultatul este stocat în obiectul cursor. Putem folosi mai multe metode pentru
a accesa aceste rezultate:

• fetchone() - returnează un singur rând ca rezultat, deşi în tabel pot exista mai multe rânduri;
valoarea returnată a acestei metode este n-tuplu;
• fetchmany(n) - parametrul n indică câte rânduri dorim, de fapt, să returnăm; valoarea returnată
a acestei metode este o listă de n-tupluri reprezentând toate rândurile care se potrivesc
condiţiilor specificate de comanda noastră SQLite;
• fetchall() - returnează toate rândurile care corespund condiţiei specificate din comanda SQLite.

În exemplul nostru, am optat pentru fetchall(), care returnează o listă de rânduri care se potrivesc
condiţiei date din comanda SQLite.

Imaginea 4.1. Reprezentare grafică a tabelului în cazul utilizării programului DB Browser for SQLite

Page 23 of 123
Notă:
Pentru lucrul cu bazele de date, este valabilă o regulă care se aplică şi lucrului cu fişierele. Deşi Python în sine
are grijă să închidă fişierele şi conexiunile la baze de date la sfârşitul executării programului - este o bună practică
pentru noi să le închidem singuri după ce am terminat de lucrat cu ele. Închidem obiectul cursor cu [Link](),
în timp obiectul care reprezintă conexiunea cu baza de date se închide cu [Link](). După ce oprim obiectul
cursor, toate datele pe care le conţine acesta devin inaccesibile, deci, dacă executăm comanda SELECT *
FROM... folosind cursorul şi apoi oprindu-l, datele returnate de acea comandă nu mai sunt disponibile.

Page 24 of 123
1.5. Şablonul MVC

Rezumat
• MVC (abrevierea de la Model-View-Controller) este un cadru de lucru (framework) care
reprezintă o modalitate de a dezvolta o aplicaţie care împarte aplicaţia în trei părţi principale:
model, vizualizare şi controler.
• Arhitectura MVC a fost menţionată pentru prima dată în anul 1979, când Trygve Reenskaug a
prezentat această idee, în timp ce în anul 1987 a fost folosită pentru prima dată ca un concept
în limbajul de programare Smalltalk.
• Modelul este o componentă care stochează date şi logica asociată cu acestea.
• Vizualizarea este partea aplicaţiei care reprezintă afişarea datelor (reprezentarea vizuală).
• Controlerul este partea aplicaţiei care se ocupă cu interacţiunea cu utilizatorul.

MVC (abreviat de la Model-View-Controller) este un cadru (framework) sau şablon de lucru care
reprezintă modul de dezvoltare a unei aplicaţii care împarte aplicaţia în trei părţi principale: model,
vizualizare şi controler. Fiecare componentă este concepută pentru a gestiona un aspect specific al
aplicaţiei. În mod tradiţional, MVC a fost utilizat mai ales pentru aplicaţii grafice desktop, iar ulterior
acest model de dezvoltare şi gândire a fost transferat atât aplicaţiilor web, cât şi aplicaţiilor mobile.

Arhitectura MVC a fost menţionată pentru prima dată în anul 1979, când omul de ştiinţă norvegian
Trygve Reenskaug a prezentat această idee, în timp ce în anul 1987 a fost folosită pentru prima dată ca
un concept în limbajul de programare Smalltalk.

Conexiunea

Pe lângă împărţirea aplicaţiei în trei componente diferite, şablonul model-vizualizare-controler defineşte


şi conexiunea între acestea. Modelul este îndatorat de gestionarea datelor aplicaţiei. Mai exact, acesta
primeşte întrebări de la controler. Vizualizarea este, de fapt, aspectul aplicaţiei în sine, în timp ce
controlerul reacţionează la acţiunile utilizatorului şi interacţionează asupra datelor modelului.
Controlerul primeşte acţiunea utilizatorului, o validează opţional şi o trimite la model, astfel încât se
poate spune că reprezintă o legătură între model şi vizualizare.

Componentele

Modelul reprezintă componenta centrală a şablonului MVC. Se potriveşte cu logica sa cu privire la datele
cu care lucrează utilizatorul. Gestionează direct aceste date, logica şi condiţiile/regulile aplicaţiei date.

Vizualizarea poate fi orice reprezentare a datelor, cum ar fi, de exemplu, o diagramă, un grafic sau un
tabel. De asemenea, este posibil ca aceleaşi date să fie afișate în mai multe vizualizări diferite.

A treia componentă, controlerul, primeşte acţiunile utilizatorului şi le transformă în comenzi pentru


model sau vizualizare. Acesta serveşte ca o legătură între model şi vizualizare, care procesează logica
şi toate cererile viitoare ale utilizatorilor, gestionează datele folosind modelul şi utilizează vizualizarea
pentru vizualizarea finală a datelor.

Modelul

Modelul este o componentă care stochează date şi logica asociată cu acestea. De exemplu, reprezintă
date care sunt transferate între controlere. Controlerul va furniza informaţii despre utilizator din baza
de date, astfel încât modelul să poată lucra pe acele date şi să le trimită înapoi în baza de date sau să i
le afişeze utilizatorului.

Page 25 of 123
Vizualizarea

Vizualizarea este partea aplicaţiei care reprezintă afişarea datelor (reprezentare vizuală). Vizualizarea
este creată folosind datele colectate din model. Vizualizarea trimite o cerere către model pentru
anumite informaţii, astfel încât să i le poată afişa utilizatorului final. De asemenea, vizualizarea prezintă
date din diagrame, tabele, grafici, meniuri derulante, casete de text şi altele.

Controlerul

Controlerul este partea aplicaţiei care se ocupă cu interacţiunea utilizatorului. Controlerul recunoaşte
intrările utilizatorului sub forma utilizării mouse-ului şi a tastaturii şi informează componentele model şi
vizualizare despre posibilele modificări ale setului. De exemplu, controlerul îi poate trimite o comandă
modelului pentru a actualiza datele despre un anumit utilizator din baza de date sau pentru a salva un
document deschis. De asemenea, controlerul trimite o comandă către vizualizare pentru a schimba
aspectul ecranului pe care îl vede utilizatorul (de exemplu, mişcarea rotiţei mouse-ului ar muta
conţinutul documentului în sus sau în jos).

Imaginea 5.1. Reprezentare grafică a şablonului MVC1

Aplicarea şablonului MVC

Şablonul MVC ne ajută atunci când planificăm o aplicaţie, mai ales pentru că ne arată cum ideile noastre
pot fi transpuse în cod. Să presupunem că creăm o aplicaţie To-Do. Această aplicaţie ar permite
utilizatorilor să creeze sarcini şi să le organizeze pe liste tematice. În acest caz, modelul ar defini care
este sarcina şi că lista este, de fapt, o colecţie de sarcini. Codul vizualizării ar defini aspectul vizual al
sarcinilor şi listelor. De exemplu, numele listelor ar avea un font mai mare decât sarcinile în sine, un font
îngroșat şi aşa mai departe. În cele din urmă, controlerul ar defini modul în care utilizatorul adaugă
sarcina sau o marchează ca terminată. Controlerul în sine conectează butonul la vizualizare. Un astfel de
exemplu poate fi văzut în imaginea de mai jos, unde unităţile tematice sunt Copy şi Design. Listele de
sarcini din cadrul acestor unităţi sunt modele (Survery users, Draft copy, Design a prototype etc.).

Page 26 of 123
Imaginea 5.2. Exemplu de aplicaţie To-Do bazată pe şablonul MVC2

Avantajele modelului MVC:

• completarea mai uşoară a codului şi adăugarea funcţiilor noi;


• suport facilitat pentru noi tipuri de clienţi;
• dezvoltarea diferitor componente poate avea loc în paralel;
• este potrivit pentru aplicaţii web mari în spatele cărora se află mulţi oameni;
• permite gruparea logică a acţiunilor conexe.

Dezavantajele utilizării MVC:

• interpretarea, modificarea şi testarea dificilă a codului;


• navigarea pe bază de cod poate fi un proces complex;
• ineficienţa datelor;
• MVC necesită cunoaşterea mai multor tehnologii de programare diferite;
• întrucât este necesară cunoaşterea mai multor tehnologii de programare diferite, sunt necesari
mai mulţi programatori pentru a lucra la aplicaţia MVC.

Page 27 of 123
Exemplu de şablon MVC în Python:

Exemplul se bazează pe ideea unei „biblioteci” - a unui software conceput pentru a stoca cărţi. Vom
declara toate cele trei componente folosind limbajul de programare Python:

Codul modelului (fişierul trebuie salvat ca ‘[Link]’)


1class Book(object):
2 def __init__(self, title = None, year = None):
3 [Link] = title
4 [Link] = year
5 #returns Book name, ex: Dorian Gray
6 #returns all people inside [Link] as list of Person objects
7 def getAll(self):
8 books = [{'title':'The Picture of Dorian Gray','year':'1890'},
9 {'title':'Pride and Prejudice','year':'1813'},
10 {"title":'The Adventures of Tom Sawyer ', 'year':'1875'}]
11 return books

În exemplul bibliotecii noastre, modelul va reprezenta un fişier cu clasa Book, care este, de fapt, o carte.
Pentru simplitate, baza noastră de date este, de fapt, o listă de dicţionare cu două perechi de cheie-
valoare: numele cărţii şi anul publicării, care sunt returnate programului apelând metoda getAll() a clasei
Book.
Codul vizualizării (fişierul trebuie salvat ca [Link])
1def showAllView(list):
2 print ('In our db we have {} books. Here they are:'.format(len(list)))
3 for item in list: print ("\t- {}".format(item['title']))
4def startView():
5 print ('MVC - example start:')
6def endView():
7 print ('End.')

În exemplul bibliotecii noastre, codul vizualizării serveşte, de fapt, pentru a comunica cu utilizatorul. Și
anume, în acest caz, pe ecran se vor afişa informaţiile transmise de controler. Funcţiile startView() şi
endView() sunt disponibile pentru informaţii despre pornirea sau oprirea programului, în timp ce funcţia
showAllView() listează întreaga bază de date a cărţilor pe ecran.
Codul controlerului (fişierul trebuie salvat ca [Link])
1from Model import Book
2import View
3def showAll():
4 #gets list of all Book objects
5 b = Book()
6 books_in_db = [Link]()
7 #calls view
8 return [Link](books_in_db)
9def start():
10 [Link]()
11 showAll()
12 [Link]()
13if __name__ == "__main__":
14 #running controller function
15 start()

Page 28 of 123
În exemplul nostru de software de bibliotecă, controlerul este responsabil de conectarea datelor la ceea
ce vede utilizatorul pe ecran (a modelului cu vizualizarea). Mai exact, în acest fişier importăm mai întâi
codul din model şi din vizualizare (form Model import Book, import View). Când rulaţi fişierul
[Link], este executată funcţia start(). Listarea după finalizarea acestui program arată astfel:

Listarea exemplului MVC:


1MVC - example start:
2In our db we have 3 books. Here they are:
3 -The Picture of Dorian Gray
4 -Pride and Prejudice
5 -The Adventures of Tom Sawyer
6End.

Printr-o analiză mai detaliată a codului, putem vedea ceea ce este, de fapt, scris în funcţia start() a
controlerului - şi anume, funcţia este executată prima.

Diagrama din exemplul nostru arată astfel:

Imaginea 5.3. Diagrama exemplului MVC – biblioteca

1. [Link]
2. [Link]

Page 29 of 123
1.6. Localizarea

Rezumat
• Localizarea nu se referă doar la traducerea conţinutului, ci şi la adaptarea conţinutului respectiv
pentru regiunea/cultura ţintă.
• Internaţionalizarea se referă la facilitarea localizării conţinutului în sine.
• Înainte de localizare, este important să cercetăm piaţa pentru care intenţionăm să localizăm
conţinutul.
• Avantajele localizării conţinutului sunt:

o vizarea unui public mai larg - localizarea deschide uşa pentru noii utilizatori care, dacă
conţinutul este într-o limbă pe care nu o înţeleg, nici măcar nu ar vizita site-ul;
o o mai bună clasare a site-ului nostru printre rezultatele căutării - poate fi creat un link
special către site-ul nostru, care se referă la versiunea localizată a site-ului, care poate
îmbunătăţi vizibilitatea site-ului nostru în timpul căutării Google;
o face site-ul mai accesibil - localizarea va facilita accesul persoanelor care nu cunosc bine
limba primară a site-ului nostru.

Deşi astăzi mai mult de jumătate dintre utilizatorii de internet din lume ştiu limba engleză, această
limbă nu este singura limbă utilizată de un număr mare de oameni. De exemplu, conform
statisticilor site-ului w3tech, mai puţin de 5% dintre site-urile în limba engleză au conţinut adaptat
zonelor în care se vorbeşte limba spaniolă, deşi această limbă este foarte răspândită în lume. Dacă
site-urile ar fi doar în limba engleză, un număr mare de utilizatori care nu ştiu limba engleză nu ar
avea acces suficient la conţinutul dorit. Pentru a evita pierderea potenţialilor utilizatori şi a câştigurilor,
se recomandă să traduceţi site-ul în alte limbi.

Aşadar, deseori este necesară localizarea site-urilor şi personalizarea conţinutului pentru anumite zone.
Trebuie avut în vedere că, pe lângă acest termen - localizare, există şi internaţionalizarea, care se referă
la facilitarea localizării conţinutului în sine.

Localizarea nu se referă doar la traducerea conţinutului, ci şi la adaptarea conţinutului respectiv la


regiunea/cultura ţintă etc. Aceasta înseamnă că localizarea se referă la monedă, unităţile de măsură şi
terminologia generală, specifică regiunii pentru care localizăm conţinutul. De aceea, este important să
cercetăm piaţa pentru care planificăm conţinut localizat.

Avantajele localizării conţinutului

Avantajele localizării conţinutului sunt:

• vizarea unui public mai larg - localizarea deschide uşa pentru noii utilizatori care, dacă
conţinutul este într-o limbă pe care nu o înţeleg, nici măcar nu ar vizita site-ul;
• o clasare mai bună a site-ului nostru printre rezultatele căutării - se poate crea un link special
către site-ul nostru care se referă la versiunea localizată a site-ului, care poate îmbunătăţi
vizibilitatea site-ului nostru în timpul căutării Google;
• site-ul devine mai accesibil - localizarea va facilita accesul persoanelor care nu cunosc bine
limba primară a site-ului nostru.

Cel mai bun mod de a decide în ce regiune va fi adaptat conţinutul se bazează pe analiza site-ului în
sine şi pe analiza traficului pe site.

Page 30 of 123
Localizarea pe partea de server

Indiferent dacă conţinutul este localizat sau nu, acest luru se vede cel mai bine dintr-un browser, dar
vom folosi clientul şi serverul Python pentru exemplul nostru. Pentru codul serverului, vom folosi
următorul cod de la cursul Python Net Programming:

Codul de server (http_local_server_localisation.py):


from [Link] import HTTPServer, BaseHTTPRequestHandler
1from [Link] import parse_qs, parse_qsl
2localisation_dict = {'en':'Greetings!', 'es':'Saludos!'}
3class RequestHandler(BaseHTTPRequestHandler):
4 def do_GET(self):
5 self.log_message
6("Incoming GET request with headers {}".format([Link]()))
7 try:
8 self.send_response_to_client(200,
9localisation_dict[[Link]
10('Accept-Language')])
11 except:
12 self.send_response_to_client
13(404, 'Incorrect language code provided')
14 self.log_message("Incorrect language code provided")
15 return
16 def send_response_to_client(self, status_code, data):
17 # Send OK status
18 self.send_response(status_code)
19 # Send headers
20 self.send_header('Content-type', 'text/plain')
21 self.end_headers()
22 # Send the response
23 [Link](str(data).encode())
24server_address = ('[Link]', 8000)
25http_server = HTTPServer(server_address, RequestHandler)
26http_server.serve_forever()

Pentru noi, cea mai importantă linie din cod este self.send_response_to_client (200, localization_dict
[[Link]('Accept-Language')]), care trimite mesajul de stare 200 către client împreună cu
mesajul. În acest caz, mesajul este generat folosind dicţionarul localisation_dict, ale cărui chei sunt nume
de limbă, iar valorile acestor chei sunt cuvântul Salut tradus în limbile corespunzătoare (în exemplul
nostru, acestea sunt limbile us – United States şi es - España). Obţinem informaţii despre limbă din
antetul clientului utilizând metoda [Link](). Metoda get() peste antetul obiectului serverului
nostru transmite numele antetului pe care dorim să îl obţinem din cerere. În cazul în care un astfel de
câmp de antet nu există, această metodă returnează None. Dacă am dori să obţinem toate perechile de
antet cheie-valoare, în loc de metoda get() am folosi metoda items(), care returnează o listă de n-tupluri
în care fiecare n-tuplu conţine doar două elemente – cheie şi valoare (numele antetului şi valoarea
acestuia). Linia [Link] returnează ea însăşi un obiect MessageClass, ale cărui atribute şi metode
pot fi găsite în documentaţia oficială. Dacă marcajul limbii transmis nu există în dicţionarul nostru
localisation_dict ca și cheie, va apărea o eroare KeyError, dar va fi captată cu un bloc de cod try-except,
care îi va trimite clientului un mesaj ca acesta: „Incorrect language code provided”, cu codul 404. Câmpul
de antet în care căutăm marcajul limbii este Accept-Language.

Page 31 of 123
Pe partea de client vom folosi biblioteca requests (requests nu face parte din bibliotecile care vin cu
instalarea Python, deci trebuie instalată ulterior), iar codul pe care îl folosim este, de asemenea, similar
cu codul client HTTP din cursul Python Net Programming, iar el arată astfel:

Codul de client (http_local_client_localisation.py):

1import requests
2headers = {'Accept-Language': 'es'}
3r = [Link]("[Link] headers = headers)
4print("Request method: GET, \
5 Response status_code: {}, Response data: {}".format(r.status_code, [Link]))

În acest exemplu, am definit anterior antetul ca un dicţionar care este transmis metodei [Link](),
care conţine câmpul Accept-Language care va trimite o cerere către server că dorim conţinut în spaniolă
(es - España, codul pentru spaniolă).

Mai întâi, trebuie să rulăm codul de server într-o fereastră separată a liniei de comandă şi apoi să rulăm
codul de client. După rulare, listarea arată astfel:

Imaginea 6.1. Listarea în consolă după rularea codului de client şi server

Dacă am transmite codul de limbă franceză - fr în loc de es, în răspunsul serverului am primi mesajul
„Incorrect language code provided”, ceea ce înseamnă că conţinutul care corespunde codului respectiv
nu există pe server. Desigur, aceasta este o practică proastă - faptul că clientul primeşte o eroare dacă
nu poate accesa conţinutul localizat, deci, în acest caz, este bine să îi trimiteţi clientului o versiune sursă
nelocalizată a conţinutului care există deja pe server.

Page 32 of 123
2.1. Limbajul de programare JavaScript

Rezumat
• JavaScript este un limbaj de scripting sau de programare care ne permite să actualizăm dinamic
conţinutul unui site. Este, de asemenea, limbajul prin care schimbăm valoarea, funcţia, forma...
datelor pe care le deţinem.
• Există trei tipuri de variabile pe care le foloseşte JavaScript: var, let şi const.
• Funcţiile încorporate sunt funcţii furnizate de un obiect window şi pot fi utilizate oriunde în
codul JavaScropt.
• Codul JavaScript poate fi introdus într-un fişier HTML în două moduri: prin introducerea directă
în tagul de script sau printr-un link extern.

JavaScript este un limbaj de scripting sau de programare care permite aplicarea funcţiilor complexe pe
pagini web, cum ar fi:

• afişarea dinamică a informaţiilor;


• actualizarea conţinutului în timp real;
• hărţile interactive;
• animaţii 2D şi 3D...

În această lecţie vom parcurge etapele de bază în limbajul de programare JavaScript, cum ar fi crearea
variabilelor, crearea funcţiilor, transmiterea parametrilor în cadrul funcţiilor şi listarea rezultatelor către
utilizator, pentru a dobândi cunoştinţele necesare pentru a trimite cu succes cererile către un server
Python, dar cererile vor fi abordate într-una din lecţiile următoare.

Notă:
JavaScript nu trebuie iniţializat, deoarece este deja iniţializat şi activ în fundalul browserului pe care îl folosim
pe computer, tabletă sau telefon.

Iniţializarea funcţiilor şi a variabilelor

Limbajul JavaScript ne permite să schimbăm dinamic conţinutul unui site. Pentru a face acest lucru
realizabil, trebuie să cunoaşteţi regulile de bază. Prin acest capitol aflăm cum putem defini variabile şi
funcţii.

Există trei cuvinte-cheie pentru crearea variabilelor, şi acestea sunt var, let şi const. Primul cuvânt-cheie
care a definit variabilele, var, a fost definit în anul 1997, în timp ce let şi const au fost introduse în anul
2015.

Cuvântul-cheie var este utilizat pentru definirea unei variabile. De exemplu:

Exemplul 1
1var name = "Ana";
[Link](name);

Rezultatul în consolă:

1"Ana"

În exemplul 1, am folosit funcţia [Link](), furnizată de obiectul window, pe care fiecare browser îl
are în el. Un obiect window este o fereastră deschisă într-un browser. Putem folosi funcţiile furnizate de

Page 33 of 123
obiectul window în orice parte a codului JavaScript. Mai multe informații despre funcţiile şi atributele
obiectului ferestrei se pot citi în documentaţia oficială.

Notă:

O explicaţie detaliată pentru rularea codului JavaScript se poate găsi în capitolul JavaScript şi HTML.

Cuvântul-cheie let pentru crearea unei variabile este similar cu cuvântul var. Acelaşi lucru este iniţializat
şi utilizat.

Exemplul 2
1let name = "Ana";
[Link](name);

Rezultatul în consolă:

1"Ana"

Folosim cuvântul-cheie const pentru a defini variabile atunci când dorim să iniţializăm o variabilă pe
care nu vrem să o schimbăm ulterior. Dacă variabila este de tip obiect sau şir, atunci puteţi să o modificaţi,
adică puteţi să adăugaţi atribute într-un obiect sau elemente dintr-un șir şi, astfel, să le manipulaţi.
Motivul acestui comportament este că, dacă atribuim o referinţă (adresă) unei variabile definite de
cuvântul-cheie const, nu este posibilă schimbarea adresei la această variabilă, ci doar adăugarea unei
valori la aceeaşi adresă. Modificând sau adăugând conţinut într-un şir sau obiect, nu schimbăm referinţa,
ci doar o îmbunătăţim pe cea existentă. În aplicaţiile moderne, variabilele let şi const sunt utilizate mai
mult. Motivul acestui fenomen este că aceste două variabile ne facilitează căutarea erorilor şi afişarea
lor.

Exemplul 3
1const name = "Ana";
[Link](name);

Rezultatul în consolă:

1"Ana"

Dacă încercăm să schimbăm valoarea variabilei const, ea va arăta astfel:

Exemplul 4
1const name = "Ana";
2name = "Michael";

Imaginea 7.1. Afişarea rezultatelor din exemplul cu nr. 4

Page 34 of 123
Iniţializarea funcţiei se realizează conform anumitor reguli. Prima regulă este să utilizaţi cuvântul-cheie
function înainte de numele funcţiei, urmat de numele funcţiei. A doua regulă este că numele funcţiei
trebuie urmat de o pereche de paranteze (deschise şi închise). Când sunt respectate aceste reguli,
deschidem un spaţiu în care scriem codul pe care ar trebui să îl îndeplinească funcţia, şi facem acest
lucru folosind acolade. Această parte a codului este numită şi corpul funcţiei.

1function nameOfFunction() { ...code }

Parametrii de intrare în funcţii

Când apelaţi o funcţie, puteţi să-i transmiteţi parametrii. Parametrii sunt introduşi între paranteze când
funcţia este iniţializată:

1functionName(param1, param2, param3...)

Un exemplu de transmitere a două numere şi adăugarea lor se poate vedea mai jos.

Exemplul 5
1function sumOfTwoNumbers(firstNum, firstNum) {
2 return (firstNum + firstNum);
3 };
4
5 let sum = sumOfTwoNumbers(5, 7);
[Link](sum)

Rezultat:

12

În exemplu, putem vedea că funcţia iniţializată se numeşte sumOfTwoNumbers, la care sunt transmişi
doi parametri (5, 7). După iniţializare, s-a făcut o variabilă let şi s-a apelat sum; funcţia
sumOfTwoNumbers este setată ca referinţă (5, 7). Acest lucru pare ciudat în acest moment - nu este
oare imposibil să atribuim o funcţie unei variabile?

Dacă o funcţie are un parametru return, acea funcţie returnează o valoare, care poate fi, de exemplu, un
şir, un număr sau un text. În cazul nostru, aceasta este suma numerelor 5 şi 7, adică numărul. Deoarece
această funcţie returnează o valoare, puteţi să atribuiţi acea valoare variabilei sum.

Deci, acum avem o variabilă sum care are valoarea 12.

JavaScript şi HTML

Una dintre funcţiile limbajului JavaScript este că ne permite să manipulăm codul HTML. Codul JavaScript
poate fi scris direct într-un fişier HTML, dar poate fi scris şi într-un fişier separat.

Scrierea codului JavaScript într-un fişier HTML se face folosind un tag de script special.

Exemplul 6
1<script>
2 ... some JavaScript code
3</script>

Page 35 of 123
Dacă vrem să scriem un cod JavaScript şi HTML separat, folosim două fişiere. Le vom denumi [Link]
pentru scrierea codului HTML şi [Link] pentru scrierea codului JavaScript.

[Link]
1... some JavaScript code

În următorul bloc de cod HTML, vedem că unui atribut din tagul de script i se atribuie un link extern
către fişierul [Link], prin care legăm pagina noastră HTML cu codul JavaScript. În acest caz, ambele
fişiere sunt localizate în acelaşi [Link]
[Link]
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <title>Document</title>
6</head>
7
8<body>
9 … some HTML code
10
11 // Next line of code is used to add external JS file
12 <script src="[Link]"></script>
13</body>
14
15</html>

Accesul la variabilele externe şi la funcţiile încorporate

Funcţiile încorporate sunt funcţii pe care le are deja obiectul window; pot fi apelate în orice parte a
codului JavaScript. Unele dintre ele sunt alert() şi [Link]().
Vom explica acest lucru prin două exemple:

Exemplul 7
1var a = 4;
2 function f() {
3 alert (a);
4 }
5 f();

Funcţia alert() a afişat o fereastră care arată ca în imaginea 7.2. Ca rezultat, în fereastră avem valoarea 4,
pe care am obţinut-o cu o variabilă globală de tip var pe care am trecut-o la funcţia f(), fără a utiliza
parametrul de intrare, deoarece variabila este globală şi poate fi accesată din funcţia f().

Imaginea 7.2. Afişarea rezultatelor din exemplul cu nr. 7

Page 36 of 123
Exemplul 8
1var a = “Ana”;
2 function f() {
3 [Link] (a);
4 }
5 f();

Pentru a testa codul, putem lista rezultatele în interiorul consolei. Există câteva modalităţi de a verifica
dacă rezultatul este listat în consolă. Iată cele mai populare două modalităţi. Una presupune simpla
apăsare a tastei F12 de pe tastatură în interiorul browserului, iar cealaltă este cu clic dreapta pe
fereastra de lucru a browserului şi selectând opţiunea InspectElement.

Imaginea 7.3. Afişarea rezultatelor din exemplul cu nr. 8

În următorul exemplu vom vedea cum arată eroarea ReferenceError în timpul apelării funcţiei
încorporate cu parametrul transmis care nu este iniţializat.

Exemplul 9

function f() {
alert (x);
{
f();

În acest exemplu, variabila x nu este definită înainte de apelarea funcţiei, motiv pentru care în consolă
se afişează eroarea RerefenceError:

Imaginea 7.4. Afişarea erorii ReferenceError în consolă

Page 37 of 123
2.2. Document Object Model

Rezumat
• DOM este abrevierea de la Document Object Model; acest model este utilizat pentru a accesa
şi modifica dinamic toate elementele unei pagini.
• Metodele pe care le folosim pentru a schimba dinamic elementele de pe o pagină sunt:
getElementById(), getElementByClassName(), getElementsByTagName(), querySelector() şi
querySelectorAll().
• Proprietatea innerHTML este o proprietate pe care o folosim pentru a modifica textul unui
element.
• Proprietatea className este o proprietate pe care o folosim pentru a schimba stilul, adică
pentru a adăuga o clasă la un element.

În cadrul lecţiei ne vom familiariza cu manipularea Document Object Model, respectiv cu manipularea
obiectelor HTML prin JavaScript. Vom aborda metodele de manipulare a DOM-ului şi vom vedea
rezultatele pe exemple practice.
În timpul acestei lecţii vom învăţa:

• ce este DOM;
• cum se schimbă conţinutul unui element HTML;
• cum se schimbă stilul/CSS al unui element HTML;
• cum să utilizaţi evenimentele şi să le asociaţi cu elemente HTML.

Ce este DOM?

DOM este abrevirea de la Document Object Model. Se poate spune că este vorba de un standard utilizat
pentru accesul dinamic şi modificarea tuturor elementelor unei pagini. Aceste standarde sunt împărţite
în trei părţi:

1. Core DOM - model standard pentru toate tipurile de documente;


2. XML DOM - model standard pentru documentele XML;
3. HTML DOM - model standard pentru documentele HTML.

Core DOM defineşte evenimentele şi modelele de documente utilizate de platforma web.


XML DOM defineşte un standard pentru accesarea şi modificarea unui document XML. În imagine se
poate vedea un arbore de obiecte pe exemplul unei biblioteci.

Imaginea 8.1. Arborele obiectului XML DOM1

Page 38 of 123
HTML DOM este alcătuit dintr-un şir de obiecte care este prezentat ca un arbore în următoarea imagine.
Acest standard este definit pentru documentele HTML.

Imaginea 8.2. Arborele obiectului HTML DOM2


Pentru a face imaginile mai clare, vom enumera proprietăţile de bază ale structurii unui arbore:
• în partea de sus avem elementul rădăcină, care poate fi tradus ca rădăcina unui arbore;
• toate elementele, cu excepţia rădăcinii, au părintele lor - elementul care se află deasupra
acestuia;
• elementul poate avea mai mulţi copii - aceștia sunt elementele care sunt dedesubt şi, prin
urmare, pot fi mai multe;
• fiecare părinte construieşte o ramură cu descendenţii săi;
• elementul situat în partea de jos a arborelui se numeşte frunză.

Analizând această structură, putem concluziona că elementele sunt interconectate pe baza anumitor
reguli şi că ele formează un arbore al obiectului DOM.

Schimbarea conţinutului unui document HTML

Acum, când suntem familiarizaţi cu structura DOM, vom parcurge exemplul şi vom manipula
documentul HTML.
Cel mai important şi de bază obiect al structurii DOM, pe care îl vom folosi deseori, este Document. Îl
accesăm folosind proprietăţile documentului obiectului Window.
Există mai multe funcţii pentru selectarea unui element folosind proprietatea document; prezentarea lor
generală este prezentată în tabelul 8.1.

Metodă Descriere
getElementById(id) Această metodă preia un element printr-un id.
Această metodă furnizează toate elementele prin
getElementsByClassName(name)
numele clasei.
Această metodă furnizează toate elementele prin
getElementsByTagName(name)
numele tagului HTML.
Această metodă furnizează elementul pentru primul
querySelector(cssSelector)
selector css.
Această metodă furnizează toate elementele pentru
querySelectorAll(cssSelector)
selectorul css dat.
Tabelul 8.1. Metodele obiectului document

Page 39 of 123
Următorul lucru de care avem nevoie pentru modificarea conţinutului, pe lângă selectarea elementului,
este proprietatea innerHTML. Folosind această proprietate, avem posibilitatea de a introduce sau de a
modifica valoarea unui element selectat prin una dintre metodele prezentate în tabel.

În exemplul următor, vom folosi metoda getElementById(id) şi vom folosi proprietatea innerHTML
pentru a modifica textul din paragraful care are id-ul p-id.

Exemplul 1
<!DOCTYPE html>
1<html>
2<head>
3 <title>My Page</title>
4</head>
5<body>
6 <h2>My Header size 2</h2>
7 <p id="p-id">Paragraph value</p>
8 <script>
9 [Link]("p-id").innerHTML = "New paragraph
10value";
11 </script>
12</body>
13</html>

Rezultatul de pe pagină, când îl vom rula în browser, va arăta astfel:

Imaginea 8.3. Afişarea rezultatelor din exemplul 1

Adăugarea clasei în elementul HTML

În acest capitol vom plasa un element <p> şi vom manipula stilul acestuia.

Pentru a putea manipula stilul, în primul rând, acel stil trebuie definit. În acest caz, stilul va fi definit în
partea <head>, în elementul <style>, care este utilizat pentru a introduce codul CSS. Aici vom crea o
clasă pstyle.

La fel ca în exemplul anterior, vom folosi metoda getElementById(id), căreia îi transmitem id-ul
elementului <p>. După aceea, folosim o nouă proprietate pentru schimbare, adică adăugăm o clasă
CSS cu numele className.

Page 40 of 123
Exemplul 2
1<!DOCTYPE html>
2<html>
3<head>
4 <title>My Page</title>
5 <style>
6 .pstyle {
7 width: 400px;
8 height: auto;
9 background-color: #066c94;
10 text-align: center;
11 color: white;
12 }
13 </style>
14</head>
15<body>
16 <h2>My Header size 2</h2>
17 <p id="p-id">Paragraph value</p>
18 <script>
19 [Link]("p-id").className = "pstyle";
20 </script>
21</body>
22</html>

Rezultatul pe pagina din browser va arăta aşa:

Imaginea 8.4. Afişarea rezultatelor din exemplul 2

Alăturarea unui eveniment la elementul HTML

După segmentele anterioare, suntem gata să analizăm în detaliu puterea obiectului DOM şi, prin
exemplu, să învăţăm să schimbăm textul din elementul <p> printr-un eveniment.

Cuvântul event înseamnă eveniment, ceea ce în cazul nostru ar însemna că trebuie făcut ceva după un
eveniment. Lista tuturor evenimentelor se poate vedea la următorul link. Există două moduri de a defini
un eveniment: unul este în interiorul codului JavaScript în sine, iar celălalt este în interiorul elementului
HTML. În exemplul nostru, vom defini evenimentul din elementul HTML ca un atribut, iar acesta va fi un
eveniment onlick. Valoarea acestui atribut va fi o funcţie care va fi apelată dând clic pe elementul HTML
menţionat, adică în exemplul nostru - dând clic pe buton.

Evenimentul onclick apelează funcţia clickFunction(), care conţine codul, deja cunoscut, care va schimba
textul elementului <p>.

Page 41 of 123
1<!DOCTYPE html>
2<html>
3<head>
4 <title>My Page</title>
5</head>
6<body>
7 <p id="p-id">Click the button...</p>
8 <button onclick="clickFunction()">Change</button>
9 <script>
10 function clickFunction(){
11 [Link]("p-id").innerHTML = "clicked";
12 }
13
14 </script>
15</body>
16</html>

Rezultatul de pe pagină, când îl rulăm printr-un browser, va arăta astfel:

Imaginea 8.5. Afişarea rezultatelor din exemplul 3

1. [Link]
2. [Link]

Page 42 of 123
2.3. AJAX

Rezumat
• AJAX este o tehnologie care utilizează protocolul HTTP. AJAX ne permite să trimitem cereri către
server.
• Metodele pe care le foloseşte AJAX sunt XMLHttpRequest(), abortion(),
getAllResponseHeaders(), getResponseHeader(), open(method, url, async, user, psw), send(),
send (string) şi setRequestHeader().
• Proprietăţile pe care le foloseşte AJAX sunt onreadystatechange, readyState, responseText,
responseXML, status şi statusText.
• Pentru ca o cerere executată utilizând tehnologia AJAX să aibă succes, trebuie să creaţi o
instanţă a obiectului XMLHttpRequest, să definiţi cererea prin metoda open(), să trimiteţi
cererea cu metoda send() şi să definiţi funcţia care va fi apelată după schimbarea readyState.

AJAX este o tehnologie care foloseşte protocolul HTTP pentru a încărca date fără a reîncărca pagina.
Termenul AJAX este o abreviere de la Asynchronous JavaScript and XML. XML va fi subiectul
cursului Service Application Development.

Folosind această tehnologie, putem trimite date către server şi putem primi răspunsul în mod asincron.
Acest lucru realizează dinamismul paginii, deoarece conţinutul se încarcă mai repede în timp real, fără
a fi nevoie să reîncărcaţi întreaga pagină.

În imaginea 9.1. putem vedea o explicaţie a modului în care funcţionează AJAX.

Imaginea 9.1. AJAX – afişarea unei apelări1

Browser - browserul prin anumite evenimente (de exemplu, dând clic pe buton) creează şi trimite o
interogare folosind obiectul XMLHttpRequest.

Browserul nostru trimite o cerere prin internet către server; serverul descarcă cererea noastră şi o
procesează. După procesarea apelării noastre, acesta compune un răspuns şi îl trimite înapoi în
browserul nostru. Ceea ce se întâmplă pe server nu este vizibil pentru utilizator.

Page 43 of 123
Browserul descarcă răspunsul şi îl procesează prin JavaScript. Răspunsul procesat în acest mod este
utilizat pentru actualizarea conţinutului site-ului nostru.

AJAX foloseşte următoarea combinaţie:

• Obiectul XMLHttpRequest, care este deja susţinut de browser şi aparţine obiectelor JavaScript
încorporate (nu sunt necesare instalări sau biblioteci suplimentare); vom folosi obiectul
XMLHttpRequest mai târziu în exemple practice pentru a trimite o cerere către server;
• JavaScript şi HTML DOM, pentru a afişa valori sau a le înlocui pe cele existente.

Deoarece am învăţat deja în lecţia anterioară ce este un HTML DOM şi cum să-l folosim, vom trece
imediat la obiectul XMLHttpRequest şi la posibilităţile pe care ni le oferă.

După cum am spus deja, XMLHttpRequest este un obiect care există deja în browserul nostru, iar modul
în care creăm o instanţă a acelui obiect este:

1var xhttp = new XMLHttpRequest();

Această metodă este adaptată pentru browserele mai noi, iar pentru browserele mai
vechi IE5 şi IE6, pentru crearea instanţei se utilizează un mod diferit:

1var xhttp = new ActiveXObject("[Link]");


În continuarea lucrului şi în exemplele suplimentare, vom folosi un mod standard, mai nou de instanţiere.

În tabelul 9.1. există metode ale obiectului XMLHttpRequest. Vom folosi unele dintre aceste metode în
exemple practice.

Metodă Descriere
new XMLHttpRequest() Creează instanţa obiectului XMLHttpRequest
abort() Opreşte toate cererile curente
getAllResponseHeaders() Returnează toate informaţiile din antet
getResponseHeader() Returnează anumite informaţii despre antet
Determină cererea
method: transmite metoda (de ex. GET)
url: locaţia la care se trimite cererea
open(method, url, async, user, psw) sync: true (asincron) sau fals (sincron) - parametru
opţional
user: nume de utilizator (parametru opţional)
psw: parola (parametru opţional)
send() Trimite cererea către server
Această metodă se foloseşte pentru setarea antetului.
setRequestHeader() Antetul se adaugă prin setarea perechii de cheie-
valoare în timpul trimiterii

Tabelul 9.1. Metodele obiectului XMLHttpRequest

În tabelul 9.2. se găsesc toate proprietăţile obiectului XMLHttpRequest. Unele dintre acestea se vor folosi
în exemple practice.

Page 44 of 123
Proprietate Descriere
Defineşte funcţia care se va apela când se schimbă
onreadystatechange
proprietatea readyState
În această proprietate se salvează starea
XMLHttpRequest0: cererea nu este iniţializată1: se
readyState stabileşte conexiunea cu serverul2: cererea este
primită3: cererea se procesează4: cererea s-a finalizat şi
răspunsul este pregătit
responseText Returnează răspunsul ca string - text
responseXML Returnează răspunsul ca obiect XML
Returnează starea cerere200: "OK" 403:
status
"Forbidden"404: "Not Found"
statusText Returnează textul stării (de ex. "OK" sau"Not Found")

Tabelul 9.2. Proprietăţile obiectului XMLHttpRequest

Vom trece prin două exemple de utilizare AJAX folosind obiectul XMLHttpRequest. Primul exemplu
trimite o cerere folosind metoda GET, iar al doilea folosind metoda POST.

Vom arăta implementările client (JavaScript) şi server (Python) într-un exemplu în care serverul conţine
un dicţionar de prenume şi nume de familie, numit names_dict, iar clientul încearcă fie să adauge un
nume de familie pentru un prenume dat (GET), fie să adauge valori noi la dicţionarul respectiv (POST)

Serverul Python

Pentru server folosim codul deja cunoscut din cursul Python Net Programming. Singura modificare este
să adăugăm această linie:

1self.send_header('Access-Control-Allow-Origin', '*')

în cadrul funcţiei send_response_to_client(). Această linie ne permite să contactăm serverul de pe orice


client.

Vom salva acest script ca http_local_server.py.

Fişierul http_local_server.py
1from [Link] import HTTPServer, BaseHTTPRequestHandler
2from [Link] import parse_qs
3names_dict = {'john':'smith',
4 'david':'jones',
5 'michael':'johnson',
6 'chris':'lee'}
7class RequestHandler(BaseHTTPRequestHandler):
8 def do_GET(self):
9 self.log_message("Incoming GET request...")
10 try:
11 name = parse_qs([Link][2:])['name'][0]
12 except:
13 self.send_response_to_client(404, 'Incorrect parameters provided')
14 self.log_message("Incorrect parameters provided")
15 return

Page 45 of 123
16
17 if name in names_dict.keys():
18 self.send_response_to_client(200, names_dict[name])
19 else:
20 self.send_response_to_client(400, 'Name not found')
21 self.log_message("Name not found")
22
23 def do_POST(self):
24 self.log_message('Incoming POST request...')
25 data = parse_qs([Link][2:])
26 try:
27 names_dict[data['name'][0]] = data['last_name'][0]
28 self.send_response_to_client(200, names_dict)
29 except KeyError:
30 self.send_response_to_client(404, 'Incorrect parameters provided')
31 self.log_message("Incorrect parameters provided")
32
33 def send_response_to_client(self, status_code, data):
34 # Send OK status
35 self.send_response(status_code)
36 # Send headers
37 self.send_header('Content-type', 'text/plain')
38 self.send_header('Access-Control-Allow-Origin', '*')
39 self.end_headers()
40
41 # Send the response
42 [Link](str(data).encode())
43
44server_address = ('[Link]', 8080)
45http_server = HTTPServer(server_address, RequestHandler)
46http_server.serve_forever()

Pentru a porni serverul, trebuie să rulaţi fereastra de comandă şi să vă poziţionaţi în directorul în care
se află fişierul http_local_server.py. Trebuie să rulaţi scriptul cu comanda python http_local_server.py.

Trimiterea cererilor GET

În acest exemplu, vom folosi evenimentul onclick care va fi setat pe butonul Get şi care va apela funcţia
loadDoc().

În cadrul funcţiei, înainte de a trimite cererea către server, trebuie să descărcaţi valoarea (numele)
introdusă de utilizator în câmpul input. Facem acest lucru folosind metoda DOM getElementById cu
atributul value, respectiv cu linia:

1var name = [Link]("name").value;

Apoi, creăm o instanţă a obiectului XMLHttpRequest şi folosim metoda send() pentru a trimite cererea
către server.

Înainte de a utiliza metodele send() şi open(), trebuie să se utilizeze proprietatea onreadystatechange,


care ne va permite să modificăm conţinutul după ce răspunde serverul.

Page 46 of 123
Proprietatea onreadystatechange defineşte o funcţie care va înlocui textul dintr-un element p prin
metoda DOM getElementById().
Luăm răspunsul de pe server cu linia [Link] şi îl listăm în interiorul elementului p. Folosim
cuvântul-cheie this pentru apelarea proprietăţii acelui obiect din instanţa pe care am creat-o.
Transmitem trei parametri către metoda open(): metoda, care în cazul nostru este GET, apoi adresa URL
care corespunde serverului nostru Python şi, în cele din urmă, valoarea True, care indică faptul că dorim
o comunicare asincronă.
Când transmitem o adresă URL în funcţia open(), folosim concatenarea pentru a uni valoarea introdusă
de utilizator în câmpul input din stringul de interogare, pe care îl stocăm în variabila name.

Exemplul 1

1<!DOCTYPE html>
2<html>
3<head>
4 <title> AJAX</title>
5</head>
6<body>
7 <label>Name:</label>
8 <input type="text" id="name">
9 <button onclick="loadDoc()">Get</button>
10 <p id="text"></p>
11 <script type="text/javascript">
12 function loadDoc() {
13 var name = [Link]("name").value;
14 var xhttp = new XMLHttpRequest();
15 [Link] = function () {
16 [Link]("text").innerHTMl = [Link];
17
18 }
19 [Link]( "GET", "[Link] true);
20 [Link]();
21 }
22 </script>
23</body>
24</html>

În imaginea 9.2. puteţi vedea reprezentarea ecranului cu câmpul input, în timp ce în imaginea 9.3. vedeţi
reprezentarea ecranului în care este afişat rezultatul, adică valoarea returnată de server ca răspuns la
cererea trimisă.

Imaginea 9.2. Afişarea paginii înainte de trimiterea cererii GET

Imaginea 9.3. Afişarea paginii după trimiterea cererii GET

Page 47 of 123
Trimiterea cererilor POST

Trimiterea unei cereri POST este foarte asemănătoare cu trimiterea unei cereri GET. În acest exemplu, a
fost adăugat un alt label, cu numele Surname, precum şi un câmp <input> în care va fi introdus numele
de familie pe care dorim să i-l trimitem serverului de lângă numele în sine.
Serverul preia prenumele şi numele şi le adaugă în listă, pe care apoi le returnează ca răspuns.
Transmitem atât prenumele, cât şi numele prin adresa URL.
Exemplul 1
1<!DOCTYPE html>
2<html>
3<head>
4 <title> AJAX</title>
5</head>
6<body>
7 <label>Name:</label>
8 <input type="text" id="name">
9 <br>
10 <label>Surname:</label>
11 <input type="text" id="surname">
12 <br>
13 <button onclick="loadDoc()">Get</button>
14 <script type="text/javascript">
15 function loadDoc() {
16 var name = [Link]("name").value;
17 var surname = [Link]("surname").value;
18 var xhttp = new XMLHttpRequest();
19 [Link] = function () {
20 [Link]([Link]);
21 }
22 [Link]( "POST", "[Link]
23 "&last_name="+surname, true);
24 [Link]();
25 }
26 </script>
27</body>
28</html>

Imaginea 9.4. Afişarea aspectului paginii înainte de trimiterea cererii POST

În consolă, rezultatul va fi returnat în următorul format:


{'john': 'smith', 'david': 'jones', 'michael': 'johnson', 'chris': 'lee', 'name':
1'surname'}

Codul marcat, adică cheia name şi valoarea surname, sunt valorile introduse de utilizator prin câmpul
input.

1. [Link]

Page 48 of 123
2.4. Biblioteca Bootstrap

Rezumat
• Bootstrap facilizează semnificativ crearea paginilor web, iar în cadrul său este deja scrisă
stilizarea care se poate folosi în timpul creării.
• Bootstrap este format din fişiere CSS şi JavaScript.
• Bootstrap poate fi activat utilizând un CDN sau local, plasând fişierele într-o locaţie în care se
află site-ul în sine.
• Grila Bootstrap reprezintă scheletul unui design de site.
• Grila Bootstrap conţine trei grupuri de elemente: containere, rânduri şi coloane.
• Numărul maxim de coloane pe rând este de 12.
• Bootstrap foloseşte Flexbox, care facilitează semnificativ alinierea verticală şi orizontală a
elementelor.

Limbajele de bază pentru dezvoltarea front-end sunt HTML, CSS şi JavaScript. Aceste limbaje sunt
recunoscute de browserele web şi tocmai din acest motiv sunt de bază atunci când vine vorba de crearea
site-urilor. Crearea site-urilor s-a schimbat mult în comparaţie cu primele zile ale web-ului. Pe lângă o
bună cunoaştere a HTML, CSS şi JavaScript, trebuie să utilizaţi şi alte limbaje, biblioteci, instrumente sau
medii.

În cadrul acestei lecţii ne vom familiariza şi cu alte cadre software, biblioteci şi abordări care facilitează
în mod semnificativ dezvoltarea şi accelerează procesul de creare a unui site modern.

Ce este Bootstrap?

Bootstrap este un instrument al companiei Twitter, creat de Mark Otto şi Jacob Thornton pentru a obţine
aspectul şi comportamentul uniform al aplicaţiei web a acestei companii. Bootstrap a devenit open-
source şi este gratuit.

Imaginea 10.1. Logoul Boostrap1

Bootstrap facilitează mult crearea site-urilor, deoarece are deja o stilizare care poate fi utilizată. Ajută la
crearea unei vizualizări uniforme, facilitează aspectul elementelor şi accelerează optimizarea pentru
diferite dispozitive de pe toate browserele web. De fapt, Bootstrap este un cadru (framework) software
conceput pentru front-end.

Există mai multe abordări pe care le putem adopta atunci când creăm un site şi, deoarece dispozitivele
mobile sunt utilizate în mod predominant, se remarcă abordarea mobile first.

Abordarea mobile first dictează modul în care se realizează proiectarea site-ului. Iniţial, implică crearea
unei structuri pentru dispozitive cu cea mai mică lăţime a ecranului (dispozitive mobile), apoi pentru alte
dispozitive.

Page 49 of 123
Avantajele utilizării Bootstrap

• Economisirea timpului - scurtează timpul de creare a paginilor web comparativ cu scrierea


codului, datorită numărului mare de clase şi elemente predefinite.

• Depăşirea diferenţelor dintre browsere - Bootstrap are o comunitate open-source şi o echipă


mare de dezvoltatori care se asigură că orice problemă este detectată şi eliminată rapid.

• Folosirea abordărilor dovedite şi a bunelor practici - există o serie de modalităţi de a obţine un


efect funcţional şi vizual identic, care uneori nu este bun. Din acest motiv, este foarte bine să
aveţi o abordare dovedită pentru rezolvarea unei probleme.

• Reducerea posibilităţii de apariţie a conflictelor - odată cu dezvoltarea Bootstrap, au fost


dezvoltate biblioteci, precum şi componente încorporate care pot fi combinate. Toate acestea
îi permit lui Bootstrap să funcţioneze fără probleme.

• Mentenanţă uşoară şi muncă facilitată în echipă - utilizarea Bootstrap-ului le permite altor


programatori, care nu au lucrat deja la site-ul în cauză, să înţeleagă codul uşor şi rapid datorită
regulilor predefinite şi a documentaţiei excelente.

Structura Bootstrap

Bootstrap este format din mai multe elemente CSS şi JavaScript. Pentru a înţelege funcţionarea în
Bootstrap, cel mai bine este să începeţi prin analiza pachetului de pe site-ul oficial.

[Link]

Pentru explicaţii suplimentare, trebuie să daţi clic pe butonul Download de pe pagina principală a
linkului specificat. După aceea, se deschide o pagină care oferă mai multe variante diferite; vom selecta
opţiunea Compiled CSS and JS şi vom continua cu descărcarea.

Bootstrap este împărţit în mai multe concepte, şi anume:

• Layout – stilizare şi componente pentru crearea unui sistem Grid pentru aranjarea elementelor
paginii web;
• Content - stilizare care se referă la elementele HTML de bază pentru afişarea conţinutului pe
toate dispozitivele;
• Components - un set de componente gata făcute pentru realizarea părţilor mai mici sau mai
mari ale mediului de utilizator;
• Utilities - un set de stilizare auxiliară de uz general, care poate fi aplicat la aproape orice
element, economisind astfel timp.

Integrarea Bootstrap în proiect

Pentru a integra Bootstrap într-un proiect, trebuie să inseraţi fişierele Bootstrap în documentele HTML.
Există două moduri de a insera fişiere:

• folosind CDN;

• local, prin plasarea fişierelor în locaţia în care se află site-ul în sine.

Page 50 of 123
Integrarea CDN

Abordarea CDN implică recuperarea conţinutului de pe un server extern care găzduieşte fişiere. În cadrul
tagului head trebuie să includeţi un link cu Bootstrap CSS:

1<link rel="stylesheet" href="[Link]


24.5.0/css/[Link]" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFm
3C26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

În plus, trebuie să includeţi fişierul Bootstrap JS:

1<script src="[Link]
2bootstrap/4.5.0/js/[Link]" integrity="sha384-OgVRvuATP1z7JjHLkuOU
37Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>

Pentru ca Bootstrap să funcţioneze fără probleme, trebuie să includeţi încă două biblioteci JavaScript
înainte de fişierul Bootstrap JS în sine: jQuery (care va fi discutat în lecţia următoare) şi [Link].

<script src="[Link] integrity="sha384-


1DfXdz2htPH0
2lsSSs5nCTpuj/
3zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
4
5<script src="[Link]
[Link]@1.16.1/dist/umd/[Link]" integrity="sha384-9/reFTGAW83EW2RDu2S0V
7KaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>

Integrarea locală

În segmentul anterior am arătat etapa pentru descărcarea Bootstrap pe un computer local. Vom folosi
aceste fişiere pentru integrarea locală. Le vom plasa în interiorul proiectului şi le vom introduce într-un
document HTML.

Se propune crearea folderelor CSS şi JS în cadrul proiectului, care vor găzdui [Link] şi
[Link]. În plus, vom avea nevoie de biblioteca jQuery, pe care o vom descărca de pe
site-ul oficial jQuery şi o vom plasa în folderul JS.

[Link]

Se recomandă să descărcaţi întotdeauna cea mai recentă versiune (Download the compressed,
production jQuery x.x.x), astfel încât să aveţi mereu biblioteca importată cu cele mai recente modificări.
Primul link de pe pagina menţionată este întotdeauna biblioteca jQuery comprimată, care este potrivită
pentru utilizarea în această lecţie. Diferenţa dintre aceste două fişiere este că versiunea necomprimată
este mai uşor de citit, cu o mulţime de cod formatat şi cu un număr mare de comentarii care pot ajuta
la înţelegerea codului, şi este adesea folosită atunci când dezvoltă aplicaţii, în timp ce avantajul versiunii
comprimate este o dimensiune mai mică a fişierului, care permite încărcarea mai rapidă a site-urilor.

Fişierul în sine nu va fi descărcat automat, ci va fi afişat în browser. Trebuie să salvaţi codul afişat ca
[Link] dând clic dreapta pe cod şi selectând opţiunea Save as, apoi Save. Procedura de salvare a
fişierului este prezentată în imaginea 10.2.

Page 51 of 123
Imaginea 10.2. Salvarea fişierului jQuery

În documentul HTML, vom plasa un link către fişierul Bootstrap CSS în tagul head:

1<link rel=”stylesheet” href=”css/[Link]”>

şi, înainte de a închide tagul body, plasăm fişierele JS:

1<script src=”js/[Link]”></script>
2<script src=”js/[Link]”></script>

Configurarea proiectului Bootstrap

Pentru ca Bootstrap să funcţioneze corect, trebuie făcute mai multe modificări în documentul HTML.
La începutul fişierului trebuie să plasaţi o declaraţie:

1<!DOCTYPE html>

Apoi, trebuie să includeţi meta tagurile în head, pentru a seta câmpul vizual.

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

Verificarea funcţionării Bootstrap

Pentru a verifica succesul Bootstrap, vom crea două butoane în interiorul părţii body:

1<button type="button">Standard Button</button>


2<button type="button" class="btn btn-primary">Bootstrap button</button>
În imagine sunt prezentate două butoane, dintre care primul este butonul HTML standard, iar al doilea
este stilizat cu clasele predefinite Bootstrap btn şi btn-primary.

Imaginea 10.3. Butonul Standard şi Bootstrap

Page 52 of 123
Grila Bootstrap

Tehnica grilei este utilizată pentru un aspect mai bun pe site. Bootstrap este renumit pentru sistemul
său de grilă, tocmai pentru că acest sistem este excelent pentru aranjarea elementelor care se adaptează
apoi la diferite lăţimi ale ecranului.

Conceptul de grilă în web design

Grila reprezintă scheletul proiectării site-ului; practic, este determinată de linii invizibile a căror
intersecţie dă structura ordinii elementelor.

Imaginea 10.4. Grila pe exemplul unui site

Cum funcţionează grila Bootstrap?

Grila Bootstrap este formată din trei grupuri de elemente:

• containere;
• rânduri;
• coloane.

Containerul înconjoară elementele în interiorul cărora sunt plasate rândurile. Rândurile reprezintă
unităţile în care sunt situate coloanele, în timp conţinutul se plasează în coloane.

Imaginea 10.5. Structura de bază a grilei Bootstrap

Page 53 of 123
În imagine este prezentată structura de bază a acestei grile. Bootstrap poate conţine cel mult 12
coloane într-un rând.

Elementele de tip container

Primul grup de elemente ale grilei Bootstrap sunt containerele. Pe lângă plasarea rândurilor cu
coloane în ele, acestea pot fi folosite şi pentru centrarea întregului conţinut.

Există două clase de containere:

• container - reprezintă un container care este indentat de o parte şi centrat pe ecran;


• container-fluid - este un container care ocupă întreaga lăţime a ecranului.

Imaginea 10.6. Diferenţa dintre clasele container şi container-fluid

Definirea rândurilor şi a coloanelor

Odată ce este creat containerul, în interiorul acestuia vor fi definite rândurile şi coloanele. Prin adăugarea
clasei .row la elementul div, definim un rând nou. Apoi, în rânduri puteţi crea coloane cu conţinut, în aşa
fel încât în tagul div, cu clasa .col definim coloana.

Notă:

Este important să urmaţi ierarhia elementelor de bază pentru crearea unei grile: să existe rânduri în container şi
coloanele trebuie să se găsească în cadrul rândurilor.

Setarea lăţimii coloanelor

Am menţionat că folosim clasa .col pentru a specifica o coloană, dar această clasă nu specifică lăţimea
coloanelor noastre. Prin urmare, trebuie să adăugăm şi alte clase pentru a adapta afişarea la diferite
dispozitive.

Clasă Semnificaţie
.col-1 coloana va avea lăţimea de 1/12 din lăţimea totală
.col-2 coloana va avea lăţimea de 2/12 din lăţimea totală
.col-3 coloana va avea lăţimea de 3/12 din lăţimea totală
.col-4 coloana va avea lăţimea de 4/12 din lăţimea totală
.col-5 coloana va avea lăţimea de 5/12 din lăţimea totală
.col-6 coloana va avea lăţimea de 6/12 din lăţimea totală
.col-7 coloana va avea lăţimea de 7/12 din lăţimea totală
.col-8 coloana va avea lăţimea de 8/12 din lăţimea totală

Page 54 of 123
.col-9 coloana va avea lăţimea de 9/12 din lăţimea totală
.col-10 coloana va avea lăţimea de 10/12 din lăţimea totală
.col-11 coloana va avea lăţimea de 11/12 din lăţimea totală
.col-12 coloana va ocupa întreaga lăţime

Tabelul 10.1. Clasele Bootstrap predefinite pentru definirea coloanelor

Exemplul 1
1<div class="container-fluid">
2 <div class="row">
3 <div class="col-sm-4" style="background-color:grey;">.col-sm-4</div>
4 <div class="col-sm-8" style="background-color:grey;">.col-sm-8</div>
5 </div>
6</div>

În exemplul 1 am făcut un container cu clasa .container-fluid, care ocupă întreaga lăţime a ecranului şi
are un rând cu două coloane, a căror lăţime este 4 şi 8.

Imaginea 10.7. Aspectul exemplului 1

Grila receptivă (Responsive Grid)

Bootstrap se bazează pe un sistem automat de recunoaştere a caracteristicilor dispozitivului.


Dispozitivele pot fi împărţite în cinci grupuri de bază; de aceasta depinde şi lăţimea câmpului vizual.

Lăţimea ecranului
Prescurtarea pentru clase Condiţie
dispozitivului
Extra Small - câmpul vizual este mai mic de 576 pixeli
câmpul vizual este egal sau mai mare de 576
Small sm
pixeli
câmpul vizual este egal sau mai mare de 768
Medium md
pixeli
câmpul vizual este egal sau mai mare de 992
Large lg
pixeli
câmpul vizual este egal sau mai mare de 1200
Extra Large xl
pixeli

Tabelul 10.2. Clasificarea dispozitivelor

Conform datelor din tabel, putem folosi interogările media utilizate pentru a crea grila.

1@media (min-width: 576px)

După cum se poate vedea din exemplu, Bootstrap face distincţia între tipurile de dispozitive, iar
diviziunea se bazează pe prima abordare mobilă.

Page 55 of 123
Proprietăţile receptive ale containerului

Extra Extra
Clasa Bootstrap Small≥576px Medium≥768px Large≥992px
Small<576px Large≥1200px
.container 100% 540px 720px 960px 1140px
.container-fluid 100% 100% 100% 100% 100%

Tabelul 10.3. Proprietăţile containerului

În tabel, putem vedea cum se comportă containerele în raport cu lăţimea dispozitivului. Diferenţa dintre
clasele .container şi .container-fluid este că .container-fluid se comportă întotdeauna la fel, indiferent
de lăţime.

Proprietăţile receptive ale coloanelor

Forma clasei Semnificaţie


.col-* definirea coloanei pentru un câmp vizual mai mic de 576
.col-sm-* definirea coloanei pentru un câmp vizual de 576 pixeli sau mai mare
.col-md-* definirea coloanei pentru un câmp vizual de 768 pixeli sau mai mare
.col-lg-* definirea coloanei pentru un câmp vizual de 992 pixeli sau mai mare
.col-xl-* definirea coloanei pentru un câmp vizual de 1200 de pixeli sau mai mare

Tabelul 10.4. Clasele coloanelor

În tabele sunt prezentate clasele coloanelor cărora li se reglează lăţimea câmpului vizual. Pe lângă clasele
precum sm, md, lg, xl, în loc de * trebuie să puneţi numărul de la 1 la 12, pentru a determina cât spaţiu
vor ocupa coloanele.

Exemplul 2
1<div class="container">
2 <div class="row">
3 <div class="col-sm-3" style="background-color:grey;">.col-sm-3</div>
4 <div class="col-sm-5" style="background-color:grey;">.col-sm-5</div>
5 <div class="col-sm-4" style="background-color:grey;">.col-sm-4</div>
6 </div>
7</div>

Exemplul arată rândul în care coloanele ocupă 3/12, 5/12 şi 4/12 din locuri pentru un câmp vizual de
576 px sau mai mult.

Imaginea 10.8. Reprezentare grafică a exemplului

Page 56 of 123
Setarea grilei Bootstrap

Pe lângă setările de mai sus, Bootstrap are opţiuni de configurare şi personalizare mai avansate. Pe
lângă lăţime, sunt importante şi alinierea orizontală şi cea verticală.

Alinierea orizontală

Pentru a obţine o aliniere orizontală, trebuie să lăsaţi spaţiu în rând.

Exemplul 3
1<div class="container">
2 <div class="row">
3 <div class="col-sm-3" style="background-color:grey;">.col-sm-3</div>
4 <div class="col-sm-3" style="background-color:grey;">.col-sm-3</div>
5 <div class="col-sm-3" style="background-color:grey;">.col-sm-3</div>
6 </div>
7</div>

În exemplu sunt realizate trei coloane, care ocupă nouă dintre cele douăsprezece coloane din rând;
astfel, am făcut spaţiu în rând pentru alinierea orizontală.

Imaginea 10.9. Afişarea exemplului

Alinierea orizontală se realizează utilizând clasele prezentate în tabelul de mai jos în cadrul
elementului .row.

Clasă Efect
.justify-content-start alinierea pe marginea stângă
.justify-content-end alinierea pe marginea dreaptă
.justify-content-center în cadrul rândului, coloanele sunt centrate
.justify-content-around în cadrul rândului, coloanele sunt centrate cu un spaţiu egal între coloane
.justify-content-between coloane centrate, dar prima şi ultima se aliniază pe marginea stângă şi dreaptă

Tabelul 10.5. Clase pentru alinierea orizontală

Page 57 of 123
Iată cum ar arăta aceasta:

Imaginea 10.10. Efectul claselor de aliniere

Alinierea verticală a coloanelor

Comparativ cu alinierea orizontală, alinierea verticală prezintă o provocare mult mai mare, datorită
modului în care sunt aranjate elementele. Odată cu apariţia Flexbox-ului, alinierea verticală a fost
facilitată. O condiţie prealabilă pentru alinierea verticală este lăsarea unui spaţiu gol pe verticală.

Centrarea verticală se realizează în felul următor:

• prin centrarea grupată a tuturor coloanelor într-un singur rând, definind clasa corespunzătoare
pe elementul .row;
• prin centrarea individuală a elementelor, specificând clasa corespunzătoare pe coloana propriu-
zisă;
• alinierea verticală grupată prin definirea unei clase pe elementul .row se poate face folosind
clasele prezentate în tabelul 10.6.

Page 58 of 123
Clasă Efect
.align-items-start aliniază toate coloanele din partea de sus a rândului
.align-items-center centrează pe verticală toate coloanele din rând
.align-items-end aliniază toate coloanele din partea de jos a rândului
extinde toate coloanele pentru ca acestea să completeze spaţiul vertical disponibil
.align-items-stretch
în cadrul rândului

Tabelul 10.6. Clasele şi efectele alinierii verticale

Definirea ordinii coloanelor

Când vorbim despre ordinea coloanelor, acestea sunt aranjate în mod implicit în ordinea în care sunt
scrise în codul HTML. Cu toate acestea, acest lucru poate fi modificat folosind anumite clase care încep
cu prefixul order-*, urmat de un număr.

Ordinea coloanelor poate fi definită şi în funcţie de lăţimea câmpului vizual. În acest caz, se utilizează
comanda order- *-*.

Exemplul 4
1<div class="container">
2 <div class="row">
3 <div class="order-sm-2">First</div>
4 <div class="order-sm-8">Second </div>
5 <div class="order-sm-2">Third itm</div>
6 </div>
7</div>
În exemplul 4, aşteptăm ordinea coloanelor First, Second şi, în cele din urmă, Third, pentru că le aranjăm
în această ordine în codul HTML; cu toate acestea, datorită utilizării clasei order, ordinea va fi First, Third,
Second.

Mutarea coloanelor în cadrul rândului

Când lăsaţi un spaţiu gol într-un rând, deseori trebuie să mutaţi coloanele într-un mod specific. Pentru
a face acest lucru, trebuie să utilizaţi clasa offset-*, care vă permite să mutaţi elementele spre dreapta
pentru un anumit număr de coloane de referinţă.

De asemenea, la fel ca în cazul clasei order, clasa poate fi definită în funcţie de câmpul vizual. În acest
caz, se foloseşte offset-*-*.

Exemplul 5
1<div class="container">
2 <div class="row">
3 <div class="col-md-4">.col-md-4</div>
4 <div class="col-md-4 offset-md-4">.col-md-4 .offset-md-4</div>
5 </div>
6</div>

În exemplul 5 putem vedea că, folosind clasa offset, mutăm a doua coloană, care ocupă 4/12 din
lăţimea totală, cu încă 4/12 în dreapta. În acest fel, avem două coloane cu lăţimea de câte 4/12, fiecare
separate printr-un spaţiu gol cu o lăţime de 4/12.

Page 59 of 123
Influenţarea vizibilităţii coloanelor

În Bootstrap, există clase care pot fi folosite pentru a face elementele vizibile. Este important să ştim că
acestea nu influenţează exclusiv coloanele, ci servesc la controlul vizibilităţii oricărui element.

Clasa utilizată este:

.d-*
.d-*-*

În locul primului asterisc, se introduce o abreviere pentru numele clasei, care determină lăţimea
ecranului, iar în locul numerelor, în locul celui de-al doilea asterisc, se setează proprietăţile display:

• none
• inline
• inline-block
• block
• table
• table-cell
• table-row
• flex
• inline-flex

Exemplu pentru exersare

Ca exemplu pentru exersare, vom crea, utilizând biblioteca Bootstrap, un design receptiv al portalului
informativ. Portalul trebuie să fie format dintr-un meniu de navigare cu categorii, un glisor cu imagini,
o secţiune cu ştiri şi un subsol cu numele portalului şi anul producţiei.

La crearea portalului Bootstrap, vom include alte biblioteci necesare prin CDN; prin urmare, codul nostru
sursă va arăta astfel:

Punctul de plecare al codului

1<!DOCTYPE html>
2<html>
3<head>
4 <title>News portal</title>
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <link rel="stylesheet" href="[Link]
7bootstrap/4.5.0/css/[Link]"
8integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQAp
9FmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
10</head>
11<body>
12 <!-- Your code here -->
13 <script src="[Link]
14integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamo
15FVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
16 <script src="[Link]
171.16.1/dist/umd/[Link]" integrity="sha384-9/reFTGAW83EW2RDu2S0V

Page 60 of 123
18KaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
19 <script src="[Link]
20bootstrap/4.5.0/js/[Link]" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+
21h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
22</body>
23</html>

După definirea bazei, vom crea un element container în tagul body folosind clasa container.

Crearea elementului de tip container


1<div class="container">
2</div>

Mai întâi, vom insera meniul de navigare în interiorul containerului şi, pentru aceasta, vom utiliza
componenta Bootstrap deja existentă, Navbar. Nu trebuie să introducem această componentă în rânduri
şi coloane. După personalizarea componentei, adică ştergerea linkurilor şi câmpurilor de căutare inutile
şi adăugarea categoriilor menţionate din cerere, meniul de navigare arată astfel:

Meniul de navigare
1<nav class="navbar navbar-expand-lg
2navbar-dark bg-dark">
3 <a class="navbar-brand" href="#">
4News portal</a>
5 <button class="navbar-toggler"
6type="button" data-toggle="collapse" data-target=
7"#navbarSupportedContent" aria-controls="navbarSupportedContent"
8aria-expanded="false" aria-label="Toggle navigation">
9 <span class="navbar-toggler-icon"></span>
10 </button>
11
12 <div class="collapse navbar-
13collapse" id="navbarSupportedContent">
14 <ul class="navbar-nav mr-auto">
15 <li class="nav-item active">
16 <a class="nav-link" href="#">Info <span class="sr-only">
17(current)</span></a>
18 </li>
19 <li class="nav-item dropdown">
20 <a class="nav-link dropdown-
21toggle" href="#" id="navbarDropdown"
22role="button" data-toggle="dropdown"
23aria-haspopup="true" aria-expanded="false">
24 Local info
25 </a>
26 <div class="dropdown-menu"
27aria-labelledby=
28"navbarDropdown">
29 <a class="dropdown-item" href=
30"#">Local economy</a>
31 <a class="dropdown-item" href="#">Marketpalce</a>
32 <div class="dropdown-divider">

Page 61 of 123
33</div>
34 <a class="dropdown-item" href="#">Breaking news</a>
35 </div>
36 </li>
37 <li class="nav-item">
38 <a class="nav-link" href="#">Sport</a>
39 </li>
40 <li class="nav-item">
41 <a class="nav-link" href="#">Life</a>
42 </li>
43 <li class="nav-item">
44 <a class="nav-link" href="#">Health</a>
45 </li>
46 <li class="nav-item">
47 <a class="nav-link" href="#">Tech</a>
48 </li>
49 <li class="nav-item">
50 <a class="nav-link" href="#">Forum</a>
51 </li>
52 <li class="nav-item">
53 <a class="nav-link" href="#">Blog</a>
54 </li>
55 </ul>
56 </div>
57 </nav>

În componenta Navbar, am înlocuit clasa navbar-light cu navbar-dark şi bg-light cu bg-dark, pentru a


obţine un aspect mai închis al meniului de navigare. Aceste clase sunt deja încorporate în Bootstrap în
sine şi informaţiile despre ele pot fi găsite în documentaţia oficială de pe pagina componentei. Un mare
avantaj al utilizării componentei Bootstrap Navbar este că această componentă este receptivă şi se
transformă într-un aşa-numit meniu Hamburger pe dispozitive cu lăţimi mici.

Următorul pas este să adăugăm un glisor. Vom implementa glisorul folosind componenta Bootstrap
gata făcută cu numele Carousel. Vom alege o componentă cu titluri (Carousel with captions), pentru a
scrie titluri şi un scurt text al ştirilor din cadrul acestuia. Este foarte important să vă asiguraţi că toate
imaginile din interiorul glisorului au aceleaşi dimensiuni, pentru a evita „sărirea” conţinutului. Codul
componentei Carousel modificată arată astfel şi o plasăm chiar sub componenta Navbar:

Crearea unui element de tip slider


1<div id="carouselExampleCaptions"
2class="carousel slide"
3data-ride="carousel">
4 <ol class="carousel-indicators">
5 <li data-target=
6"#carouselExampleCaptions"
7data-slide-to="0" class="active"></li>
8 <li data-target=
9"#carouselExampleCaptions"
10data-slide-to="1"></li>
11 </ol>
12 <div class="carousel-inner">
13 <div class="carousel-item active">

Page 62 of 123
14 <img src="[Link]" class=
15"d-block w-100" alt="mobile-phone">
16 <div class="carousel-caption
17d-none d-md-block">
18 <h5>First slide label</h5>
19 <p>Nulla vitae elit libero, a pharetra
20augue mollis interdum.</p>
21 </div>
22 </div>
23 <div class="carousel-item">
24 <img src="[Link]" class="d-block w-100" alt="laptop">
25 <div class="carousel-caption
26d-none d-md-block">
27 <h5>Third slide label</h5>
28 <p>Praesent commodo cursus magna, vel scelerisque nisl
29consectetur.</p>
30 </div>
31 </div>
32 </div>
33 <a class="carousel-control-prev" href="#carouselExampleCaptions"
34role="button" data-slide="prev">
35 <span class="carousel-control-prev-icon"
36aria-hidden="true"></span>
37 <span class="sr-only">Previous</span>
38 </a>
39 <a class="carousel-control-next" href="#carouselExampleCaptions"
40role="button" data-slide="next">
41 <span class="carousel-control-next-icon"
42aria-hidden="true"></span>
43 <span class="sr-only">Next</span>
44 </a>
</div>

Notă:
Toate imaginile utilizate pentru realizarea exemplelor sunt gratuite şi disponibile pentru descărcare. Imaginile
trebuie salvate în acelaşi folder cu documentul HTML.

Când este creat glisorul, vom crea o secţiune în care vom afişa ştirile. Vrem ca coloana să ocupe lăţimea
ecranului complet pe dispozitivele cu lăţimi mici ale ecranului şi o treime din ecran pe dispozitivele cu
lăţimi medii şi mai mari ale ecranului. Codul nostru va arăta astfel:

Crearea secţiunii pentru afişarea ştirii


1<div class="row">
2 <div class="col-xs-12 col-md-4"></div>
3 <div class="col-xs-12 col-md-4"></div>
4 <div class="col-xs-12 col-md-4"></div>
5</div>

Page 63 of 123
În fiecare dintre coloane vom crea o structură pentru afişarea ştirilor, care va conţine o fotografie, titlul,
textul ştirii, precum şi un buton Read more. Vom utiliza componenta Bootstrap Card încorporată pentru
structura menţionată. După modificare, codul pentru structura ştirilor individuale arată astfel:

Structura ştirilor individuale


1<div class="card">
2 <img src="[Link]" class="card-img-top" alt="AI">
3 <div class="card-body">
4 <h5 class="card-title">News portal title</h5>
5 <p class="card-text">Some quick
6example text to build
7on the card title and make up
8the bulk of the card's content.</p>
9 <a href="#" class="btn btn-primary">Read more</a>
10 </div>
11</div>

În fiecare coloană trebuie să adăugaţi o componentă Card cu informaţii diferite. Dacă spaţiul vă permite,
în funcţie de cerinţe şi design, se poate afişa încă un rând de ştiri.

La final, trebuie să creăm o secţiune de subsol (footer). Cea mai simplă modalitate de a face acest lucru
este reutilizarea componentei Navbar, unde în locul linkurilor vom introduce numele portalului și anul
de creaţie. Codul subsolului va arăta astfel:

Crearea subsolului
1<nav class="navbar navbar-dark bg-dark">
2 <a href="#" id="footer">Copyright 2020 News portal</a>
3</nav>

Vom seta codul pentru crearea subsolului după închiderea div-ului cu clasa row. După acest cod, portalul
a primit, practic, aspectul final. Folosind CSS, putem stiliza suplimentar portalul, adică să adăugăm
margini în anumite locuri, pentru a evita lipirea elementelor, de exemplu, între meniul de navigare şi
glisor. De asemenea, vom schimba culoarea textului din subsol în alb. În secţiunea head a paginii HTML,
vom adăuga tagurile style şi următorul cod CSS:

Stilizarea CSS
1<style>
2 .navbar {
3 margin-top: 10px;
4 margin-bottom: 10px;
5 }
6 .carousel {
7 margin-bottom: 10px;
8 }
9 #footer {
10 color: white;
11 }
12</style>

Page 64 of 123
Aspectul final al portalului informativ este prezentat în imaginea 10.11.

Imaginea 10.11. Aspectul final al exemplului pentru exersare - portalul informativ

1. [Link]

Page 65 of 123
2.5. Biblioteca jQuery

Rezumat
• Biblioteca jQuery este implementată în două moduri: local şi folosind CDN.
• Un număr mare de selectori poate fi utilizat pentru a selecta elemente.
• Metodele append(), prepend(), after() şi before() sunt utilizate cel mai frecvent pentru a adăuga
elemente.
• Folosim metodele empty() şi remove() pentru a elimina elemente.
• Putem folosi metodele jQuery get() şi post() pentru a trimite cereri asincrone către server.

Biblioteca jQuery a fost creată iniţial de John Resig, în ianuarie 2006, influenţată de populara bibliotecă
cssQuery, creată anterior de Dean Edwards.

Această bibliotecă simplifică scrierea codului JavaScript sau facilitează:

• manipularea HTML/DOM;
• manipularea CSS;
• manipularea evenimentelor;
• lucrul cu efecte şi animaţii;
• AJAX.

Biblioteca jQuery este utilizată de 73% dintre cele mai populare zece milioane de site-uri. Analiza web
arată că este cea mai răspândită bibliotecă JavaScript, cu o utilizare de trei până la patru ori mai mare
decât orice altă bibliotecă JavaScript.

În această lecţie, vom acoperi elementele de bază ale bibliotecii jQuery, implementarea, selectorii,
manipularea DOM, gestionarea evenimentelor şi trimiterea şi procesarea cererilor către server.

Există două moduri de adăugare a unei biblioteci jQuery la un proiect, şi anume:

• prin descărcarea bibliotecii jQuery de pe site-ul oficial;


• folosind CDN.

Importarea bibliotecii jQuery

Prima modalitate de a importa biblioteca jQuery într-un proiect este să descărcaţi fişierul de pe site-ul
oficial:

[Link]

Se recomandă să descărcaţi întotdeauna cea mai recentă versiune (), astfel încât să aveţi mereu
biblioteca importată cu cele mai recente modificări. Primul link de pe pagina menţionată este
întotdeauna biblioteca jQuery comprimată, care este potrivită pentru utilizarea în această lecţie.
Diferenţa dintre cele două fişiere este că versiunea necomprimată este mai uşor de citit, cu o mulţime
de cod formatat şi un număr mare de comentarii care pot ajuta la înţelegerea codului şi este deseori
utilizată la dezvoltarea aplicaţiilor, în timp ce avantajul versiunii comprimate este o dimensiune mai mică
a fişierului, ceea ce asigură încărcarea mai rapidă a site-urilor.

Fişierul în sine nu va fi descărcat automat, ci va fi afişat în browser. Trebuie să salvaţi codul afişat ca
[Link], dând clic dreapta pe cod şi selectând opţiunea Save As, apoi Save. Procedura de salvare a
fişierului este prezentată în imaginea 11.1.

Page 66 of 123
Imaginea 11.1. Salvarea bibliotecii jQuery

Biblioteca jQuery este implementată înainte ca tagul body să fie închis.

1<script src=”[Link]”></script>

Notă:
Este important să se ia în considerare calea specificată în partea src, deoarece de aceasta depinde dacă biblioteca
jQuery va funcţiona. În exemplul nostru, biblioteca jQuery se află în acelaşi folder cu fişierul HTML în care
includem biblioteca.

Importarea bibliotecii jQuery prin CDN

O altă modalitate de a adăuga o bibliotecă jQuery este prin CDN. Aceasta înseamnă că trebuie să trageţi
biblioteca dintr-o altă sursă:

[Link]

Linkul menţionat anterior listează toate versiunile de jQuery. Se sugerează să descărcaţi cea mai recentă
versiune, care este întotdeauna în partea de sus a paginii.
Biblioteca jQuery este implementată înainte ca tagul body să fie închis.

1<script src="[Link]
[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/
3faTqgFtohetphbbj0=" crossorigin="anonymous"></script>

Sintaxa jQuery – selectorii

Oricine este familiarizat cu CSS va observa că logica jQuery este similară cu logica CSS. Iniţial, ne vom
ocupa de selecţia elementelor din lecţie. La fel ca în cazul CSS, selecţia sau selectarea este utilizată
pentru a găsi elemente pe o pagină web. Sunt importante doar criteriile conform cărora se va face
selecţia; unele dintre cele mai des utilizate sunt: id, class, atribute, tipul de element etc. O listă a tuturor
selectorilor se poate vedea în documentaţia oficială.

Caracterul $ se utilizează pentru accesul la biblioteca jQuery.

Page 67 of 123
Selectarea elementelor după tipul de element

Exemplul 1
1<!DOCTYPE html>
2<html>
3<head>
4 <title>jQuery - Selektovanje elemenata prema tipu elementa</title>
5</head>
6<body>
7 <p>Text 1</p>
8 <p>Text 2</p>
9 <p>Text 3</p>
10
11 <script src="[Link]
[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y
13/faTqgFtohetphbbj0=" crossorigin=
14"anonymous"></script>
15 <script type="text/javascript">
16 $("p").text("New text!");
17 </script>
18</body>
19</html>

Aşa cum am arătat în exemplu, în prima paranteză introducem atributul pe care dorim să îl selectăm -
de data aceasta este vorba de elementul p. Folosind selectarea în funcţie de tipul de element, vom
obţine toate elementele care sunt de acel tip. În exemplul nostru, vom schimba textul din toate
elementele p la valoarea New text!

Selectarea elementelor pe baza atributelor ID

Cel mai des, selectarea se face prin atribute ID. Pentru ca selectarea să aibă succes, este important ca
ID-ul de pe pagina pe care o căutăm să fie unic.

Exemplul 2
1<!DOCTYPE html>
2<html>
3<head>
4 <title>jQuery - Selektovanje elemenata na osnovu ID atributa</title>
5</head>
6<body>
7 <div id="text">Text</div>
8
9 <script src="[Link]
10/[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y
11/faTqgFtohetphbbj0=
12" crossorigin="anonymous"></script>
13 <script type="text/javascript">
14 $("#text").text("New text!");
15 </script>
16</body>
17</html>

Page 68 of 123
În acest exemplu, similar cu CSS, atunci când selectăm un element după ID, folosim semnul # şi valoarea
atributului ID în sine. Valoarea din tagul p care are atributul ID text, va fi schimbată în New text!

Selectarea după atributul class

În acest fel, sunt selectate toate valorile care au un atribut class. Această procedură este foarte
asemănătoare cu selecţia conform atributului ID; diferenţa este că rezultatul poate genera mai multe
elemente.

Exemplul 3
1<!DOCTYPE html>
2<html>
3<head>
4 <title>jQuery - Selektovanje po class atributu</title>
5</head>
6<body>
7 <p class="Class1">Text 1</p>
8 <p class="Class1">Text 2</p>
9 <p>Text 3</p>
10
11 <script src="[Link]
[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/
13faTqgFtohetphbbj0=" crossorigin=
14"anonymous"></
15script>
16 <script type="text/javascript">
17 $(".Class1").text("New text!");
18 </script>
19</body>
20</html>

Exemplul arată selecţia a două elemente p pe baza atributului class, în timp ce al treilea element p,
care nu are un atribut class, nu este selectat şi valoarea acestuia nu va fi modificată.

jQuery – gestionarea evenimentelor

Multe evenimente pot fi controlate folosind biblioteca jQuery. Cele mai frecvent utilizate metode de
gestionare a evenimentelor sunt prezentate în tabel:
Gestionarea Gestionarea Gestionarea
Gestionarea
evenimentelor mouse- evenimentelor evenimentelor
evenimentelor tastaturii
ului formularului documentului
click keypress submit load
dbclick keydown change resize
mouseenter keyup focus scroll
mouseleave blur unload

Tabelul 11.1. Cele mai frecvent utilizate evenimente

Sintaxa pentru definirea evenimentelor este la fel pentru toate metodele:

1$(selector).numele_metodei();

Page 69 of 123
Exemplul 5
<!DOCTYPE html>
1<html>
2<head>
3 <title>jQuery - Upravljanje događajima</title>
4</head>
5<body>
6 <p>Text</p>
7 <script src="[Link] integrity="sha256-
89/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
9 <script type="text/javascript">
10 $("p").click(function(){
11 alert("Click");
12 });
13 </script>
14</body>
15</html>

În exemplul 5 folosim metoda click, pe care o legăm de elementul p. Dacă dăm clic pe elementul p, se
apelează funcţia care va lista textul Click în interiorul ferestrei de alertă (alert).

Exemplul 6
1<!DOCTYPE html>
2<html>
3<head>
4 <title>jQuery - Upravljanje događajima</title>
5 <style>
6 .box {
7 background-color: red;
8 width: 100px;
9 height: 100px;
10 }
11 </style>
12</head>
13<body>
14 <div class="box"></div>
15 <h1>OUT</h1>
16 <script src="[Link]
[Link]/[Link]" integrity
18="sha256-9/aliU8dGd2tb6OSsuzixeV4y
19/faTqgFtohetphbbj0="
20crossorigin="anonymous"></script>
21 <script type="text/javascript">
22 var div1=$("div").mouseenter(function(){
23 $("div").css("background-color","blue");
24 $("h1").text("IN");
25 })
26</script>
27</body>
28</html>

În exemplul 6 am definit evenimentul mouseenter peste elementul div. Dacă utilizatorul trece cu mouse-

Page 70 of 123
ul peste div-ul menţionat, metoda css() va fi executată pe div, care primeşte proprietatea ca prim
parametru de intrare şi valoarea ca al doilea; în cazul nostru, acestea sunt proprietatea background-
color şi valoarea blue. De asemenea, în cadrul funcţiei care va fi apelată în timpul evenimentului,
schimbăm valoarea elementului h1 la textul IN folosind metoda text.

Ne vom afla adesea într-o situaţie în care trebuie să executăm o anumită parte a codului numai atunci
când pagina este încărcată (când este încărcat DOM). Putem face acest lucru folosind metoda ready().

1$(document).ready(function(){
2//Code...
3});

Crearea elementelor HTML noi

Pentru a crea elemente HTML noi, trebuie să parcurgem doi paşi. Primul este să creăm un element nou,
să îi atribuim conţinut şi, în cele din urmă, să-i setăm atribute, iar al doilea este să plasăm elementul în
locaţia dorită din document. Am învăţat deja cum să creăm un element folosind HTML şi DOM. Sintaxa
este următoarea:

1var new_p_tag =$(“<p></p>”).text(“New text”);

Îi putem adăuga un atribut elementului nou creat, folosind metoda attr(), care primeşte doi parametri
de intrare: atributul în sine (tip de atribut) şi valoarea acestuia. Vom atribui atributul id elementului
new_p_tag, care va avea valoarea new_id.

1var new_p_tag =$(“<p></p>”).text(“New text”);


2new_p_tag.attr(“id”,”new_id”);

Acest atribut nu este încă vizibil pe pagină, deoarece nu a fost încă setat la locaţia dorită. Avem mai
multe metode diferite disponibile pentru setarea atributelor.

Metode pentru adăugarea elementelor noi într-un document HTML


Există patru metode pentru adăugarea elementelor noi într-o pagină HTML:

• append() - adaugă conţinut la sfârşitul elementului selectat;


• prepend() - adaugă conţinut la începutul elementului selectat;
• after() - adaugă conţinut după elementul selectat;
• before() - adaugă conţinut înainte de elementul selectat.

Adăugarea conţinutului la sfârşitul elementului selectat - append()

Metoda append() este cea mai frecvent utilizată printre aceste metode; ea adaugă un element la sfârşitul
elementului selectat.

Page 71 of 123
Imaginea 11.2. jQuery append()

Dacă dăm drept exemplu că dorim să adăugăm un nou element span la un element div care are deja
trei elemente span, noul element va fi plasat după cele trei existente.

Adăugarea conţinutului la începutul elementului selectat - prepend()

Imaginea 11.3. jQuery prepend()

Noul element va fi adăugat chiar la început, înainte de elementele existente.

Page 72 of 123
Adăugarea conţinutului după elementul selectat - after()

Această metodă nu îi adaugă un element nou elementului selectat, ci plasează un element nou după
elementul selectat.

Imaginea 11.4. jQuery after()

Adăugarea conţinutului înainte de elementul selectat - before()

Această metodă este similară celei anterioare; diferenţa este că setează conţinutul înaintea elementului
selectat.

Imaginea 11.5. jQuery before()

Page 73 of 123
Eliminarea conţinutului din elementul selectat - empty()

Elementul div pe care l-am folosit în exemplele anterioare are trei elemente span în el. În cazul în care
dorim să-i schimbăm structura, trebuie să golim elementul div în prealabil, adică să-i eliminăm toate
elementele (în acest caz - trei elemente span). Din acest motiv, a fost creată o metodă specială empty(),
pe care o putem apela peste element.

Exemplul 7
1<!DOCTYPE html>
2<html>
3<head>
4<title>jQuery - method empty()</title>
5</head>
6<body>
7 <div>
8 <button type="button">Action</button>
9 <div id="element">
10 <h1>Header H1</h1>
11 <p>Paragraf 1</p>
12 <p>Paragraf 2</p>
13 </div>
14</div>
15
16<script src="[Link]
[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/
18faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
19<script type="text/javascript">
20 $(document).ready(function(){
21 $("button").click(function(){
22 $("#element").empty();
23 })
24 })
25</script>
26</body>
27</html>

Eliminarea elementului întreg – remove()

Spre deosebire de metoda empty(), care elimină doar descendenţii elementului selectat, metoda
remove() elimină atât descendenţii elementului, cât şi elementul care este selectat.

Efectele jQuery

Biblioteca jQuery ne oferă efecte gata făcute. Una dintre metodele de bază pentru efecte este hide().

Doar selectaţi elementul şi apelaţi metoda hide() pentru a obţine efectul. Pe lângă metoda hide(), avem
şi metoda show(). O listă a tuturor efectelor este disponibilă în documentaţia oficială.

Exemplul 8
1<!DOCTYPE html>
2<html>
3<head>

Page 74 of 123
4 <title>jQuery - show/hide effects</title>
5</head>
6<body>
7 <p>If you click on the "Hide" button, I will disappear.</p>
8
9 <button id="hide">Hide</button>
10 <button id="show">Show</button>
11
12 <script src="[Link]
[Link]" integrity=
14"sha256-9/aliU8dGd2tb6OSsuzixeV4y/
15faTqgFtohetphbbj0=" crossorigin=
16"anonymous"></script>
17
18 <script type="text/javascript">
19 $(document).ready(function(){
20 $("#hide").click(function(){
21 $("p").hide();
22 });
23 $("#show").click(function(){
24 $("p").show();
25 });
26 });
27 </script>
28</body>
29</html>

Exemplul arată cum funcţionează metodele hide() şi show(). În momentul în care dăm clic pe butonul
cu atributul ID hide, elementul p va dispărea, iar când dăm clic pe butonul cu atributul ID show,
elementul p va fi afişat din nou.

AJAX

Vom trece prin două exemple de utilizare a AJAX-ului folosind metoda jQuery. Primul exemplu trimite
o cerere folosind metoda GET, iar al doilea folosind metoda POST.

Vom prezenta implementările client (JavaScript/jQuery) şi ale serverului (Python) într-un exemplu în care
serverul conţine un dicţionar de prenume şi nume de familie, numit names_dict, iar clientul încearcă fie
să adauge un nume de familie pentru un prenume dat (GET), fie să adăuge valori noi la acel dicţionar
(POST), folosind stringuri interogative.

Serverul Python

Pentru server folosim codul deja cunoscut din cursul Python Net Programming. Singura modificare este
adăugarea următoarei linii:

1self.send_header('Access-Control-Allow-Origin', '*')

în cadrul funcţiei send_response_to_client(). Această linie ne permite să contactăm serverul de la orice


client.

Vom salva acest script ca http_local_server.py.

Page 75 of 123
Fişierul: http_local_server.py
1from [Link] import HTTPServer, BaseHTTPRequestHandler
2from [Link] import parse_qs
3names_dict = {'john':'smith',
4 'david':'jones',
5 'michael':'johnson',
6 'chris':'lee'}
7class RequestHandler(BaseHTTPRequestHandler):
8 def do_GET(self):
9 self.log_message("Incoming GET request...")
10 try:
11 name = parse_qs([Link][2:])['name'][0]
12 except:
13 self.send_response_to_client(404, 'Incorrect parameters provided')
14 self.log_message("Incorrect parameters provided")
15 return
16 if name in names_dict.keys():
17 self.send_response_to_client(200, names_dict[name])
18 else:
19 self.send_response_to_client(400, 'Name not found')
20 self.log_message("Name not found")
21
22 def do_POST(self):
23 self.log_message('Incoming POST request...')
24 data = parse_qs([Link][2:])
25 try:
26 names_dict[data['name'][0]] = data['last_name'][0]
27 self.send_response_to_client(200, names_dict)
28 except KeyError:
29 self.send_response_to_client(404, 'Incorrect parameters provided')
30 self.log_message("Incorrect parameters provided")
31
32 def send_response_to_client(self, status_code, data):
33 # Send OK status
34 self.send_response(status_code)
35 # Send headers
36 self.send_header('Content-type', 'text/plain')
37 self.send_header('Access-Control-Allow-Origin', '*')
38 self.end_headers()
39
40 # Send the response
41 [Link](str(data).encode())
42
43server_address = ('[Link]', 8080)
44http_server = HTTPServer(server_address, RequestHandler)
45http_server.serve_forever()

Pentru a porni serverul, trebuie să rulaţi fereastra de comandă şi să vă poziţionaţi în directorul în care
se află fişierul http_local_server.py. Trebuie să rulaţi scriptul cu comanda python http_local_server.py.

Page 76 of 123
GET request

Următorul exemplu va explica apelarea GET folosind metoda JQuery get(). Metoda are doi parametri:
unul este un string, care este, de asemenea, o adresă URL dorită către server, iar celălalt este o funcţie
care este executată imediat după răspunsul de la server. Funcţia primeşte primul parametru, care este
un răspuns de la server.

Notă:
Pentru a transmite numele din câmpul input prin URL-ul în sine, trebuie să preluaţi valoarea introdusă folosind
metoda val() asupra câmpului selectat $(“#name”).

După preluarea parametrului, listarea se face prin metoda text(), care listează valoarea pe care am setat-
o în elementul p, care, în acest caz, este răspunsul de la server.

Exemplul 8
1<!DOCTYPE html>
2<html>
3<head>
4<title>jQuery - AJAX GET method</title>
5</head>
6<body>
7 <div>
8 <label>Name:</label>
9 <input id="name">
10 <button onclick="checkForName()">Check</button>
11 <br>
12 <p id="response"></p>
13 </div>
14
15
16 <script src="[Link]
[Link]" integrity="sha256-9/
18aliU8dGd2tb6OSsuzixeV4y/
19faTqgFtohetphbbj0=" crossorigin=
20"anonymous"></script>
21
22 <script>
23 function checkForName() {
24 var name = $('#name').val();
25 $.get("[Link]
26?name=" + name, function (data) {
27 $("#response").text(data);
28 }).fail(function(err){
29 $("#response").text([Link]);
30 });
31 }
32</script>
33</body>
34</html>

Page 77 of 123
Din exemplu, putem vedea că, după ce serverul i-a returnat utilizatorului răspunsul din datele variabilei,
listăm acelaşi răspuns în interiorul elementului p care are valoarea response a atributului ID. Serverul
nostru Python este setat astfel încât, dacă numele nu există în dicţionar, el returnează o eroare, adică
starea 400. Prin urmare, trebuie să captăm această eroare cu funcţia înlănţuită fail(). Funcţia primeşte
primul parametru, un obiect, care conţine informaţii despre cererea trimisă şi răspunsul primit de la
server, unde putem vedea anumite informaţii, precum răspunsul text, starea returnată de server şi altele.
Următoarele imagini ne prezintă cum arată pagina înainte şi după apelarea către server.

Imaginea 11.6. Afişarea imaginii înainte de trimiterea cererii GET

Imaginea 11.7. Afişarea paginii după trimiterea cererii GET şi listarea rezultatelor

POST request

POST se deosebeşte foarte puţin de cererile GET. Singura diferenţă este că se foloseşte metoda post().
A fost adăugat încă un element input, pentru a îndeplini cererea URL, şi anume pentru a trimite
prenumele şi numele.
Când cererea a reuşit, am primit un răspuns pe care l-am afişat în consola browserului.

Page 78 of 123
Exemplul 9
1<!DOCTYPE html>
2<html>
3<head>
4 <title>jQuery - AJAX POST method</title>
5</head>
6<body>
7 <div>
8 <label>Name:</label>
9 <input id="name">
10 <br>
11 <label>Surname:</label>
12 <input id="surname">
13 <br>
14 <button onclick="addNew()">Get</button>
15
16 </div>
17
18
19 <script src="[Link]
[Link]" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y
21/faTqgFtohetphbbj0="
22crossorigin="anonymous"></script>
23
24 <script>
25 function addNew() {
26 var name = $('#name').val();
27 var surname = $('#surname').val();
28 $.post("[Link]
29/?name=" + name + "&last_name="
30+ surname, function (data) {
31 [Link](data);
32 });
33 }
34
35</script>
36</body>
37</html>

Imaginea 11.8. Afişarea paginii înainte de trimiterea cererii POST

Ca răspuns de la server, obţinem rezultatul în următorul format:


{'john': 'smith', 'david': 'jones', 'michael': 'johnson', 'chris': 'lee',
1'sonja': 'elton'}

Codul marcat, respectiv cheia 'sonja' şi valoarea 'elton' sunt valorile introduse de utilizator prin câmpul
input.

Page 79 of 123
3.1. Mediul şi ciclul de viaţă

Rezumat
• Django este un cadru web Python de nivel superior conceput pentru dezvoltarea rapidă a site-
urilor sigure şi uşor de întreţinut.
• Django a fost dezvoltat iniţial între 2003 şi 2005, de o echipă de programatori web care a lucrat
la un site de ziar online.
• Django în sine se bazează pe şablonul MVT (Model-View-Template).
• Un proiect poate conţine mai multe aplicaţii, în timp ce o aplicaţie nu poate conţine mai multe
proiecte; prin urmare, o aplicaţie poate face parte din mai multe proiecte.
• Înainte de a utiliza Django, trebuie să-l instalaţi cu comanda pip3 install django.
• Pentru a crea un proiect Django, apelăm: django-admin startproject first_project.
• Pentru a crea o aplicaţie în cadrul proiectului Django creat, apelăm: python [Link] startapp
my_first_app.
• Intermediarii (middlewares) sunt utilizaţi chiar înainte ca afişarea noastră să poată procesa
cererea şi să returneze răspunsul.
• Aplicaţia Django este formată din şase niveluri:

o intermediarii (middleware) cererii;


o routerele URL;
o vizualizările;
o procesoarele de context;
o vizualizarea şablonului;
o intermediarii răspunsului.

Django este un cadru web Python de nivel superior, conceput pentru dezvoltarea rapidă a site-urilor
sigure, uşor de întreţinut. Ca atare, rezolvă o mulţime de probleme care pot apărea în timpul creării,
configurării şi instalării site-urilor, toate pentru motivul ca noi, programatorii, să ne concentrăm mai
mult pe scrierea aplicaţiilor din spatele site-ului. Este gratuit şi open-source, cu o bază mare de utilizatori
şi o documentaţie destul de detaliată.

Django a fost iniţial dezvoltat de o echipă de programatori web care a lucrat la un site de ziar online
între anii 2003 şi 2005. După ce au creat mai multe site-uri similare, şi-au dat seama că o mare parte din
baza de coduri tocmai a fost reutilizată. Acest cod partajat care a fost utilizat pe mai multe site-uri, a
evoluat într-un open-source numit Django, în iulie 2005.

Din prima zi a creaţiei sale, Django a evoluat şi a progresat ca un cadru web - de la prima sa versiune
1.0, în septembrie 2008, la versiunea 3.0 din decembrie 2019. Fiecare versiune nouă a adăugat remedieri
pentru erorile cunoscute, precum şi funcţionalităţi noi, cum ar fi suport pentru noile sisteme de baze de
date, sisteme de şabloane, stocare în cache, funcţii generice pentru vizualizări şi clase, toate acestea
pentru a reduce timpul necesar programatorului pentru a realiza codificarea la unele dintre aceste
aspecte generale.
Django în sine se bazează pe şablonul MVT (Model-View-Template), care diferă de şablonul MVC
discutat anterior. Componentele şablonului MVT sunt:

• Model - la fel ca modelul din şablonul MVC, este responsabil pentru lucrul cu datele în sine;
• Vizualizare - la fel ca în controlorul din şablonul MVC, acesta conţine toată logica pe care o
foloseşte aplicaţia web şi serveşte ca o legătură între model şi şablon;
• Şablon - similar cu vizualizarea din şablonul MVC; se referă la prezentarea datelor către utilizator.

Page 80 of 123
Imaginea 12.1. MVC vs. modelul MTV1

Fapt interesant
De ce numele Django? Adrian Holovaty, unul dintre autorii săi, este, de asemenea, un chitarist de jazz talentat
şi un mare fan al lui Django Reinhardt, unul dintre cei mai mari şi mai influenţi chitarişti de jazz. Reinhardt este
cunoscut pentru dexteritatea sa excepţională atunci când vine vorba de chitară, deşi din cauza unui incediu şi-a
pierdut capacitatea de a mişca trei degete ale mâinii stângi, nu a putut utiliza decât degetul arătător şi mijlociu.
Acest lucru l-a inspirat pe Adrian, care a spus că a creat Django pentru a permite crearea de site-uri folosind doar
două degete.

Avantajele Django

Unele dintre avantajele utilizării Django sunt:

• suport pentru maparea obiectual-relaţională - Django asigură legătura dintre modelul obiect şi
cele mai cunoscute sisteme de baze de date (MySQL, Oracle, PostgreSQL...);
• suport multilingv - Django susţine site-uri multilingve utilizând un sistem de internaţionalizare
încorporat, care ne permite să creăm un site care poate fi afişat în mai multe limbi;
• suport pentru cadre - Django are suport integrat pentru Ajax, RSS, cache şi alte cadre;
• interfaţă grafică administrativă - Django oferă o interfaţă cu utilizatorul gata pregătită pentru
acţiunile administratorului;
• mediu de dezvoltare - Django în sine vine cu un server web simplu care facilitează testarea şi
dezvoltarea unei aplicaţii web.

Notă:
Proiecte şi aplicaţii

Până acum, am menţionat aplicaţiile în text, dar în Django există şi proiecte. Sub noţiunea de aplicaţie, aici ne
referim la o aplicaţie web care efectuează o anumită activitate - de exemplu, o aplicaţie de vot cu răspunsuri
oferite, o aplicaţie pentru înregistrarea evenimentelor, o bază de date cu informaţii disponibile publicului etc. Un
proiect este o colecţie de configuraţii şi aplicaţii pentru un anumit site. Un proiect poate conţine mai multe
aplicaţii, în timp ce o aplicaţie nu poate conţine mai multe proiecte. Prin urmare, o aplicaţie poate face parte din
mai multe proiecte.

Page 81 of 123
Crearea primului proiect Django

Înainte de a lucra cu Django, mai întâi trebuie să-l instalaţi cu comanda pip3 install django. După
instalarea cu succes, deschideţi linia de comandă şi poziţionaţi-vă în folderul în care doriţi să realizaţi
primul vostru proiect. Odată ce aţi făcut acest lucru, trebuie să executaţi comanda django-admin
startproject first_project. Cu aceasta, aţi creat un folder în folderul curent, numit first_project, care este
şi numele primului proiect Django. Dacă am deschide folderul, am vedea următoarea structură:

first_project /

├── [Link]

├── first_project/

├── __init__.py

├── [Link]

├── [Link]

├── [Link]

└── wsgi.p

Aceste fişiere sunt create automat după execuţia comenzii „startproject”. Semnificaţia acestor fişiere
este următoarea:

• first_project/ - reprezintă directorul care include proiectul nostru; acest nume este irelevant
pentru Django şi se poate schimba ulterior;
• py - un script auxiliar care ne permite să lucrăm cu serverul prin linia de comandă; unele dintre
comenzile pe care le primeşte acest script sunt: runserver, startapp, createsuperuser, migrările
etc.;
• first_project/ - reprezintă numele directorului care va conţine pachetul (ca şi cum am fi creat un
pachet pentru Python) care face parte din proiectul nostru;
• first_project/__init__.py - un fişier gol care îi spune compilatorului Python să trateze ca un pachet
directorul care conţine __init__.py;
• first_project/[Link] - fişierul principal de configurare al proiectului nostru; în el adăugăm şi
informaţii despre aplicaţiile pe care le creăm, mai precis, în lista „INSTALLED_APPS”;
• first_project/[Link] - fişier care definește rutele (căile de pe server) cu vizualizări;
• first_project/[Link] - WSGI (Web Server Gateway Interface) este un intermediar între cererile
clientului către server şi permite conectarea cererii de utilizator la proiectul serverului;
• first_project/[Link] - Asynchronous Server Gateway Interface este succesorul intermediarului
WSGI.

În acest moment avem un proiect creat şi este încă necesar să creăm prima aplicaţie. Facem acest lucru
poziţionându-ne în promptul de comandă, în folderul în care se găseşte fişierul [Link] şi apoi rulăm
următoarea comandă:

1python [Link] runserver [Link]:8000

Page 82 of 123
În acest fel, am folosit fişierul [Link] pentru a crea prima aplicaţie my_first_app. Acum, folderul
my_first_app a apărut în folderul nostru, a cărui structură este:

my_first_app/

├── migrations/

├── __init__.py

├── [Link]

├── [Link]

├── [Link]

├── [Link]

└── [Link]

Am întâlnit deja unele dintre aceste fişiere la crearea proiectului, în timp ce celelalte fişiere sunt:

• [Link] - fişier în care conectăm modele cu interfaţa administrativă Django;


• [Link] - fişier în care stocăm configurarea aplicaţiei noastre;
• [Link] - fişier corespunzător modelului aplicaţiei noastre, responsabil de lucrul cu datele;
• [Link] - fişier care ne permite să scriem cazuri de testare pentru aplicaţia noastră;
• [Link] - fişier în care scriem logica de afişare, care este, de fapt, responsabilă pentru
gestionarea cererilor şi a răspunsurilor.

După ce am creat prima noastră aplicaţie, putem testa succesul acestui proces şi succesul instalării
Django în sine. Facem acest lucru astfel încât, mai întâi, în promptul de comandă, ne poziţionăm în
folderul care conţine [Link] şi rulăm următoarea comandă:

1python [Link] runserver [Link]:8000

Prima dată transmitem fişierul [Link] la această comandă şi acestuia îi transmitem comanda
runserver cu care pornim serverul, precum şi adresa şi numărul portului prin care acest server poate fi
accesat. În cazul nostru, este vorba de o adresă IP locală - [Link] cu portul 8000. Acest număr de port
este administrat neoficial de IANA ca Django Development Webserver, ceea ce înseamnă că este
înregistrat ca atare. O listă detaliată a porturilor TCP înregistrate şi ocupate poate fi găsită la acest link.
Dacă se întâmplă ca portul să fie ocupat, vom primi o astfel de eroare (pentru a provoca intenţionat o
eroare, pentru a observa o astfel de situaţie, am ales portul deja ocupat 1604):

Page 83 of 123
Imaginea 12.2. Rularea proiectului Django pe un port ocupat

În acest caz, încercaţi un alt port.

Dacă deschidem adresa cu care am început iniţial proiectul în browser ([Link]:8000), vom obţine
următorul ecran:

Imaginea 12.3. Afişarea instalării cu succes şi a rulării cadrului Django

În acest fel, am stabilit cu siguranţă că instalarea Django, configuraţia sa şi crearea primului proiect şi a
aplicaţiei s-au realizat cu succes.

Intermediarii Django

Intermediarii (middlewares) sunt utilizaţi chiar înainte ca afişarea nostru să poată procesa cererea şi să
returneze răspunsul. Lista acestor intermediari se află în fişierul [Link], în variabila MIDDLEWARE
de tip listă. Lista iniţială a acestor intermediari, care este creată la crearea proiectului, arată astfel:

Page 84 of 123
Lista MIDDLEWARE:
1MIDDLEWARE = [
2 '[Link]',
3 '[Link]',
4 '[Link]',
5 '[Link]',
6 '[Link]',
7 '[Link]',
8 '[Link]',
9]

De asemenea, se poate rula un proiect Django fără aceşti intermediari, dar dacă creăm un proiect prin
comanda django-admin startproject, vom obţine această listă încorporată de intermediari.

Fiecare dintre aceşti intermediari are diferite etape şi metode ale ciclului de viaţă care sunt apelate la
procesarea cererilor. Rezultă că cererea HTTP trebuie să treacă prin toţi intermediarii şi trebuie să
returneze aceeaşi cale ca răspuns. De exemplu, deoarece am spus că fiecare dintre intermediari
îndeplineşte o funcţie specifică, intermediarul din lista noastră, AuthenticationMiddleware, este
responsabil de conectarea clienţilor la cererile lor folosind sesiuni. De asemenea, este important ca
Django să aplice intermediarii MIDDLEWARE de sus în jos atunci când vine vorba de o cerere primită şi
să-i aplice din nou în ordine, dar de jos în sus atunci când vine vorba de răspuns, așa cum se poate
vedea în imaginea de mai jos:

Imaginea 12.4. Crearea cererii şi a răspunsului prin intermediari 2

Page 85 of 123
Nivelurile aplicaţiei Django şi ciclul de viaţă al fluxului de cerere-răspuns

Există şase niveluri care alcătuiesc o aplicaţie Django:

1. intermediarii (middleware) cererii;


2. routerele URL;
3. vizualizările;
4. procesoarele de context;
5. vizualizarea şablonului;
6. intermediarii răspunsului.

La primirea cererii HTTP, apar şi intermediarii cererii, iar aceştia sunt aplicaţi în ordinea în care au ajuns
cererile. După această procesare, cererea este trimisă la routerul URL, care ia calea din cererea propriu-
zisă şi încearcă să potrivească calea care vine cu cererea cu căile disponibile pe serverul definit în fişierul
[Link]. Dacă se găseşte calea potrivită, cererea este redirecţionată către vizualizarea ([Link]) care se
potriveşte cu acea cale.

Vizualizările folosesc acum datele din cererea trimisă pentru a o procesa şi a genera feedbackul necesar
(context), care va folosi efectiv şablonul pentru a genera un răspuns (vizualizarea şablonului) care va fi
trimis înapoi clientului.

Din nou, acum, acest răspuns trebuie să treacă prin aceiaşi intermediari prin care a trecut și la început,
înainte ca clientul să-l primească. Intermediarii de răspuns prin care trece răspunsul îl pot modifica
ulterior prin adăugarea/modificarea câmpurilor de antet.

Imaginea 12.5. Navigarea cererii şi a răspunsului prin cadrul Django 3

1. [Link]
2. [Link]
3. [Link]

Page 86 of 123
3.2. Lucrul cu vizualizări şi şabloane

Rezumat
• O vizualizare (view) Django este un intermediar între informaţii (date) şi prezentarea acestor
date către utilizator printr-un şablon.
• Vizualizările sunt reprezentate ca funcţii sau clase Python.
• În fişierul [Link], lista INSTALLED_APPS este utilizată pentru a adăuga aplicaţiile noastre la
proiectul curent.
• Clasa HttpResponse se folosește la convertirea răspunsului sau a valorii returnate a funcţiei
index() într-un obiect HttpResponse. Cu acest obiect, convertim datele pe care dorim să le
trimitem într-un răspuns. Folosind obiectul HttpResponse(), putem seta şi câmpurile dorite în
antet.
• Argumentul funcţiei include() este de tip string şi reprezintă calea către fişierul [Link] pe care
vrem să îl folosim, de fapt, la maparea căii dorite.
• Scopul funcţiei render() este de a genera răspunsuri din şablon şi de a le trimite clientului;
aceasta ia ca parametri cererea curentă a clientului, numele şablonului, precum şi un dicţionar
ale cărui chei sunt numele variabilelor pe care le folosim în şablon şi valorile variabilelor din
Python.
• Limbajul şablonului Django ne permite să generăm întreaga pagină HTML de pe server şi, ca
atare, fără niciun alt cod Python, să îl trimitem clientului, câştigând astfel siguranţa şi eficienţa
generării şabloanelor.

O vizualizare (view) Django este un intermediar între informaţii (date) şi prezentarea acestor date către
utilizator printr-un şablon. Astfel, vizualizarea este responsabilă pentru livrarea datelor cerute de
utilizator. Vizualizările sunt reprezentate ca funcţii sau clase Python.

Vizualizările ca clase

Designul original al lui Django implica doar utilizarea funcţiilor în vizualizare, iar principiul orientat pe
obiecte a fost adăugat abia mai târziu. Cu această adăugare, posibilitatea de a reutiliza acelaşi cod a
crescut şi, odată cu aceasta, și productivitatea.

Vizualizările ca funcţii

Aceste funcţii acceptă cererea web ca argument şi returnează răspunsul web ca valoare returnată a
acelei funcţii. Răspunsul poate fi orice, de la cod HTML, cod XML, documente PDF, imagini, până la
erori şi redirecţionări. În acest fel, ne putem configura rapid aplicaţia. Problema apare numai în
proiecte Django mari, unde în acest fel ridicăm complexitatea codului. În exemplul nostru, care se
bazează pe proiectul din lecţia anterioară, pentru o explicaţie şi o înţelegere mai uşoară, vom folosi
vizualizări bazate pe funcţii.

Implementarea vizualizării

Implementarea primei noastre vizualizări se va baza pe proiectul deja creat, first_project, din lecţia
anterioară. Folosind linia de comandă, ne vom poziţiona în folderul first_project, care conţine şi fişierul
[Link] şi vom executa o comandă pentru a crea o nouă aplicaţie, numită book_library:

1python [Link] startapp book_library

După execuţia acestei comenzi, în directorul proiectului apare un alt folder, numit book_library, care
corespunde aplicaţiei noastre. În el vom găsi fişierul [Link], la care vom adăuga funcţia index(), care
se va ocupa cu listarea textului Book library atunci când utilizatorul deschide pagina de pornire a site-

Page 87 of 123
ului nostru. Înainte de a continua, trebuie să deschidem fişierul [Link] şi să adăugăm un element
nou în lista INSTALLED_APPS, a cărei valoare va fi de tip sting, adică numele aplicaţiei noastre, astfel
încât Django să ştie că am adăugat o altă aplicaţie:

Aspectul listei INSTALLED_APPS:


1INSTALLED_APPS = [
2 '[Link]',
3 '[Link]',
4 '[Link]',
5 '[Link]',
6 '[Link]',
7 '[Link]',
8 'book_library',
9]

Imediat după crearea aplicaţiei noastre, fără modificări, [Link] arată astfel:

1from [Link] import render


2# Create your views here.

Deci, singurul lucru care există în acel fişier după creare este comanda de importare a funcţiei render,
responsabilă pentru trimiterea şablonului către utilizator, despre care vom discuta mai târziu. Deoarece
am spus că vizualizările bazate pe funcţii au request ca parametru de intrare - cererea utilizatorului către
server, iar ca valoare returnată au response - răspunsul serverului către client, fişierul nostru [Link],
după implementarea funcţiei index(), arată astfel:

book_library/[Link] după implementare:


1from [Link] import render
2from [Link] import HttpResponse
3books = [{'title':'The Picture of Dorian Gray','year':'1890'},
4 {'title':'Pride and Prejudice','year':'1813'},
5 {'title':'The Adventures of Tom Sawyer ', 'year':'1875'},
6 {'title':'The Raw Youth', 'year':'1875'},
7 {'title':'Twelve Years a Slave ', 'year':'1853'},
8 {'title':'Hamlet', 'year':'1603'}]
9# Create your views here.
10def index(request):
11 return HttpResponse("<h1>Book library</h1>")

De asemenea, pe lângă implementarea acestei funcţii, am adăugat o listă de dicţionare cu numele


cărţilor şi anul publicării, de care vom avea nevoie mai târziu. Primul lucru pe care îl vedem în legătură
cu [Link] de bază creat după crearea aplicaţei, este că a fost importată clasa HttpResponse. Acesta
serveşte la convertirea răspunsului, adică a valorii returnate a funcţiei index(), într-un obiect
HttpResponse. Cu acest obiect convertim datele pe care dorim să le trimitem într-un răspuns. Folosind
obiectul HttpResponse(), putem seta şi câmpurile dorite în antet. Instrucţiuni mai detaliate pentru
utilizarea acestui obiect pot fi găsite în documentaţia oficială.

Am redirecţionat o parte simplă a codului HTML către constructorul acestei clase – un tag h1 cu
textul Book library, care îi va fi listat utilizatorului pe ecran.

Page 88 of 123
După ce am adăugat funcţionalitatea dorită la fişierul [Link], trebuie să setăm proiectul nostru Django
pentru a lista Book library pe pagina principală. Putem face acest lucru în două moduri:

Starea unei adrese URL din directorul proiectului:

Pentru acest mod, trebuie să deschideţi fişierul [Link], care se află în directorul proiectului
(first_project/[Link]), care după conectarea URL-ului la care va corespunde funcţia/vizualizarea index()
arată astfel:

Codul first_project/[Link]:
1from [Link] import admin
2from [Link] import path, include
3from book_library import views
4urlpatterns = [
5 # path('', [Link]),
6 path('', [Link], name = 'index')
7]

În raport cu fişierul [Link], care a fost generat în timpul creării iniţiale a proiectului, vedem câteva
diferenţe. Prima este că, pe lângă funcţia path, am importat şi funcţia include, care ne va fi folositoare
mai târziu. Am comentat calea iniţială path('', [Link]), deoarece nu avem nevoie de ea acum. De
asemenea, acum am importat fişierul [Link] din aplicaţia noastră book_library, pentru a accesa funcţia
index(). Lista urlpatterns, care este, de asemenea, creată la crearea proiectului în sine, este plină de
elemente care reprezintă apelarea funcţiei path(). Cel puţin doi parametri sunt transmişi către această
funcţie, unde primul este de tip string şi conţine calea de-a lungul căreia, atunci când utilizatorul o
accesează, funcţia din vizualizare va procesa cererea, iar al doilea argument obligatoriu al funcţiei path()
este tocmai funcţia din vizualizarea pe care am definit-o. În acest caz, aceasta este [Link]. Despre
maparea detaliată a căilor vom vorbi în următoarea lecţie. Deoarece în exemplul nostru, pe pagina de
pornire dorim să listăm textul Book library, ca declanşator al funcţiei [Link]() setăm calea '', care
reprezintă pagina de pornire.

Deoarece am salvat ambele fişiere (atât first_project/[Link], cât şi book_library/[Link]), putem porni
serverul cu comanda python [Link] runserver [Link]:8000. Dacă deschidem această adresă într-
un browser, vom primi următorul ecran:

Imaginea 13.1. Rezultatul implementării funcţiei noastre index()

Setarea adresei URL din directorul aplicaţiei

În exemplul anterior, am văzut cum să mapăm un URL dintr-un fişier [Link] al proiectului, dar acest
lucru este posibil şi prin combinarea unui fişier [Link] de proiect şi aplicaţie. Acest lucru este deosebit
de bun pentru proiectele Django mai mari, deoarece facilitează organizarea şi maparea unui număr
mare de linkuri şi partajarea lor prin aplicaţie. Şi anume, pentru a economisi timp, vom copia [Link] din

Page 89 of 123
folderul proiectului în folderul aplicaţiei noastre. Modificările necesare utilizatorului care deschide site-
ul nostru pentru a obţine o listare Book library pe ecran, sunt următoarele:

first_project/[Link]:
1from [Link] import admin
2from [Link] import path, include
3urlpatterns = [
4 # path('', [Link]),
5 # path('', [Link], name = 'index')
6 path('', include('book_library.urls'))
7]

book_library/[Link]

1from [Link] import admin


2from [Link] import path, include
3# from book_library import views
4from . import views
5urlpatterns = [
6 # path('', [Link]),
7 path('', [Link], name = 'index')
8]

Prima diferenţă pe care o vedem în fişierul first_project/[Link] modificat este că acum, în loc să apelăm
în mod explicit funcţia index() din vizualizarea noastră, în apelarea path(), folosim funcţia include(). De
aceea nu importăm mai multe fişiere [Link] din directorul aplicaţiei. Argumentul său este de tip string
şi reprezintă calea către fişierul [Link] pe care dorim să îl folosim, de fapt, în acest caz. Pe de altă parte,
în book_library/[Link] importăm fişierul [Link], care se află în acelaşi director ca în
book_library/[Link] şi îl implementăm la fel ca în primul caz. După pornirea serverului nostru, vom primi
aceeaşi afişare pe ecran. Pentru a vedea mai clar avantajul acestei metode, vom folosi un exemplu simplu.
Să presupunem că în fişierul first_project [Link] listăm următoarea apelare a funcţiei path():

1path('about/', include('book_library.urls'))

şi că în book_library/[Link] avem următoarea organizare a listei urlpatterns:

1urlpatterns = [
2 path('', [Link], name = 'index'), # line 1
3 path('contact/', views.index_2, name = 'index_2'), # line 2
4 path('blog/', views.index_3, name = 'index_3'), # line 3
5 ]

Aceasta înseamnă că atunci când utilizatorul deschide pagina about/ (maparea efectuată din fişierul
first_project/[Link]), se afişează funcţia index(); dacă utilizatorul a accesat pagina about/contact/, ar fi
afişată funcţia index_2(), în timp ce în al treilea caz, dacă utilizatorul a accesat pagina about/blog/, ar fi
afişată funcţia index_3. Prin urmare, se poate spune că, cu această abordare, linkurile din
first_project/[Link] sunt linkuri de bază care vor fi actualizate ulterior, în funcţie de aplicaţie.

Page 90 of 123
Şabloanele Django (templates)

Până acum am folosit vizualizări pentru afişarea datelor. Dar, pe măsură ce site-ul bazat pe Django creşte,
un astfel de mod devine dificil, iar şabloanele sunt utilizate atât pentru implementare, cât şi pentru
întreţinere, pentru a separa logica de forma acelor date. Şabloanele predefinesc aspectul acestor date
şi ordinea acestora, şi trebuie doar să le transmitem datele. Primul lucru pe care îl vom schimba este
funcţia noastră index(), astfel încât în loc de obiectul HttpResponse să returneze un şablon:

1def index(request):
2 # return HttpResponse("<h1>Book library</h1>")
3 return render(request, '[Link]', {'books':books})

După cum putem vedea din linia comentată, diferenţa dintre abordarea anterioară şi aceasta este în
utilizarea funcţiei render(), al cărei scop este să genereze răspunsuri din şablon şi să i le trimită clientului,
care ia ca parametri:

• request - o cerere de client trimisă către un server care este necesară datorită contextului
suplimentar la crearea unui şablon destinat acelui client;
• html – fişier şablon;
• {'books':books} - un dicţionar cu valorile pe care funcţia render() le va folosi pentru a completa
câmpurile din fişierul .[Link].

Înainte de a crea un fişier şablon [Link], în acelaşi director al aplicaţiei noastre vom crea şi folderul
templates, în care vom plasa acest fişier cu următorul cod HTML:
Fişierul [Link]:
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>Book Library</title>
5 </head>
6 <body>
7 <h1>Our book repository</h1>
8 <div>
9 <ul>
10 {% for book in books %}
11 <li>
12 <h2>Title: {{ [Link] }} </h2>
13 <p> Year: {{ [Link]}} </p>
14 </li>
15 {% endfor %}
16 </ul>
17 </div>
18 </body>
19</html>

Ceea ce diferă de codul HTML în sine sunt liniile care încep cu acolade simple sau duble, care ajută la
transferul datelor din vizualizare în şablon şi care vor fi discutate mai târziu în lecţie. Salvaţi fişierul ca
[Link].

De asemenea, este important să reţineţi că pentru o configurare mai avansată a şablonului, este utilizat
fişierul [Link], şi anume lista TEMPLATES. În exemplul nostru, pentru a-i arăta lui Django în ce

Page 91 of 123
director să caute fişierul [Link], trebuie să modificăm exact acea listă, câmpul DIRS, unde, după
modificare, întreaga listă arată astfel:
Lista TEMPLATES:
1TEMPLATES = [
2 {
3 'BACKEND': '[Link]',
4 'DIRS': [[Link](BASE_DIR,'templates')],
5 'APP_DIRS': True,
6 'OPTIONS': {
7 'context_processors': [
8 '[Link].context_processors.debug',
9 '[Link].context_processors.request',
10 '[Link].context_processors.auth',
11 '[Link].context_processors.messages',
12 ],
13 },
14 },
15]

Adăugând linia ’ [Link](BASE_DIR,'templates'), i-am spus lui Django că pentru fiecare dintre
aplicaţiile din lista ’INSTALLED_APPS’, când vine vorba de şabloane – să le caute în folderul ’templates’.
Notă:
Dacă în orice moment în timpul rulării obţinem un ImportError:no module named os, trebuie să deschidem
fişierul [Link] şi la început să adăugăm linia import os.

După schimbarea configuraţiei din fişierul [Link], precum şi modificarea fişierului [Link], putem
rula exemplul, iar pe pagina de pornire vom vedea un aspect puţin mai diferit de cel anterior.

Imaginea 13.2. Pagina principală cu utilizarea şablonului

Page 92 of 123
Limbajul şablonului Django

Pentru a utiliza codul Python şi în HTML, Django oferă propriul limbaj de şabloane, pe care l-am întâlnit
deja folosind fişierul [Link] (acolade simple şi duble). Limbajul şablonului Django ne permite să
generăm întreaga pagină HTML de pe server şi, ca atare, fără niciun alt cod Python, să îl trimitem
clientului, câştigând astfel siguranţa şi eficienţa generării şabloanelor. În ceea ce priveşte limbajul
şablonului Django, există patru tipuri de sintaxă:

Variabilele

Sintaxa: {{variable_name}} - înconjurată de acolade duble.

Variabilele sunt utilizate pentru a lista o valoare, deci, în exemplul nostru [Link], în loc de <h2>
Title {{[Link]}} </h2>, va fi listat: „Title The Picture of Dorian Gray”. Cu toate acestea, din moment
ce am transmis lista de dicţionare în exemplul nostru, în loc să folosim sintaxa Python key[‘value’], am
accesat cheia dicţionarului folosind notaţia punct.

Tagurile/logica de control

Sintaxa: {% tag_name %} – înconjurată de acolade simple şi semnul procent.

Folosim aceste taguri pentru a implementa logica în şablon. În exemplul nostru, în fişierul [Link]
am folosit o buclă for care începe cu linia {% for book in books %} şi se termină cu linia {% endfor %}. În
acest fel, am activat iteraţia prin variabila books, pe care am transmis-o folosind funcţia index().

Lista cu toate tagurile disponibile se poate găsi în documentaţia oficială.

Filtrele

Sintaxa: {{ variable_name | filter_name }} - înconjurată de acolade duble, iar lângă numele variabilei este
trecut semnul |, urmat de numele filtrului. De asemenea, este posibil să conectaţi filtre, deci, şi acest
exemplu este posibil: {{variable_name | escape | linebreaks}}.

Folosind aceste filtre, putem modifica şi transforma valoarea unei variabile direct în şablon.

Lista cu toate filtrele disponibile se poate găsi în documentaţia oficială.

Comentariile

Sintaxa: {% comment %} - înconjurată de acolade simple şi de semnul %. Această parte a codului trebuie
închisă cu linia {% endcomment %}, deoarece tot conținutul dintre aceste două rânduri va fi omis de
compilatorul limbajului de şablon Django. La fel ca în Python, comentariile ne permit să clarificăm
suplimentar liniile şi secţiunile de cod. Aceste comentarii vor fi prezente numai în fişierele noastre;
acestea nu se vor vedea în codul sursă HTML care ajunge la client.

Utilizarea şabloanelor este posibilă şi prin maparea căilor URL de pe site-ul nostru, utilizând fişierul de
proiect [Link] (first_project/[Link]), dar în acest caz nu se va executa niciuna din sintaxele limbajului
şablonului, deci, după deschiderea paginii de pornire, pe ecranul utilizatorului nostru va fi listat doar Our
book repository (deşi avem în continuare URL-uri mapate în fişierul book_library/[Link]), iar lista
urlpatterns ar arăta astfel:

Page 93 of 123
first_project/[Link]:
1from [Link] import admin
2from [Link] import path, include
3from [Link] import TemplateView
4urlpatterns = [
5 # path('', [Link]),
6 # path('', [Link], name = 'index')
7 # path('', include('book_library.urls')),
8 path('',TemplateView.as_view(template_name='[Link]'))
9]

Principala diferenţă dintre acest mod de afişare a datelor către utilizator şi modul anterior - prin funcţia
de vizualizare - este că importăm o clasă TemplateView care mapează întregul fişier [Link] la script,
atunci când utilizatorul accesează pagina de pornire. Această metodă ne ajută în situaţiile în care avem
deja pregătit un fişier static .html în care nu există sintaxă de şablon (ci un HTML/CSS/JS curat).

Mai multe informaţii despre limbajul şablonului pot fi găsite în documentaţia oficială.

Page 94 of 123
3.3. Maparea cererilor

Rezumat
• Maparea cererilor în Django se face în fişierul [Link], utilizând o listă urlpatterns în care
elementele sunt, de fapt, apelări către funcţia path().
• Variabila ROOT_URLCONF din fişierul [Link], a cărei valoare este de tip string, reprezintă
calea către primul fişier [Link] în care trebuie căutată adresa URL solicitată.
• Conversia căii se activează printr-o sintaxă specială. Acest proces se bazează pe ideea că
anumite părţi ale unui URL sunt segmentate de o anumită logică şi că anumite funcţii sau clase
de vizualizări sunt executate în funcţie de rezultatul respectiv. Prin urmare, convertoarele de căi
sunt, în mare parte, legate de anumite tipuri de date, cum ar fi numere întregi, stringuri şi altele.
• Lista tipurilor disponibile în conversia căii este următoarea:
o str;
o int;
o slug;
o uuid;
o path.

• Funcţiile pentru lucrul cu maparea adreselor URL sunt:

o path(calea URL, vizualizarea, numele);


o re_path(calea URL, vizualizarea, numele);
o include(numele modulului/fişierului);
o register_converter(convertor, numele de convertor).

Maparea cererilor în Django se face în fişierul [Link], folosind o listă urlpatterns, în care elementele sunt,
de fapt, apelări către funcţia path(). Această funcţie, aşa cum am văzut din exemplele anterioare, poate
primi mai multe argumente diferite, dar conceptul este acelaşi - pentru a efectua o anumită
funcţionalitate pentru cererea de utilizator primită. În cazul în care calea solicitată nu este disponibilă
sau, mai degrabă, nu este mapată, clientul va întâlni o eroare cu codul 404, ceea ce înseamnă că resursa
solicitată nu a fost găsită pe server.

De asemenea, este important să reţineţi că Django „ştie” în primul rând în ce fişier să caute un anumit
link mapat. Aceasta este variabila ROOT_URLCONF din fişierul [Link], a cărei valoare de tip string
reprezintă calea către primul fişier [Link] în care să caute adresa URL cerută. În exemplul proiectului
nostru, first_project, vedem că valoarea variabilei ROOT_URLCONF este, de fapt, first_project.urls, ceea
ce înseamnă că Django va căuta mai întâi adresa URL în fişierul proiectului [Link].

În exemplele anterioare, în funcţia path() am definit doar cerinţe specifice - deci, dacă utilizatorul ar
deschide pagina [Link]/books, ar primi exact acea pagină. Exemplele, precum [Link]/book sau
[Link]/book, ar raporta o eroare 404. Uneori, ne putem găsi într-un scenariu în care avem o listă de
produse în care fiecare produs duce la o pagină cu un link care conţine numărul de identificare al acelui
produs - de exemplu, dacă un utilizator a deschis [Link]/8334/ pentru a i se afişa produsul sub acel
număr de identificare. Când ne gândim la mod - cum să implementăm această logică în fişierul [Link],
ajungem la problema în care pentru fiecare dintre produsele care există în baza de date, ar trebui să
definim şi să mapăm în mod explicit linkul. Aceasta ar deveni în curând o mare problemă, de aceea,
Django ne oferă o soluţie - este vorba de o sintaxă specială utilizată în funcţia path().

Sintaxa utilizată în timpul conversiei căilor

Conversia căilor se activează printr-o sintaxă specială. Acest proces se bazează pe ideea că anumite părţi
ale unui URL sunt segmentate de o anumită logică şi că anumite funcţii sau clase de vizualizări sunt

Page 95 of 123
executate în funcţie de rezultatul respectiv. Prin urmare, convertoarele de căi sunt în mare parte legate
de anumite tipuri de date, cum ar fi numere întregi, stringuri şi altele, iar lista acestor tipuri este
următoarea:

• str - corespunde oricărui string care nu este gol (''), fără un separator de cale (/); acesta este
convertorul de cale implicit;
• int - corespunde oricărui număr întreg pozitiv, inclusiv zero; returnează tipul Python int;
• slug - corespunde ultimei secţiuni a linkului în care se află clientul ([Link]/hello-
world, slug ar fi: ’hello-world);
• uuid - corespunde unui număr unic de identificare asociat modelelor;
• path - corespunde oricărui string care nu este gol; ne permite să găsim exact adresele URL
specificate, în loc de părţi, aşa cum este cazul cu int sau str;

Funcţiile pentru lucrul cu maparea URL-urilor

Funcţiile pentru lucrul cu maparea URL sunt:

• path(calea URL, vizualizarea, numele) - returnează un element pentru procesare ulterioară;


exemplu: path(’index/’, [Link], name=’index-view’);
• re_path(calea URL, vizualizarea, numele) - returnează un element pentru procesare ulterioară;
diferenţa dintre aceasta şi funcţia path() este că re_path() se bazează pe utilizarea expresiilor
regulate, care nu fac obiectul acestui curs datorită sferei şi complexităţii lor;
• include(numele modulului/fişierului) - o funcţie la care este transmis un tip de string care
conţine calea către următorul fişier [Link] care ar trebui să proceseze în continuare adresa URL
solicitată; register_converter (convertor, numele convertorului) - o funcţie utilizată pentru a
înregistra convertorul nostru de cale pe care îl vom folosi în funcţia path(); deoarece pentru
crearea convertoarelor noastre de căi este utilizată o sintaxă specială împreună cu expresii
regulate, mai multe despre acest subiect se poate afla în documentaţia oficială.

Exemplu de utilizare a convertorului de cale

Ca exemplu, vom folosi proiectul din lecţia anterioară, doar că acum [Link] de proiect şi aplicaţie
(aplicaţia book_library) arată astfel:

1first_project/[Link]
2
3from [Link] import admin
4from [Link] import path, include
5from [Link] import TemplateView
6urlpatterns = [
7 # path('', [Link]),
8 # path('', [Link], name = 'index')
9 # path('',TemplateView.as_view(template_name='[Link]')),
10 path('', include('book_library.urls')),
11]
12
13
14
15book_library/[Link]
16
17from [Link] import admin
18from [Link] import path, include

Page 96 of 123
19from . import views
20# from [Link] import TemplateView
21urlpatterns = [
22 # path('', [Link]),
23 path('',views.main_page, name = 'main_page'),
24 path('books/', [Link], name = 'index_page'),
25 path('books/<int:int_key>/', views.int_test, name = 'int_test'),
26 path('books/<str:book_name>',[Link], name = 'str_test'),
27]
Liniile comentate sunt liniile pe care le-am folosit în versiunile anterioare ale fişierului.

Din aceste exemple, mai întâi vedem că am mutat toată logica de mapare URL din proiect în aplicaţia
[Link] cu funcţia include(’book_library.urls’). În fişierul book_library/[Link] am folosit deja convertoarele
de căi, şi anume cele legate de stringuri şi numere întregi:

• path('books/<int:int_key>/', views.int_test, name = 'int_test'), - cu această linie am mapat toate


linkurile începând cu books/ şi terminându-se cu un număr întreg pozitiv - books/1, books/1236
etc. Sintaxa convertorului de cale este aceeaşi şi se aplică şi la celelalte, conform următorului
principiu:
o < > - paranteze care în string indică partea în care definim convertorul de cale;
o <int:> - cuvânt-cheie int care indică tipul de convertor de cale;
o <int:int_key> - după numele convertorului de cale folosim două puncte, care separă
convertorul cu un nume definit arbitrar, pe care i l-am atribuit acelui tip pentru adresa
URL curentă.

Aceasta înseamnă că am denumit fiecare număr pe care îl transmitem după partea books/ ca int_key. Şi
cu numele int_key îl putem accesa prin intermediul funcţiilor de vizualizare. În acest fel, putem transmite
anii către aplicaţia noastră, iar ca răspuns, clientului îi putem returna cărţile publicate în acel an.

• path('books/<str:book_name>',[Link], name = 'str_test'), - cu această linie am mapat toate


linkurile care încep cu books/ şi se termină cu stringul: books/Hamlet, 'books/The Raw Youth’
etc. La fel ca în cazul convertorului de căi int, am folosit şi tipul str şi l-am numit book_name.
Prin acest nume, îi putem accesa valoarea ca funcţie de vizualizare care se referă la această
adresă URL mapată. În acest fel, reuşim să obţinem numele cărţii de la utilizator şi să returnăm
anul în care a fost publicată cartea.

Fişierul book_library/[Link] s-a schimbat mult în comparaţie cu versiunea din lecţia anterioară pentru
a urmări modificările aduse fişierului [Link], şi aceasta arată astfel:

1book_library/[Link]:
2
3from [Link] import render
4from [Link] import HttpResponse
5books = [{'title':'The Picture of Dorian Gray','year':'1890'},
6 {'title':'Pride and Prejudice','year':'1813'},
7 {'title':'The Adventures of Tom Sawyer ', 'year':'1875'},
8 {'title':'The Raw Youth', 'year':'1875'},
9 {'title':'Twelve Years a Slave ', 'year':'1853'},
10 {'title':'Hamlet', 'year':'1603'}]
11# Create your views here.
12def index(request, book_name = None):

Page 97 of 123
13
14 # return HttpResponse("<h1>Book library</h1>")
15 # return HttpResponse(render(request, '[Link]',
16{'books':books}))
17 if not book_name:
18 return render(request, '[Link]', {'books':books})
19 temp_res = []
20 if book_name:
21 for x in books:
22 if str(book_name).lower() == x['title'].lower():
23temp_res.append(x['year'])
24
25 if temp_res:
26 return HttpResponse("<h1>Book {} was published in {}
27year.</h1>".format(book_name, temp_res[0]))
28 else:
29 return HttpResponse("<h1>We couldnt find year published for book:
30{}.</h1>".format(book_name))
31
32def main_page(request):
33 return HttpResponse("<h1>Welcome to our book library!</h1>")
34
35def int_test(request,int_key):
36 temp_res = []
37 for x in books:
38 if str(int_key) == x['year']:
39 temp_res.append(x['title'])
40 if temp_res:
41 return HttpResponse("<h1>You picked year - {}.\
42 <br>Book published in that year is:
{}.</h1>".format(int_key, temp_res[0]))
else:
return HttpResponse("<h1>There is no book for a given year: {} in our
database.</h1>".format(int_key))

Aici vedem deja câteva diferenţe cheie faţă de versiunile din lecţiile anterioare:

1. Pe lângă obiectul standard, care reprezintă cererea de utilizator, funcţiei index() îi transmitem
argumentul book_name corespunzător convertorului de cale. Valoarea implicită este None.
Acest lucru se face din cauză că folosim funcţia index() în două cazuri - atunci când utilizatorul
accesează calea books/ (în acest caz, book_name este None) şi când utilizatorul accesează calea
book/ Hamlet (în acest caz, book_name ia valoarea Hamlet). De aceea, există o logică care
verifică dacă valoarea acestei variabile, adică numele cărţii există în lista noastră de dicţionare
şi pe baza acesteia trimite un răspuns către client (în cazul în care cartea există, se trimite anul
publicării sau se trimite un răspuns cu mesajul că acea carte nu există).
2. Funcţia main_page() a fost adăugată pentru ca utilizatorului să i se listeze pe ecran mesajul –
Welcome to our book library! după deschiderea paginii de pornire a site-ului ([Link]:8000).
3. Funcţia int_test(request, int_key) se utilizează pentru a găsi o carte care a fost publicată în anul
solicitat şi pentru a returna aceste informaţii utilizatorului. Acesteia i se redirecţionează obiectul
de cerere standard, precum şi int_key - numele convertorului de cale, pe care le-am definit în
fişierul book_library.[Link] (linia: path ('books/<int:int_key>/', views.int_test, name =

Page 98 of 123
'int_test')). Această funcţie va fi efectuată dacă utilizatorul adaugă un număr întreg după books/,
astfel încât unele dintre linkurile necesare pot arăta în felul acesta:

o books/1853 – va returna mesajul You picked year 1853. Book published in that year is:
Twelve Years a Slave.
o books/ 2000 - va returna mesajul There is no book for a given year: 2000 in our database.

Logica din spatele fişierului book_library/[Link] este de aşa natură încât dacă utilizatorul deschide
pagina de pornire - va fi realizată o vizualizare a funcţiei main_page(), care listează doar Welcome to
our book library! În cazul în care utilizatorul deschide pagina books/ - pe ecran va fi listată lista de cărţi
generată de funcţia index(). Dacă am transmis books/Hamlet – deci, valoarea convertorului de cale
path_name devine Hamlet, am primi mesajul Book Hamlet was published in 1603 year, iar în cazul în
care clientul a tastat books/1853 - am primi un răspuns cu numele cărţii care a fost publicată atunci.

Salvaţi toate fişierele şi rulaţi serverul cu comanda python [Link] runserrver ‘[Link]:8000’ şi
vizualizaţi răspunsurile serverului la următoarele linkuri:

• [Link]:8000
• [Link]:8000/books
• [Link]:8000Books
• [Link]:8000/books/1853
• [Link]:8000/books/0
• [Link]:8000/books/Hamlet i [Link]:8000/books/hamlet
• [Link]:8000/books/Pride and Prejudice
• [Link]:8000/books/The Adventures of Tom Sawyer

Page 99 of 123
3.4. Procesarea formularelor

Rezumat
• Formularele în sine, de fapt, fac parte din protocolul HTTP.
• Tagurile HTML pe care le folosim pentru a trimite date către server sunt form, input şi select.
Tagul form este, de fapt, un obiect container pentru toate datele pe care clientul doreşte să le
trimită în acel moment. Acesta conţine două atribute esenţiale – action şi method.
• Formularele Django reprezintă legătura dintre formularele HTML şi funcţiile şi clasele Python.
Logica de lucru cu formulare este găzduită într-o clasă forms încorporată, care ne facilitează
lucrul cu formularele.
• Pentru lucrul cu formularele, pe lângă fişierul .html, trebuie să implementăm o clasă care va
gestiona acel fişier. Un astfel de fişier se stochează în directorul rădăcină al folderului aplicaţiei.

În lecţiile anterioare, ne-am ocupat doar de cererile GET - situaţii în care utilizatorul livrează ceva de pe
server. În practică, vom fi deseori într-o situaţie în care dorim să-i oferim utilizatorului posibilitatea de a
ne trimite date. Acesta este adesea cazul cu comentariile, formularele de autentificare (logare) sau
trimiterea mesajelor. În aceste cazuri folosim metoda HTTP POST.

Formularele în sine, de fapt, fac parte din protocolul HTTP, pe care se bazează metoda de lucru a lui
Django. Tagurile HTML pe care le folosim pentru a trimite date către server sunt form, input şi select.
Tagul form este, de fapt, un obiect container pentru toate datele pe care clientul doreşte să le trimită în
acel moment. El conţine două atribute importante – action şi method:

• Atributul action poate fi numit şi de destinaţie sau link. Anume, valoarea acestui atribut este
linkul către care ar trebui trimisă cererea client, după completarea acelui formular. Dacă acest
atribut din formular nu este utilizat, cererea respectivă va merge la linkul în care se află clientul
în prezent.
• Atributul method ne spune ce metodă HTTP utilizează cererea client. Deci, cu atributul action
ştim unde este trimisă cererea, iar prin atributul method - cum va fi trimisă cererea respectivă.

Să presupunem că dorim să adăugăm funcţionalitatea adăugării unei cărţi noi şi a anului publicării
acesteia în aplicaţia noastră Django. Modalitatea de a realiza acest lucru folosind codul HTML este
următoarea:

Codul formularului HTML:

1<form method="POST" action="/book-added/">


2 <input type="text" name="book_title">
3 <input type="text" name="book_year">
4 <button type="submit">Add book!</button>
5</form>

În acest exemplu HTML, formularul are două câmpuri: book_title şi book_year. După ce daţi clic pe buton,
ambele date sunt trimise către linkul /book-added/ (atributul action) folosind metoda POST (atributul
method). De asemenea, metoda POST trimite aceste date în corpul mesajului din cererea HTTP, ceea ce
înseamnă că, dacă site-ul este pe HTTPS, acest câmp de cerere este criptat. Dacă am avea GET în loc de
metoda POST, aceste date ar fi trimise ca parte a unui link (stringuri de interogare-query strings).
Atributul de tip type al tagului input ne spune de ce tip este acest element HTML. În acest caz, este
vorba de text, dar poate fi şi un checkbox, un datapicker etc. Pentru o listă detaliată de tipuri,
consultaţi documentaţia protocolului HTTP.

Page 100 of 123


Formularele Django

Formularele Django reprezintă legătura dintre formularele HTML şi funcţiile şi clasele Python. Logica de
lucru cu formularele este găzduită într-o clasă încorporată forms, ceea ce ne facilitează lucrul cu
formularele. Formularele pe care utilizatorul le poate crea prin cadrul Django sunt, de obicei, subclasele
clasei forms (moşteneşte clasa [Link]), iar câmpurile pe care dorim să le folosim pentru
introducerea datelor sunt atributele sale. De asemenea, acceptarea datelor din formularul trimis se face
în fişierul [Link], în timp ce formularul în sine (sau mai multe formulare) poate fi definit într-un fişier
separat. Pentru nevoile aplicaţiei noastre de bibliotecă, vom crea un formular cu două câmpuri prin care
utilizatorul va introduce numele şi anul publicării cărţii, iar înainte de a defini un astfel de obiect, ar
trebui să creăm un fişier şablon, format dintr-un cod HTML pentru a ajuta Django să creeze şi să afişeze
pagina finală. Vom apela acest fişier şablon [Link] şi îl vom plasa în folderul /templates din aplicaţia
noastră:

book_library/templates/[Link]

1<form action="{% url 'book_added' %}" method="POST">


2 {% csrf_token %}
3 {{ form }}
4 <p><input type="submit" value="Send the form!"></p>
5</form>

În acest cod, vedem că există un singur element input, care reprezintă butonul de trimitere a
formularului. Alte elemente de tip input vor fi create după apelarea funcţiei render() în vizualizare.
Elementele limbajului şablonului Django din acest exemplu sunt:

• {% url 'book_added' %} - url este un tag încorporat într-un limbaj de şablon al cărui singur
argument necesar este, de fapt, calea corespunzătoare vizualizării, adică – cea care corespunde
numelui specificat de argumentul parametrului name în funcţia path() a fișierului [Link]
(denumirea URL-ului în sine). Argumentele pot fi, de asemenea, transmise către acest tag, ceea
ce poate fi văzut în documentaţia oficială. Scopul atributului action al elementului formularului,
în acest caz, este literal: să trimită date din formular către linkul pe care l-am denumit în lista
urlpatterns ca book_added. Ceea ce înseamnă că după ce dăm clic pe butonul Send the form!,
browserul deschide pagina din spatele numelui book_added (la acest link se vor trimite date
din formular). Deşi putem folosi aceeaşi pagină, vom folosi o altă pagină, pe care ulterior vom
scrie un mesaj despre succesul formularului trimis, adică despre actualizarea listei de cărţi.
• {% csrf_token %} - CSRF (cross-site request forgery) este un token utilizat pentru securitatea
aplicaţiei şi este necesar la trimiterea datelor către server pentru validarea sursei cererii. Mai
exact, serverul verifică prin intermediul acestuia dacă cererea provine din domeniul de la care
Django aşteaptă cererea. De asemenea, se leagă de sesiunea curentă. Este cel mai des folosit în
timpul cererilor POST pe partea de client. Mai multe despre acest subiect pot fi găsite la
acest link şi în lecţiile următoare.
• {{ form }} - locul în care vom transmite obiectul formularului.

Odată ce am definit un fişier .html şablon, care va colecta date de la utilizatori în browser, trebuie să
specificăm adresa URL unde va fi afişat acel fişier şi trebuie să implementăm un obiect formular Django
care să accepte aceste date, precum şi o funcţie de vizualizare care va procesa datele respective.
Linkul către care dorim să fie trimis acest fişier şablon va fi add-book/, iar funcţia din vizualizare, pe care
o vom defini mai târziu, se va numi get_book, aşadar, în lista fişierului urlpatterns cu numele
book_library/[Link] adăugăm un alt element, path('add-book/', views.get_book, name = 'book_added'),.
Înainte de a defini o funcţie, mai întâi trebuie să implementaţi o clasă care va gestiona fişierul [Link].
Vom apela un astfel de fişier [Link] şi îl vom plasa în folderul aplicaţiei:

Page 101 of 123


book_library/[Link]

1from django import forms


2class BookForm([Link]):
3 book_title = [Link](max_length=100)
4 book_year = [Link](max_length=4)

Din acest exemplu, vedem că clasa noastră BookForm() moşteneşte clasa forms din Django. În clasa
BookForm am definit două câmpuri - book_title şi book_year; ambele aparţin tipului [Link]().
Obiectul CharField() este, de fapt, un câmp de text pe care utilizatorul îl va vedea pe ecran. Mai exact,
va reprezenta unul dintre HTML-urile de tip input. Ca parametru, am adăugat şi numărul maxim de
caractere care se pot încadra în acel câmp cu argumentul max_length. Django ne oferă o mulţime de
opţiuni atunci când vine vorba de lucrul cu taguri, deci, acceptă obiecte specializate pentru e-mail
([Link]()), dată ([Link]()), linkuri ([Link]()) etc. Restul listei acestor câmpuri
pe care Django le suportă nativ poate fi găsită în documentaţia oficială.

În exemplul BookForm, am folosit argumentul max_length pentru câmpuri. În plus, există şi alte
argumente care pot fi aplicate, și anume:
• required - un argument care se referă la faptul dacă câmpul este obligatoriu sau nu; în mod
implicit, fiecare câmp creat este obligatoriu, dar dacă dorim să schimbăm această stare, trebuie
să transmitem required=False la crearea câmpului. Exemplu: [Link](max_length= 100,
required=False);
• label - un argument care ne permite să adăugăm un label în câmpul nostru pentru a-i clarifica
utilizatorului exact la ce date se referă câmpul în sine; funcţionalitatea implicită a acestui
argument este să luăm numele câmpului nostru, să convertim toate caracterele _ în spaţii şi să
convertim prima literă a acestui nume într-o majusculă; dacă dorim un nume diferit, este
suficient să transmitem argumentul label şi numele dorit; de exemplu:
[Link](max_length=100, label = ’Name of the book’);
• initial - un argument care se referă la faptul dacă un câmp va conţine sau nu o valoare iniţială;
acest lucru este bun în situaţiile în care vrem să-i arătăm utilizatorului cum ar trebui să arate
formatul de date introdus. Dacă folosim această abordare, utilizatorul trebuie mai întâi să
şteargă valoarea iniţială şi apoi să o introducă pe a sa.

Deoarece în prezent avem o clasă implementată şi un fişier şablon definit, este timpul să definim funcţiile
din vizualizare. De asemenea, trebuie remarcat faptul că fluxul aplicaţiei va fi astfel încât, după ce
utilizatorul trimite datele din formular, aceste date vor fi trimise la acelaşi link în care se află în prezent
([Link]:8000/add-book), şi că succesul acestei cereri POST îi solicită clientului să redirecţioneze către
linkul [Link]:8000/book-added, pe care va scrie Ading successful!. După aceea, utilizatorul poate
vedea cartea introdusă atunci când deschide adresa [Link]:8000/books. Prin urmare, înainte de a
implementa funcţiile din vizualizare, trebuie să adăugăm un alt element la lista urlpatterns din fişierul
book_library/[Link], care acum arată astfel:
Aspectul listei urlpatterns din fişierul book_library/[Link]:

1urlpatterns = [
2 # path('', [Link]),
3 path('',views.main_page, name = 'main_page'),
4 path('books/', [Link], name = 'index_page'),
5 path('books/<int:int_key>/', views.int_test, name = 'int_test'),
6 path('books/<str:book_name>',[Link], name = 'str_test'),
7 path('add-book/', views.get_book, name='book_added'),
8 path('book-added/', views.redirect_test, name='redirect'),
9]

Page 102 of 123


Deci, este timpul să definim funcţiile get_book() şi redirect_test(). Folosind codul din lecţia anterioară, la
acesta adăugăm următorul cod în fişierul book_library/[Link]:
Codul suplimentar pentru fişierul book_library/[Link]

1from [Link] import HttpResponseRedirect


2from .forms import BookForm
3from [Link] import reverse
4def redirect_test(request):
5 return HttpResponse("<h1>Adding successful!</h1>")
6 def get_book(request):
7 if [Link] == 'POST':
8 form = BookForm([Link])
9 if form.is_valid():
10 [Link]({'title':form.cleaned_data['book_title'],
11'year':form.cleaned_data['book_year']})
12
13 return HttpResponseRedirect(reverse('redirect'))
14 else:
15 form = BookForm()
16 return render(request, '[Link]', {'form': form})

• se referă la argumentul name al funcţiei path());

În funcţia get_book(), verificăm mai întâi despre ce metodă a cererii HTTP este vorba. Şi anume, dacă
este vorba de o metodă POST, aceasta înseamnă că utilizatorul ne trimite datele şi noi trebuie să le
acceptăm şi să le stocăm. În acest caz, mai întâi instanţiem formularul nostru - BookForm(), căruia îi
transmitem datele cererii POST ([Link] - această comandă este de tip dicţionar). Este important
să verificăm apoi corectitudinea formularului cu metoda is_valid(), care este implementată astfel încât,
pentru toate câmpurile trimise, care sunt create cu argumentul necesar care are valoarea True, se verifică
dacă acestea conţin date; dacă conţin, returnează True. În cazul în care valoarea returnată a metodei
is_valid() este False, utilizatorul va rămâne pe aceeaşi pagină cu toate datele pe care le-a completat, iar
pentru câmpurile care sunt obligatorii şi goale, va apărea o notificare. După ce programul stabileşte că
formularul este valid, acesta continuă până la linia prin care introduce datele trimise în dicţionar
([Link]()) şi, în cele din urmă, îi returnează utilizatorului un răspuns cu codul 302 - redirecţionare
către un link pe care l-am numit redirect în lista urlpatterns. Acesta este linkul [Link]:8000/book-
added/ care se ocupă de funcţia redirect_test(), care la rândul ei listează Adding Successful!. Când
adăugăm, mai precis când citim date din formular, folosim metoda cleaned_data, care converteşte
datele din formular în tipuri Python native (int, str). De asemenea, în acest caz, deoarece dorim să
redirecţionăm utilizatorul către o pagină cu un mesaj de confirmare despre adăugarea cărţilor, folosim
HttpResponseRedirect în locul răspunsului anterior HttpResponse. În cazul în care cererea care vine la
funcţia get_book () nu este POST, ci GET - înseamnă că utilizatorul tocmai ajunge pe pagina unde trebuie
completat formularul și, în acest caz, este trimis un fişier şablon [Link].
Salvaţi toate fişierele şi rulaţi serverul cu comanda python [Link] runserver ‘[Link]:8000’ şi
vizualizaţi răspunsurile serverului la următoarele linkuri:

• [Link]:8000
• [Link]:8000/books
• [Link]:8000/add-books
• [Link]:8000/books

Page 103 of 123


4.1. Publicarea aplicaţiei web Python

Rezumat
• Printre multitudinea de platforme şi soluţii, am decis să folosim
platforma [Link] pentru a publica aplicaţia noastră Django.
Această platformă ne permite să publicăm o singură aplicaţie, cu un domeniu care va fi în
proprietatea numelui de utilizator al contului (dacă selectăm numele de utilizator
student_2020 la crearea contului, linkul către aplicaţia noastră va arăta
astfel: [Link] - linkul nu este disponibil, deoarece este un
exemplu.
• După închiderea proiectului şi încărcarea acestuia pe serverul PythonAnywhere, trebuie să
creăm un mediu virtual din consola Bash, cu comanda mkvirtualenv django20 --python
=/usr/bin/python3.6.
• După crearea mediului virtual, trebuie să facem modificări în fişierul [Link] al aplicaţiei
Django şi în fişierul WSGi al serverului pythonanyhwere, astfel încât această aplicaţie de pe
server să poată fi accesată de pe internet, prin intermediul linkului definit de aplicaţie.

După ce am creat aplicaţia şi am curăţat-o de erori, o putem instala şi pe un server de pe internet.


Deoarece majoritatea soluţiilor de ocupare a serverelor web şi a domeniilor sunt plătite, vom folosi un
serviciu care ne va permite să publicăm aplicaţia noastră pe server, dar cu limite adecvate. Platforma
care ne va permite să facem acest lucru se numeşte [Link] Câteva dintre
restricţiile pentru un cont gratuit sunt:

• posibilitatea de a publica o singură aplicaţie cu un domeniu care va fi în proprietatea numelui


de utilizator al contului (dacă selectăm numele de utilizator student_2020 la crearea contului,
linkul către aplicaţia noastră va arăta astfel: [Link]
• incapacitatea de acces SSH la cont;
• acces limitat la site-uri externe;
• capacitatea de a programa o singură sarcină;
• incapacitatea de a executa procese care funcţionează constant.

Mai multe informaţii despre planurile disponibile pot fi găsite pe pagina oficială.
În această lecţie vom prezenta cum să publicaţi codul din modulul anterior, adică proiectul Django, chiar
pe acest site.
Mai întâi, trebuie să creaţi un cont pe [Link]. Deschideţi
pagina [Link] şi selectaţi Create Beginner Account:

Imaginea 16.1. Aspectul primei pagini a site-ului [Link]

Page 104 of 123


După ce am creat cu succes contul, ni se va afişa pagina principală ca în imaginea următoare.

Imaginea 16.2. Aspectul paginii după crearea cu succes a contului

Acum, trebuie să arhivăm proiectul (în arhiva zip) înainte de a-l muta în PythonAnywhere. Vom face
acest lucru dând clic dreapta pe folderul principal al proiectului nostru şi selectând, pe platforma
Windows, folderul Send To -> Compressed (Zipped) Folder. Denumim fişierul creat după această
compresie first_project.zip. Deci, trebuie să arhivăm folderul care conţine fişierul [Link] împreună
cu first_project şi book_library şi alte foldere. Dacă am arhivat folderul corect - putem verifica dând
dublu clic pe first_project.zip; dacă în el, primul şi singurul lucru pe care îl vedem este folderul
first_project (care după deschidere conţine first_project, book_library, [Link]...) - atunci am urmat
corect instrucţiunile.

Odată ce am arhivat fişierul, acesta este gata să fie încărcat pe server. Pe interfaţa din imaginea 16.2.,
trebuie să dăm clic pe butonul Browse Files, care ne va duce la tab-ul Files, la o pagină care arată în felul
acesta:

Imaginea 16.3. Afişarea conţinutului serverului nostru în tab-ul Files

Alegem Upload a file şi selectăm fişierul nostru first_project.zip din fereastra deschisă. După transferul
cu succes al fişierului, directorul serverului nostru ar trebui să arate astfel:

Page 105 of 123


Imaginea 16.4. Afişarea fişierelor după încărcarea corectă a fişierului first_project.zip

Din moment ce am adăugat proiectul nostru zip, acesta trebuie, de asemenea, dezarhivat.
PythonAnywhere oferă această funcţionalitate în consolă după ce dăm clic pe Open Bash console here.
În acel moment, se deschide un nou tab în browser, care este o consolă prin care putem gestiona şi
manipula folderele şi fişierele de pe serverul nostru. În acea consolă, mai întâi trebuie să creăm un mediu
virtual folosind comanda:

mkvirtualenv django20 --python=/usr/bin/python3.6

În acest fel, creăm un mediu de lucru virtual. Mediile virtuale nu sunt specifice doar consolei şi serverului
respectiv, ci pot fi utilizate şi local pe maşinile noastre. Aceste medii sunt, de fapt, directoare separate
pe disc (în acest caz, acel director se va numi django20). Valoarea de utilizare a mediilor virtuale se
bazează pe izolarea mediului de lucru din directorul principal de instalare Python, astfel încât să putem
instala în mod liber versiunile necesare de pachete şi biblioteci în mediul de lucru, deoarece vom lucra
adesea la proiecte care necesită versiuni specifice de pachete. Pentru a nu şterge şi a instala în mod
constant pachetele necesare din directorul principal de instalare, mediile virtuale ne oferă un director
special, izolat, pentru lucru.
În imaginea următoare, putem vedea cum arată un exemplu de execuţie cu succes a acelei comenzi. De
asemenea, atenţie la textul din paranteze de la începutul fiecărei linii - care reprezintă numele mediului
nostru virtual:

Imaginea 16.5. Listarea consolei PythonAnywhere după ce am rulat mkvirtualenv

Page 106 of 123


În listarea comenzii dir, putem vedea şi numele proiectului nostru zip. Îl vom dezarhiva cu comanda
unzip first_project.zip. După ce serverul termină dezarhivarea fişierului, instalarea pip django este
pornită în aceeaşi consolă. Mai exact, în acest pas instalăm toate bibliotecile necesare proiectului nostru.
Deoarece aplicaţia noastră se bazează numai pe Django, avem nevoie doar de acel pachet. După
finalizarea instalării Django şi după dezarhivarea proiectului într-un folder de pe server, putem continua
cu configurarea serverului.

Din pagina de pornire a site-ului PythonAnywhere (pagina care conţine tabloul de bord şi alte tab-uri şi
informaţii) selectăm tab-ul Web şi dăm clic pe Add a new web app, ca în imagine:

Imaginea 16.6. Pagina tab-ului Web pentru crearea aplicaţiei pythonanyhwere

După listarea informaţiilor că nu putem alege un nume şi un domeniu arbitrar al aplicaţiei, dăm clic pe
Next. În pagina următoare, selectăm Manual configuration (including virtualenvs) din opţiunile oferite:

Imaginea 16.7. Al doilea pas în crearea aplicaţiei pythonanyhwere

Pe pagina care apare în continuare, selectăm Python 3.6 ca versiune. Această alegere ne va duce la
pagina următoare, care se referă la informaţiile despre configuraţia manuală a serverului, unde trebuie
să selectăm Next. După aceea, se va deschide o pagină cu aplicaţia noastră, unde o putem configura în
continuare. Linkul către aplicaţia noastră se află în partea de sus a paginii:

Page 107 of 123


Imaginea 16.8. Aplicaţia PythonAnywhere creată cu succes

Deşi am plasat folderul proiectului aplicaţiei noastre pe server şi am creat aplicaţia pe server - nu le-am
conectat încă, aşa că dacă am urma linkul subliniat din imaginea anterioară, s-ar deschide pagina
generică Hello, World! Câteva lucruri trebuie schimbate în aplicaţia noastră PythonAnywhere pentru
configurare. Pentru începători - pe pagina din imaginea anterioară găsiţi secţiunea despre cod, mai
exact ’Code’ şi un link către fişierul WSGI:

Imaginea 16.9. Câmpul cu calea către fişierul de server WSGI

Daţi clic pe linkul care duce la acel fişier. Conţinutul acestui fişier se va deschide într-un nou tab în
browser. În prezent conţine codul care rulează prima pagină generică Hello, World! Ştergeţi totul din
acel fişier, cu excepţia secţiunii care începe cu # +++++++++++ DJANGO +++++++++++, în timp ce
în acea secţiune trebuie doar să comentaţi liniile cu cod. După aceea, trebuie să schimbăm variabila path
pentru a indica directorul rădăcină al proiectului nostru (calea directorului rădăcină de pe server) pe care
l-am dezarhivat anterior, precum şi să setăm variabila ['DJANGO_SETTINGS_MODULE'] pentru a afişa în
ce folder din aplicaţia Django este fişierul [Link]. Deci, în cele din urmă, fişierul WSGI ar trebui să
arate astfel:

Page 108 of 123


Imaginea 16.10. Aspectul fişierului WSGI după modificări

După ce am terminat cu toate modificările necesare - salvăm fişierul dând clic pe Save în colţul din
dreapta sus al tab-ului şi închidem tab-ul respectiv. Pe pagina aplicaţiei noastre PythonAnywhere, găsim
secţiunea Virtualenv (imediat după secţiunea Code) şi adăugăm calea către mediul virtual dând clic pe
Enter path to a virtualenv, if desired, pe care l-am creat anterior prin consolă folosind comanda
mkvirtualenv.

Imaginea 16.11. Secţiunea Virtualenv de pe pagina din aplicaţia PythonAnywhere

În câmpul de text care apare, tastăm numele mediului nostru virtual, care este django20, şi confirmăm
acest lucru dând clic pe butonul de lângă acesta. Rămâne să verificăm setările aplicaţiei Django dacă
aplicaţia noastră poate fi accesată din orice domeniu, deschizând tab-ul Files al aplicaţiei noastre
pythonaynwhere, navigând la directorul first_project/first_project/ şi dând clic pe fişierul [Link], aşa
cum s-a făcut în imagine:

Imaginea 16.12. Parte a aspectului fişierului [Link] care se referă la lista ALLOWED_HOSTS

Page 109 of 123


În fişierul care se deschide, verificăm dacă lista ALLOWED_HOSTS este definită în felul următor:

ALLOWED_HOSTS = ['*']

Dacă nu este definită astfel, o corectăm pentru a arăta în felul acesta şi salvăm fişierul dând clic pe Save
în colţul din dreapta sus. Revenim la tab-ul Web al aplicaţiei noastre şi dăm clic pe Reload
<username>.[Link] (secţiunea <username> se referă la numele de utilizator pe care l-
am ales la crearea contului). Aşteptăm finalizarea reîncărcării aplicaţiei şi deschidem linkul către aplicaţia
noastră.

Imaginea 16.13. Ultimul pas, clic pe Reload

Ceea ce vedem este vizualizarea iniţială, definită în aplicaţia Django, ca şi când am accesat-o prin adresa
locală. Alte linkuri pe care le-am folosit pe adresa IP locală, cum ar fi [Link]:8000/,
[Link]:8000/books, [Link]:8000/add-book şi altele, funcţionează şi pe platforma PythonAnywhere.
Avantajul acestei abordări este că putem accesa aplicaţia noastră de pe orice dispozitiv, precum şi faptul
că, atâta timp cât nu dăm clic pe reîncărcarea aplicaţiei - Reload, toate cărţile pe care le adăugăm în
bibliotecă rămân salvate chiar şi după ce oprim browserul.

De asemenea, este important să menţionăm că pythonanyhwere nu este singurul serviciu care oferă
publicarea gratuită, limitată a aplicaţiilor, ci există şi altele, precum Heroku, AlwaysData...

Page 110 of 123


4.2. Securizarea, testarea şi echilibrarea încărcării aplicaţiei

Rezumat
• Django are propria comandă pentru analiza securităţii proiectelor şi aplicaţiilor, comandă
numită Check. Pentru a rula această analiză, utilizăm comanda din linia de comandă: [Link]
check --deploy.
• Dacă avem deja o aplicaţie publicată, putem folosi site-ul Observatory al companiei Mozilla,
care ne va scana site-ul şi ne va informa despre potenţialele probleme şi soluţii sugerate.
• Testarea poate fi prezentată ca o bucată de cod care verifică dacă o altă bucată de cod dorită
funcţionează aşa cum ne-am imaginat.
• Există mai multe tipuri de teste:

o teste izolate;
o teste de integrare;
o teste funcţionale.

• Testarea se face prin crearea unei clase care moşteneşte clasa TestCase, care face parte din
biblioteca [Link].
• Există o metodă specifică setUp(self), care va fi executată înainte de toate celelalte funcţii de
testare, astfel încât să o putem folosi pentru a configura un scenariu de testare.
• Metodele din clasa care va conţine logica de testare trebuie să înceapă cu test_, astfel încât
compilatorul să ştie că se aplică testului.
• Rolul echilibrării încărcărilor (Load Balancers) este de a distribui încărcarea, adică de a distribui
cererile client către servere. Cererile întregi pot fi alocate separat, precum şi resursele necesare
pentru a efectua o singură cerere.
• Există mai multe tipuri diferite de echilibrare:

o elastică;
o geografică;
o virtuală.

• Printre cei mai utilizaţi algoritmi pentru echilibrarea cererilor/resurselor se numără: round robin,
weighted round robin, least connection şi IP hashing.

Din moment ce ne-am dat seama până acum de ce este nevoie pentru a crea o aplicaţie Django şi cum
se plasează pe web sau cum pare accesibilă tuturor, trebuie să luăm în considerare aspectul de securitate.
Există multe metode şi modalităţi de securizare a site-ului şi sunt descoperite în mod constant noi
metode pentru aceasta. În această lecţie vom enumera câteva dintre cele mai importante metode legate
de securitatea site-ului.

Versiunea

Versiunea Django joacă un rol foarte important în securitate şi este esenţial să folosim cea mai recentă
versiune stabilă. De exemplu, versiunea Django 1.11 (folosim Django 3.0.8 în acest curs) a fost cea mai
recentă versiune care a susţinut Python 2.x. De asemenea, alegerea versiunii Django determină direct în
ce procent şi la ce atacuri va fi susceptibilă aplicaţia noastră.

Autentificarea

Deşi nu am avut un exemplu de autentificare a utilizatorului în aplicaţia noastră Django, asta nu


înseamnă că această funcţionalitate nu este utilizată des. Există atacuri de tip brute force, care pot trimite
sute de cereri către serverul nostru pentru a ghici perechea corectă de nume de utilizator şi parolă.

Page 111 of 123


Pentru a evita acest lucru, este important să configuraţi corect aplicaţia Django pentru a accepta doar
un anumit număr de posibile încercări de autentificări eronate.

Codul sursă al site-ului

Deşi codul se află pe un server protejat, nu înseamnă că nu este supus atacurilor, cel mai des prin
execuţia sa neplanificată. Una dintre cele mai simple modalităţi de a rezolva această problemă este să
mutaţi codul „mai adânc” pe server, adică să nu fie în directorul rădăcină iniţial al serverului. De
asemenea, dacă codul site-ului este plasat pe GitHub, GitLab şi pe platforme similare, este de dorit să
utilizaţi un repozitoriu privat.

Folosirea antetului (headerului)

Pentru o mai mare securitate a comunicării între client şi server, este o bună practică să utilizaţi câmpul
refferrer request din antet. Acesta îi permite clientului trimiterea informaţiilor privind linkul de la care
este trimisă cererea. În cazul în care linkul din care provine cererea nu aparţine serverului cu linkuri
cunoscute, utilizatorul ar trebui să fie informat.

Încărcarea (upload-ul) fişierelor

În aplicaţiile web care îi permit utilizatorului să trimită un fişier, ar trebui să fim deosebit de atenţi.
Anume, sunt posibile atacuri care sunt executate din acel fişier, indiferent de faptul că aplicaţia noastră
poate aştepta doar un anumit tip de fişier. De exemplu, cândva era o problemă obişnuită pentru un
server să aştepte o imagine de tip .jpg şi ca în fişierul respectiv, deşi antetul acestuia este setat să arate
ca o imagine, hackerul a pregătit un cod maliţios care se va executa pe server. Pentru a rezolva aceste
şi alte probleme, este important să limitaţi tipul de fişier care poate fi trimis şi dimensiunea fişierului, să
validaţi fişierul în sine, precum şi să limitaţi posibilitatea de a executa codul din aceste fişiere statice.

În ceea ce priveşte Django în sine ca un cadru de lucru, el este foarte bine echipat atunci când vine vorba
de instrumente de securitate. Este rezistent la multe atacuri, dar întreaga securitate nu vine în mod
implicit după instalarea şi crearea proiectului, ci trebuie să setăm singuri parametrii de securitate doriţi.

Comanda Check

Django are propria comandă pentru analiza securităţii proiectelor şi aplicaţiilor, numită Check. Pentru a
rula această analiză, utilizaţi comanda din linia de comandă: [Link] check --deploy. Dacă am fi rulat-
o în proiectul nostru, am primi următoarea listare:

Listarea comenzii ‘[Link] check --deploy’ :

System check identified some issues:

WARNINGS:

?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. If your entire site is
served only over SSL, you may want to consider setting a value and enabling HTTP Strict Transport Security.
Be sure to read the documentation first; enabling HSTS carelessly can cause serious, irreversible problems.

?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. Unless your site should be
available over both SSL and non-SSL connections, you may want to either set this setting True or configure a
load balancer or reverse-proxy server to redirect all connections to HTTPS.

Page 112 of 123


?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only session cookie
makes it more difficult for network traffic sniffers to hijack user sessions.

?: (security.W016) You have '[Link]' in your MIDDLEWARE, but you


have not set CSRF_COOKIE_SECURE to True. Using a secure-only CSRF cookie makes it more difficult for
network traffic sniffers to steal the CSRF token.

?: (security.W018) You should not have DEBUG set to True in deployment.

?: (security.W022) You have not set the SECURE_REFERRER_POLICY setting. Without this, your site will
not send a Referrer-Policy header. You should consider enabling this header to protect user privacy.

System check identified 6 issues (0 silenced).

Vedem câteva avertismente în această listare:

• W004 - avertismentul se referă la câmpul de fişier SECURE_HSTS_SECONDS [Link] şi face


parte din politica HTTP Strict Transport Security, unde site-ul informează browserul să nu se
conecteze la server pentru un anumit număr de secunde (definit de câmpul
SECURE_HSTS_SECONDS) dacă serverul nu furnizează resurse şi răspunsuri HTTPS formate
corespunzător;
• W008 - un avertisment care se referă din nou la HTTPS şi care spune că, dacă dorim ca site-ul
nostru să utilizeze HTTPS în loc de HTTP, trebuie să setăm câmpul SECURE_SSL_REDIRECT la
True (redirecţionând traficul HTTP către HTTPS);
• W012 - un avertisment privind cookie-urile din cerere, care spune: dacă câmpul
SESSION_COOKIE_SECURE din fişierul [Link] este setat la True, clientul trebuie să trimită
cookie-uri exclusiv printr-o conexiune criptată, dar aceasta nu modifică valoarea cookie-ului în
sine;
• W016 - avertisment similar cu cel precedent, dar se referă la cookie-ul CSRF;
• W018 - un mesaj care ne avertizează că serverul nostru local este încă în modul de dezvoltare
şi că trebuie să îl comutăm în modul de producţie (DEBUG = False) atunci când dorim să plasăm
aplicaţia Django pe server;
• W022 - un avertisment legat de câmpul SECURE_REFERRER_POLICY din fişierul [Link], care
face parte din politica Referrer-Policy, care se referă la cât de multe informaţii, despre linkul de
la care provine clientul care trimite cererea curentă, îi vor fi trimise aplicaţiei noastre.

Dacă avem deja o aplicaţie publicată, putem folosi site-ul Observatory al companiei Mozilla, care va
scana site-ul nostru şi ne va informa despre potenţialele probleme şi soluţiile propuse. Trebuie să
deschidem site-ul şi în câmp trebuie să tastăm numele domeniului aplicaţiei de pe platforma
PythonAnywhere. Dăm clic pe Scan Me. Pe pagina care apare, vom primi un raport de securitate detaliat,
ca în imaginea de mai jos:

Page 113 of 123


Imaginea 17.1. Raportul Observatory privind securitatea aplicaţiei noastre

Ca mesaj principal în secţiunea Recommendation, vedem informaţii despre HTTPS. Și anume, scrie că
serverul nostru acceptă conexiunea HTTPS şi că aplicaţia Django foloseşte HTTP. Pe baza informaţiilor
din textul anterior, recunoaştem că acesta este un câmp SECURE_SSL_REDIRECT din fişierul [Link].
Deci, dacă deschidem fişierul [Link] în pagina PythonAnwhere şi adăugăm linia
SECURE_SSL_REDIRECT = True, salvăm fişierul, reîncărcăm aplicaţia şi rulăm din nou site-
ul [Link] vom vedea un rezultat mai bun:

Imaginea 17.2. Raportul Observatory privind securitatea aplicaţiei noastre după asigurarea conexiunii
HTTPS

Page 114 of 123


În noul raport, în secţiunea Recommendation, vedem un mesaj despre modul în care am corectat
această eroare, precum şi diferenţa de rating. De asemenea, pe aceeaşi pagină, putem vedea în detaliu
care dintre verificările de securitate au trecut site-ul nostru şi care nu, şi, prin urmare, reacţionăm după
necesitate. Este important să menţionăm că, dacă folosim SECURE_SSL_REDIRECT = True, atunci când
lucrăm pe un server local, vom primi o eroare, deoarece serverul local (django web development server)
este setat la HTTP.
Pentru mai multe informaţii despre securitatea în Django, consultaţi documentaţia disponibilă.

Testarea aplicaţiei

Testarea poate fi prezentată ca o bucată de cod care verifică dacă cealaltă bucată de cod dorită
funcţionează aşa cum ne-am imaginat.
Testarea poate fi împărţită în mai multe tipuri:

• Testare izolată (teste unitare-unit tests) - codul este izolat de restul bazei de cod şi astfel este
testat doar el; acestea sunt cele mai rapide teste; cel mai frecvent se referă la testarea unei
funcţii sau clase la un moment dat;
• Teste de integrare (intergation tests) - teste care testează mai multe bucăţi de cod diferite
pentru a verifica dacă funcţionează corect; un exemplu simplu al acestui tip de testare ar fi
verificarea datelor între cereri şi răspunsuri pe server;
• Teste funcţionale (functional tests) - se referă la testarea de la început până la sfârșit. Mai exact,
ele imită utilizatorul real şi comportamentul acestuia (emulează întregul browser) pe site.
Acestea sunt cele mai lente de executat.

Testarea este o sarcină destul de complexă, dar şi extrem de importantă pentru aplicaţia noastră,
deoarece testarea poate afecta direct stabilitatea aplicaţiei. De asemenea, este important să menţionăm
că principiile generale de testare din această unitate nu se aplică numai la testarea aplicaţiei Django, ci
se pot aplica oricărui cod scris în Python. Deci, indiferent de Django, în Python există un cuvânt-cheie
rezervat, assert, cu care putem verifica oricare două comenzi, variabile etc.:

1assert "hello" == "hello", "Hello World, error!"


2assert "hello" == "world", "Hello World, error!"

Dacă am rula acest cod, prima linie ar fi executată fără nicio listare, dar eroarea AssertionError ar apărea
deja pe a doua linie. Prin urmare, valoarea returnată a comenzii assert va exista numai dacă condiţia pe
care o verificăm nu este adevărată (false). De asemenea, pe lângă primul parametru pe care îl transmitem
când utilizăm cuvântul-cheie assert - condiţia pe care o verificăm, există un alt cuvânt-cheie, opţional,
care se referă la mesajul care va fi listat lângă AssertionError.

Testarea în Django

În exemplul următor, ne vom concentra direct pe testarea aplicaţiei noastre book_library. În acel folder
ar trebui să găsim fişierul [Link], care este întotdeauna gol după crearea aplicaţiei.

Testarea se face prin crearea unei clase care moşteneşte clasa TestCase, care face parte din biblioteca
[Link]. Clasa Client ar trebui introdusă din aceeaşi bibliotecă, care va fi utilizată pentru a trimite
cereri GET şi POST. Pe lângă aceste două importări, în [Link] introducem o funcţie reverse cu care vom
apela linkurile peste care dorim să efectuăm testele. Opţional, dacă dorim acces la variabilele din fişierul
[Link], putem importa şi form .import views.

Metodele clasei pe care o vom crea, care moşteneşte clasa TestCase, sunt puţin mai specifice şi le
acceptă doar o anumită sintaxă. Şi anume, există o metodă specifică setUp (self), care va fi executată

Page 115 of 123


înainte de toate celelalte funcţii de testare, astfel încât să o putem folosi pentru a configura un scenariu
de testare. În cazul nostru, dorim să-l folosim doar pentru a iniţializa un obiect client din clasa Client(),
astfel încât codul nostru actual al fişierului [Link] arată astfel:

1# Create your tests here.


2from [Link] import TestCase, Client
3from [Link] import reverse
4from . import views
5class ExampleViewTestCase(TestCase):
6 def setUp(self):
7 [Link] = Client()

Acum putem lista primul nostru test. Metodele din clasa noastră ExampleViewTestCase, care vor conţine
logica testului, trebuie să înceapă cu test_, astfel încât compilatorul să ştie că se aplică testului. De
exemplu, dacă am avea o metodă în clasa ExampleViewTestCase care nu începe cu test_, aceasta nu ar
fi executată. Pentru început, dorim să testăm dacă clientul, dacă deschide pagina principală books/,
primeşte un şablon [Link], precum şi dacă cererea are succes (cod de stare):

1 def test_index_GET(self):
2 response = [Link](reverse('index_page'))
3 [Link](response.status_code, 200)
4 [Link](response, '[Link]')

Am apelat mai întâi metoda get a obiectului client către care am transmis funcţia reverse() cu parametrul
index_page, care reprezintă linkul denumit din fişierul [Link] a listei urlpatterns: path('books/',
[Link], name = 'index_page'). Plasăm răspunsul care soseşte de la apelarea GET în răspunsul cu
ajutorul căruia verificăm:

• dacă codul de stare cu răspuns este 200 - prin urmare, dacă cererea GET a reuşit:
[Link](response.status_code, 200); în acest caz, verificăm cu metoda assertEqual, în
care ambele componente ale stării trebuie să fie absolut egale pentru ca starea să fie adevărată;
• dacă cererea GET către pagina books/ a returnat [Link]’: [Link](response,
'[Link]'); în acest caz, verificăm metoda assertTemplateUsed, unde verificăm corectitudinea
fişierului şablon furnizat după cererea GET.

Pentru a rula acest test, folosim o apelare similară cu cea pentru pornirea serverului:

python [Link] test book_library -v 2

Deci, ne poziţionăm în linia de comandă din directorul care conţine fişierul [Link]. Celelalte
argumente ale acestei comenzi sunt următoarele:

• test - comandă de testare prin care subliniem că dorim testarea;


• book_library' - subliniem faptul că dorim să testăm aplicaţia book_library - ceea ce înseamnă că
fişierul 'book_library/[Link]' va fi utilizat pentru testare;
• -v 2 - parametrul v este o abreviere de la verbose; aceasta permite o listare mai extinsă pe ecran
în timpul testării (când rulăm teste, experimentăm cu valorile 1, 2, 3 pentru parametrul v).

Page 116 of 123


Imaginea 17.3. Afişarea testului efectuat cu succes

În listarea din linia de comandă, linia subliniată este cea mai importantă pentru noi, deoarece se referă
la succesul testului care se află în metoda def test_index_GET (self). Mai jos vedem testele Run 1 tests in
0.21s OK, ceea ce înseamnă că toate testele care au fost rulate (un test - o metodă), au avut succes.

Pentru următorul test, dorim să verificăm cum funcţionează descărcarea unei cărţi după nume, de la
linkul numit str_test în lista urlpatterns:

path('books/<str:book_name>',[Link], name = 'str_test')

Şi în acest caz vom trimite o cerere GET, dar de această dată vom transmite şi un argument care va fi
mapat la <str:book_name> (astfel încât argumentul să trimită book_name) pentru a trimite cu succes
numele cărţii al cărui an de publicare dorim să-l descărcăm:

def test_get_book_GET(self):
1 response = [Link](reverse('str_test', args=['Hamlet']))
2 [Link](response.status_code, 200)
3 [Link]([Link], b"<h1>Book Hamlet was published in
41603 year.</h1>")

În această metodă de testare, pe lângă metoda assertEqual, care verifică doar codul de stare, avem şi
assertEqual, care va verifica conţinutul răspunsului serverului. Mai exact, va verifica dacă codul HTML
obţinut în răspuns se potriveşte cu stringul "<h1>Book Hamlet was published in 1603 year.</h1>". În
acea linie, am convertit şi acest string într-un byte string, deoarece serverul HTTP trimite ca răspuns un
byte string.

Pentru a ne asigura, pentru o securitate mai mare, că testele nu sunt omise pur şi simplu, ceea ce uneori
se poate întâmpla din cauza unui proiect Django configurat greşit, a unor comenzi greşite etc. - putem

Page 117 of 123


stabili intenţionat o condiţie incorectă în metodele assert şi, astfel, putem verifica dacă testele sunt
efectuate. Pentru nevoile unui astfel de scenariu, în metoda test_get_book_get(self) putem modifica
stringul aşteptat din:

[Link]([Link], b"<h1>Book Hamlet was published in 1603 year.</h1>")

în:

[Link]([Link], b"<h1>Book The Raw Youth was published in 1603 year.</h1>")

Un exemplu de test eşuat la apelarea test_get_book_GET(self) arată astfel:

Imaginea 17.4. Afişarea unui test care a eşuat

Din acea listare a liniei de comandă, vedem că ne-a fost raportat un test care nu a „trecut” (FAILED
(failures = 1)), o metodă care nu a trecut, precum şi informaţii detaliate despre eroare. Reîntoarcem
verificarea răspunsului HTML la afirmaţia adevărată, care va continua descărcarea cărţii Hamlet.

După ce am testat cu succes navigarea prin site, este timpul să testăm şi metoda POST, completând şi
trimiţând formularul. În aceste scopuri, în loc de metoda get(), vom folosi metoda post() peste obiectul
client, către care transmitem mai întâi calea la care se află formularul sub forma unui link denumit din

Page 118 of 123


lista urls/urlpattenrs (path('add-book/', views.get_book, name='book_added')). Al doilea parametru al
metodei post() este de tip dicţionar şi reprezintă numele câmpurilor din formular ca valori și chei pe
care dorim să le testăm, pe care le transmitem la acel formular (numele exacte ale câmpurilor
formularului pot fi găsite în fişierul book_library/[Link]). Deoarece după trimiterea cu succes a
numelui şi a anului cărţii, aplicaţia noastră trimite o cerere de redirecţionare (redirect) cu codul de stare
302 către linkul book-added/, dorim să verificăm şi acest lucru. Deci, metoda noastră de testare pentru
cererea POST arată astfel:
def test_book_post_POST(self):
1 response = [Link](reverse('book_added'),
2{'book_title' :'Oliver Twist', 'book_year' : '1838'})
3 [Link](response.status_code, 302)
4 [Link]([Link], '/book-added/')

În acest caz de testare, dorim să îl includem pe Oliver Twist în lista noastră de cărţi [Link]/books. Dar
înainte de asta, trebuie să verificăm succesul cererii redirecţionate şi dacă trecem la pagina /book-
added/ după ce am adăugat cartea. Dacă testul a trecut, înseamnă că nu trebuie să schimbăm nimic în
codul fişierului [Link] şi putem continua cu ultima verificare - dacă cartea a fost într-adevăr adăugată.
Acest lucru se poate face în mai multe moduri, iar unul dintre ele verifică, pur şi simplu, dacă cuvântul
Twist se află în lista cărţilor de la linkul ’/books/’:
1 def test_if_book_is_present(self):
2 response = [Link](reverse('index_page'))
3 [Link](response.status_code, 200)
4 [Link](b'Twist',[Link])<br><br>
Deoarece aceasta este o cerere GET, folosim metoda client cu numele get(), căreia îi transmitem din nou
un link numit index_page. Din această cerere, verificăm codul de stare cu metoda assertEqual, precum
şi condiţia: dacă byte stringul Twist se află în acel răspuns al serverului HTML, cu metoda assertIn.

Echilibrarea încărcărilor (Load Balancer)

Echilibrarea (aranjarea) încărcărilor este o componentă esenţială în arhitectura serverului. După cum ne
sugerează şi numele, rolul acestor componente este de a distribui încărcarea, adică de a aloca cererile
client pe servere. Cererile întregi pot fi alocate separat, precum şi resursele necesare pentru a efectua o
singură cerere.

Imaginea 17.5. Reprezentare schematică a Load Balancer-ului în comunicarea client-server

Page 119 of 123


Întrucât cele mai populare site-uri de astăzi au un trafic mare în care cererile client sunt în mod constant
numărate în milioane, trebuie să se reducă încărcarea serverului. Tocmai în aceste scenarii se foloseşte
echilibrarea.

Durata sesiunii

O problemă importantă care apare în urma echilibrării este scenariul modului în care se tratează
informaţiile care trebuie stocate pe mai multe cereri în timpul unei sesiuni de utilizator. Dacă aceste
informaţii sunt stocate local pe server, atunci serverele către care merg cererile ulterioare nu vor avea
informaţii despre acestea. Astfel, dacă echilibrarea nu este configurată corect, poate avea exact efectul
opus şi poate ocupa mai multe resurse decât este necesar.

Modul de funcţionare a echilibrării

Modul în care funcţionează un astfel de proces este simplu. Echilibrarea se face mai ales fie prin
software, fie prin hardware, iar principiul se rezumă la acelaşi lucru: ascultarea cererilor client pe un
anumit port. Când soseşte o cerere, Balancer-ul o preia şi, conform logicii şi criteriilor prestabilite,
transmite cererea către un server care se află sub o încărcare acceptabilă. După ce serverul procesează
cererea, acesta trimite un răspuns către Balancer, care transmite acel răspuns către client. În acest fel,
clientul nu este conştient de această comunicare dintre Balancer şi server şi nu poate şti cum se
determină distribuirea funcţionalităţilor web pe server.

Tipuri de echilibrare

Există mai multe tipuri diferite de echilibrare, şi anume:


• echilibrarea elastică - extinde automat resursele necesare pe baza cantităţii de trafic; face acest
lucru monitorizând în permanenţă utilizarea serverului şi evaluând şi alocând mai multe resurse
conform logicii prestabilite;
• echilibrarea geografică - se referă la distribuirea traficului între diferite servere şi centre de date
pentru a împărţi cererile client după locaţia geografică din care provin;
• echilibrarea virtuală - se referă la lucrul cu maşini şi servere virtuale.

Imaginea 17.6. Reprezentare schematică a echilibrării virtuale

Page 120 of 123


Echilibrarea software şi hardware

În practică, echilibrarea hardware apare, de obicei, ca o soluţie pe care compania o oferă împreună cu
software-ul său, care include un hardware special conceput pentru echilibrare. Aceste soluţii sunt, de
obicei, scumpe şi sunt recomandate pentru site-urile mari.

Avantajul echilibrării software este că software-ul poate funcţiona pe hardware-ul deja existent pe care
îl avem, deci, este o echilibrare mai ieftină şi mai flexibilă, dar, prin urmare, mai puţin eficientă atunci
când vine vorba de site-uri cu milioane de cereri.

Avantajele şi dezavantajele echilibrării

Există multe avantaje în implementarea echilibrării site-ului. Câteva dintre aceste avantaje sunt:

• perioadă mai scurtă de timp pe care serverul o va petrece ca indisponibilă, deoarece dacă un
server se blochează - celălalt îi va accepta cererile;
• o impresie mai bună care îi rămâne utilizatorului, deoarece site-ul se încarcă mai repede, astfel
încât clientul primeşte informaţiile dorite mai repede; în acest caz, Load Balancers sunt
configurate fie pentru a distribui cererile pe servere, fie pentru a furniza utilizatorilor datele
dorite de la cel mai apropiat server geografic;
• încărcare mai mică pe un server; acest lucru se aplică şi pentru paragraful anterior; deoarece un
server este mai puţin ocupat, utilizatorii vor primi un răspuns mai rapid şi există mai puţine
şanse ca acelaşi server să fie supraîncărcat de cereri.

Desigur, există şi dezavantaje ale implementării echilibrării. Dacă o facem noi înşine - va creşte
complexitatea întregului server, precum şi timpul investit. Dacă folosim soluţii deja existente, fie software,
fie hardware - trebuie să plătim pentru aceste servicii, deci, este bine să analizăm mai întâi dacă site-ul
nostru are nevoie de echilibrarea încărcărilor sau, pur şi simplu, să investim într-un server existent.

Rolul componentei de echilibrare

După ce am văzut câteva dintre avantajele şi dezavantajele echilibrării, să aruncăm o privire asupra
rolurilor sale:

• după cum am menţionat deja, unul dintre rolurile principale este de a distribui cereri pe server
pe baza unui algoritm de echilibrare predefinit;
• monitorizarea activă a „sănătăţii” serverului în clusterul de servere, adică monitorizarea stării
acestuia şi a ocupării resurselor;
• facilitarea adăugării şi eliminării dinamice a listelor de servere, ceea ce îmbunătăţeşte extinderea
spaţiului.

Niveluri de reţea şi partajarea încărcărilor

Am văzut că Load Balancers pot fi împărţite în software şi hardware. De asemenea, pot fi împărţite în
funcţie de nivelul modelului OSI în care sunt implementate:

• Load Balancers în nivelul de transport (L4 Load Balancers) sunt Balancers care funcţionează pe
al patrulea nivel al modelului OSI. Adresa IP şi portul TCP sunt singurele informaţii disponibile
din pachetele de rețea ale celui de-al patrulea nivel, astfel încât aceste Balancers au
funcţionalitate limitată;
• Load Balancers în nivelul de aplicaţie (L7 Load Balancers) sunt Balancers care funcţionează pe
nivelul de aplicaţie al modelului OSI. În această privinţă, acestea au acces la un set de date mult

Page 121 of 123


mai mare decât cele din al patrulea nivel, care includ anteturi HTTP, date trimise în formular,
numărul sesiunii etc.; acest set suplimentar de informaţii contribuie ca Load Balancers de nivelul
şapte să fie mai inteligente şi mai răspândite.

Algoritmi de echilibrare

După ce ne-am familiarizat cu principiul de funcţionare al Balancer-ului, vom menţiona câţiva dintre cei
mai utilizaţi algoritmi pentru echilibrarea cererilor/resurselor:

Algoritmul round robin

Când se foloseşte acest algoritm, serverul echilibrează cererile în aşa fel încât trimite cererea primită
către server, care procesează şi trimite cererea înapoi. Următoarea cerere merge la următorul server şi
aşa continuă din clusterul de servere. În practică, această metodă este utilizată mai rar.

Imaginea 17.7. Reprezentare schematică a algoritmului round robin

Algoritmul round robin selectiv (weighted round robin)

Funcţionează într-un mod similar cu metoda round robin, dar fiecăruia dintre servere i se atribuie un
număr care indică practic câte cereri poate trimite serverul respectiv. Cu cât acest coeficient este mai
mare, cu atât mai multe cereri pot fi trimise, iar pe baza acestui număr, Load Balancer-ul echilibrează
cererile.

Imaginea 17.8. Reprezentare schematică a algoritmului round robin selectiv

Page 122 of 123


Algoritmul cu cele mai puţine conexiuni (least-connection)

Aceasta este o metodă simplă şi foarte eficientă. După cum ne sugerează şi numele său, Balancer-ul va
găsi serverul cu cele mai puţine conexiuni deschise şi îi va atribui o cerere. De exemplu, dacă un server
poate gestiona maximum zece conexiuni simultan şi are în prezent două conexiuni deschise, iar un alt
server poate primi douăzeci de conexiuni simultan şi în prezent are patru deschise - Balancer-ul
implementat de aceşti algoritmi va decide că ambele servere au acelaşi număr de conexiuni. De aceea,
se spune că acest algoritm este destul de corect în comparaţie cu round robin.

Imaginea 17.9. Reprezentare schematică a algoritmului cu cele mai puţine conexiuni

IP hashing

Acest algoritm poate fi aplicat în situaţiile în care trebuie să-i permitem aceluiaşi utilizator să acceseze
întotdeauna aceleaşi servere sau, de exemplu, atunci când trebuie stabilit cel mai apropiat server
geografic. Acesta va utiliza o funcţie hash, care va determina către ce server să redirecţioneze cererea
pe baza adresei IP a cererii.

Page 123 of 123

S-ar putea să vă placă și