'Why is the database of the webapp in the book "Head First Python 2nd Edition" not working as it should?

I'm currently studying Python by reading the book "Head First Python 2nd Edition" and it is really helpful and well-written, but I think it is a little anachronistic (since it was released on December 16, 2016, I think that various things changed since then).

The problem is: I have just finished writing (for what I had to do so far) the webapp vsearch4web.py, and it looks like this:

from flask import Flask, render_template, request, escape
from vsearch import search4letters

from DBcm import UseDatabase

app = Flask(__name__)


app.config['dbconfig'] = {'host': '127.0.0.1',
                          'user': 'vsearch',
                          'password': 'vsearchpasswd',
                          'database': 'vsearchlogDB', }


def log_request(req: 'flask_request', res: str) -> None:
    """Log details of the web request and the results"""

    with UseDatabase(app.config['dbconfig']) as cursor:
        _SQL = """insert into log
              (phrase, letters, ip, browser_string, results) 
              values
              (%s, %s, %s, %s, %s)"""
        cursor.execute(_SQL, (req.form['phrase'],
                              req.form['letters'],
                              req.remote_addr,
                              req.user_agent.browser,
                              res, ))
    
@app.route('/search4', methods=['POST'])
def do_search() -> 'html':
    phrase = request.form['phrase']
    letters = request.form['letters']
    title = 'Here are your results:'
    results = ''.join(search4letters(phrase, letters))
    log_request(request, results)
    return render_template('results.html',
                           the_title=title,
                           the_phrase=phrase,
                           the_letters=letters,
                           the_results=results,)


@app.route('/')
@app.route('/entry')
def entry_page() -> 'html':
    return render_template('entry.html',
                           the_title='Welcome to search4letters on the web!')


@app.route('/viewlog')
def view_the_log() -> 'html':
    with UseDatabase(app.config['dbconfig']) as cursor:
        _SQL = """Select phrase, letters, ip, browser_string, results
                  from log"""
        cursor.execute(_SQL)
        contents = cursor.fetchall()
    titles = ('Phrase', 'Letters', 'Remote_addr', 'User_agent', 'Results')
    return render_template('viewlog.html',
                           the_title='View Log',
                           the_row_titles=titles,
                           the_data=contents,)


if __name__ == '__main__':
    app.run(debug=True)

This is the class I used in the code:

import mysql.connector

class UseDatabase:

    def __init__(self, config: dict) -> None:
        self.configuration = config

    def __enter__(self) -> 'cursor':
        self.conn = mysql.connector.connect(**self.configuration)
        self.cursor = self.conn.cursor()
        return self.cursor

    def __exit__(self, exc_type, exc_value, exc_trace) -> None:
        self.conn.commit()
        self.cursor.close()
        self.conn.close()

Then I created the database "vsearchlogDB" in the MySQL console, and afterwards (logged in as the user "vsearch"), I created the table "log" (typing exactly what was written on the book, even if the resulting table is slightly different, this is why I said before that this book may be a little anachronistic):

table log

(This is the table that is shown in the book):

table log of the book

Now when I run my webapp locally and try it out, this error comes out:

mysql.connector.errors.IntegrityError: 1048 (23000): Column 'browser_string' cannot be null

Someone can please explain why the code is not able to extract the value of the browser_string?

I tried to re-create the table from scratch and to put the browser_string column null, and in fact in the column of browser_string (in the /viewlog page) it always displays None (even though I think that this is a useless test, that because I do not know how to use MySQL), but it should not be like this, can someone explain?

Here I also add the HTML and CSS codes of (all) of the pages of the webapp (sorry for all the code, but I really can't figure out where the problem is):

base.html:

<!doctype html>

<html>

    <head>
    
        <title>{{ the_title }}</title>
        
        <link rel="stylesheet" href="static/hf.css" />
        
    </head>
    
    <body>
    
        {% block body %}
        

        {% endblock %}
    </body>
    
</html>

entry.html:

{% extends 'base.html' %}

{% block body %}

<h2>{{ the_title }}</h2>

<form method='POST' action='/search4'>
<table>
<p>Use this form to submit a search request:</p>
<tr><td>Phrase:</td><td><input name='phrase' type='TEXT' width='60'></td></tr>
<tr><td>Letters:</td><td><input name='letters' type='TEXT' value='aeiou'></td></tr>
</table>
<p>When you're ready, click this button:</p>
<p><input value="Do it!" type="submit"></p>
</form>

{% endblock %}

results.html:

{% extends 'base.html' %}

{% block body %}

<h2>{{ the_title }}</h2>

<p>You submitted the following data:</p>
<table>
<tr><td>Phrase:</td><td>{{ the_phrase }}</td></tr>
<tr><td>Letters:</td><td>{{ the_letters }}</td></tr>
</table>

<p>When "{{ the_phrase }}" is searched for "{{ the_letters }}", the following 
results are returned:</p>
<h3>{{ the_results }}</h3>

{% endblock %}

viewlog.html:

{% extends 'base.html' %}

{% block body %}

<h2>{{ the_title }}</h2>

<table>
    <tr>
        {% for row_title in the_row_titles %}
            <th>{{row_title}}</th>
        {% endfor %}
    </tr>
    {% for log_row in the_data %}
        <tr>
            {% for item in log_row %}
                <td>{{item}}</td>
            {% endfor %}
        </tr>
    {% endfor %}
</table>

{% endblock %}

hf.css:

body {
    font-family:      Verdana, Geneva, Arial, sans-serif;
    font-size:        medium;
    background-color: tan;
    margin-top:       5%;
    margin-bottom:    5%;
    margin-left:      10%;
    margin-right:     10%;
    border:           1px dotted gray;
    padding:          10px 10px 10px 10px;
  }
  a {
    text-decoration:  none; 
    font-weight:      600; 
  }
  a:hover {
    text-decoration:  underline;
  }
  a img {
    border:           0;
  }
  h2 {
    font-size:        150%;
  }
  table {
    margin-left:      20px;
    margin-right:     20px;
    caption-side:     bottom;
    border-collapse:  collapse;
  }
  td, th {
    padding:          5px;
    text-align:       left;
  }
  .copyright {
    font-size:        75%;
    font-style:       italic;
  }
  .slogan {
    font-size:        75%;
    font-style:       italic;
  }
  .confirmentry {
    font-weight:      600; 
  }
  
  /*** Tables ***/
  
  table {
  font-size:          1em;
  background-color:   #fafcff;
  border:             1px solid #909090;
  color:              #2a2a2a;
  padding:            5px 5px 2px;
  border-collapse:    collapse;
  }
  
  td, th {
  border:             thin dotted gray;
  }
  
  /*** Inputs ***/
  input[type=text] {
    font-size:        115%;
    width:            30em;
  }
  input[type=submit] {
    font-size:        125%;
  }
  select {
    font-size:        125%;
  }


Solution 1:[1]

Browser data is not transferred.

Solution:
Change the line req.user_agent.browser to req.headers.get('User-Agent')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 S.B