Садржај
3.0 SQL: Промена садржаја базе
3.2 SQL: Ажурирање података у табелама
3.3 SQL: Брисање података из табела
7.0 Библиотека Flask - пројектни задатак

AJAX

Сви досадашњи скрипови су били организовани на традиционални начин – након што стигне захтев, скрипт на серверској страни формра HTML страну и шаље је клијенту који ту страну приказује у прегледачу веба, мењајући њоме страну која је била отворена. У овом поглављу ћемо приказати другачију организацију апликације у којој клијент, помоћу скрипта написаног у програмском језику JavaScript, на основу неке акције корисника над тренутно отвореном страном шаље захтев серверу да му достави неке податке, а онда када ти подаци стигну приказује их у склопу стране која је тренутно отворена. Оqвај начин рада се назива AJAX (Asynchronous JavaScript and XML). Разлог је то што су некада подаци са сервера ка клијенту стизали у формату XML, док се данас јако често користи формат JSON (JavaScript Object Notation).

Употребу AJAX технологије приказаћемо на примеру апликације која врши претрагу свих извођача из наше базе података. Корисник уноси текст у поље за претрагу, а у листи испод тог поља се приказује првих десет извођача чије име почиње унетим карактерима. Апликација ће имати две путање. Иницијално се шаље захтев ка кореној путањи и приказује се страна које садржи поље за претрагу. У датотеци app.py се налази следећа функција.

@app.route("/")
def index():
    return render_template("index.html")

Шаблон садржи поље за унос текста, неуређену листу (за сада празну) у којој ће се приказивати називи извођача и JavaScript скрипт који остварује комуникацију са сервером.

<html>
  <head>
    <title>Artists</title>
  </head>
  <body>
    <form>
      <input type="text" id="Name" />
    </form>

    <ul id="Artists">
    </ul>

    <script type="text/javascript">
      });
    </script>
  </body>
</html>

JavaScript скрипт (чији ћемо кôд касније приказати) реагује на промену садржаја у том пољу и шаље захтев за подацима скрипту који се налази на путањи /artists и који као GET параметар прима текст уписан у поље за претрагу. Скрипт врши упит ка бази података, чита све извођаче чије име почиње унетим карактерима и враћа серверу тај низ имена извођача у формату JSON. Датотека app.py садржи, дакле, и следећу функцију.

@app.route("/artists")
def artists():
    name = request.args.get("name")
    artists = query_db("SELECT Name FROM Artist WHERE Name LIKE ?", (name + "%", ))
    return jsonify(artists)

Овај серверски скрипт, дакле, прима GET параметар name (то је ниска карактера којом мора да почне назив извођача). Након тога формира упит коришћењем израза naziv LIKE 'name%', где је name почетак назива извођача, док специјални знак %, подсетимо се, у језику SQL означава произвољну ниску карактера. За ово, је наравно, потребно користити параметризовани упит. Иако би се могло помислити да је параметризовани упит могуће направити тако што се на место почетка назива извођача стави упитник. тј. тако да се у упиту напише naziv LIKE '?%', то не би радило коректно. Наиме, упитник који је параметар не сме да се јави унутар наводника, јер се иначе неће сматрати параметром. Стога је потребно да цела ниска која следи иза оператора LIKE буде параметар, тј. да се у упиту напише naziv LIKE ?. Додавање специјалног карактера % се онда врши приликом задавања вредности параметра (у склопу другог аргумента функције query_db). Нагласимо да, можда супротно очекивању, на том месту није потребно додавати наводнике нити апострофе. Наиме, систем аутоматски приликом замене параметара третира тај параметар као ниску. Пошто упит има само један параметар, као и увек приликом параметризованих упита, морамо га навести у облику једночлане торке. На крају добијену листу ниски претварамо у JSON формат коришћењем Flask функције jsonify (она мора бити увезена тј. наведена у склопу наредбе import, на почетку скрипта).

Рад овог скрипта можемо једноставно испробати, посетом URL-у облика http://127.0.0.1/artists?name=Me. Као резултат, добијамо следећи садржај.

[["Metallica"],["Men At Work"],["Mela Tenenbaum, Pro Musica Prague & Richard Kapp"]]

