One place for hosting & domains

      December 2021

      How to Set Up a Website Hit Counter With Redis and PHP on Ubuntu 20.04


      The author selected the Apache Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      A hit counter is an application that records and indicates the number of visits your web page has received. The counter starts from 1 and is incremented once every time a web page is visited.

      To keep track of the visits, the hit counter application requires a form of a database. While disk-based database management systems like MySQL can work, an in-memory database is better in terms of speed, performance, scalability, simplicity, and ease of use. This is where the Redis server comes into play. Redis stores data in your computer’s RAM instead of hitting the disk every time you’re performing an input/output operation. This increases the throughput significantly.

      To track your site visits, you require a Redis hash map. This is a data structure that implements a key-value pair. A hash map provides a hash table that maps keys to values. Once a user visits your web page, you create a key based on their public IP address or username (for authenticated users), and then you initialize their total visits to a value of 1. Then, every time the user revisits your web page, you check their total visits from the Redis hash map based on their IP address/username and increment the value.

      In this guide, you’ll set up a website hit counter with Redis and PHP on your Ubuntu 20.04 server. The PHP scripts in this guide use the visitors’ public IP addresses to track their visits.

      Prerequisites

      To follow along with this guide, make sure you have the following:

      Step 1 — Installing PHP Redis Extension

      In this step, you’ll install a Redis extension that allows PHP to talk to the Redis server. You’ll also create a test web page that implements the Redis hash map to track web visits.

      Before installing the Redis extension, refresh your Ubuntu package information index:

      Then, run the following command to install php-redis. The extension provides an API for communicating with the Redis server key-value store:

      • sudo apt install -y php-redis

      Restart Apache to load the new extension:

      • sudo systemctl restart apache2

      You’ve now installed a PHP extension that talks to your Redis server. Next, you’ll create a test.php web page under the root directory of the Apache webserver. This is just a sample file that visitors request when they visit your website with a browser. Under the hood, the test.php page file loads a hit_counter.php script which you’ll later create to track page visits using the Redis server.

      In a real-life scenario, your website might have tens or even hundreds of web pages. For this guide, you’ll set up a single web page for demonstration purposes.

      In your terminal window, use nano to create a new test.php file under the root directory of your web-server /var/www/html/:

      • sudo nano /var/www/html/test.php

      Then, enter the following information into the test.php file:

      /var/www/html/test.php

      <?php
        require_once 'hit_counter.php';
      ?>
      
      <!DOCTYPE html>
      <html>
      
        <head>
          <title>Sample Test Page</title>
        </head>
      
        <body>
          <h1>Sample test page</h1>
          <p>This is a sample test page.</p>
        </body>
      
      </html>
      
      

      Save and close the file when you’re through with editing. In this step, you’ve created a simple HTML web page that loads a hit_counter.php file when visited. Next, you’ll code the hit_counter.php file to track the test page visits.

      Step 2 — Creating a Redis Hit Counter Script

      When working in a production environment, it’s very conventional to separate re-usable PHP files. This allows you to implement the logic in these files on different parts of the project just by including their paths instead of copy-pasting the code. This makes maintenance easier since you only need to edit a single file in case you need to change the logic. This saves you a lot of time.

      You’re going to apply the same strategy in this guide. You’ll create a single hit_counter.php file that you can include on any web page that requires visitors’ tracking.

      In this file, you’ll use the php-redis library to connect to the Redis server from PHP. Then, you’ll create a Redis hash map to store the number of visits a visitor has made to your website. You’ll use the visitors’ unique IP addresses as Redis keys to distinguish each visitor’s hit counts in the Redis server.

      In your terminal window, open a new hit_counter.php file using nano for editing purposes:

      • sudo nano /var/www/html/hit_counter.php

      With the hit_counter.php file now created, open a new PHP tag <?php. Then, inside a try { block enter the following code to connect to your local Redis server on port 6379. Replace EXAMPLE_PASSWORD with the authentication password for the Redis server:

      /var/www/html/hit_counter.php

      
      <?php
      
          try {
      
              $redis = new Redis();
              $redis->connect('127.0.0.1', 6379);
              $redis->auth('EXAMPLE_PASSWORD');
      

      Next, give the Redis hash map($siteVisitsMap) a name of your choice. This guide uses siteStats for demonstration purposes:

      /var/www/html/hit_counter.php

      
              $siteVisitsMap = 'siteStats';
      
      

      After defining the Redis hash map, you’ll now initialize an empty Redis key ($visitorHashKey). Then, you’ll populate it with the visitors’ IP addresses. You’ll use the value of the $visitorHashKey variable to uniquely identify each visitor requesting your web page:

      /var/www/html/hit_counter.php

      
      
              $visitorHashKey = '';           
      
              if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
      
                  $visitorHashKey = $_SERVER['HTTP_CLIENT_IP'];
      
              } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
      
                  $visitorHashKey = $_SERVER['HTTP_X_FORWARDED_FOR'];
      
              } else {
      
                  $visitorHashKey = $_SERVER['REMOTE_ADDR'];
              }
      
      

      In this code, you’re using the PHP if statement to determine the visitor’s IP address by checking whether the $_SERVER['HTTP_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], or $_SERVER['REMOTE_ADDR'] variables are populated.

      Following this, initialize a $totalVisits variable to store the total visits for each IP address and assign it a value of 0. Then, use the PHP if (...) {...} else {...} and $redis->hExists($siteVisitsMap, $visitorHashKey) statements to check if the IP address has any entries in the Redis server.

      You’ll use the statement if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {...} to check whether a $visitorHashKey exists in a map named $siteVisitsMap.

      In case the map and the key with the named IP address exist in the Redis server, retrieve it with the statement $visitorData = $redis->hMget($siteVisitsMap, array($visitorHashKey)); and use $totalVisits = $visitorData[$visitorHashKey] + 1; to increment the $totalVisits variable. You’re using the $redis->hMget statement to get hit count data associated with an IP address. The hMget function accepts the name of your map ($siteVisitsMap) and an array of the keys that you want to retrieve from the Redis server. In this case, you only have one key ($visitorHashKey), but you must convert it to an array using the statement array($visitorHashKey).

      In case your script encounters the IP address for the first time, set the $totalVisits variable to 1. Finally, use $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits); to set the value of the $visitorHashKey according to the results of the previous if (...) {...} else {...} statement. The $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits) statement creates a $siteVisitsMap hash map in the Redis server with a key named $visitorHashKey with a value of $totalVisits.

      Then, welcome the visitor by echoing the total visits and close the } catch (...) {...} block:

      /var/www/html/hit_counter.php

      
              $totalVisits = 0;
      
              if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {
      
                  $visitorData = $redis->hMget($siteVisitsMap, array($visitorHashKey));
                  $totalVisits = $visitorData[$visitorHashKey] + 1;
      
              } else {
      
                  $totalVisits = 1;
      
              }
      
              $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits);
      
              echo "Welcome, you've visited this page " .  $totalVisits . " timesn";
      
          } catch (Exception $e) {
              echo $e->getMessage();
          }
      
      

      Once completed, your /var/www/html/hit_counter.php file should be similar to the following code:

      /var/www/html/hit_counter.php

      
      <?php
      
          try {
      
              $redis = new Redis();
              $redis->connect('127.0.0.1', 6379);
              $redis->auth('EXAMPLE_PASSWORD');
      
              $siteVisitsMap  = 'siteStats';
              $visitorHashKey = '';           
      
              if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
      
                 $visitorHashKey = $_SERVER['HTTP_CLIENT_IP'];
      
              } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
      
                 $visitorHashKey = $_SERVER['HTTP_X_FORWARDED_FOR'];
      
              } else {
      
                 $visitorHashKey = $_SERVER['REMOTE_ADDR'];
              }
      
              $totalVisits = 0;
      
              if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {
      
                  $visitorData = $redis->hMget($siteVisitsMap,  array($visitorHashKey));
                  $totalVisits = $visitorData[$visitorHashKey] + 1;
      
              } else {
      
                  $totalVisits = 1;
      
              }
      
              $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits);
      
              echo "Welcome, you've visited this page " .  $totalVisits . " timesn";
      
          } catch (Exception $e) {
              echo $e->getMessage();
          }
      

      Save and close the file when you’re through with editing. You’ve now coded a hit_counter.php script. Next, you’ll create another PHP script that generates a report from the data gathered in the Redis hash map.

      Step 3 — Creating a Site Stats Report Script

      Once you’ve collected data in a Redis hash map, it might not make any sense if you’re not able to retrieve and represent the information in a report. In this step, you’ll create a log report to show the different site visitors and the total visits they’ve made on the test web page.

      To create the log report script, run nano on your terminal window and create a new /var/www/html/log_report.php file:

      • sudo nano /var/www/html/log_report.php

      Then, enter the information below into the file. Replace EXAMPLE_PASSWORD with the correct password for the Redis server:

      /var/www/html/log.php

      
      <!DOCTYPE html>
      <html>
      
        <head>
          <title>Site Visits Report</title>
        </head>
      
        <body>
      
            <h1>Site Visits Report</h1>
      
            <table border="1">
              <tr>
                <th>No.</th>
                <th>Visitor</th>
                <th>Total Visits</th>
              </tr>
      
              <?php
      
                  try {
      
                      $redis = new Redis();
                      $redis->connect('127.0.0.1', 6379);
                      $redis->auth('EXAMPLE_PASSWORD');
      
                      $siteVisitsMap = 'siteStats';                          
      
                      $siteStats = $redis->HGETALL($siteVisitsMap);
      
                      $i = 1; 
      
                      foreach ($siteStats as $visitor => $totalVisits) {
      
                          echo "<tr>";
                            echo "<td align = 'left'>"   . $i . "."     . "</td>";
                            echo "<td align = 'left'>"   . $visitor     . "</td>";
                            echo "<td align = 'right'>"  . $totalVisits . "</td>";
                          echo "</tr>";
      
                          $i++;
                      }
      
                  } catch (Exception $e) {
                      echo $e->getMessage();
                  }
      
              ?>
      
            </table>
        </body>
      
      </html>
      

      Save and close the file when you’re through with editing. In the above script, you’re connecting to the Redis server and you’re using the statement $redis->HGETALL($siteVisitsMap); to retrieve your web page visits’ hash map. Then, you’re using the PHP foreach ($siteStats as $visitor => $totalVisits) { statement to loop and display the visitors’ IP addresses and the number of visits they’ve made to your site. You’re using the Redis HGETALL command to retrieve all fields (IP addresses) and values (total visits per each IP address) from the siteVisitsMap map.

      You now have a test page, a hit counter script, and a report page to check your site stats. Next, you’ll test the functionalities of your hit counter and see if everything works.

      Step 4 — Testing the Redis Hit Counter

      In this step, you’ll test the whole logic for your hit counter. Navigate to the following URL on your web browser. Replace your-server-IP with your server’s public IP address or domain name.

      http://your-server-IP/test.php
      

      Refresh the page several times using different devices to generate enough stats. After each visit, you should receive the following output.

      https://xpresservers.com/wp-content/uploads/2021/12/How-to-Set-Up-a-Website-Hit-Counter-With-Redis.png

      Next, visit the following URL to get your site visits report displayed in an HTML table

      http://your-server-IP/log_report.php
      
      

      You should now see a report similar to the following output.

      https://xpresservers.com/wp-content/uploads/2021/12/1640824415_889_How-to-Set-Up-a-Website-Hit-Counter-With-Redis.png

      Your hit counter is now working as expected.

      Conclusion

      In this guide, you’ve set up a website hit counter with Redis and PHP on your Ubuntu 20.04 server.

      As you can see from the sample source code in this guide, Redis provides cleaner methods for creating and updating hash maps.

      As mentioned at the beginning of this guide, using a relational database management system may still work but you’ll write tons of code to insert and update data in underlying tables. In addition, disk-based databases may experience scalability issues when your site grows.

      For more information on using Redis in-memory database, follow the guides below:



      Source link

      How To Use Opacity and Transparency to Create a Modal in CSS


      The author selected the Diversity in Tech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      When styling HTML with CSS, there are multiple ways to adjust the opacity of elements, and multiple reasons to employ this effect in a design. Opacity can help soften a shadow, de-emphasize non-essential content during a specific task, or fade content in or out of view. You can accomplish these effects with the opacity property, the transparent color name, or alpha channels, which are an extension of color values with an additional segment for controlling opacity.

      Throughout this tutorial, you will use various ways to apply opacity and extra properties to effectively accomplish certain effects. You will create a modal that appears with a no-JavaScript approach using the :target pseudo class and the opacity, pointer-events, and transition properties. Then you will use each of the alpha channel color values to create shadow, border, and content overlays. You will also use the transparent color value to help make a gradient animate colors on a :hover event.

      Prerequisites

      • An understanding of CSS’s cascade and specificity features, which you can get by reading How To Apply CSS Styles to HTML with Cascade and Specificity.
      • Knowledge of type selectors, combinator selectors, and selector groups, which you can find in How To Select HTML Elements to Style with CSS.
      • An understanding of color properties. See How To Use Color Values with CSS to learn more about working with color in CSS.
      • Knowledge of CSS gradients with the background properties. Check out How To Apply Background Styles to HTML Elements with CSS to gain experience creating gradient backgrounds.
      • Experience with the box-shadow property, which you can learn more about in How To Style the Edges of HTML Elements with Borders, Shadows, and Outlines in CSS.
      • An empty HTML file saved on your local machine as index.html that you can access from your text editor and web browser of choice. To get started, check out our How To Set Up Your HTML Project tutorial, and follow How To Use and Understand HTML Elements for instructions on how to view your HTML in your browser. If you’re new to HTML, try out the whole How To Build a Website in HTML series.

      Setting Up the Base HTML and CSS

      In this first section, you will set up the HTML for the visual styles you will write throughout the tutorial. You will also create your styles.css file and add styles that set the layout of the content.

      To begin, open the index.html file in your text editor. Then, add the following HTML to the file:

      index.html

      <!doctype html>
      <html>
        <head>
          <meta charset="utf-8" />
          <title>Destination: Moon</title>
          <link rel="preconnect" href="https://fonts.googleapis.com"> 
          <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 
          <link href="https://fonts.googleapis.com/css2?family=MuseoModerno:wght@400;700&display=swap" rel="stylesheet">
          <link href="styles.css" rel="stylesheet" />
        </head>
        <body>
        </body>
      </html>
      

      Several page settings are defined inside the <head> element of the HTML. The <meta> element defines the character set to use for the text, which tells the browser how to interpret special characters in the code without using HTML character codes. The <title> element provides the browser with the title of the page. The <link> elements load in the page styles. The first three load in the font, Museo Moderno, from Google Fonts, and the last one loads the styles you will add to styles.css.

      Next, the page will need content to style. You will use sample content from Sagan Ipsum as filler copy to use with the styles. You will also apply HTML for a site header, containing the site name and a small navigation bar. Return to index.html in your text editor and add the highlighted HTML from the following code block:

      index.html

      <!doctype html>
      <html>
        <head>
          ...
        </head>
        <body>
          <header class="site-header">
            <h1 class="site-name">Destination: <strong>Moon</strong></h1>
            <nav>
              <ul class="nav-list">
                <li><a href="#" class="nav-link">Base Station</a></li>
                <li><a href="#" class="nav-link">Travel Packages</a></li>
                <li><a href="#" class="nav-link">Accommodations</a></li>
                <li><a href="#" class="nav-link">Plan Your Trip</a></li>
            </ul>
            </nav>
          </header>
          <main>
            <section>
              <h2>Schedule Your Trip</h2>
              <p>Sea of Tranquility great turbulent clouds with pretty stories for which there's little good evidence extraordinary claims require extraordinary evidence. Citizens of distant epochs rings of Uranus intelligent beings birth take root and flourish across the centuries. Corpus callosum invent the universe as a patch of light the only home we've ever known a mote of dust suspended in a sunbeam made in the interiors of collapsing stars. Kindling the energy hidden in matter Orion's sword.</p>
              <p>Vastness is bearable only through love emerged into consciousness not a sunrise but a galaxyrise emerged into consciousness courage of our questions across the centuries and billions upon billions upon billions upon billions upon billions upon billions upon billions.</p>
                <a href="#" class="button">Read the Disclaimer!</a>
            </section>
          </main>
        </body>
      </html>
      

      Be sure to save your index.html file and leave it open in your text editor. Next, create a new file called styles.css and open it in the text editor. This is the file that is referenced in the <head> element of index.html. In the styles.css file, add the following code:

      styles.css

      body {
        margin: 0;
        font: 100%/1.5 sans-serif;
      }
      
      main {
        margin: 6rem auto;
        width: 75ch;
        font-size: 1.125rem;
      }
      
      h2 {
        font: 400 1.875rem/1.25 MuseoModerno, sans-serif;
        color: #6b2d6b;
      }
      
      .site-header {
        font: 1.125rem / 1.25 MuseoModerno, sans-serif;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 0 2rem;
        color: white;
        background: linear-gradient(135deg, #8e3d8e, #230f23);
      }
      
      .site-name {
        margin: 0;
        font-size: 1.75rem;
        font-weight: 400;
      }
      
      .nav-list {
        margin: 0;
        padding: 0;
        list-style: none;
        display: flex;
        align-items: stretch;
      }
      
      .nav-link {
        color: inherit;
        display: block;
        text-decoration: none;
        padding: 1.25rem 1.5rem;
      }
      
      .nav-link:hover,
      .nav-link:focus {
        color: #230f23;
        background-color: white;
      }
      
      .button {
        text-decoration: none;
        display: inline-block;
        padding: 0.5rem 1.25rem;
        color: white;
        background: #4c90b2;
        border: 1px solid #2d566b;
        border-radius: 0.5rem;
      }
      
      .button:hover,
      .button:focus {
        background-color: #2d566b;
      }
      

      These styles set up the general aesthetic and layout of the page, with the styles applied to the body and main elements. The .site-header, .site-name, .nav-list, and .nav-link selectors all define the styles on the page header. The .button and .button:hover rules change an <a> element to appear like a large, clickable button.

      Save your changes to styles.css, then open a web browser. Select the File menu item and then select the Open option. Next, navigate to your project folder and load your index.html file in the browser. The following image demonstrates how the page will render in the browser:

      Sample website with a purple nav bar, a centered block of copy text, and a blue button with the words

      The CSS you have written so far creates a purple header at the top of the page with a site title and navigation in white text. Below, the content consists of a purple heading and two paragraphs of text. The width of the content is constrained to 75 characters with the max-width: 76ch property value on the main element selector. Lastly, the blue button with the text Read the Disclaimer! is a large, interactive element below the content.

      Throughout this section you set up your HTML in the index.html file and created the base styles in the styles.css file. In the next section, you will use the opacity property to cause a new element to disappear and reappear with the :target pseudo class.

      Creating :target State with opacity to Show and Hide Elements

      A useful application of the opacity property is to cause content to fade in and out on the screen. One instance of such an effect is when a modal, a UI element (also known as a light box) that appears in front of the rest of your page’s content, is transitioned into view. You can create this effect with a combination of the opacity and pointer-events properties and the :target pseudo-class.

      Start by opening index.html to create the contents of the modal. Add the highlighted HTML from the following code block between the </section> and </main> closing tags:

      index.html

      <!doctype html>
      <html>
        <head>
          ...
        </head>
        <body>
          ...
          <main>
            <section>
              ...
            </section>
            <div class="modal-container">
              <section class="modal">
                <header class="modal-header">
                  <h2 class="modal-title">Destination: Moon Disclaimer</h2>
                  <a href="#" class="modal-close">Close</a>
                </header>
                <div class="modal-content">
                  <p><strong>Disclaimer:</strong> Vastness is bearable only through love emerged into consciousness not a sunrise but a galaxyrise emerged into consciousness courage of our questions across the centuries and billions upon billions upon billions upon billions upon billions upon billions upon billions.</p>
                </div>
              </section>
            </div>
          </main>
        </body>
      </html>
      

      Save your changes to index.html, then return to styles.css in your text editor and append the highlighted CSS in the following code block to your file:

      styles.css

      ...
      
      .button:hover {
        background-color: #2d566b;
      }
      
      .modal-container {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 1000;
        background-color: black;
        display: flex;
      }
      
      .modal {
        margin: auto;
        width: 90%;
        max-width: 40rem;
        background-color: white;
        border-radius: 1.5rem;
      }
      
      .modal-header,
      .modal-content {
        padding: 1.5rem;
      }
      

      The .modal-container class defines an area that will cover the full visible space with a fixed element. Then the display: flex on the .modal-container combined with the margin: auto on the .modal selector will center the content to the page both vertically and horizontally.

      Save your changes to styles.css and return to you browser to refresh index.html. The page’s contents are no longer visible as a black overlay has taken over the page with a white container, as rendered in the following image:

      White modal box with a black background covering the rest of the page

      Now that the modal is in place and covering the contents of the page, it needs to be hidden until instantiated. The opacity property is an older property that allows translucency to be placed on an element and its contents. The value of the opacity property can range from 0 to 1, with any decimal point between.

      To begin using the opacity property, return to styles.css in your text editor. On the .modal class selector, add an opacity property with a value of 0, as highlighted in the following code block:

      styles.css

      ...
      .modal-container {
         position: fixed;
         top: 0;
         right: 0;
         bottom: 0;
         left: 0;
         z-index: 1000;
         background-color: black;
         display: flex;
        opacity: 0;
      }
      ...
      

      This will cause the entire modal view to be visually hidden on the screen. The modal should only be visible when it needs to be. To achieve this conditional appearance, you can use the :target pseudo-class.

      After the .modal-container selector, add a new selector for .modal-container:target. Inside the selector block, set another opacity property to a value of 1. The highlighted CSS in the following code block shows how this is formatted:

      styles.css

      ...
      .modal-container {
        ...
        opacity: 0;
      }
      
      .modal-container:target {
        opacity: 1;
      }
      ...
      

      Save these changes to styles.css.

      The :target is instantiated when an element has a URL focus. In web browsers, the id attribute on an HTML element can be referenced in the URL as denoted by the pound or hash symbol (#). In order for the .modal-container:target to activate, the same element needs an id element, and the page needs a way to trigger that URL.

      Return to index.html in your text editor. On the <div class="modal-container"> element, add an id attribute set to the value disclaimer. Then, on the <a href="#" class="button"> element, change the href value from # to #disclaimer. Reference the highlighted HTML in the following code block for how this is written:

      index.html

      <!doctype html>
      <html>
        <head>
          ...
        </head>
        <body>
          ...
          <main>
            <section>
               ...
               <a href="https://www.digitalocean.com/community/tutorials/#disclaimer" class="button">Read the Disclaimer!</a>
             </section>
             <div id="disclaimer" class="modal-container">
               ...
             </div>
          </main>
        </body>
      </html>
      

      The change to the href value tells the browser to go to the element with the id value of disclaimer of the current page. Once the #disclaimer is added to the URL, then the :target in the CSS will activate.

      Save these changes to index.html, then return to styles.css.

      The way the page is structured now, the modal will capture all click and touch events coming from a mouse or touchscreen interaction. This is because, though completely transparent, the modal element is still covering the whole page. In order to remove interactivity from the element, you’ll add a pointer-events property with a value of none to the .modal-container selector. Then, once the modal is visible, it will need to be able to receive interaction events again. On the :target pseudo-class add pointer-events set to all, as highlighted in the following code block:

      styles.css

      ...
      .modal-container {
        ...
        opacity: 0;
        pointer-events: none;
      }
      
      .modal-container:target {
        opacity: 1;
        pointer-events: all;
      }
      ...
      

      The pointer-events property changes how an element interacts with a mouse or touch-based input device. By setting the value to none, the element becomes invisible not only to sighted users, but to pointer-based input devices as well. Then, the all value reinstates the interaction, but only when the .modal-container is specified in the URL to be active and visible.

      Lastly, to cause the modal to fade in and out of view, you’ll add a transition property to animate between 0 and 1 values for opacity.

      Return to styles.css and add a transition property to the .modal-container selector, as highlighted in the following code block:

      styles.css

      ...
      .modal-container {
        ...
        opacity: 0;
        pointer-events: none;
        transition: opacity 250ms ease;
      }
      
      .modal-container:target {
        opacity: 1;
        pointer-events: all;
      }
      ...
      

      The transition property is shorthand for a series of properties. The opacity tells the browser that this is the property to transition between. The 250ms is the time the transition should take to complete, with the unit standing for milliseconds. Finally, the ease describes how the transition will occur. In this case, ease means it will start slow, speed up, and then slow down again near the end of the transition.

      The transition will work when the modal appears and disappears by pressing the Close link inside the modal. This Close link has an href value set to #, which will change the URL from #disclaimer to #, removing the :target state.

      Save your changes to styles.css and refresh index.html in the browser. The following animation illustrates how the modal will appear and disappear:

      Animation of the modal appearing in a smooth transition, then disappearing with a smooth transition.

      This section introduced you to the opacity property, which you used to visually hide a modal container. You also used the :target pseudo-class, pointer-events property, and transition property to create a fade-in and fade-out effect. In the next section, you will use colors with alpha channels to make the modal more translucent.

      Using Alpha Channels to Create Transparent Color Values

      An alpha channel is like the opacity property, but instead is an additional segment for a color value defined via rgb(), hsl(), or hexadecimal. Where the opacity property adjusts the whole element and its children, the alpha channel only adjust the opacity of the color on a given property. Throughout this section, you will use each of the alpha channel color values and put them into practice.

      To begin working with alpha channel color values, open stlyes.css in your text editor. Then go to the .modal-container class selector. Change the background-color property’s value from #000 to rgba(0, 0, 0, 0.75), as highlighted in the following code block:

      styles.css

      ...
      .modal-container {
        ...
        background-color: rgba(0,0,0,0.75);
        ...
      }
      ...
      

      The rgba() color value works like the rgb(), containing three comma-separated numbers that indicate a level of red, green, and blue light. When one of the color values is set to 0, it is completely off (black), and 255 means it is at full brightness (white). Between these three color channels, millions of colors can be produced. The fourth number is the alpha channel, which works like the opacity property and is a decimal point value from 0 to 1. An adjustment to the alpha channel causes the color to become transparent, allowing the content behind it to become visible through the color.

      Save your changes to styles.css and open index.html in a web browser. Press the Read the Disclaimer! button so the modal activates. The black overlay background is still black, but is now also transparent, revealing the page behind it:

      The disclaimer modal, with the rest of the page visible behind a translucent black background.

      Now that the overlay is transparent, turn to the modal and give it more visual styling by changing the background to a purple gradient with white text. Return to styles.css in your text editor and add the following highlighted CSS from the next code block:

      styles.css

      ...
      .modal {
        margin: auto;
        width: 90%;
        max-width: 40rem;
        background: linear-gradient(135deg, hsl(300, 40%, 20%),hsl(300, 40%, 5%));
        border-radius: 1.5rem;
      }
      
      .modal-header,
      .modal-content {
        padding: 1.5rem;
      }
      
      .modal-header {
        display: flex;
        justify-content: space-between;
      }
      
      .modal-title {
        margin: 0;
        color: white;
      }
      
      .modal-close {
        color: white;
      }
      
      .modal-content {
        color: white;
      }
      
      

      Save this update to styles.css, then refresh index.html in your browser. The style of the modal will update and render as illustrated in the following image:

      The modal with a gradient background between purple and black and white lettering.

      Now, return to styles.css in your text editor. You will now use the hsla() color value to lighten the color of the modal header. You will also need to set the top corners to have a border-radius value that matches the modal, so the header doesn’t appear outside the modal area. The highlighted CSS in the following code block demonstrate how to set this up:

      styles.css

      ...
      .modal-header {
        display: flex;
        justify-content: space-between;
        background-color: hsla(300, 80%, 90%, 0.2);
        border-radius: 1.5rem 1.5rem 0 0;
      }
      ...
      

      The background-color value uses the hsla() format, and like the rgba() value, it is the hsl() format but with an alpha channel. The hsl() consists of three parts: a degree value from the color wheel, a saturation percent value, and a lightness percent value, which generates a final color. The 300 places the color value between blue and red on the color wheel, the 80% is a heavy saturation meaning more color and less gray, and the 90% lightens the final color. Lastly, the alpha channel works the same as the opacity property, and 0.2 sets the value closer to fully transparent. This will create a lightened overlay on top of the gradient, providing definition to the header.

      Save these changes to styles.css and return to the browser to refresh index.html. The header of the modal now has a pinker highlight to the area, distinguishing it from the content of the modal. The following image shows how the modal header is now rendered in the browser:

      Modal with header brightened to distinguish it from the modal content.

      Another way to create transparent color values is with hexadecimal values. Hexadecimal color values consist of three pairs of a combination of 0 to 9 or a to f and equate to a number ranging from 0 to 255. The first three digits are a red, green, and blue value, formatted as #RRGGBB. To create an alpha channel, a fourth set is added, making the pattern #RRGGBBAA.

      To begin working with hexadecimal alpha channels, return to styles.css in your text editor. You will now add a border to the modal’s header and content areas to give it more definition. These borders will use the same hexadecimal value, but will be given different values for the alpha channel. The highlighted CSS from the following code block shows how to write these styles:

      styles.css

      ...
      .modal-header {
        display: flex;
        justify-content: space-between;
        background-color: hsla(300, 80%, 90%, 0.2);
        border-radius: 1.5rem 1.5rem 0 0;
        border: 4px solid #f7baf72f;
        border-bottom: none;
      }
      ...
      .modal-content {
        color: white;
        border-radius: 0 0 1.5rem 1.5rem;
        border: 4px solid #f7baf744;
        border-top: none;
      }
      ...
      

      The header and the content each have the same hexadecimal color with #f7baf7, but they have different alpha channel values. The modal-header selector’s border-color has an alpha channel set to 2f, which is more transparent, since 00 is a fully transparent alpha channel value. The .modal-content has its alpha channel set to 44, which makes it more opaque.

      Save your changes to styles.css and refresh index.html in the web browser. The following image illustrates how these borders are rendered in the browser:

      Modal with added border around the modal container, rendered by adding transparency.

      Lastly, a six-digit hexadecimal color can be written as a three digit shorthand, where #33ccee is the same as #3ce. Likewise, a hexadecimal value with an alpha channel can be written as a four digit shorthand so that #33ccee99 can be shortened to #3ce9 and be the same color.

      To begin working with a shorthand hexadecimal with alpha channel, return to stlyes.css in your text editor. Then, go to the .modal class selector and add a box-shadow property. Here you will create a large drop shadow on the modal, which will be black but softened by an alpha channel. Add the highlighted CSS in the following code block to your .modal selector block:

      styles.css

      ...
      .modal {
        margin: auto;
        width: 90%;
        max-width: 40rem;
        background: linear-gradient(135deg, hsl(300, 40%, 20%),hsl(300, 40%, 5%));
        border-radius: 1.5rem;
        box-shadow: 0 1rem 2rem #000a;
      }
      ...
      

      This shadow drops down the x-axis by 1rem and spreads out the blur 2rem. Next, the #000a value defines a full black color by turning off all three color values. The a, which is equivalent to aa and has a numerical value of 170, provides the alpha channel with approximately a 66% transparency. This dims the shadow slightly but keeps it substantial enough to provide depth below the modal.

      Be sure to save this addition to styles.css, then refresh index.html in the browser. The modal now has much more definition and depth. The following image provides a rendering of the modal with the various color values:

      Modal with added shadow to make it look like it is floating above the page content.

      In this section, you used the three different color values with alpha channels to apply opacity to colors on specific properties. You added these colors to background-color properties, border properties, and a box-shadow property. In the next section, you will use the named color value of transparent to create unique gradients and hide content.

      Adding the transparent Color Value to a linear-gradient

      The various color values that support alpha channels are helpful for when a color still needs to be identifiable. However, when no color is needed, the transparent named color becomes useful. In this section, you will hide the Close button in the modal and create an X shape with a linear-gradient(), all with the use of the transparent value.

      To start using the transparent value, open styles.css in your text editor. Then, go to the .modal-close class selector that you added earlier. Inside the selector, change the color property value from white to transparent, as highlighted in the following code block:

      styles.css

      ...
      .modal-close {
        color: transparent;
      }
      ...
      

      This change will not remove the text from the space; it will only remove it from visually rendering on the page.

      Next, you will create a square out of the close link so there is a place to create the X shape. Start by adding a display property set to block, which allows the <a> to be more visually configurable. Next, create a height and width property and set each to 1.5rem, which creates the square shape. Finally, add an overflow property set to hidden, which will prevent text from going outside the container and adding interactive space. The highlighted CSS from the following code block shows how to set up the square:

      styles.css

      ...
      .modal-close {
        color: transparent;
        display: block;
        height: 1.5rem;
        width: 1.5rem;
        overflow: hidden;
      }
      ...
      

      The last part is to create the X shape with a multiple-background instance consisting of two linear-gradient() values. To set this up, add the highlighted code from the following code block:

      styles.css

      ...
      .modal-close {
        color: transparent;
        display: block;
        height: 1.5rem;
        width: 1.5rem;
        overflow: hidden;
        background-image:
          linear-gradient(
            to top right,
            transparent 48%,
            white 48%,
            white 52%,
            transparent 52%
          ),
          linear-gradient(
            to top left,
            transparent 48%,
            white 48%,
            white 52%,
            transparent 52%
          );
      }
      ...
      

      The first thing to note about this code is that the different segments of the linear-gradient() are on separate lines, which is done to help make the complex background more comprehensible and legible. This CSS is still valid, and it is not required that values are on the same line as the property. Next, the duplicated percent values for transparent and white mean there will be no gradation. Instead the color will flip immediately from transparent to white. Lastly, the to the right and to the top makes two gradients on 45 degree angles that overlap.

      Save this change to styles.css and open index.html in a web browser. Select the Read the Disclaimer! button and the modal will now have a large, thin X shape instead of a close link, as rendered in the following image:

      Modal box with the 'Close' button replaced with a thin white X.

      Lastly, a :hover and :focus state is needed to help make the X shape more noticeable when it is the center of interaction. For this, you will duplicate the previous gradients and adjust the position to grow the solid white area.

      To create an interactive state for the X, return to styles.css in your text editor. Following the .modal-close class selector, create a new group selector consisting of .modal-close:hover and .modal-close:focus. Then, duplicate the background-image property and value from .modal-close into the new selector. Lastly, decrease the 48% segments to 46% and increase the 52% to 54%.

      style.css

      ...
      .modal-close {
        ...
      }
      
      .modal-close:hover,
      .modal-close:focus {
        background-image:
          linear-gradient(
            to top right,
            transparent 46%,
            white 46%,
            white 54%,
            transparent 54%
          ),
          linear-gradient(
            to top left,
            transparent 46%,
            white 46%,
            white 54%,
            transparent 54%
          );
      }
      ...
      

      Save these changes to styles.css and refresh the page in your browser. Now, as the X is hovered over or given keyboard focus, the size of the gradients creating the shape will increase, giving an effect as though the X is bolded. The following animation demonstrates how this effect is rendered in a browser during a hover event:

      Animation of the cursor hovering over the X button. The lines of the X become visibly wider.

      This section introduced you to the transparent property, and you used it to hide content and create an X icon using linear-gradient() values. In the last section, you will use the transparent value on a gradient to help provide an animation effect on a button-styled element.

      Using the :hover State to Transition Between Transparent color Values

      One aspect of the transition property that sometimes requires creative solutions is that not all properties can be animated. One of these properties is the background-image, meaning the values of a linear-gradient on this property cannot animate. However, the background-color value can animate even if a background-image is present. In this section, you will create a transition animation that appears to be a gradient animation with the use of transparent and alpha channel color values.

      To create an animated gradient, open styles.css in your text editor. Then go to the .button class selector. The .button class already has a change in the background-color between its selector and the .button:hover. Add the highlighted transition property and value from the following code block to your styles.css file:

      styles.css

      ...
      .button { 
        text-decoration: none;
        display: inline-block;
        padding: 0.5rem 1.25rem;
        color: white;
        background: #4c90b2;
        border: 1px solid #2d566b;
        border-radius: 0.5rem;
        transition: background-color 250ms ease;
      }
      
      .button:hover,
      .button:focus {
        background-color: #2d566b;
      }
      ...
      

      Save your changes to styles.css and open index.html in you web browser. Hovering the button with a cursor will now cause the background-color to animate between light blue and dark blue. The following animation shows how this is rendered in the browser:

      An animation of the cursor hovering over the

      Now, to add the gradient, go back to styles.css in your text editor. Return to the .button selector and add a background-image property with a linear-gradient(). The direction of the gradient will be to bottom and will start with an alpha channel light blue then go to transparent. The animation will end with an alpha channel dark blue. The highlighted CSS in the following code block demonstrates how to write this gradient:

      styles.css

      ...
      .button { 
        text-decoration: none;
        display: inline-block;
        padding: 0.5rem 1.25rem;
        color: white;
        background: #4c90b2;
        border: 1px solid #2d566b;
        border-radius: 0.5rem;
        background-image:
          linear-gradient(
            to bottom,
            hsla(200, 40%, 80%, 0.4),
            transparent,
            hsla(200, 40%, 20%, 0.6)
          );
        transition: background-color 250ms ease;
      }
      ...
      

      This gradient overlays the background-color, giving the appearance that the gradient is passing from a light blue to a middle blue and then a dark blue. When the button encounters an interactive event, the background-color changes to a darker blue, creating an illusion that the overall gradient darkened.

      Save these updates to styles.css and then return to your index.html file in the browser and refresh the page. As shown in the following animation, as the cursor hovers the button the gradient appears to animate from a light blue gradient to a dark blue gradient:

      An animation of the cursor hovering over the

      In this last section, you used color values, the transparent named value, and the transition property to create an illusion of a gradient animating. Due to this, you also learned what kinds of properties can be animated.

      Conclusion

      Throughout this tutorial, you used the opacity property and many color values with alpha channels. You used opacity with pointer-events and transition to create a fade-in effect to display a modal on demand. You also used the various color values available to control the transparency and overlay of color on the content. You used the transparent named value to create an X icon to close the modal. Finally, you used a combination of colors, transparent value, gradients, and transition to create the illusion of an animated gradient on a button.

      There are many useful reasons to employ opacity and transparent colors to a web design. The opacity property can be used to animate a fade-in and fade-out effect on elements that are meant to be visible when needed. The various color values that allow for transparency control provide many ways to blend colors and content together. Altogether, these methods of creating transparency can create many unique effects and styles.

      If you would like to read more CSS tutorials, try out the other tutorials in the How To Style HTML with CSS series.



      Source link

      How To Use and Validate Web Forms with Flask-WTF


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Web forms, such as text fields and text areas, give users the ability to send data to your application, whether that’s a drop-down or a radio button that the application will use to perform an action, or to send large areas of text to be processed or displayed. For example, in a social media application, you might give users a box where they can add new content to their pages.

      Flask is a lightweight Python web framework that provides useful tools and features for creating web applications in the Python Language. To render and validate web forms in a safe and flexible way in Flask, you’ll use Flask-WTF, which is a Flask extension that helps you use the WTForms library in your Flask application.

      WTForms is a Python library that provides flexible web form rendering. You can use it to render text fields, text areas, password fields, radio buttons, and others. WTForms also provides powerful data validation using different validators, which validate that the data the user submits meets certain criteria you define. For example, if you have a required field, you can ensure data the user submits is provided, or has a certain length.

      WTForms also uses a CSRF token to provide protection from CSRF attacks, which are attacks that allows the attacker to execute unwanted actions on a web application in which the user is authenticated. A successful CSRF attack can force the user to perform state-changing requests like transferring funds to the attacker’s bank account in a banking application, changing the user’s email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.

      In this tutorial, you’ll build a small web application that demonstrates how to render and validate web forms using Flask-WTF. The application will have a page for displaying courses that are stored in a Python list, and the index page will have a form for entering the course title, its description, price, availability, and level (beginner, intermediate, or advanced).

      Prerequisites

      Step 1 — Installing Flask and Flask-WTF

      In this step, you’ll install Flask and Flask-WTF, which also installs the WTForms library automatically.

      With your virtual environment activated, use pip to install Flask and Flask-WTF:

      • pip install Flask Flask-WTF

      Once the installation is successfully finished, you’ll see a line similar to the following at the end of the output:

      Output

      Successfully installed Flask-2.0.2 Flask-WTF-1.0.0 Jinja2-3.0.3 MarkupSafe-2.0.1 WTForms-3.0.0 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1

      As you can see, the WTForms library was also installed as a dependency of the Flask-WTF package. The rest of the packages are Flask dependencies.

      Now that you’ve installed the required Python packages, you’ll set up a web form next.

      Step 2 — Setting up Forms

      In this step, you’ll set up a web form using fields and validators you’ll import from the WTForms library.

      You’ll set up the following fields:

      • Title: A text input field for the course title.
      • Description: A text area field for the course description.
      • Price: An integer field for the price of the course.
      • Level: A radio field for the course level with three choices: Beginner, Intermediate, and Advanced.
      • Available: A checkbox field that indicates whether the course is currently available.

      First, open a new file called forms.py in your flask_app directory. This file will have the forms you’ll need in your application:

      This file will have a class that represents your web form. Add the following imports at the top:

      flask_app/forms.py

      from flask_wtf import FlaskForm
      from wtforms import (StringField, TextAreaField, IntegerField, BooleanField,
                           RadioField)
      from wtforms.validators import InputRequired, Length
      
      

      To build a web form, you will create a subclass of the FlaskForm base class, which you import from the flask_wtf package. You also need to specify the fields you use in your form, which you will import from the wtforms package.

      You import the following fields from the WTForms library:

      In the line from wtforms.validators import InputRequired, Length, you import validators to use on the fields to make sure the user submits valid data. InputRequired is a validator you’ll use to ensure the input is provided, and Length is for validating the length of a string to ensure it has a minimum number of characters, or that it doesn’t exceed a certain length.

      Next, add the following class after the import statements:

      flask_app/forms.py

      
      class CourseForm(FlaskForm):
          title = StringField('Title', validators=[InputRequired(),
                                                   Length(min=10, max=100)])
          description = TextAreaField('Course Description',
                                      validators=[InputRequired(),
                                                  Length(max=200)])
          price = IntegerField('Price', validators=[InputRequired()])
          level = RadioField('Level',
                             choices=['Beginner', 'Intermediate', 'Advanced'],
                             validators=[InputRequired()])
          available = BooleanField('Available', default="checked")
      

      Save and close the file.

      In this CourseForm class, you inherit from the FlaskForm base class you imported earlier. You define a collection of form fields as class variables using the form fields you imported from the WTForms library. When you instantiate a field, the first argument is the field’s label.

      You define the validators for each field by passing a list of the validators you import from the wtforms.validators module. The title field, for example, has the string 'Title' as a label, and two validators:

      • InputRequired: To indicate that the field should not be empty.
      • Length: Takes two arguments; min is set to 10 to make sure that the title is at least 10 characters long, and max is set to 100 to ensure it doesn’t exceed 100 characters.

      The description text area field has an InputRequired validator and a Length validator with the max parameter set to 200, with no value for the min parameter, which means the only requirement is that it doesn’t exceed 200 characters.

      Similarly you define a required integer field for the price of the course called price.

      The level field is a radio field with multiple choices. You define the choices in a Python list and pass it to the choices parameter. You also define the field as required using the InputRequired validator.

      The available field is a check box field. You set a default 'checked' value by passing it to the default parameter. This means the check box will be checked when adding new courses unless the user unchecks it, meaning courses are available by default.

      For more on how to use the WTForms library, see the Crash Course page on the WTForms documentation. See the Fields page for more fields, and the Validators page for more validators to validate form data.

      You’ve configured your web form in a forms.py file. Next, you’ll create a Flask application, import this form, and display its fields on the index page. You’ll also display a list of courses on another page.

      Step 3 — Displaying the Web Form and Courses

      In this step, you’ll create a Flask application, display the web form you created in the previous step on the index page, and also create a list of courses and a page for displaying the courses on it.

      With your programming environment activated and Flask installed, open a file called app.py for editing inside your flask_app directory:

      This file will import the necessary class and helpers from Flask, and the CourseForm from the forms.py file. You’ll build a list of courses, then instantiate the form and pass it to a template file. Add the following code to app.py:

      flask_app/app.py

      from flask import Flask, render_template, redirect, url_for
      from forms import CourseForm
      
      app = Flask(__name__)
      app.config['SECRET_KEY'] = "https://www.digitalocean.com/community/tutorials/your secret key'
      
      
      courses_list = [{
          'title': 'Python 101',
          'description': 'Learn Python basics',
          'price': 34,
          'available': True,
          'level': 'Beginner'
          }]
      
      
      @app.route('/', methods=('GET', 'POST'))
      def index():
          form = CourseForm()
          return render_template('index.html', form=form)
      

      Save and close the file.

      Here you import the following from Flask:

      • The Flask class to create a Flask application instance.
      • The render_template() function to render the index template.
      • The redirect() function to redirect the user to the courses page once a new course is added.
      • The url_for() function for building URLs.

      First you import the CourseForm() class from the forms.py file, then you create a Flask application instance called app.

      You set up a secret key configuration for WTForms to use when generating a CSRF token to secure your web forms. The secret key should be a long random string. See Step 3 of How To Use Web Forms in a Flask Application for more information on how to obtain a secret key.

      Then you create a list of dictionaries called courses_list, which currently has one dictionary with a sample course titled 'Python 101'. Here, you use a Python list as a data store for demonstration purposes. In a real world scenario, you’ll use a database such as SQLite. See How To Use an SQLite Database in a Flask Application to learn how to use a database to store your courses’ data.

      You create a / main route using the app.route() decorator on the index() view function. It accepts both GET and POST HTTP methods in the methods parameter. GET methods are for retrieving data, and POST requests are for sending data to the server, through a web form for example. For more, see How To Use Web Forms in a Flask Application.

      You instantiate the CourseForm() class that represents the web form and save the instance in a variable called form. You then return a call to the render_template() function, passing it a template file called index.html and the form instance.

      To display the web form on the index page, you will first create a base template, which will have all the basic HTML code other templates will also use to avoid code repetition. Then you’ll create the index.html template file you rendered in your index() function. To learn more about templates, see How to Use Templates in a Flask Application.

      Create a templates folder in your flask_app directory where Flask searches for templates, then open a template file called base.html, which will be the base template for other templates:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside the base.html file to create the base template with a navbar and a content block:

      flask_app/templates/base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %} - FlaskApp</title>
          <style>
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for('index') }}">FlaskApp</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      This base template has all the HTML boilerplate you’ll need to reuse in your other templates. The title block will be replaced to set a title for each page, and the content block will be replaced with the content of each page. The navigation bar has two links, one for the index page where you use the url_for() helper function to link to the index() view function, and the other for an About page if you choose to include one in your application.

      Save and close the file.

      Next, open a template called index.html. This is the template you referenced in the app.py file:

      • nano templates/index.html

      This file will have the web form you passed to the index.html template via the form variable. Add the following code to it:

      flask_app/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Add a New Course {% endblock %}</h1>
      
          <form method="POST" action="/">
              {{ form.csrf_token }}
              <p>
                  {{ form.title.label }}
                  {{ form.title(size=20) }}
              </p>
      
              {% if form.title.errors %}
                  <ul class="errors">
                      {% for error in form.title.errors %}
                          <li>{{ error }}</li>
                      {% endfor %}
                  </ul>
              {% endif %}
      
              <p>
                  {{ form.description.label }}
              </p>
              {{ form.description(rows=10, cols=50) }}
      
              {% if form.description.errors %}
                  <ul class="errors">
                      {% for error in form.description.errors %}
                          <li>{{ error }}</li>
                      {% endfor %}
                  </ul>
              {% endif %}
      
              <p>
                  {{ form.price.label }}
                  {{ form.price() }}
              </p>
      
              {% if form.price.errors %}
                  <ul class="errors">
                      {% for error in form.price.errors %}
                          <li>{{ error }}</li>
                      {% endfor %}
                  </ul>
              {% endif %}
      
              <p>
                  {{ form.available() }} {{ form.available.label }}
              </p>
      
              {% if form.available.errors %}
                  <ul class="errors">
                      {% for error in form.available.errors %}
                          <li>{{ error }}</li>
                      {% endfor %}
                  </ul>
              {% endif %}
      
              <p>
                  {{ form.level.label }}
                  {{ form.level() }}
              </p>
      
              {% if form.level.errors %}
                  <ul class="errors">
                      {% for error in form.level.errors %}
                          <li>{{ error }}</li>
                      {% endfor %}
                  </ul>
              {% endif %}
      
              <p>
                  <input type="submit" value="Add">
              </p>
          </form>
      
      {% endblock %}
      

      Save and close the file.

      You extend the base template, and set a title in an <h1> tag. Then you render the web form fields inside a <form> tag, setting its method to POST and the action to the / main route, which is the index page. You first render the CSRF token WTForms uses to protect your form from CSRF attacks using the line {{ form.csrf_token }}. This token gets sent to the server with the rest of the form data. Remember to always render this token to secure your forms.

      You render each field using the syntax form.field() and you render its label using the syntax form.field.label. You can pass arguments to the field to control how it is displayed. For example, you set the size of the title input field in {{ form.title(size=20) }}, and you set the numbers of rows and columns for the description text area via the parameters rows and cols the same way you would do normally in HTML. You can use the same method to pass additional HTML attributes to a field such as the class attribute to set a CSS class.

      You check for validation errors using the syntax if form.field.errors. If a field has errors, you loop through them with a for loop and display them in a list below the field.

      While in your flask_app directory with your virtual environment activated, tell Flask about the application (app.py in this case) using the FLASK_APP environment variable. Then set the FLASK_ENV environment variable to development to run the application in development mode and get access to the debugger. For more information about the Flask debugger, see How To Handle Errors in a Flask Application. Use the following commands to do this (on Windows, use set instead of export):

      • export FLASK_APP=app
      • export FLASK_ENV=development

      Next, run the application:

      With the development server running, visit the following URL using your browser:

      http://127.0.0.1:5000/
      

      You’ll see the web form displayed on the index page:

      Index Page

      Try to submit the form without filling in the title. You’ll see an error message informing you that the title is required. Experiment with the form by submitting invalid data (such as a short title less than 10 characters long, or a description over 200 characters long) to see other error messages.

      Filling the form with valid data does nothing so far because you don’t have code that handles form submission. You’ll add the code for that later.

      For now, you need a page to display the courses you have in your list. Later, handling the web form data will add a new course to the list and redirect the user to the courses page to see the new course added to it.

      Leave the development server running and open another terminal window.

      Next, open app.py to add the courses route:

      Add the following route at the end of the file:

      flask_app/app.py

      # ...
      
      @app.route('/courses/')
      def courses():
          return render_template('courses.html', courses_list=courses_list)
      

      Save and close the file.

      This route renders a template called courses.html, passing it the courses_list list.

      Then create the courses.html template to display courses:

      • nano templates/courses.html

      Add the following code to it:

      flask_app/templates/courses.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Courses {% endblock %}</h1>
          <hr>
          {% for course in courses_list %}
              <h2> {{ course['title'] }} </h2>
              <h4> {{ course['description'] }} </h4>
              <p> {{ course['price'] }}$ </p>
              <p><i>({{ course['level'] }})</i></p>
              <p>Availability:
                  {% if course['available'] %}
                      Available
                  {% else %}
                      Not Available
                  {% endif %}</p>
              <hr>
          {% endfor %}
      {% endblock %}
      

      Save and close the file.

      You set a title and loop through the items of the courses_list list. You display the title in an <h2> tag, the description in an <h4> tag, and the price and course level in a <p> tag.
      You check whether the course is available using the condition if course['available']. You display the text “Available” if the course is available, and the text “Not Available” if it’s not available.

      Use your browser to go to the courses page:

      http://127.0.0.1:5000/courses/
      

      You’ll see a page with one course displayed, because you only have one course in your course list so far:

      Courses Page

      Next, open base.html to add a link to the courses page in the navigation bar:

      Edit it to look as follows:

      flask_app/templates/base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>{% block title %} {% endblock %} - FlaskApp</title>
          <style>
              nav a {
                  color: #d64161;
                  font-size: 3em;
                  margin-left: 50px;
                  text-decoration: none;
              }
          </style>
      </head>
      <body>
          <nav>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("index') }}">FlaskApp</a>
              <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("courses') }}">Courses</a>
              <a href="#">About</a>
          </nav>
          <hr>
          <div class="content">
              {% block content %} {% endblock %}
          </div>
      </body>
      </html>
      

      Save and close the file.

      Refresh the index page, and you’ll see a new Courses link in the navigation bar.

      You’ve created the pages you need for your application: An index page with a web form for adding new courses and a page for displaying the courses you have in your list.

      To make the application functional, you need to handle the web form data when the user submits it by validating it and adding it to the courses list. You’ll do this next.

      Step 4 — Accessing Form Data

      In this step, you’ll access data the user submits, validate it, and add it to the list of courses.

      Open app.py to add code for handling the web form data inside the index() function:

      Edit the index() function to look as follows:

      flask_app/app.py

      # ...
      @app.route('/', methods=('GET', 'POST'))
      def index():
          form = CourseForm()
          if form.validate_on_submit():
              courses_list.append({'title': form.title.data,
                                   'description': form.description.data,
                                   'price': form.price.data,
                                   'available': form.available.data,
                                   'level': form.level.data
                                   })
              return redirect(url_for('courses'))
          return render_template('index.html', form=form)
      

      Save and close the file.
      Here, you call the validate_on_submit() method on the form object, which checks that the request is a POST request, and runs the validators you configured for each field. If at least one validator returns an error, the condition will be False, and each error will be displayed below the field that caused it.

      If the submitted form data is valid, the condition is True, and the code below the if statement will be executed. You build a course dictionary, and use the append method to add the new course to the courses_list list. You access the value of each field using the syntax form.field.data. After you add the new course dictionary to the courses list, you redirect the user to the Courses page.

      With the development server running, visit the index page:

      http://127.0.0.1:5000/
      

      Fill the form with valid data and submit it. You’ll be redirected to the Courses page, and you’ll see the new course displayed on it.

      Conclusion

      You made a Flask application that has a web form you built using the Flask-WTF extension and the WTForms library. The form has several types of fields to receive data from the user, validate it using special WTForms validators, and add it to a data store.

      If you would like to read more about Flask, check out the other tutorials in the How To Create Web Sites with Flask series.



      Source link