Skip to content

32. Sürüm 12'deki HTML modu

Sürüm 12'nin başında, uygulamayı birkaç aşamada geliştireceğimizi belirtmiştik. Şöyle yazmıştık:

  • HTML uygulamasının görünümlerinden yola çıkarak, web uygulamasının gerçekleştirmesi gereken eylemleri tanımlayacağız. Burada gerçek görünümleri kullanacağız, ancak bunlar sadece kağıt üzerinde çizilmiş görünümler de olabilir;
  • bu eylemlerden yola çıkarak, HTML uygulamasının URL hizmetlerini tanımlayacağız;
  • bu hizmet URL'lerini, jSON sunan bir sunucu ile uygulayacağız. Bu, sunulacak HTML sayfalarıyla ilgilenmeden web sunucusunun iskeletini tanımlamamızı sağlar. Bu URL hizmetlerini Postman ile test edeceğiz;
  • ardından jSON sunucumuzu bir konsol istemcisiyle test edeceğiz;
  • jSON sunucusu onaylandıktan sonra, HTML uygulamasını yazmaya geçeceğiz;

jSON ve XML sunucularımız çalışır durumda. Artık HTML sunucusuna geçebiliriz. Göreceğiz ki bu sunucu, jSON / XML sunucuları için geliştirilen tüm mimariyi devralıyor ve bunlara HTML görünüm yönetimini ekliyor.

32.1. MVC Mimarisi

MVC olarak adlandırılan mimari modelini (Model – Görünüm – Denetleyici) şu şekilde uygulayacağız:

Bir istemciden gelen isteğin işlenmesi şu şekilde gerçekleşecektir:

  • 1 - istek

Talep edilen URL'ler, http://machine:port/action/param1/param2/… biçiminde olacaktır. [Contrôleur principal], talebi doğru denetleyiciye "yönlendirmek" için bir yapılandırma dosyası kullanacaktır. Bunun için, URL'in [action] alanını kullanacaktır. URL ile [param1/param2/…] arasındaki kısım, eyleme iletilecek isteğe bağlı parametrelerden oluşur. Burada MVC'in C'si, [Contrôleur principal, Contrôleur / Action] dizesidir. İstenen eylemi işleyebilecek hiçbir denetleyici yoksa, web sunucusu istenen URL'in bulunamadığını bildirir.

  • 2 - İşleme
  • Seçilen [2a] eylemi, [Contrôleur principal] tarafından kendisine iletilen parami parametrelerini kullanabilir. Bu parametreler iki kaynaktan gelebilir:
      • [/param1/param2/…]'in URL'teki yolu,
      • istemcinin isteğinin gövdesine gönderilen parametrelerden;
    • kullanıcının isteğinin işlenmesi sırasında, eylem [métier] ve [2b] katmanlarına ihtiyaç duyabilir. İstemcinin isteği işlendikten sonra, bu eylem çeşitli yanıtları tetikleyebilir. Klasik bir örnek şudur:
      • istek düzgün bir şekilde işlenemediğinde bir hata yanıtı;
      • aksi takdirde bir onay yanıtı;
    • [Contrôleur / Action], yanıtını [2c] olarak ana denetleyiciye iletecek ve bir durum kodu gönderecektir. Bu durum kodları, uygulamanın bulunduğu durumu benzersiz bir şekilde temsil edecektir. Bu, ya bir başarı kodu ya da bir hata kodu olacaktır;
  • 3 - yanıt
    • müşterinin jSON yanıtını talep etmesine bağlı olarak, XML veya HTML yanıtını talep etmiş olmasına bağlı olarak, [Contrôleur principal], uygun yanıt türünü [3a]'te oluşturacak ve ondan yanıtı müşteriye göndermesini isteyecektir. [Contrôleur principal], çalıştırılan [Contrôleur / Action] tarafından sağlanan yanıtı ve durum kodunu ona iletecektir;
    • istenen yanıt jSON veya XML türündeyse, seçilen yanıt kendisine verilen [Contrôleur / Action] yanıtını biçimlendirecek ve [3c] aracılığıyla gönderecektir. Bu yanıtı işleyebilen istemci, bir Python konsol komut dosyası veya bir HTML sayfasında barındırılan bir JavaScript komut dosyası olabilir;
    • istenen yanıt HTML türündeyse, seçilen yanıt kendisine verilen durum kodunu kullanarak [3b], HTML veya [Vuei] görünümlerinden birini seçecektir. Bu, MVC'in V'sidir. Her durum koduna tek bir görünüm karşılık gelir. Bu V görünümü, yürütülen [Contrôleur / Action]'in yanıtını görüntüleyecektir. Bu görünüm, HTML, CSS ve JavaScript kullanarak bu yanıtın verilerini işler. Bu verilere görünüm modeli denir. Bu, MVC'teki M harfidir. İstemci ise çoğu zaman bir tarayıcıdır;

32.2. HTML sunucusunun komut dosyası dizin yapısı

Image

  • [1]'te, HTML sunucusunun statik öğeleri;
  • [2-3]'te, HTML sunucusunun V görünümleri. [2] parçaları, [3] görünümlerinde yeniden kullanılabilir öğelerdir;
  • [4]'te, görünümlerin statik olarak test edilmesinde kullanılacak bir klasör;
  • [5] içinde, V görünümlerinin M şablonlarının bulunduğu klasör; MVC'in M şablonu;

32.3. Görünümlerin Tanıtımı

HTML web uygulaması dört görünüm kullanır. İlk görünüm, kimlik doğrulama görünümüdür:

  • bu ilk görünüme yönlendiren eylem, [/init-session] [1] eylemidir;
  • [Valider] düğmesine tıklandığında, iki parametre gönderilerek [/authentifier-utilisateur] eylemi tetiklenir: [2-3];

Vergi hesaplama görünümü:

Image

  • [1]'te, bu görünümü getiren [/authentifier-utilisateur] eylemi;
  • [2]'te, [Valider] düğmesine tıklanması, üç parametreyle ([2-5]) [/calculer-impot] eyleminin yürütülmesini tetikler;
  • [6] bağlantısına tıklandığında, [/lister-simulations] eylemi parametre olmadan tetiklenir;
  • [7] bağlantısına tıklandığında, [/fin-session] eylemi parametre olmadan tetiklenir;
  1. görünüm, kimliği doğrulanmış kullanıcı tarafından yapılan simülasyonlara aittir:

Image

  • [1]'te, bu görünümü getiren [/lister-simulations] eylemi;
  • [2]'te, [Supprimer] bağlantısına tıklanması, listeden silinecek simülasyon numarası parametresiyle [/supprimer-simulation] eylemini tetikler;
  • [3] bağlantısına tıklandığında, parametre içermeyen [/afficher-calcul-impot] eylemi tetiklenir ve vergi hesaplama ekranı yeniden görüntülenir;
  • [4] bağlantısına tıklandığında, parametre olmadan [/fin-session] eylemi tetiklenir;
  1. ekran, beklenmedik hatalar ekranı olarak adlandırılacaktır:
  • [1]'te: kullanıcı URL'i kendisi girmiştir. Oysa bu örnekte herhangi bir simülasyon yoktu. Dolayısıyla [2] hata mesajı alınır. Bu hatayı biliyoruz. jSON / XML'te de karşılaşmıştık. Bu tür hatalara “beklenmedik hata” diyeceğiz, çünkü bunlar uygulamanın normal kullanımı sırasında ortaya çıkamaz. Bu hatalar, kullanıcı URL kodlarını kendisi girdiğinde ortaya çıkabilir; Image
  • beklenmedik bir hata durumunda, [3-5] bağlantıları diğer üç görünümden birine geri dönmenizi sağlar;

jSON / XML sunucusunun çeşitli URL hizmetlerini hatırlayalım:

Eylem
Rol
Çalıştırma bağlamı
/init-session
İstenen yanıtların türünü (json, xml, html) belirlemek için kullanılır
GET isteği
Herhangi bir zamanda gönderilebilir
/kullanıcı-doğrulama
Bir kullanıcının oturum açmasına izin verir veya vermez
POST isteği.
İstek, [user, password] adlı iki POST parametresine sahip olmalıdır
Yalnızca oturum türü (json, xml, html) biliniyorsa gönderilebilir
/calculer-impot
Vergi hesaplaması simülasyonu yapar
POST isteği.
İstek, üç adet POST parametresi içermelidir: [marié, enfants, salaire]
Yalnızca oturum türü (json, xml, html) biliniyorsa ve kullanıcı kimliği doğrulanmışsa gönderilebilir
/simülasyonları-listele
Oturumun başlangıcından itibaren gerçekleştirilen simülasyonların listesini görüntüleme isteği
GET isteği.
Yalnızca oturum türü (json, xml, html) biliniyor ve kullanıcı kimliği doğrulanmışsa gönderilebilir
/simülasyonu-sil/numara
Simülasyon listesinden bir simülasyonu siler
GET isteği.
Yalnızca oturum türü (json, xml, html) biliniyor ve kullanıcı kimliği doğrulanmışsa gönderilebilir
/vergi-hesaplamasını-göster
Vergi hesaplama sayfası HTML'i görüntüler
GET isteği.
Yalnızca oturum türü (json, xml, html) biliniyorsa ve kullanıcı kimliği doğrulanmışsa gönderilebilir
/oturumu-sonlandır
Simülasyon oturumunu sonlandırır.
Teknik olarak eski web oturumu silinir ve yeni bir oturum oluşturulur
Yalnızca oturum türü (json, xml, html) biliniyor ve kullanıcı kimliği doğrulanmışsa gönderilebilir

Bu farklı URL hizmet kodları, HTML sunucusu için de kullanılacaktır.

32.4. Görünümlerin yapılandırılması

Bir eylem, bir denetleyici tarafından işlenir. Bu denetleyici, bir tuple (sonuç, status_code) döndürür; burada:

  • [résultat], [action, état, réponse] anahtarlarından oluşan bir sözlüktür;
  • [status_code], istemciye gönderilecek HTTP yanıtının durum kodudur;