На крају, прикажимо и JavaScript скрипт који шаље AJAX захтев, прихвата га и резултат тј. називе извођача приказује у листи.

    <script type="text/javascript">
      var inputName = document.getElementById("Name");
      inputName.addEventListener("keyup", function(e) {
          if (inputName.value.length >= 2) {
              var xhttp = new XMLHttpRequest();
              xhttp.onreadystatechange = function() {
                  if (this.readyState == 4 && this.status == 200) {
                      var artists = JSON.parse(xhttp.responseText);
                      var ul = document.getElementById("Artists");
                      ul.innerHTML = "";
                      artists.forEach(artist => {
                          var li = document.createElement("li");
                          li.innerHTML = artist[0];
                          ul.append(li);
                      });
                  }
              }
              const params = new URLSearchParams({"name": inputName.value});
              const url = "{{ url_for('artists') }}" + "?" + params.toString();
              xhttp.open("GET", url);
              xhttp.send();
          }
      });
    </script>

У првом реду само прихватамо DOM објекат који одговара пољу за унос текста. У другом региструјемо реакцију на догађај отпуштања дугмета тастатуре док се курсор налази у том пољу (то се дешава након сваког откуцаног карактера и то нам указује да је садржај поља за унос текста промењен и да треба освежити садржај листе. Реакција на догађај се задаје у облику анонимне функције. У њој користимо основни механизам за постављање AJAX захтева, заснован на објекту XMLHttpRequest. Прво конструишемо овај објекат. Затим, постављањем атрибута onreadystatechange дефинишемо функцију која ће бити позвана када год се промени стање у комуникацији клијента и сервера. То стање се види кроз атрибут readyState и оно је једнако:

  • 0 пре него што се пошаље захтев од клијента ка серверу,

  • 1 када је отворена комуникациона веза између њих (што се ради позивом методе open()),

  • 2 када је захтев послат и примљен је статус и заглавље одговора (што се дешава након позива методе send()),

  • 3 док је у току преузимање одговора са сервера и

  • 4 када је одговор примљен и комуникација је завршена.

Наша анонимна функција ће бити позвана сваки пут када се статус промени, али она заправо треба да прикаже добијене податке само када су они у потпуности успешно примљени, што се може одредити тако што атрибут readyState има вредност 4, док атрибут status има вредност 200 (подсетимо се да је овде у питању троцифрени статус HTTP одговора, где 200 означава OK, где статуси који почињу цифром 3 означавају да је тражени ресурс премештен, где они који почињу цифром 4 означавају да је нека грешка у захтеву клијента, а они који почињу цифром 5 означавају да је настала нека грешка на страни сервера). Ако је одговор успешно стигао, онда прво позивамо функцију JSON.parse да од ниске у JSON формату направи JavaScript структуре података. У нашем примеру то ће да буде низ једночланих низова. Након тога прихватамо DOM објекат који одговара листи (елементу ul) која ће садржати називе извођача и празнимо је (постављајући јој унутрашњи HTML садржај inerHTML на празну ниску). Након тога обрађујемо сваки елемент добијене листе извођача (то је једночлана листа) тако што узимамо једини члан листе, смештамо га у елемент li који градимо функцијом document.createElement и затим тај елемент li убацујемо на крај листе, коришћењем методе append.

Када је дефинисана функција која ће прихватити одговор са сервера, требало би послати захтев ка серверу. Пошто користимо методу GET, позваћемо методу open објекта xhttp тако што ћемо јој као први аргумент проследити ниску "GET", а као други URL на који ће се послати захтев. Да не бисмо зависили од путање, поново ћемо унутар шаблона употребити функцију url_for којом ћемо добити путању на којој одговара Python функција artists. Иако параметре URL-а можемо да изградимо и ручно (изразом "naziv=" + inputNaziv.value), препоручени начин је да користимо уграђену JavaScript функционалност за то. Класа URLSearchParams ће изградити део URL-а који кодира GET параметре, ако јој те параметре проследимо као речник. Након изграње URL-а и отварање везе према њему методом open, захтев коначно шаљемо методом send().

(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+