Bir HTML oturumunda, bir eylemin ardından görüntülenen sayfa, denetleyici tarafından döndürülen durum koduna bağlıdır. Bu bağımlılık, [config] yapılandırmasında şu şekilde somutlaşır:


        # HTML görünümleri ve bunların şablonları, denetleyici tarafından döndürülen duruma bağlıdır
        "views"[
            {
                # kimlik doğrulama sayfası
                "états": [
                    # /oturum-açma başarılı
                    700,
                    # /kullanıcı-kimlik-doğrulama başarısız
                    201
                ],
                "view_name""views/vue-authentification.html",
                "model_for_view": ModelForAuthentificationView()
            },
            {
                # vergi hesaplama sayfası
                "états"[
                    # /kullanıcı-kimlik-doğrulama başarılı
                    200,
                    # /vergi-hesaplama başarılı
                    300,
                    # /vergi-hesapla başarısız
                    301,
                    # /vergi-hesaplamasını-göster
                    800
                ],
                "view_name""views/vue-calcul-impot.html",
                "model_for_view": ModelForCalculImpotView()
            },
            {
                # simülasyon listesini görüntüle
                "états"[
                    # /simülasyonları-listele
                    500,
                    # /simülasyonu-sil
                    600
                ],
                "view_name""views/vue-liste-simulations.html",
                "model_for_view": ModelForListeSimulationsView()
            }
        ],

        # beklenmedik hataların görünümü
        "view-erreurs": {
            "view_name""views/vue-erreurs.html",
            "model_for_view": ModelForErreursView()
        },

        # yönlendirmeler
        "redirections"[
            {
                "états": [
                    400,  # /oturumu-başarıyla-sonlandır
                ],
                # yönlendirme
                "to""/init-session/html",
            }
        ],
    }
  • 2-40. satırlar: [views] bir görünüm listesidir. 3-13. satırlardaki görünümü ele alalım:
    • 11. satır: görüntülenen V görünümü;
    • satır 12: bu görünümün M modelini oluşturmakla görevli sınıf örneği;
    • satır 5-10: bu görünüme yönlendiren durumlar;
  • 3-13. satırlar: kimlik doğrulama görünümü;
  • 14-28. satırlar: vergi hesaplama görünümü;
  • satır 29-39: simülasyon listesi görünümü;
  • satır 42-46: beklenmedik hatalar görünümü;
  • satır 49-57: Bazı raporlar, bir yönlendirme yoluyla bir görünüme yönlendirir. [/fin-session] eyleminin başarılı olduğu 400 numaralı rapor da bu durumdadır. Bu durumda, istemciyi [http://machine:port/chemin/init-session/html] eylemine yönlendirmek gerekir;

Şimdi çeşitli görünümleri tanıtacağız.

32.5. Kimlik doğrulama görünümü

Image

32.5.1. Görünümün tanıtımı

Kimlik doğrulama görünümü şu şekildedir:

Image

Görünüm, "parça" olarak adlandırılan iki öğeden oluşur:

  • [1] parçası, [v-bandeau.html] parçası tarafından oluşturulur;
  • [2] parçası, [v-authentification.html] parçası tarafından oluşturulur;

Kimlik doğrulama sayfası, aşağıdaki [vue-authentification.html] sayfası tarafından oluşturulur:


<!-- HTML belgesi -->
<!doctype html>
<html lang="fr">
<head>
    <!-- Gerekli meta etiketleri -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <title>Application impôts</title>
</head>

<body>
<div class="container">
    <!-- başlık çubuğu -->
    {% include "fragments/v-bandeau.html" %}
    <!-- iki sütunlu satır -->
    <div class="row">
        <div class="col-md-9">
            {% include "fragments/v-authentification.html" %}
        </div>
    </div>
    <!-- hata durumunda - bir hata uyarısı görüntülenir -->
    {% if modèle.error %}
    <div class="row">
        <div class="col-md-9">
            <div class="alert alert-danger" role="alert">
                Les erreurs suivantes se sont produites :
                <ul>{{modèle.erreurs|safe}}</ul>
            </div>
        </div>
    </div>
    {% endif %}
</div>
</body>
</html>

Açıklamalar

  • 2. satır: HTML belgesi bu satırla başlar;
  • 3-36. satırlar: HTML sayfası, <html> </html> etiketleri arasında yer alır;
  • 4-11. satırlar: HTML belgesinin başlığı (head);
  • 6. satır: <meta charset> etiketi, belgenin UTF-8 karakter kümesinde kodlandığını belirtir;
  • 7. satır: <meta name=’viewport’> etiketi, görünümün ilk görüntülenişini belirler: görüntüyü gösteren ekranın tüm genişliği (width) üzerinde, daha küçük bir ekran boyutuna uyum sağlamak için yeniden boyutlandırılmadan (shrink-to-fit) orijinal boyutunda (initial-scale);
  • 9. satır: <link rel=’stylesheet’> etiketi, görünümün görünümünü belirleyen CSS dosyasını tanımlar. Burada CSS Bootstrap 4.4.1 [https://getbootstrap.com/docs/4.0/getting-started/introduction/] çerçevesini kullanıyoruz ;
  • 10. satır: <title> etiketi, sayfanın başlığını belirler:

Image

  • 13-35. satırlar: Web sayfasının gövdesi <body></body> etiketleri arasında yer alır;
  • 14-34. satırlar: <div> etiketi, görüntülenen sayfanın bir bölümünü sınırlar. Görünümde kullanılan [class] özniteliklerinin tümü, CSS Bootstrap çerçevesine atıfta bulunur. <div class=’container’> etiketi (14. satır), bir Bootstrap konteynerini sınırlar;
  • 26. satır: [v-bandeau.html] parçası eklenir. Bu parça, sayfanın [1] başlık çubuğunu oluşturur. Bunu birazdan açıklayacağız;
  • 18-22. satırlar: <div class=’row’> etiketi bir Bootstrap satırını sınırlar. Bu satırlar 12 sütundan oluşur;
  • 19. satır: <div class=’col-md-9’> etiketi, 9 sütunluk bir bölümü sınırlar;
  • 20. satır: Sayfadaki [2] kimlik doğrulama formunu görüntüleyen [v-authentification.html] parçası eklenmiştir. Bunu yakında açıklayacağız;
  • 24-33. satırlar: Bu satırlardaki HTML kodu, yalnızca [modèle.error] değeri True ise kullanılır. Her zaman şu şekilde ilerleyeceğiz: HTML görünümünün şablonu, [modèle] sözlüğü içinde kapsüllenecektir;
  • 24-33. satırlar: Kullanıcı yanlış kimlik bilgilerini girerse kimlik doğrulama başarısız olur. Bu durumda, kimlik doğrulama görünümü bir hata mesajıyla birlikte yeniden görüntülenir. [modèle.error] özniteliği, bu hata mesajının görüntülenip görüntülenmeyeceğini belirtir;
  • satır 27-30: pembe arka planlı bir alanı sınırlar (class="alert alert-danger") (satır 27);

Image

  • 28. satır: bir metin;
  • satır 29: HTML <ul> (sırasız liste) etiketi, madde işaretli bir liste görüntüler. Listenin her bir öğesi <li>öğe</li> sözdizimine sahip olmalıdır. Burada [modèle.erreurs] değerinin değeri görüntülenir. Bu değer, [safe] filtresi tarafından (| karakterinin varlığı nedeniyle) filtrelenir. Varsayılan olarak, bir karakter dizisi tarayıcıya gönderilmesi gerektiğinde, Flask, tarayıcının bunları yorumlamaması için içinde bulunabilecek tüm HTML etiketlerini "etkisiz hale getirir". Ancak bazen bu etiketlerin yorumlanmasını isteyebiliriz. Bu durum, [modèle.erreurs] dizesinin, liste öğesini sınırlandırmak için kullanılan HTML <li> ve </li> etiketlerini içerdiği durumlarda geçerlidir. Bu durumda, Flask’a görüntülenecek dizgenin güvenli (safe) olduğunu ve dolayısıyla içinde bulacağı HTML etiketlerini etkisiz hale getirmemesi gerektiğini belirten [safe] filtresini kullanırız;

Bu koddan tanımlanması gereken dinamik öğeleri şöyle özetleyebiliriz:

  • [modèle.error]: bir hata mesajı görüntülemek için;
  • [modèle.erreurs]: bir hata mesajları listesi (terimin HTML anlamında);

32.5.2. [v-bandeau.html] parçası

[v-bandeau.html] parçası, web uygulamasının tüm görünümlerinde üst başlığı görüntüler:

Image

[v-bandeau.html] parçasının kodu şöyledir:


<!-- Bootstrap Jumbotron -->
<div class="jumbotron">
    <div class="row">
        <div class="col-md-4">
            <img src="{{ url_for('static', filename='images/logo.jpg') }}" alt="Cerisier en fleurs"/>
        </div>
        <div class="col-md-8">
            <h1>
                Calculez votre impôt
            </h1>
        </div>
    </div>
</div>

Açıklamalar

  • 2-13. satırlar: Başlık çubuğu, Jumbotron tipi bir Bootstrap bölümü ([<div class="jumbotron">]) içinde yer almaktadır. Bu Bootstrap sınıfı, görüntülenen içeriği öne çıkarmak için özel bir şekilde biçimlendirir;
  • 3-12. satırlar: bir Bootstrap satırı;
  • 4-6. satırlar: [img] adlı bir resim, satırın ilk dört sütununa yerleştirilmiştir;
  • 5. satır: sözdizimi:
{{ url_for('static', filename='images/logo.jpg') }}

Flask'ın [url_for] işlevini kullanır. Burada, bu işlevin değeri [static] klasöründeki [images/logo.pg] dosyasının URL değeri olacaktır;

  • 7-11. satırlar: satırdaki diğer 8 sütun (toplamda 12 sütun olduğunu hatırlatırız), bir metni (9. satır) büyük harflerle (<h1>, 8-10. satırlar) yerleştirmek için kullanılacaktır;

32.5.3. [v-authentification.html] parçası

[v-authentification.html] parçası, web uygulamasının kimlik doğrulama formunu görüntüler:

Image

[v-authentification.html] parçasının kodu şöyledir:


<!-- form HTML - değerler [authentifier-utilisateur] eylemiyle gönderilir -->
<form method="post" action="/authentifier-utilisateur">

    <!-- başlık -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>

    <!-- Bootstrap formu -->
    <fieldset class="form-group">
        <!-- 1. satır -->
        <div class="form-group row">
            <!-- açıklama -->
            <label for="user" class="col-md-3 col-form-label">Nom d'utilisateur</label>
            <div class="col-md-4">
                <!-- metin giriş alanı -->
                <input type="text" class="form-control" id="user" name="user"
                       placeholder="Nom d'utilisateur" value="{{ modèle.login }}" required>
            </div>
        </div>
        <!-- 2. satır -->
        <div class="form-group row">
            <!-- etiket -->
            <label for="password" class="col-md-3 col-form-label">Mot de passe</label>
            <!-- metin giriş alanı -->
            <div class="col-md-4">
                <input type="password" class="form-control" id="password" name="password"
                       placeholder="Mot de passe" required>
            </div>
        </div>
        <!-- 3. satırda [submit] tipi düğme -->
        <div class="form-group row">
            <div class="col-md-2">
                <button type="submit" class="btn btn-primary">Valider</button>
            </div>
        </div>
    </fieldset>

</form>

Açıklamalar

  • 2-39. satırlar: <form> etiketi, bir HTML formunu sınırlar. Bu form genellikle aşağıdaki özelliklere sahiptir:
    • giriş alanları tanımlar (17. ve 27. satırlardaki <input> etiketleri);
    • [submit] türünde bir düğme içerir (satır 34); bu düğme, girilen değerleri [form] etiketinin [action] özniteliğinde belirtilen URL adresine gönderir (2. satır) içinde belirtilen URL etiketine gönderir. Bu URL etiketine istek göndermek için kullanılan HTTP yöntemi, [form] etiketinin (2. satır) [method] özniteliğinde belirtilmiştir;
    • burada, kullanıcı [Valider] düğmesine (satır 34) tıkladığında, tarayıcı formda girilen değerleri URL [/authentifier-utilisateur]'e (satır 2) gönderecektir (satır 2);
    • Gönderilen değerler, kullanıcının 17. ve 27. satırlardaki giriş alanlarına girdiği değerlerdir. Bu değerler, tarayıcının HTTP biçiminde oluşturacağı isteğin gövdesinde [x-www-forl-urlencoded] şeklinde gönderilecektir. [user, password] parametrelerinin adları, 17. ve 27. satırlardaki giriş alanlarının [name] özniteliklerinin adlarıdır;
  • 5-7. satırlar: mavi arka plan üzerinde bir başlık görüntülemek için bir Bootstrap bölümü:
  • satır 10-37: bir Bootstrap formu. Formun tüm öğeleri belirli bir şekilde biçimlendirilecektir; Image
  • satır 12-20: formun ilk Bootstrap satırını tanımlar:

Image

  • 14. satır, [1] etiketini üç sütuna yayacak şekilde tanımlar. [label] etiketinin [for] özniteliği, bu etiketi 17. satırdaki giriş alanının [id] özniteliğine bağlar;
  • 15-19. satırlar: giriş alanını dört sütunlu bir gruba yerleştirir;
  • 17-18. satırlar: HTML ve [input] etiketleri bir giriş alanını tanımlar. Bu etiketlerin birkaç parametresi vardır:
    • [type=’text’]: Bu bir metin giriş alanıdır. Buraya herhangi bir şey yazılabilir;
    • [class=’form-control’]: giriş alanı için Bootstrap stili;
    • [id=’user’]: Giriş alanının kimliği. Bu kimlik genellikle CSS ve JavaScript kodu tarafından kullanılır;
    • [name=’user’]: giriş alanının adı. Kullanıcı tarafından girilen değer, tarayıcı tarafından bu adla gönderilir;
    • [placeholder=’invite’]: Kullanıcı henüz hiçbir şey yazmadığında giriş alanında görüntülenen metin;

Image

  • (devam)
    • [value=’valeur’]: Giriş alanı görüntülendiği anda, yani kullanıcı başka bir şey girmeden önce, bu alana "değer" metni görüntülenecektir. Bu mekanizma, bir hata oluştuğunda hataya neden olan girişi görüntülemek için kullanılır. Burada bu değer, [modèle.login] değişkeninin değeri olacaktır;
    • [required]: Formun sunucuya gönderilebilmesi için kullanıcının bir değer girmesini zorunlu kılar:
  • 21-30. satırlar: şifre girişi için benzer bir kod;
  • 27. satır: [type=’password’], bir metin giriş alanı oluşturur (istediğiniz herhangi bir şeyi yazabilirsiniz) ancak yazılan karakterler gizlenir:

Image

  • 32-36. satırlar: [Valider] düğmesi için üçüncü bir Bootstrap satırı;
  • 34. satır: [type=submit] özniteliğine sahip olduğu için, bu düğmeye tıklandığında, daha önce açıklandığı gibi girilen değerler tarayıcı tarafından sunucuya gönderilir. CSS [class="btn btn-primary"] özniteliği mavi bir düğme görüntüler: Image

Son bir şeyi daha açıklamamız gerekiyor. 2. satırda, [action="/authentifier-utilisateur"] özniteliği, eksik bir URL tanımlar (http://machine:port/chemin ile başlamaz). Örneğimizde, uygulamadaki tüm URL'ler [http://machine:port/chemin/action/param1/param2/..] biçimindedir; burada [http://machine:port/chemin], hizmet URL'lerinin köküdür. [action="/authentifier-utilisateur"]'te, URL'e göre mutlak bir konum vardır; yani bu konum, URL kökünden ölçülmüştür. Dolayısıyla, POST'in tamamlayıcı parçası olan URL, [http://machine:port/chemin/authentifier-utilisateur]'tir ve tarayıcı bunu kullanacaktır.

Bu parçanın [modèle.login] şablonunu kullandığını unutmayalım.

32.5.4. Görsel testler

Görünümler uygulamaya entegre edilmeden çok önce test edilebilir. Burada amaç, görünümlerin görsel yönünü test etmektir. Tüm test görünümlerini projenin [tests_views] klasöründe toplayacağız:

Image

V [vue-authentification.html] görünümünü test etmek için, bu görünümün görüntüleyeceği M veri modelini oluşturmamız gerekiyor. Bunu [test_vue_authentification.py] komut dosyasıyla yapıyoruz:


from flask import Flask, render_template, make_response

# Flask uygulaması
app = Flask(__name__, template_folder="../templates", static_folder="../static")

# Ana Sayfa URL
@app.route('/')
def index():
    # sayfa verilerini şablona yerleştiriyoruz
    modèle = {}
    # kullanıcı kimliği
    modèle["login"] = "albert"
    # hata listesi
    modèle["error"] = True
    erreurs = ["erreur1""erreur2"]
    # hataların bir listesi oluşturulur HTML
    content = ""
    for erreur in erreurs:
        content += f"<li>{erreur}</li>"
    modèle["erreurs"] = content
    # sayfanın görüntülenmesi
    return make_response(render_template("views/vue-authentification.html", modèle=modèle))

# ana sayfa
if __name__ == '__main__':
    app.config.update(ENV="development", DEBUG=True)
    app.run()

Açıklamalar

  • 1-3. satırlar: Tek amacı [vue-authentification.html] görünümünü (22. satır) görüntülemek olan bir Flask uygulaması oluşturulur;
  • 7. satır: Uygulamanın tek bir URL hizmet nesnesi vardır;
  • 9-20. satırlar: Kimlik doğrulama görünümünde, [modèle] nesnesi tarafından kontrol edilen dinamik bölümler bulunmaktadır. Bu nesneye görünüm modeli denir. MVC kısaltması için verilen iki tanımdan birine göre, burada MVC'in M'si bulunmaktadır. [vue-authentification.html] görünümünü tanımlarken üç dinamik değer belirlemiştik:
    • [modèle.error]: bir hata mesajının görüntülenip görüntülenmeyeceğini belirten boole değeri;
    • [modèle.erreurs]: HTML hata mesajları listesi;
    • [modèle.login]: bir kullanıcının oturum açma bilgisi;

Dolayısıyla bu üç dinamik değeri tanımlamamız gerekiyor.

  • 9-20. satırlar: kimlik doğrulama görünümünün üç dinamik öğesini tanımlıyoruz;

Testi yapmak için [tests_views/test_vue_authentification.py] komut dosyasını çalıştırıyoruz ve URL ile [/localhost:5000/]'i istiyoruz:

Sonuçtan memnun kalana kadar bu görsel testlere devam edilir.

Image

32.5.5. Görünüm modelinin hesaplanması

Görünümün görsel yapısı belirlendikten sonra, gerçek koşullarda görünüm modelinin hesaplanmasına geçilebilir. Görünüm modelleri, [models_for_views] klasöründe toplanan sınıflar tarafından oluşturulacaktır:

Image

Bir görünüm modeli oluşturan her sınıf, aşağıdaki [InterfaceModelForView] arayüzüne uyacaktır:


from abc import ABC, abstractmethod

from flask import Request
from werkzeug.local import LocalProxy

class InterfaceModelForView(ABC):

    @abstractmethod
    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        pass
  • 8-10. satırlar: [get_model_for_view] yöntemi, bir sözlük içinde kapsüllenmiş bir görünüm şablonu üretmekle görevlidir. Bunun için aşağıdaki bilgileri alır:
    • [request, session, config], eylem denetleyicisi tarafından kullanılan parametrelerin aynısıdır. Dolayısıyla bu parametreler modele de aktarılır;
    • denetleyici, modele de aktarılan bir [résultat] sonucu üretmiştir. Bu sonuç, mevcut eylemin yürütülmesinin nasıl gerçekleştiğini gösteren önemli bir öğe olan [état]'i içerir. Model bu bilgiyi kullanacaktır;

Uygulamanın [config] yapılandırmasında, denetleyiciler tarafından döndürülen durum kodlarının, görüntülenecek HTML görünümünü belirlemek için kullanıldığını gördük:


        # HTML görünümleri ve şablonları, denetleyici tarafından döndürülen duruma bağlıdır
        "views"[
            {
                # kimlik doğrulama görünümü
                "états": [
                    # /init-session başarılı
                    700,
                    # /kullanıcı-kimlik-doğrulama başarısız
                    201
                ],
                "view_name""views/vue-authentification.html",
                "model_for_view": ModelForAuthentificationView()
            },
            {
                # vergi hesaplama sayfası
                "états"[
                    # /kullanıcı-kimlik-doğrulama başarılı
                    200,
                    # /vergi-hesaplama başarılı
                    300,
                    # /vergi-hesapla başarısız
                    301,
                    # /vergi-hesaplamasını-göster
                    800
                ],
                "view_name""views/vue-calcul-impot.html",
                "model_for_view": ModelForCalculImpotView()
            },
            {
                # simülasyon listesini görüntüle
                "états"[
                    # /simülasyonları-listele
                    500,
                    # /simülasyonu-sil
                    600
                ],
                "view_name""views/vue-liste-simulations.html",
                "model_for_view": ModelForListeSimulationsView()
            }
        ],
        # beklenmedik hataların görünümü
        "view-erreurs": {
            "view_name""views/vue-erreurs.html",
            "model_for_view": ModelForErreursView()
        },
        # yönlendirmeler
        "redirections"[
            {
                "états": [
                    400,  # /oturumu-başarıyla-sonlandır
                ],
                # yönlendirme
                "to""/init-session/html",
            }
        ],
    }

Dolayısıyla, kimlik doğrulama ekranının görüntülenmesini sağlayan kodlar [700, 201] (7. ve 9. satırlar) kodlarıdır. Bu kodların anlamını öğrenmek için, jSON uygulaması üzerinde gerçekleştirilen [Postman] testlerinden yararlanılabilir:

  • [init-session-json-700]: 700, [init-session] eyleminin başarıyla tamamlanmasının ardından ortaya çıkan durum kodudur; bu durumda boş kimlik doğrulama formu görüntülenir;
  • [authentifier-utilisateur-201]: 201, [authentifier-utilisateur] eyleminin başarısızlıkla sonuçlanmasının ardından (kimlik bilgileri tanınmadı) verilen durum kodudur: bu durumda, düzeltilmesi için kimlik doğrulama formu görüntülenir;

Artık kimlik doğrulama formunun hangi durumlarda görüntülenmesi gerektiğini bildiğimize göre, [ModelForAuthentificationView] (12. satır) içinde form şablonunu hesaplayabiliriz:


from flask import Request
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class ModelForAuthentificationView(InterfaceModelForView):

    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # sayfa verilerini şablona yerleştiriyoruz
        modèle = {}
        # uygulama durumu
        état = résultat["état"]
        # şablon duruma bağlı
        if état == 700:
            # formun boş olarak görüntülenmesi durumu
            modèle["login"] = ""
            # görüntülenecek hata yok
            modèle["error"] = False
        elif état == 201:
            # kimlik doğrulama hatası
            # Başlangıçta girilen kullanıcı yeniden görüntüleniyor
            modèle["login"] = request.form.get("user")
            # görüntülenecek bir hata var
            modèle["error"] = True
            # HTML hata mesajları listesi
            erreurs = ""
            for erreur in résultat["réponse"]:
                erreurs += f"<li>{erreur}</li>"
            modèle["erreurs"] = erreurs

        # şablon oluşturuluyor
        return modèle

Açıklamalar

  • 8. satır: Kimlik doğrulama görünümünün [get_model_for_view] yöntemi, üç anahtarlı bir sözlük sağlamalıdır: [error, erreurs, login]. Bu hesaplama, eylemin denetleyicisi tarafından döndürülen durum koduna göre yapılır;
  • 12. satır: mevcut eylemi işleyen denetleyici tarafından döndürülen durum kodunu alırız;
  • 14-29. satırlar: model, bu durum koduna bağlıdır;
  • satır 15-18: boş bir kimlik doğrulama formunun görüntülenmesi gereken durum;
  • 20-29. satırlar: kimlik doğrulama hatası durumunda, kullanıcı tarafından girilen kimlik bilgisi görüntülenir ve bir hata mesajı gösterilir. Kullanıcı, klavyeyi kullanarak kimlik doğrulamayı yeniden deneyebilir;
  • 22. satır: Kullanıcı tarafından başlangıçta girilen kimlik bilgisi, istemci isteğinde bulunabilir;
  • 24. satır: Görüntülenecek hatalar olduğu bildirilir;
  • 26-29. satırlar: Hata durumunda, result[‘réponse’] bir hata listesi içerir;

32.5.6. HTML yanıtlarının oluşturulması

HTML uygulamasının MVC modeline geri dönelim:

  • 2'de (2a, 2b): denetleyici bir eylem gerçekleştirir;
  • 3'te (3a, 3b, 3c): bir görünüm seçilir ve istemciye gönderilir;

[3a]'te bir yanıt türü (jSON, XML, HTML) seçilir. jSON ve XML yanıtlarının nasıl oluşturulduğunu gördük, ancak HTML yanıtlarını henüz ele almadık. Bunlar, [HtmlResponse] sınıfı tarafından oluşturulur:

Image

Ana komut dosyası [main]'te kullanıcıya verilecek yanıt türünün nasıl belirlendiğini hatırlayalım:


        ….
        # gönderilecek yanıt oluşturuluyor
        response_builder = config["responses"][type_response]
        response, status_code = response_builder \
            .build_http_response(request, session, config, status_code, résultat)
        # yanıt gönderiliyor
        return response, status_code

Burada, 3. satırda config[‘responses’] şu sözlükten oluşur:


        # farklı yanıt türleri (json, xml, html)
        "responses": {
            "json": JsonResponse(),
            "html": HtmlResponse(),
            "xml": XmlResponse()
        },

Dolayısıyla, [HtmlResponse] sınıfı, HTML yanıtını oluşturur. Bu sınıfın kodu şöyledir:


# sonuçta yer alan duruma göre yanıt sözlüğü HTML

from flask import make_response, render_template
from flask.wrappers import Response
from werkzeug.local import LocalProxy

from InterfaceResponse import InterfaceResponse

class HtmlResponse(InterfaceResponse):

    def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
                            résultat: dict) -> (Response, int):
        # HTML yanıtı, denetleyici tarafından döndürülen durum koduna bağlıdır
        état = résultat["état"]

        # Yönlendirme yapılmalı mı?
        for redirection in config["redirections"]:
            # Yönlendirme gerektiren durumlar
            états = redirection["états"]
            if état in états:
                # yönlendirme yapılmalıdır
                return redirect(f"/{redirection['to']}"), status.HTTP_302_FOUND

        # her duruma bir görünüm karşılık gelir
        # bu görünüm, görünümler listesinde aranır
        views_configs = config["views"]
        trouvé = False
        i = 0
        # görünüm listesi taranıyor
        nb_views = len(views_configs)
        while not trouvé and i < nb_views:
            # i numaralı görünüm
            view_config = views_configs[i]
            # i numaralı görünüme ilişkin raporlar
            états = view_config["états"]
            # aranan durum, i numaralı görünüme ilişkin durumlar arasında yer alıyor mu
            if état in états:
                trouvé = True
            else:
                # sonraki görünüm
                i += 1
        # bulundu mu?
        if not trouvé:
            # uygulamanın mevcut durumu için herhangi bir görünüm yoksa
            # hata görünümünü oluşturur
            view_config = config["view-erreurs"]

        # görüntülenecek görünüm şablonu hesaplanıyor
        model_for_view = view_config["model_for_view"]
        modèle = model_for_view.get_model_for_view(request, session, config, résultat)
        # yanıtın kodunu oluşturur HTML
        html = render_template(view_config["view_name"], modèle=modèle)
        # HTTP yanıtı oluşturulur
        response = make_response(html)
        response.headers['Content-Type'] = 'text/html; charset=utf-8'
        # sonuç döndürülür
        return response, status_code
  • 11. satır: HTML yanıtını oluşturmakla görevli [build_http_response] yöntemi, aşağıdaki parametreleri alır:
    • [request, session, dict]: Bunlar, denetleyici tarafından devam eden eylemi işlemek için kullanılan parametrelerdir;
    • [status_code, résultat], aynı denetleyici tarafından üretilen iki sonuçtur;
  • 14. satır: Daha önce de belirttiğimiz gibi, sunucunun HTML yanıtı, [résultat] sözlüğünde yer alan durum koduna bağlıdır;
  • 16-22. satırlar: Öncelikle yönlendirmeler işlenir. Şu an için, bir yönlendirme örneğiyle karşılaşana kadar bu durumu göz ardı edeceğiz. Yönlendirmelerin tipik olarak HTML sunucusunun bir kullanım durumu olduğunu belirtelim. jSON ve ouXML sunucularında bu durumla karşılaşılmamaktadır;
  • 24-41. satırlar: Görünümler arasında, [états] listesinde aranan durumu içeren görünüm aranır;
  • satır 42-46: Eğer hiçbir görünüm bulunamadıysa, bu beklenmedik bir hatadır. Bir örnek verelim. Uygulamanın normal çalışması sırasında, [/supprimer-simulation] eylemi asla hata vermemelidir. Nitekim, bu simülasyonların silinmesinin kod tarafından oluşturulan bağlantılar aracılığıyla gerçekleştiğini göreceğiz. Bu bağlantılar doğrudur ve bir hataya yol açamaz. Ancak daha önce gördüğümüz gibi, kullanıcı doğrudan URL veya [/supprimer-simulation/id] yazarak bir hataya neden olabilir. Bu durumda, [SupprimerSimulationController] denetleyicisi 601 durum kodunu döndürür. Oysa bu durum kodu, HTML sayfasının görüntülenmesine yol açan durum kodları listesinde yer almamaktadır. Bu nedenle hata sayfası görüntülenecektir. Bu sayfa, yapılandırmada şu şekilde tanımlanmıştır:

        # beklenmedik hataların görünümü
        "view-erreurs": {
            "view_name""views/vue-erreurs.html",
            "model_for_view": ModelForErreursView()
        },
  • 49. satır: Görüntülenecek görünüm belirlendikten sonra, bu görünümün modelini oluşturan sınıf alınır. Bu sınıf da [config] yapılandırmasında bulunur;
  • satır 50: bu sınıf bulunduktan sonra, görünümün modeli oluşturulur;
  • satır 52: V görünümünün M modeli hesaplandıktan sonra, görünümün HTML kodunu oluşturabiliriz;
  • satır 54-55: HTML gövdesi ile HTTP yanıtı oluşturulur;
  • satır 56-57: HTTP yanıtı, durum koduyla birlikte döndürülür;

32.5.7. [Postman] Testleri

Oturum açma ekranını görüntüleyen [700, 201] kodlarını üreten sorguları çalıştıracağız:

  • [init-session-html-700]: 700, [init-session] eyleminin başarıyla tamamlanmasının ardından elde edilen durum kodudur; bu durumda boş kimlik doğrulama formu görüntülenir;
  • [authentifier-utilisateur-201]: 201, [authentifier-utilisateur] eyleminin başarısızlıkla sonuçlanmasının ardından (kimlik bilgileri tanınmadı) verilen durum kodudur: bu durumda, düzeltilmesi için kimlik doğrulama formu görüntülenir;

Bunları tekrar kullanıp kimlik doğrulama ekranının düzgün bir şekilde görüntülenip görüntülenmediğini kontrol etmek yeterlidir. Burada iki durum gösterilmektedir:

Durum 1: [init-session-html-700], HTML oturumunun başlangıcı;

Image

Cevap şöyledir:

Image

  • [5]'te, [Preview] modu, alınan HTML sayfasını görüntülemeye olanak tanır;
  • [6]'te beklendiği gibi boş form görünüyor;
  • [7]'te, Postman sayfanın resim bağlantısını takip etmemiştir;
  • [8]'te, [Raw] modu, alınan HTML'e erişim sağlar;

Image

  • [3]'te, Postman'ın yüklemediği bağlantı. Resim yüklenemediğinde görüntülenen [alt=alternative] özniteliğinin değerini gösterdi. Burada ise Postman'ın resmi yüklemek istemediği anlaşılıyor. Bunu, Postman ile URL ve [http://localhost :5000/static/images.logo.jpg]'i sorgulayarak doğrulayabiliriz:

Durum 2: [authentifier-utilisateur-201], hatalı kimlik doğrulama

Image

Şimdi, HTML oturumunun başarılı bir şekilde başlatılmasının ardından hatalı bir kimlik doğrulama yapalım:

Image

Yukarıda:

  • [4,7]'te: istek, [user=bernard&password=thibault] dizesini gönderir;

Yanıt şu şekildedir:

Image

  • [4]'te bir hata mesajı görüntülenir;
  • [3]'te, hatalı kullanıcı yeniden görüntülenir;

32.5.8. Sonuç

Diğer görünümleri yazmadan [vue-authentification.html] görünümünü test edebildik. Bunun mümkün olmasının nedeni şudur:

  • tüm denetleyiciler yazılmış;
  • [Postman], tüm görünümler olmadan da sunucuya istek göndermemize olanak tanır. Denetleyicileri yazarken, hiçbir görünümün izin vermeyeceği istekleri işleme hazırlıklı olmak gerekir. Asla önceden “bu istek imkansız” diye düşünmemeliyiz. Kontrol etmek gerekir;

32.6. Vergi hesaplama görünümü

Image

32.6.1. Görünümün tanıtımı

Vergi hesaplama görünümü şöyledir:

Image

Görünüm üç bölümden oluşur:

  • 1: Üst başlık, daha önce tanıtılan [v-bandeau.html] parçası tarafından oluşturulur;
  • 2: [v-calcul-impot.html] parçası tarafından oluşturulan vergi hesaplama formu;
  • 3: [v-menu.html] parçası tarafından oluşturulan, iki bağlantı içeren bir menü;

Vergi hesaplama görünümü, aşağıdaki [vue-calcul-impot.html] kodu tarafından oluşturulur:


<!-- HTML belgesi -->
<!doctype html>
<html lang="fr">
<head>
    <!-- Gerekli meta etiketleri -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <title>Application impôts</title>
</head>

<body>
<div class="container">
    <!-- başlık çubuğu -->
    {% include "fragments/v-bandeau.html" %}
    <!-- iki sütunlu satır -->
    <div class="row">
        <!-- menü -->
        <div class="col-md-3">
            {% include "fragments/v-menu.html" %}
        </div>
        <!-- hesaplama formu -->
        <div class="col-md-9">
            {% include "fragments/v-calcul-impot.html" %}
        </div>
    </div>
    <!-- başarı durumu -->

    {% if modèle.success %}
    <!-- başarı uyarısı görüntülenir -->
    <div class="row">
        <div class="col-md-3">

        </div>
        <div class="col-md-9">
            <div class="alert alert-success" role="alert">
                {{modèle.impôt}}</br>
                {{modèle.décôte}}</br>
                {{modèle.réduction}}</br>
                {{modèle.surcôte}}</br>
                {{modèle.taux}}</br>
            </div>
        </div>
    </div>
    {% endif %}

    {% if modèle.error %}
    <!-- 9 sütunlu hata listesi -->
    <div class="row">
        <div class="col-md-3">

        </div>
        <div class="col-md-9">
            <div class="alert alert-danger" role="alert">
                Les erreurs suivantes se sont produites :
                <ul>{{modèle.erreurs | safe}}</ul>
            </div>
        </div>
    </div>
    {% endif %}
</div>
</body>
</html>

Yorumlar

  • Yalnızca henüz karşılaşılmamış yenilikler hakkında yorum yapıyoruz;
  • 16. satır: Görünümün üst başlığının, görünümün ilk Bootstrap satırına eklenmesi;
  • 21. satır: Görünümün ikinci Bootstrap satırında (18., 20. satırlar) üç sütun kaplayacak menünün eklenmesi;
  • 25. satır: görünümün ikinci Bootstrap satırında (18. satır) dokuz sütun (24. satır) kaplayacak vergi hesaplama formunun eklenmesi;
  • satır 30-46: [modèle.success=True] vergi hesaplaması başarılı olursa, vergi hesaplamasının sonucu yeşil bir çerçeve içinde görüntülenir (satır 37-43). Bu çerçeve, görünümün üçüncü Bootstrap satırında (satır 32) yer alır ve üç boş sütunun (satır 33-35) sağında dokuz sütun (satır 36) kaplar. Dolayısıyla bu çerçeve, vergi hesaplama formunun altında yer alacaktır;
  • satır 48-61: [modèle.error=True] vergi hesaplaması başarısız olursa, pembe bir çerçeve içinde bir hata mesajı görüntülenir (satır 55-58). Bu çerçeve, görünümün üçüncü Bootstrap satırında (satır 50) yer alır ve üç boş sütunun (satır 51-53) sağında dokuz sütun (satır 54) kaplar. Dolayısıyla bu çerçeve de vergi hesaplama formunun altında yer alacaktır;

32.6.2. [v-calcul-impot.html] parçası

[v-calcul-impot.html] parçası, web uygulamasının vergi hesaplama formunu görüntüler:

[v-calcul-impot.html] parçasının kodu şöyledir:

Image


<!-- HTML formu gönderildi -->
<form method="post" action="/calculer-impot">
    <!-- mavi arka planlı 12 sütunlu mesaj -->
    <div class="col-md-12">
        <div class="alert alert-primary" role="alert">
            <h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
        </div>
    </div>
    <!-- form öğeleri -->
    <fieldset class="form-group">
        <!-- 9 sütunlu ilk satır -->
        <div class="row">
            <!-- 4 sütunlu başlık -->
            <legend class="col-form-label col-md-4 pt-0">Etes-vous marié(e) ou pacsé(e)?</legend>
            <!-- 5 sütunlu radyo düğmeleri-->
            <div class="col-md-5">
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="marié" id="gridRadios1" value="oui" {{modèle.checkedOui}}>
                    <label class="form-check-label" for="gridRadios1">
                        Oui
                    </label>
                </div>
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="marié" id="gridRadios2" value="non" {{modèle.checkedNon}}>
                    <label class="form-check-label" for="gridRadios2">
                        Non
                    </label>
                </div>
            </div>
        </div>
        <!-- 9 sütunlu ikinci satır -->
        <div class="form-group row">
            <!-- 4 sütunlu etiket -->
            <label for="enfants" class="col-md-4 col-form-label">Nombre d'enfants à charge</label>
            <!-- 5 sütunlu çocuk sayısı sayısal giriş alanı -->
            <div class="col-md-5">
                <input type="number" min="0" step="1" class="form-control" id="enfants" name="enfants" placeholder="Nombre d'enfants à charge" value="{{modèle.enfants}}" required>
            </div>
        </div>
        <!-- 9 sütunlu üçüncü satır -->
        <div class="form-group row">
            <!-- 4 sütunlu açıklama -->
            <label for="salaire" class="col-md-4 col-form-label">Salaire annuel net imposable</label>
            <!-- 5 sütunlu maaş için sayısal giriş alanı -->
            <div class="col-md-5">
                <input type="number" min="0" step="1" class="form-control" id="salaire" name="salaire" placeholder="Salaire annuel net imposable" aria-describedby="salaireHelp" value="{{modèle.salaire}}" required>
                <small id="salaireHelp" class="form-text text-muted">Arrondissez à l'euro inférieur</small>
            </div>
        </div>
        <!-- dördüncü satır, 5 sütunlu [submit] düğmesi -->
        <div class="form-group row">
            <div class="col-md-5">
                <button type="submit" class="btn btn-primary">Valider</button>
            </div>
        </div>
    </fieldset>

</form>

Açıklamalar

  • 2. satır: HTML formu, [method] özniteliği aracılığıyla URL'e gönderilecektir ([/calculer-impot] ([action] özniteliği). Gönderilen değerler, giriş alanlarının değerleri olacaktır:
  • işaretlenen radyo düğmesinin değeri şu şekilde olacaktır:
      • [marié=oui] (eğer [Oui] radyo düğmesi işaretlenmişse (satır 17-22). [marié], 18. satırdaki [name] özniteliğinin değeridir; [oui] ise 18. satırdaki [value] özniteliğinin değeridir;
      • [marié=non], [Non] radyo düğmesi işaretlendiğinde (satır 23-28). [marié], 24. satırdaki [name] özniteliğinin değeridir; [non] ise 24. satırdaki [value] özniteliğinin değeridir;
    • 37. satırdaki sayısal giriş alanının değeri [enfants=xx] biçimindedir; burada [enfants], 37. satırdaki [name] özniteliğinin değeridir, ve [xx], kullanıcı tarafından klavyeyle girilen değerdir;
    • 46. satırdaki sayısal giriş alanının değeri [salaire=xx] biçimindedir; burada [salaire], 46. satırdaki [name] özniteliğinin değeridir, ve [xx], kullanıcı tarafından klavyeden girilen değerdir;

Sonuç olarak, kaydedilen değer [marié=xx&enfants=yy&salaire=zz] şeklinde olacaktır.

  • (devam)
    • kullanıcı 53. satırdaki [submit] türündeki düğmeye tıkladığında girilen değerler gönderilecektir;
  • 16-30. satırlar: iki radyo düğmesi:

Image

Bu iki radyo düğmesi, aynı [name] özniteliğine sahip oldukları için (satır 18, 24) aynı radyo düğmesi grubuna aittir. Tarayıcı, bir radyo düğmesi grubunda herhangi bir anda yalnızca bir tanesinin işaretli olmasını sağlar. Dolayısıyla, birine tıklandığında, önceden işaretli olan diğerinin seçimi kaldırılır;

  • [type="radio"] özniteliği nedeniyle (18. ve 24. satırlar) bunlar radyo düğmeleridir;
  • Formun görüntülenmesinde (veri girişi öncesinde), radyo düğmelerinden biri işaretlenmiş olmalıdır: bunun için ilgili <input type="radio"> etiketine [checked=’checked’] özniteliğini eklemek yeterlidir. Bu, dinamik değişkenler kullanılarak gerçekleştirilir:
    • 18. satırda [modèle.checkedOui];
    • 24. satırda [modèle->checkedNon];

Bu değişkenler görünüm şablonunun bir parçası olacaktır.

  • 37. satır: minimum değeri 0 olan bir sayısal giriş alanı [type="number"]. Güncel tarayıcılarda bu, kullanıcının yalnızca >=0 olan bir sayı girebileceği anlamına gelir. Aynı güncel tarayıcılarda, giriş, yukarı veya aşağı yönde tıklanabilen bir kaydırma çubuğu ile yapılabilir. 37. satırdaki [step="1"] özniteliği, kaydırma çubuğunun 1 birimlik adımlarla çalışacağını belirtir. Bunun sonucu olarak, kaydırma çubuğu yalnızca 0'dan n'ye kadar 1'er birimlik adımlarla artan tamsayıları değer olarak kabul edecektir. Manuel giriş için bu, virgül içeren sayıların kabul edilmeyeceği anlamına gelir;
  • 37. satır: Bazı ekranlarda, alt öğelerin giriş alanı bu alana en son girilen değerle önceden doldurulmalıdır. Bunun için, giriş alanında görüntülenecek değeri belirleyen [value] özniteliği kullanılır. Bu değer dinamik olacak ve [modèle.enfants] değişkeni tarafından oluşturulacaktır; Image
  • 37. satır: [required] özniteliği, formun onaylanabilmesi için kullanıcının bir veri girmesini zorunlu kılar;
  • 46. satır: Maaş girişi için de çocuk girişi ile aynı açıklamalar geçerlidir;
  • satır 53: [submit] türündeki düğme, girilen değerleri POST'e aktararak URL ve [/calculer-impot]'e (satır 2) gönderir;

Image

32.6.3. [v-menu.html] parçası

Bu parça, vergi hesaplama formunun sol tarafında bir menü görüntüler:

Image

Bu parçanın kodu şöyledir:


<!-- Bootstrap menüsü -->
<nav class="nav flex-column">
    <!-- bağlantı listesinin görüntülenmesi HTML -->
    {% for optionMenu in modèle.optionsMenu %}
      <a class="nav-link" href="{{optionMenu.url}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

Açıklamalar

  • 2-7. satırlar: HTML [nav] etiketleri, diğer belgelere yönlendirme bağlantıları içeren HTML belgesinin bir bölümünü çerçeveler;
  • 5. satır: HTML [a] etiketi bir gezinme bağlantısı ekler:
  • [optionMenu.url]: URL bağlantısına tıklandığında yönlendirilen hedeftir. Bu durumda tarayıcı tarafından bir [GET optionMenu.url] işlemi gerçekleştirilir. [optionMenu.url], uygulamanın [http://machine :port/chemin] kök dizininden ölçülen mutlak bir URL olacaktır. Dolayısıyla [1]'te şu bağlantı oluşturulacaktır:
<a href=’/lister-simulations’>Liste des simulations</a>
  • 5. satır: Parçanın [modèle.optionsMenu] şablonu şu biçimde bir liste olacaktır:
[‘Liste des simulations’:’/liste-simulations’,
‘Fin de session’:’/fin-session’]
  • 2. ve 7. satırlar: CSS ve [nav, flex-column, nav-link] sınıfları, menüye görünümünü veren Bootstrap sınıflarıdır;

32.6.4. Görsel test

Bu farklı öğeleri [Tests] klasöründe bir araya getiriyoruz ve [vue-calcul-impot.html] görünümü için bir test şablonu oluşturuyoruz:

Image

[test_vue_calcul_impot] test komut dosyası şu şekilde olacaktır:


from flask import Flask, render_template, make_response

# Flask uygulaması
app = Flask(__name__, template_folder="../templates", static_folder="../static")

# Ana Sayfa URL
@app.route('/')
def index():
    # sayfa verilerini şablona yerleştirme
    modèle = {}
    # form
    modèle["checkedOui"] = ""
    modèle["checkedNon"] = 'checked="checked"'
    modèle["enfants"] = 2
    modèle["salaire"] = 300000
    # başarı mesajı
    modèle["success"] = True
    modèle["impôt"] = "Montant de l'impôt : 1000 euros"
    modèle["décôte"] = "Décôte : 15 euros"
    modèle["réduction"] = "Réduction : 20 euros"
    modèle["surcôte"] = "Surcôte : 0 euros"
    modèle["taux"] = "Taux d'imposition : 14 %"
    # hata mesajı
    modèle["error"] = True
    erreurs = ["erreur1""erreur2"]
    # hataların bir listesi oluşturulur HTML
    content = ""
    for erreur in erreurs:
        content += f"<li>{erreur}</li>"
    modèle["erreurs"] = content
    # menü
    modèle["optionsMenu"] = [
        {"text"'Liste des simulations'"url"'/lister-simulations'},
        {"text"'Fin de session'"url"'/fin-session'}]
    # sayfa görüntüleniyor
    return make_response(render_template("views/vue-calcul-impot.html", modèle=modèle))

# ana sayfa
if __name__ == '__main__':
    app.config.update(ENV="development", DEBUG=True)
    app.run()

Açıklamalar

  • 9-34. satırlar: [vue-calcul-impot.html] görünümünün ve [v-calcul-impot.html] ile [v-menu.html] parçalarının tüm dinamik kısımları başlatılır;
  • 36. satır: [vue-calcul-impot.html] görünümü görüntülenir;

[test_vue_calcul_impot] test komut dosyasını çalıştırdığımızda şu sonuç elde edilir:

Görsel olarak elde edilen sonuç bizi tatmin edene kadar bu görünüm üzerinde çalışılır. Ardından, bu görünümü yazım aşamasında olan web uygulamasına entegre etmeye geçilebilir.

Image

32.6.5. Görünüm modelinin hesaplanması

Görünümün görsel yapısı belirlendikten sonra, gerçek koşullarda görünüm modelinin hesaplanmasına geçilebilir. Bu görünüme yönlendiren durum kodlarını hatırlayalım. Bunlar yapılandırma dosyasında bulunur:


            {
                # vergi hesaplama görünümü
                "états"[
                    # /kullanıcı-kimlik-doğrulama başarılı
                    200,
                    # /vergi-hesapla başarılı
                    300,
                    # /vergi-hesapla başarısız
                    301,
                    # /vergi-hesaplamasını-göster
                    800
                ],
                "view_name""views/vue-calcul-impot.html",
                "model_for_view": ModelForCalculImpotView()
            },

Dolayısıyla, vergi hesaplama görünümünü görüntüleyen durum kodları [200, 300, 301, 800]'tir. Bu kodların anlamını bulmak için, jSON uygulaması üzerinde gerçekleştirilen [Postman] testlerinden yararlanabiliriz:

  • [authentifier-utilisateur-200]: 200, [authentifier-utilisateur] eyleminin başarıyla tamamlanmasının ardından ortaya çıkan durum kodudur; bu durumda boş vergi hesaplama formu görüntülenir;
  • [calculer-impot-300]: 300, [calculer-impot] işleminin başarıyla tamamlanmasının ardından verilen durum kodudur. Bu durumda, girilen veriler ve vergi tutarı ile birlikte hesaplama formu görüntülenir. Kullanıcı daha sonra başka bir hesaplama yapabilir;
  • [301] durum kodu, hatalı bir vergi hesaplaması sonucunda elde edilen koddur;
  • [800] durum kodu daha sonra ele alınacaktır. Henüz bu kodla karşılaşmadık;

Artık vergi hesaplama formunun hangi durumlarda görüntülenmesi gerektiğini bildiğimize göre, [ModelForCalculImpotView] sınıfında form şablonunu hesaplayabiliriz:

Image


from flask import Request
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class ModelForCalculImpotView(InterfaceModelForView):

    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # görünüm verilerini şablona aktarıyoruz
        modèle = {}
        # uygulamanın durumu
        état = résultat["état"]
        # şablon duruma bağlıdır
        if état in [200800]:
            # boş bir formun ilk görüntülenmesi
            modèle["success"] = False
            modèle["error"] = False
            modèle["checkedNon"] = 'checked="checked"'
            modèle["checkedOui"] = ""
            modèle["enfants"] = ""
            modèle["salaire"] = ""
        elif état == 300:
            # hesaplamanın başarılı olması - sonucun görüntülenmesi
            modèle["success"] = True
            modèle["error"] = False
            modèle["impôt"] = f"Montant de l'impôt : {résultat['réponse']['impôt']} euros"
            modèle["décôte"] = f'Décôte : {résultat["réponse"]["décôte"]} euros'
            modèle["réduction"] = f"Réduction : {résultat['réponse']['réduction']} euros"
            modèle["surcôte"] = f'Surcôte : {résultat["réponse"]["surcôte"]} euros'
            modèle["taux"] = f"Taux d'imposition :  {résultat['réponse']['taux'] * 100} %"
            # form, girilen değerlerle yeniden oluşturuldu
            modèle["checkedOui"] = 'checked="checked"' if request.form.get("marié") == "oui" else ""
            modèle["checkedNon"] = 'checked="checked"' if request.form.get("marié") == "non" else ""
            modèle["enfants"] = request.form.get("enfants")
            modèle["salaire"] = request.form.get("salaire")
        elif état == 301:
            # hata oluştu - form, girilen değerlerle yeniden yüklendi
            modèle["checkedOui"] = 'checked="checked"' if request.form.get("marié") == "oui" else ""
            modèle["checkedNon"] = 'checked="checked"' if request.form.get("marié") == "non" else ""
            modèle["enfants"] = request.form.get("enfants")
            modèle["salaire"] = request.form.get("salaire")
            # hata
            modèle["success"] = False
            modèle["error"] = True
            modèle["erreurs"] = ""
            for erreur in résultat['réponse']:
                modèle['erreurs'] += f"<li>{erreur}</li>"

        # menü seçenekleri
        modèle["optionsMenu"] = [
            {"text"'Liste des simulations'"url"'/lister-simulations'},
            {"text"'Fin de session'"url"'/fin-session'}]
        # şablon geri yükleniyor
        return modèle

Açıklamalar

  • 12. satır: Görüntülenecek görünüm, denetleyici tarafından döndürülen durum koduna bağlıdır;
  • 14-21. satırlar: Boş bir formun görüntülenmesi;
  • 22-35. satırlar: vergi hesaplamasının başarılı olması durumu. Girilen değerler ve vergi tutarı yeniden görüntülenir;
  • 36-47. satırlar: vergi hesaplamasının başarısız olması durumu;
  • satır 49-52: menüdeki iki seçeneğin hesaplanması;

32.6.6. [Postman] Testleri

[init-session-html-700] isteği ile bir HTML oturumu başlatılır, ardından [authentifier-utilisateur-200] isteği ile kimlik doğrulaması yapılır. Ardından şu [calculer-impot-300] isteği kullanılır:

Sunucudan gelen yanıt şöyledir:

Image

Image

Şimdi şu [calculer-impot-301] isteğini deneyelim:

Image

Sunucunun yanıtı şöyledir:

Şimdi beklenmedik bir durumu deneyelim; POST'te parametrelerin eksik olduğu durumu. Bu durum, uygulamanın normal işleyişinde mümkün değildir. Ancak herkes, şu anda yaptığımız gibi bir HTTP isteğini "uyarlayabilir":

Image

Image

  • [6]'te, [marié]'te gönderilen parametrenin işaretini kaldırdık;

Sunucunun yanıtı şöyledir:

Image

  • [3]'e dönüştü; bu, sunucunun hata mesajıdır;

Bu uygulamada bir seçeneğimiz vardı. Bu hata durumuna, beklenmedik hatalar sayfasını yönlendiren bir durum kodu atayabilirdik. Bu uygulamada her denetleyici için iki durum kodu seçtik:

  • [xx0]: başarılı işlem için;
  • [xx1]: başarısızlık için;

Başarısızlık durumlarında, hataları daha ayrıntılı bir şekilde yönetmek için durum kodlarını çeşitlendirebiliriz. Örneğin şunları kullanabilirdik:

  • [xx1]: hatayı oluşturan sayfada görüntülenecek hatalar için;
  • [xx2]: uygulamanın normal kullanımı sırasında ortaya çıkan beklenmedik hatalar için;

32.7. Simülasyon listesi görünümü

Image

32.7.1. Görünümün tanıtımı

Simülasyonların listesini gösteren görünüm şöyledir:

Image

[vue-liste-simulations.html] kodu tarafından oluşturulan görünüm üç bölümden oluşur:

  • 1: Üst başlık, daha önce tanıtılan [v-bandeau.html] parçası tarafından oluşturulur;
  • 3: [v-liste-simulations.html] parçası tarafından oluşturulan simülasyon tablosu;
  • 2: Daha önce tanıtılan [v-menu.html] parçası tarafından oluşturulan, iki bağlantı içeren bir menü;

Simülasyonlar sayfası, aşağıdaki [vue-liste-simulations.html] kodu ile oluşturulur:


<!-- HTML belgesi -->
<!doctype html>
<html lang="fr">
<head>
    <!-- Gerekli meta etiketleri -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <title>Application impôts</title>
</head>

<body>
<div class="container">
    <!-- başlık çubuğu -->
    {% include "fragments/v-bandeau.html" %}
    <!-- iki sütunlu düzen -->
    <div class="row">
        <!-- üç sütunlu menü-->
        <div class="col-md-3">
            {% include "fragments/v-menu.html" %}

        </div>
        <!-- 9 sütunlu simülasyon listesi-->
        <div class="col-md-9">
            {% include "fragments/v-liste-simulations.html" %}
        </div>
    </div>
</div>
</body>
</html>

Açıklamalar

  • 16. satır: [1] uygulama başlığının eklenmesi;
  • 21. satır: [2] menüsünün eklenmesi. Bu menü, başlığın altında üç sütun halinde görüntülenecektir;
  • 26. satır: [3] simülasyon tablosunun eklenmesi. Bu tablo, başlığın altında ve menünün sağında dokuz sütun halinde görüntülenecektir;

Bu görünümdeki üç parçadan ikisini daha önce yorumlamıştık:

[v-liste-simulations.html] parçası şöyledir:


{% if modèle.simulations is undefined or modèle.simulations|length==0 %}
<!-- mavi arka planlı mesaj -->
<div class="alert alert-primary" role="alert">
    <h4>Votre liste de simulations est vide</h4>
</div>
{% endif %}

{% if modèle.simulations is defined and modèle.simulations|length!=0 %}
<!-- mavi arka planlı mesaj -->
<div class="alert alert-primary" role="alert">
    <h4>Liste de vos simulations</h4>
</div>

<!-- simülasyon tablosu -->
<table class="table table-sm table-hover table-striped">
    <!-- tablonun altı sütununun başlıkları -->
    <thead>
    <tr>
        <th scope="col">#</th>
        <th scope="col">Marié</th>
        <th scope="col">Nombre d'enfants</th>
        <th scope="col">Salaire annuel</th>
        <th scope="col">Montant impôt</th>
        <th scope="col">Surcôte</th>
        <th scope="col">Décôte</th>
        <th scope="col">Réduction</th>
        <th scope="col">Taux</th>
        <th scope="col"></th>
    </tr>
    </thead>
    <!-- tablonun gövdesi (görüntülenen veriler) -->
    <tbody>
    <!-- simülasyon tablosunu tarayarak her bir simülasyon görüntülenir -->
    {% for simulation in modèle.simulations %}

    <!-- 6 sütunlu bir tablo satırının görüntülenmesi - <tr> etiketi -->
    <!-- 1. sütun: satır başlığı (simülasyon numarası) - <th scope='row'> etiketi -->
    <!-- 2. sütun: parametre değeri [marié] - <td> etiketi -->
    <!-- 3. sütun: [enfants] parametre değeri - <td> etiketi -->
    <!-- 4. sütun: [salaire] parametre değeri - <td> etiketi -->
    <!-- 5. sütun: [impôt] parametre değeri (vergi) - <td> etiketi -->
    <!-- 6. sütun: [surcôte] parametre değeri - <td> etiketi -->
    <!-- 7. sütun: [décôte] parametre değeri - <td> etiketi -->
    <!-- 8. sütun: [réduction] parametre değeri - <td> etiketi -->
    <!-- 9. sütun: [taux] parametre değeri (vergi) - <td> etiketi -->
    <!-- 10. sütun: simülasyonu silme bağlantısı - <td> etiketi -->
    <tr>
        <th scope="row">{{simulation.id}}</th>
        <td>{{simulation.marié}}</td>
        <td>{{simulation.enfants}}</td>
        <td>{{simulation.salaire}}</td>
        <td>{{simulation.impôt}}</td>
        <td>{{simulation.surcôte}}</td>
        <td>{{simulation.décôte}}</td>
        <td>{{simulation.réduction}}</td>
        <td>{{simulation.taux}}</td>
        <td><a href="/supprimer-simulation/{{simulation.id}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

Yorumlar

  • HTML tablosu, <table> etiketiyle oluşturulmuştur (15. ve 62. satırlar);
  • tablonun sütun başlıkları <thead> etiketi içinde yer almaktadır (satır 17, 30). <tr> etiketi (table row, satır 18 ve 29) bir satırı sınırlar. Satır 19-28'de, <th> etiketi (table header) bir sütun başlığını tanımlar. Dolayısıyla on tane vardır. [scope="col"], başlığın sütuna uygulandığını gösterir. [scope="row"], başlığın satıra uygulandığını gösterir;
  • 32-61. satırlar: <tbody> etiketi, tablo tarafından görüntülenen verileri çerçeveler;
  • 47-58. satırlar: <tr> etiketi, tablonun bir satırını çerçeveler;
  • 48. satır: <th scope=’row’> etiketi, satırın başlığını tanımlar. Tarayıcı bu başlığı vurgular;
  • 49-57. satırlar: Her bir <td> etiketi (tablo verisi), satırın bir sütununu tanımlar;
  • satır 34: Simülasyon listesi, bir sözlük listesi olan [modèle.simulations] şablonunda bulunur;
  • satır 57: simülasyonu silmek için bir bağlantı. URL, satırda görüntülenen simülasyon numarasını kullanır;

32.7.2. Görsel test

[vue-liste-simulations.html] görünümü için bir test komut dosyası oluşturuyoruz:

[test_vue_liste_simulations] komut dosyası şu şekildedir:

Image


from flask import Flask, make_response, render_template

# Flask uygulaması
app = Flask(__name__, template_folder="../templates", static_folder="../static")

# Ana Sayfa URL
@app.route('/')
def index():
    # sayfanın verilerini şablona yerleştiriyoruz
    modèle = {}
    # simülasyonları sayfanın beklediği biçime getiriyoruz
    modèle["simulations"] = [
        {
            "id"7,
            "marié""oui",
            "enfants"2,
            "salaire"60000,
            "impôt"448,
            "décôte"100,
            "réduction"20,
            "surcôte"0,
            "taux"0.14
        },
        {
            "id"19,
            "marié""non",
            "enfants"2,
            "salaire"200000,
            "impôt"25600,
            "décôte"0,
            "réduction"0,
            "surcôte"8400,
            "taux"0.45
        }
    ]
    # menü
    modèle["optionsMenu"] = [
        {"text""Calcul de l'impôt""url"'/afficher-calcul-impot'},
        {"text"'Fin de session'"url"'/fin-session'}]
    # sayfanın görüntülenmesi
    return make_response(render_template("views/vue-liste-simulations.html", modèle=modèle))

# ana sayfa
if __name__ == '__main__':
    app.config.update(ENV="development", DEBUG=True)
    app.run()

Açıklamalar

  • 12-35. satırlar: modele iki simülasyon ekliyoruz
  • 37-39. satırlar: menü seçenekleri tablosu;

Bu komut dosyasını çalıştırarak bu görünümü görüntüleyelim. Aşağıdaki sonuç elde edilir:

Image

Görsel olarak elde ettiğimiz sonuç bizi tatmin edene kadar bu görünüm üzerinde çalışırız. Ardından, yazmakta olduğumuz web uygulamasına bu görünümü entegre etmeye geçebiliriz.

32.7.3. Görünüm modelinin hesaplanması

Görünümün görsel yapısı belirlendikten sonra, gerçek koşullarda görünüm modelinin hesaplanmasına geçilebilir. Bu görünüme yönlendiren durum kodlarını hatırlayalım. Bunlar yapılandırma dosyasında bulunur:

Image


            {
                # simülasyon listesinin görünümü
                "états"[
                    # /simülasyonları-listele
                    500,
                    # /simülasyonu-sil
                    600
                ],
                "view_name""views/vue-liste-simulations.html",
                "model_for_view": ModelForListeSimulationsView()
            }

Dolayısıyla, simülasyon görünümünü görüntüleyen kodlar [500, 600]’tir. Bu kodların anlamını öğrenmek için, jSON uygulaması üzerinde gerçekleştirilen [Postman] testlerinden yararlanılabilir:

  • [lister-simulations-500]: 500, [lister-simulations] eyleminin başarıyla tamamlanmasının ardından ortaya çıkan durum kodudur; bu durumda, kullanıcı tarafından gerçekleştirilen simülasyonların listesi görüntülenir;
  • [supprimer-simulation-600]: 600, [supprimer-simulation] eyleminin başarıyla tamamlanmasının ardından verilen durum kodudur. Bu silme işleminin ardından elde edilen yeni simülasyon listesi görüntülenir;

Artık simülasyon listesinin hangi durumlarda görüntülenmesi gerektiğini bildiğimize göre, [ModelForListeSimulationsView] sınıfında bu listenin modelini hesaplayabiliriz:


from flask import Request
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class ModelForListeSimulationsView(InterfaceModelForView):

    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # sayfa verilerini şablona yerleştiriyoruz
        modèle = {}
        # simülasyonlar, eylemi gerçekleştiren denetleyicinin yanıtında bulunur
        # sözlüklerden oluşan bir tablo biçiminde TaxPayer
        modèle["simulations"] = résultat["réponse"]
        # menü
        modèle["optionsMenu"] = [
            {"text""Calcul de l'impôt""url"'/afficher-calcul-impot'},
            {"text"'Fin de session'"url"'/fin-session'}]
        # şablon oluşturulur
        return modèle

Açıklamalar

  • 13. satır: Görüntülenecek simülasyonlar [["réponse"] sonucu] içinde bulunur;
  • 15-17. satırlar: görüntülenecek menü seçenekleri;

32.7.4. [Postman] Testleri

Burada

  • HTML oturumu başlatılır;
  • kimlik doğrulaması yapılır;
  • üç vergi hesaplaması yapılıyor;

[lister-simulations-500] testi, 500 durum kodunu almamızı sağlar. Bu, simülasyonları görüntüleme talebine karşılık gelir:

Image

Sunucudan gelen yanıt şöyledir:

Image

[supprimer-simulation-600] testi, durum kodu 600'ü almamızı sağlar. Burada 2 numaralı simülasyonu sileceğiz.

Geri gönderilen sonuç, bir simülasyonun çıkarıldığı simülasyon listesidir:

Image

Image

32.8. Beklenmedik hataların görüntülenmesi

Burada, web uygulamasının normal kullanımı kapsamında meydana gelmemesi gereken bir hataya "beklenmedik hata" diyoruz. Örneğin, kimlik doğrulaması yapılmadan vergi hesaplaması talep edilmesi gibi. Bir kullanıcının tarayıcısına doğrudan URL [/calcul-impot] yazmasını engelleyen hiçbir şey yoktur. Ayrıca, daha önce gördüğümüz gibi, beklenen parametreleri göndermeyerek URL [/calcul-impot] adresine POST şeklinde bir istek gönderebilir. Web uygulamamızın bu isteğe doğru şekilde yanıt verebildiğini gördük. HTML uygulaması kapsamında meydana gelmemesi gereken bir hataya “beklenmedik hataadını vereceğiz. Böyle bir hata meydana gelirse, muhtemelen birisi uygulamayı “hacklemeye” çalışıyordur. Eğitim amaçlı olarak, bu tür durumlar için bir hata sayfası görüntülemeye karar verdik. Gerçekte, müşteriye gönderilen son sayfayı yeniden görüntüleyebiliriz. Bunun için, gönderilen son HTML yanıtını oturumda kaydetmek yeterlidir. Beklenmedik bir hata durumunda, bu yanıtı geri göndeririz. Böylece, görüntülenen sayfa değişmediği için kullanıcı, sunucunun hatalarına yanıt vermediğini düşünür.

32.8.1. Görünümün Sunumu

Image

Beklenmedik hataları gösteren görünüm şöyledir:

Image

[vue-erreurs.html] kodu tarafından oluşturulan görünüm üç bölümden oluşur:

  • 1: Üst başlık, daha önce tanıtılan [v-bandeau.html] parçası tarafından oluşturulur;
  • 2: Beklenmedik hata veya hatalar;
  • 3: Daha önce tanıtılan [v-menu.html] parçası tarafından oluşturulan, üç bağlantı içeren bir menü;

Beklenmedik hataların görüntüsü, aşağıdaki [vue-erreurs.html] komut dosyası tarafından oluşturulur:


<!-- belge HTML -->
<!doctype html>
<html lang="fr">
<head>
    <!-- Gerekli meta etiketleri -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
    <title>Application impôts</title>
</head>

<body>
<div class="container">
    <!-- 12 sütunlu başlık -->
    {% include "fragments/v-bandeau.html" %}
    <!-- iki bölümlü satır -->
    <div class="row">
        <!-- 3 sütunlu menü-->
        <div class="col-md-3">
            {% include "fragments/v-menu.html" %}
        </div>
        <!-- 9 sütunlu hata listesi -->
        <div class="col-md-9">
            <div class="alert alert-danger" role="alert">
                Les erreurs inattendues suivantes se sont produites :
                <ul>{{modèle.erreurs|safe}}</ul>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Açıklamalar

  • 16. satır: [1] uygulamasının başlık çubuğunun eklenmesi;
  • 21. satır: [3] menüsünün eklenmesi. Bu menü, başlığın altında üç sütun halinde görüntülenecektir;
  • 24-29. satırlar: hata alanının dokuz sütun halinde görüntülenmesi;
  • 25. satır: Bu görüntüleme, pembe arka planlı bir Bootstrap çerçeve içinde yapılacaktır;
  • 26. satır: bir tanıtım metni;
  • 27. satır: <ul> etiketi, bir madde işaretli listeyi çerçeveler. Bu madde işaretli liste, [modèle.erreurs] şablonu tarafından sağlanır;

Bu görüntünün iki parçasını daha önce yorumlamıştık:

32.8.2. Görsel test

[vue-erreurs.html] görünümü için bir test komut dosyası oluşturuyoruz:

Image


from flask import Flask, render_template, make_response

# Flask uygulaması
app = Flask(__name__, template_folder="../templates", static_folder="../static")

# Ana Sayfa URL
@app.route('/')
def index():
    # sayfa verilerini şablona yerleştiriyoruz
    modèle = {}
    # hataların bir listesi oluşturulur HTML
    content = ""
    for erreur in ["erreur1""erreur2"]:
        content += f"<li>{erreur}</li>"
    modèle["erreurs"] = content
    # menü seçenekleri
    modèle["optionsMenu"] = [
        {"text""Calcul de l'impôt""url"'/calculer-impot'},
        {"text"'Liste des simulations'"url"'/lister-simulations'},
        {"text"'Fin de session'"url"'/fin-session'}]

    # sayfanın görüntülenmesi
    return make_response(render_template("views/vue-erreurs.html", modèle=modèle))

# ana sayfa
if __name__ == '__main__':
    app.config.update(ENV="development", DEBUG=True)
    app.run()

Yorumlar

  • 11-15. satırlar: HTML hata listesinin oluşturulması;
  • 17-20. satırlar: menü seçenekleri tablosu;

Bu komut dosyasını çalıştıralım. Aşağıdaki sonucu elde ederiz:

Görsel olarak elde ettiğimiz sonuç bizi tatmin edene kadar bu görünüm üzerinde çalışırız. Ardından, yazmakta olduğumuz web uygulamasına bu görünümü entegre etmeye geçebiliriz.

Image

32.8.3. Görünüm modelinin hesaplanması

Image

Görünümün görsel yapısı belirlendikten sonra, gerçek koşullarda görünüm modelinin hesaplanmasına geçilebilir. Bu görünüme yönlendiren durum kodlarını hatırlayalım. Bunlar yapılandırma dosyasında bulunur:


        # HTML görünümleri ve şablonları, denetleyici tarafından döndürülen duruma bağlıdır
        "views"[
            {
                # kimlik doğrulama görünümü
                "états": [
                    # /init-session başarılı
                    700,
                    # /oturumu-sonlandır
                    400,
                    # /kullanıcı-kimlik-doğrulama başarısız
                    201
                ],
                "view_name""views/vue-authentification.html",
                "model_for_view": ModelForAuthentificationView()
            },
            {
                # vergi hesaplama sayfası
                "états"[
                    # /kullanıcı-doğrulama başarılı
                    200,
                    # /vergi-hesaplama başarılı
                    300,
                    # /vergi-hesapla başarısız
                    301,
                    # /vergi-hesaplamasını-göster
                    800
                ],
                "view_name""views/vue-calcul-impot.html",
                "model_for_view": ModelForCalculImpotView()
            },
            {
                # simülasyon listesini görüntüle
                "états"[
                    # /simülasyonları-listele
                    500,
                    # /simülasyonu-sil
                    600
                ],
                "view_name""views/vue-liste-simulations.html",
                "model_for_view": ModelForListeSimulationsView()
            }
        ],
        # beklenmedik hataların görünümü
        "view-erreurs": {
            "view_name""views/vue-erreurs.html",
            "model_for_view": ModelForErreursView()
        },

3-41. satırlardaki HTML görünümüne yönlendirmeyen durum kodları, beklenmedik hataların görünümünü görüntülemektedir.

[vue-erreurs.html] görünümünün model hesaplaması, aşağıdaki [ModelForErreursView] sınıfı tarafından yapılır:


from flask import Request
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class ModelForErreursView(InterfaceModelForView):

    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # model
        modèle = {}
        # hatalar
        modèle["erreurs"] = ""
        for erreur in résultat['réponse']:
            modèle['erreurs'] += f"<li>{erreur}</li>"
        # menü
        modèle["optionsMenu"] = [
            {"text""Calcul de l'impôt""url"'/afficher-calcul-impot'},
            {"text"'Liste des simulations'"url"'/lister-simulations'},
            {"text"'Fin de session'"url"'/fin-session'}]
        # şablonu oluşturuyoruz
        return modèle

Açıklamalar

  • 11-14. satırlar: [vue-erreurs.html] görünümü tarafından kullanılan [modèle.erreurs] modelinin hesaplanması;
  • satır 16-197: [v-menu.html] parçası tarafından kullanılan [modèle.optionsMenu] şablonunun hesaplanması;

32.8.4. [Postman] Testleri

Şu işlem yapılır:

  • [/init-session/html] eylemi;
  • ardından [/init-session/x] eylemi;

HTML yanıtı şu şekildedir:

Image

32.9. Uygulama menüsündeki eylemlerin uygulanması

Burada menü eylemlerinin uygulanmasını ele alacağız. Karşılaştığımız bağlantıların anlamlarını hatırlayalım

Görünüm
Bağlantı
Hedef
Rol
Vergi Hesaplama
[Liste des simulations]
[/lister-simulations]
Simülasyon listesini talep et
 
[Fin de session]
[/fin-session]
Simülasyon listesi
[Calcul de l’impôt]
[/afficher-calcul-impot]
Vergi hesaplama görünümünü görüntüle
 
[Fin de session]
[/fin-session]
Beklenmedik hatalar
[Calcul de l’impôt]
[/afficher-calcul-impot]
Vergi hesaplama görünümünü görüntüle
 
[Liste des simulations]
[/lister-simulations]
 
[Fin de session]
[/fin-session]

Bir bağlantıya tıklandığında, bağlantının hedefine yönlendiren bir GET eylemi tetiklendiğini hatırlatmak gerekir. [/lister-simulations, /fin-session] eylemleri, bir GET işlemiyle uygulanmıştır; bu sayede bunları bağlantı hedefleri olarak belirleyebiliyoruz. Eylem bir POST ile gerçekleştirildiğinde, JavaScript ile entegre edilmedikçe bağlantı kullanımı artık mümkün değildir.

32.9.1. [/afficher-calcul-impot] hisse senedi

Yukarıdaki işlemlerden, [/afficher-calcul-impot] işleminin henüz uygulanmadığı anlaşılmaktadır. Bu, iki görünüm arasında gezinme işlemidir: jSON veya XML sunucularının bunu uygulaması için hiçbir neden yoktur, çünkü bu sunucularda görünüm kavramı yoktur. Bu kavramı getiren HTML sunucusudur.

Bu nedenle [/afficher-calcul-impot] eylemini uygulamamız gerekiyor. Bu, sunucu içinde bir eylemin uygulanma sürecini gözden geçirmemizi sağlayacaktır.

Öncelikle, yeni bir ikincil denetleyici eklememiz gerekiyor. Buna [AfficherCalculImpotController] adını vereceğiz:

Image

Bu denetleyici, [config] yapılandırma dosyasına eklenmelidir:


    # denetleyiciler
    from AfficherCalculImpotController import AfficherCalculImpotController
    from AuthentifierUtilisateurController import AuthentifierUtilisateurController
    from CalculerImpotController import CalculerImpotController
    from CalculerImpotsController import CalculerImpotsController
    from FinSessionController import FinSessionController
    from GetAdminDataController import GetAdminDataController
    


        # izin verilen eylemler ve bunların denetleyicileri
        "controllers": {
            # hesaplama oturumunun başlatılması
            "init-session": InitSessionController(),
            # bir kullanıcının kimlik doğrulaması
            "authentifier-utilisateur": AuthentifierUtilisateurController(),
            # bireysel modda vergi hesaplaması
            "calculer-impot": CalculerImpotController(),
            # toplu modda vergi hesaplaması
            "calculer-impots": CalculerImpotsController(),
            # simülasyon listesi
            "lister-simulations": ListerSimulationsController(),
            # simülasyon silme
            "supprimer-simulation": SupprimerSimulationController(),
            # hesaplama oturumunun sonlandırılması
            "fin-session": FinSessionController(),
            # vergi hesaplama görünümünün görüntülenmesi
            "afficher-calcul-impot": AfficherCalculImpotController(),
            # vergi dairesinden verilerin alınması
            "get-admindata": GetAdminDataController(),
            # ana denetleyici
            "main-controller": MainController()
        },

       # HTML görünümleri ve bunların şablonları, denetleyicinin döndürdüğü duruma bağlıdır
        "views"[
            {
                # kimlik doğrulama görünümü
                
            },
            {
                # vergi hesaplama sayfası
                "états": [
                    # /kullanıcı-kimlik-doğrulama başarılı
                    200,
                    # /vergi-hesapla başarılı
                    300,
                    # /vergi-hesapla başarısız
                    301,
                    # /vergi-hesaplamasını-göster
                    800
                ],
                "view_name""views/vue-calcul-impot.html",
                "model_for_view": ModelForCalculImpotView()
            },
            {…
            }
        ],
  • 2. satır: yeni denetleyici;
  • 28. satır: yeni eylem ve kontrolörü;
  • 51. satır: yeni denetleyici 800 durum kodunu döndürecektir. Görünüm değiştiğinde hata oluşmamalıdır. Görüntülenen görünüm, incelediğimiz, açıkladığımız ve test ettiğimiz [vue-calcul-impot.html] görünümüdür;

[AfficherCalculImpotController] denetleyicisi şu şekilde olacaktır:


from flask_api import status
from werkzeug.local import LocalProxy

from InterfaceController import InterfaceController

class AfficherCalculImpotController(InterfaceController):

    def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
        # yolun öğeleri alınır
        dummy, action = request.path.split('/')

        # görünüm değişikliği - sadece bir durum kodu ayarlanması gerekiyor
        return {"action": action, "état"800"réponse"""}, status.HTTP_200_OK

Açıklamalar

  • 6. satır: Diğer ikincil denetleyiciler gibi, yeni denetleyici de [InterfaceController] arayüzünü uygular;
  • 13. satır: Görünüm değişiklikleri uygulaması kolaydır: Hedef görünüme ilişkin bir durum kodu döndürmek yeterlidir; burada, yukarıda görüldüğü gibi 800 kodu kullanılır;

32.9.2. [/fin-session] eylemi

[/fin-session] eylemi özel bir durumdur. Bu eylem doğrudan bir görünüme değil, bir yönlendirmeye götürür. Yönlendirmelerin, [config] yapılandırmasında şu şekilde ayarlandığını hatırlayalım:


        # yönlendirmeler
        "redirections"[
            {
                "états": [
                    400,  # /oturum sonlandırma başarılı
                ],
                # şuraya yönlendirme
                "to""/init-session/html",
            }
        ],

Uygulamada tek bir yönlendirme vardır:

  • denetleyici [400] durum kodunu döndürdüğünde (5. satır), istemciyi URL [http://machine:port/chemin/init-session/html] adresine yönlendirmek gerekir (8. satır);

[400] durum kodu, [/fin-session] eyleminin başarıyla tamamlanmasının ardından döndürülen koddur. Öyleyse neden istemciyi URL ve [/init-session/html] durum kodlarına yönlendirmek gerekiyor? Çünkü [/fin-session] eyleminin kodu, web oturumunda bulunan oturum türünü siler. Bu durumda, bir HTML oturumunda olduğumuz artık bilinmez. Bu nedenle yeniden yönlendirme yapılması gerekir. Bu, [/init-session/html] eylemi kullanılarak gerçekleştirilir.

HTML yönlendirmeleri, [HtmlResponse] sınıfı tarafından yönetilir:


   def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
                            résultat: dict) -> (Response, int):
        # HTML yanıtı, denetleyici tarafından döndürülen durum koduna bağlıdır
        état = résultat["état"]

        # Yönlendirme yapılmalı mı?
        for redirection in config["redirections"]:
            # yönlendirme gerektiren durumlar
            états = redirection["états"]
            if état in états:
                # yönlendirme yapılmalıdır
                return redirect(f"{redirection['to']}"), status.HTTP_302_FOUND

        # her duruma bir görünüm karşılık gelir
        # bu görünüm, görünümler listesinde aranır
 ..
  • 6-12. satırlar yönlendirmeleri işler;
  • 7. satır: config[‘redirections’], yönlendirmelerin bir listesidir. Her yönlendirme, aşağıdaki anahtarları içeren bir sözlükten oluşur:
    • [états]: denetleyici tarafından döndürülen ve bir yönlendirmeye yol açan durumlar;
    • [to]: yönlendirme adresi;
  • 7-12. satırlar: yönlendirme listesi taranır;
  • 9. satır: her yönlendirme için, ona yol açan durumlar alınır;
  • 10. satır: Test edilen durum bu listede yer alıyorsa, yönlendirme yapılır (12. satır);
  • 12. satır: [build_http_response] yönteminin iki elemanlı bir tuple döndürmesi gerektiği hatırlatılır:
    • [response]: Verilecek HTTP yanıtı. Bu yanıt, parametresi yönlendirme adresi olan [redirect] işleviyle oluşturulur;
    • [status_code]: HTTP yanıtının durum kodu; burada, istemciye yeniden yönlendirilmesi gerektiğini belirten [status.HTTP_302_FOUND] kodu;

[Postman] ile bir test yapalım. Şu adımları uygulayalım:

  • bir oturum başlatıyoruz: HTML [init-session/html];
  • kimlik doğrulaması yapıyoruz: [/authentifier-utilisateur];
  • [/fin-session] oturumunu sonlandırıyoruz;

Image

Sunucudan gelen yanıt şöyledir:

Image

Kimlik doğrulama ekranı görüntülendi. Tam da beklediğimiz ekran bu. Şimdi, bu ekranın nasıl elde edildiğine bir bakalım. [Postman] konsoluna geçelim (Ctrl-Alt-C):

Image

  • [1]'te, [/fin-session] eylemi;
  • [2-3]'e, sunucu tarafından döndürülen HTTP durum kodu 302, istemciye yeniden yönlendirileceğini bildirir;
  • [4]'te, [Postman] istemcisi yönlendirmeye uyar;

32.10. HTML uygulamasının gerçek koşullarda test edilmesi

Kod yazıldı ve her eylem [Postman] ile test edildi. Geriye, gerçek ortamda sayfa akışını test etmek kalıyor. HTML oturumunu başlatmanın bir yoluna ihtiyacımız var. Sunucuya [/init-session/html] isteğini göndermemiz gerektiğini biliyoruz. Bu pek kullanışlı bir URL değil. URL ve [/] ile başlamayı tercih ederiz.

Ana komut dosyası [main]’e şu yolu yazdık:


from flask import request, Flask, session, url_for, redirect


@app.route('/', methods=['GET'])
def index() -> tuple:
    # /init-session/html adresine yönlendirme
    return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)

# init-session
@app.route('/init-session/<string:type_response>', methods=['GET'])
def init_session(type_response: str) -> tuple:
    # eylemle ilişkili denetleyici çalıştırılır
    return front_controller()
  • 4-7. satırlar: [/] yolunun yönetimi. Web uygulamasının giriş noktası URL[/init-session/html] olacaktır (10. satır). Ayrıca 7. satırda, istemciyi bu URL adresine yönlendiriyoruz:
  • [url_for] işlevi 1. satırda içe aktarılmıştır. Bu işlevin burada iki parametresi vardır (7. satır):
      • 1. parametre, yönlendirme fonksiyonlarından birinin adıdır; burada 11. satırdaki fonksiyon. Bu fonksiyonun, müşterinin istediği yanıt türünü (json, xml, html) belirten [type_response] parametresini beklediği görülmektedir;
      • ikinci parametre, 11. satırdaki parametrenin adını ([type_response]) alır ve ona bir değer atar. Başka parametreler olsaydı, bu işlem her biri için tekrarlanırdı;
      • bu işlem, kendisine verilen iki parametre ile belirtilen fonksiyona atanan URL değerini döndürür. Burada, parametrenin [/init-session/html] değeriyle değiştirildiği 10. satırdaki URL değeri elde edilir;
    • [redirect] işlevi 1. satırda içe aktarılmıştır. Bu işlevin görevi, istemciye bir HTTP yönlendirme başlığı göndermektir:
      • 1. parametre, müşterinin yönlendirilmesi gereken URL adresidir;
      • 2. parametre, müşteriye verilen HTTP yanıtının durum kodudur. [status.HTTP_302_FOUND] kodu, bir HTTP yönlendirmesine karşılık gelir;

Hazırız. Şimdi birkaç sayfa akışını göstereceğiz.

Tarayıcımızda istek izlemeyi etkinleştiriyoruz (Chrome, Firefox, Edge'de F12) ve [http://localhost:5000/] başlatma isteği olan URL'i gönderiyoruz. Sunucunun yanıtı şöyledir:

Image

İstemci ile sunucu arasında gerçekleşen ağ iletişimine bakıldığında:

Image

  • [4, 5] adresinde, tarayıcının URL adresine yönlendirme isteği aldığını görüyoruz;

Aldığımız formu dolduralım;

Image

Ardından birkaç simülasyon yapalım:

Image

Image

Simülasyonların listesini isteyelim:

Image

İlk simülasyonu silelim:

Image

Oturumu sonlandıralım:

Image

Okuyucu, başka testler de yapmaya davet edilir.