<jupyter_start><jupyter_text>https://blog.miguelgrinberg.com/ Hello Worldfile `microblog/app/__init__.py````Pythonfrom flask import Flaskapp = Flask(__name__)from app import routes```* `__name__`For all practical purposes, passing __name__ is almost always going to configure Flask in the correct way.* `app` package* `app` variable: an instance of class Flask. a member of the *app* package`import routes` __bottom import__ is a workaround to _circular imports_`routes` are the different URLs that the application implements._view function_: handler for the application routes`app/routes.py````Pythonfrom app import app@app.route('/')@app.route('/index')def index():    return "Hello, World!"```* Decorators: A common pattern with decorators is to use them to register functions as callbacks for certain events.* the @app.route decorator creates an association between the URL given as an argument and the function.`microblog.py`: defines the Flask application instance.Run:```$ export FLASK_APP=microblog.py$ flask run --host=0.0.0.0``` Templates - Jinja2Templates help achieve this separation between presentation and business logic. In Flask, templates are written as separate files, stored in a templates folder that is inside the application package. `app/templates/index.html``{{ ... }}` dynamic content```Pythondef index():    user = {'username': 'Steve'}    return render_template('index.html', title='Home', user=user)```* Rendering: the operation that converts a template into a complete HTML page* `render_template()`: takes a template filename and a variable list of template arguments and returns the same template, but with all the placeholders in it replaced with actual values.* `render_template()` invokes the _Jinja2_ template engine Conditional Statements```Html{% if title %}{{ title }} - Microblog{% else %}Welcome to Microblog!{% endif %}``` Loops```Html{% for post in posts %}{{ post.author.username }} says: {{ post.body }}{% endfor %}``` Template Inheritance* Navigation bar -> `base.html`* move the parts of the page layout that are common to all templates to a base template```Html    Microblog: Home        {% block content %}{% endblock %}````block` define the place where the derived templates can insert themselves* the two templates have matching _block_ statements with name _content_`app/template/index.html````Html{% extends "base.html" %}{% block content %}    Hi, {{ user.username }}!    {% for post in posts %}    {{ post.author.username }} says: {{ post.body }}    {% endfor %}{% endblock %}```  Web forms - Flask-WTF Configuration options* most basic: define variables as keys in `app.config````Pythonapp = Flask(__name__)app.config['SECRET_KEY'] = 'you-will-never-guess' ... add more variables here as needed```Enforce the principle of _separation of concerns_ -> separate file* Use a class to store configuration variables* In separate Python module`config.py` in top-level directory```Pythonimport osclass Config(object):    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'```* `SECRET_KEY`: important; cryptographic key, useful to generate signatures or tokens> * value = Environment variable `SECRET_KEY` `or` hardcoded string> * a value sourced from an environment variable is preferred, but if the environment does not define the variable, then the hardcoded string is used instead.`app.config.from_object()` after Flask app instance is created```Pythonfrom flask import Flaskfrom config import Configapp = Flask(__name__)app.config.from_object(Config)from app import routes````config`: name of the Python module `config.py``Config`: actual class User Login FormPython classes -> web forms* defines the field of the form as class variables* `app/forms.py````Pythonclass LoginForm(FlaskForm):    username = StringField('Username', validators=[DataRequired()])    password = PasswordField('Password', validators=[DataRequired()])    remember_me = BooleanField('Remember Me')    submit = SubmitField('Sign in')```* expects a form object instantiated from the LoginForm class to be given as an argument> Snippet for `app/templates/login.html`> ```Html    {{ form.hidden_tag() }}            {{ form.username.label }}        {{ form.username(size=32) }}                {{ form.password.label }}        {{ form.password(size=32) }}        {{ form.remember_me() }} {{ form.remember_me.label }}    {{ form.submit() }}```> * `LoginForm` class> * `action` tell the browser the URL that should be used when submitting the information the user entered in the form>> When the action is set to an empty string the form is submitted to the URL that is currently in the address bar, which is the URL that rendered the form on the page. > * `method` The default is to send it with a GET request, but in almost all cases, using a POST request makes for a better user experience because requests of this type can submit the form data in the body of the request, while GET requests add the form fields to the URL, cluttering the browser address bar.> * `form.hidden_tag()` generates a hidden field <- CSRF attacks>> include this hidden field and have the SECRET_KEY variable defined in the Flask configuration Form Views a view function that renders the template.* mapped to `/login` URL in `apps/routes.py````Python ...from app.forms import LoginForm ...@app.route('/login')def login():    form = LoginForm()    return render_template('login.html', title='Sign In', form=form)```form=form: LHS: template with name _form_, RHS: form object Receiving Form Data_Method Not Allowed_ error: no logic to process data submitted. Browser send a POST request```Pythonfrom flask import render_template, flash, redirect@app.route('/login', methods=['GET', 'POST'])def login():    form = LoginForm()    if form.validate_on_submit():        flash('Login requested for user {}, remember_me={}'.format(            form.username.data, form.remember_me.data))        return redirect('/index')    return render_template('login.html', title='Sign In', form=form)````methods`: this view function accepts GET and POST (Default: GET only)`form.validate_on_submit()`: process form. > if browser sends GET, return False> if browser sends POST, this function gathers all the data, run all the validators attached to fields then return True> `redirect()` auto navigate to a page> `flash()` show a message to user>  flashed messages will NOT magically appear in web pages. Templates need to render these flashed message (`base.html`)Extending `base.html````Html{% with messages = get_flashed_messages() %}{% if messages %}    {% for message in messages %}    {{ message }}    {% endfor %}{% endif %}{% endwith %}```* using a `with` construct to assign result of `get_flashed_messages()` to `message` var* `get_flashed_messages()` returns a list of all the messages that have been registered with `flash()` previously* once they are requested once through the get_flashed_messages function they are removed from the message list Improving Field Validationadd a meaningful error message next to each field that failed validation.* form validators generated descriptive error messages already -> template to render```Html{% for error in form.username.errors %}[{{ error }}]{% endfor %}``` Generating Links`url_for()` generates URLs using its internal mapping of URLs to view functions.* `url_for('login')` returns `/login`* base template: `Home`* `login()` view function: `return redirect(url_for('index'))`  DatabaseFlask does not support db natively: freedom to choose db* relational vs NoSQL> * relational: structured data like lists of users, blog posts> * NoSQL: less defined structure Extension: Flask-SQLAlchemy Configuration* SQLite for development* in config file:```Pythonimport osbasedir = os.path.abspath(os.path.dirname(__file__))class Config(object):     ...    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \        'sqlite:///' + os.path.join(basedir, 'app.db')    SQLALCHEMY_TRACK_MODIFICATIONS = False```> * It is in general a good practice to set configuration from environment variables, and provide a fallback value when the environment does not define the variable.> * `SQLALCHEMY_TRACK_MODIFICATIONS = False` disable signalling the app everytime a change is about to be made in the db.* Create database instance and database migration engine instance```Python ...app = Flask(__name__)app.config.from_object(Config)db = SQLAlchemy(app)migrate = Migrate(app, db)from app import routes, models```> * `db` object represents the database> * `migrate`engine> * _pattern_ in Flask extensions> * importing `models`: define the structure of the databaseWWW SQL Designer tool: http://ondras.zarovi.cz/sql/demo/![users table](Asset/ch04-users[1].png)* `password_hash`: good security practice. `app/models.py````Pythonfrom app import dbclass User(db.Model):    id = db.Column(db.Integer, primary_key=True)    username = db.Column(db.String(64), index=True, unique=True)    email = db.Column(db.String(120), index=True, unique=True)    password_hash = db.Column(db.String(128))    def __repr__(self):        return ''.format(self.username)    ```* `db.Model`: base class for all models from Flask-SQLAlchemy* `db.Column`: fields* `__repr__`: print object of this class. (useful for debugging) Migration Repositorymodel class defines the initial database struecture (schema). Alembic (the migration framework used by Flask-Migrate) make schema changes doesn't require DB recreate. * Alembic maintains a _migration repository_ (stores migration scripts)* `flask db` - added by Flask-Migration to manage everything related to db migrations* `flask db init` new _migration_ directory First DB Migration [User]* auto vs manual* auto: `flask db migrate -m "users table"`> * `-m` adds short text to migration> * generate migration script only> * `upgrade()` and `downgrade()`* `flask db upgrade` apply the changes to the db Upgrade and Downgrade Workflow* Without migrations, need to figure out how to change the schema of db, both in dev and production* With migration, modefy the models then generate a new migration script `flask db migrate`. Review then apply the change `flask db upgrade`. Add to source control. * On production, grab the updated version of the app (includes migration script) then run `flask run upgrade`. Alembic detecs PROD db is not at latest version and run migration scripts.* `flask run downgrade` might be useful during development: downgrade, delete migration script and generate a new one. DB Relationships![ER posts table](Asset/ch04-users-posts[1].png)* one-to-many* `user_id`: FK`app/models.py````Pythonclass User(db.Model):     ...    posts = db.relationship('Post', backref='author', lazy='dynamic')        def __repr__(self):        ...class Post(db.Model):    id = db.Column(db.Integer, primary_key=True)    body = db.Column(db.String(140))    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))                            def __repr__(self):        return ''.format(self.body)```* Pass function `datatime.utcnow` instad of result. Use _UTC dates and times_ on a server application* `user_id` init as a FK to `user.id`. * `user`: name of db table (SQLAlchemy auto set to the name of the model class converted to lowercase)* new `posts` field in `User` class - `db.relationship` - a high-level view of the relationship between users and posts> * One-to-many relationship: `db.relationship` normally defined on the "one" side. - Convenient way to get access to the "many" (`u.posts`)> * 1st argument: the class that represents the "many" side of the relationship> * `backref`: name of a field that will be added to the objects of the "many" class that points back at the "one" object. (add `post.author`)> * `lazy`: how the db query for the relationship will be issued.  Session* Changes to db are done in the context of a session. `db.session`* `db.session.commit()` write all the changes* `db.sesion.rollback()` abourt the session and remove any changes stored in it* Sesion guaratee that the db will never be left in an inconsistent state.* `.add( )`, `.delete( )` Query* Return all the suers: `users = User.query.all()`* all models have a `query` attribute that is the entry point to run db queries.* most basic: `all()`* `.get(id)` Adding a blog post```Pythonu = User.query.get(1)p = Post(body='my first post!', author=u)db.session.add(p)db.session.commit()```* `User`/`db.relationship` adds a `posts` attribute to users, `author` attribute to posts* `author` virtual field instead of user id: high-level abstraction over relationship and foreign keys Shell Contexttest things out in Python shell very often* `flask shell` start Python interpreter in the context of the application* add db instance and modesl to the shell session `microblog.py````Python@app.shell_context_processordef make_shell_context():    return {'db': db, 'User':User, 'Post':Post}```* `@app.shell_context_processor` registers the function as a shell context function* returns a dinctionary instead of a list  User Logins Password Hashingpackage like _Werkzeug_ implement password hashingHash the same password multiple times, different results```Pythonfrom werkzeug.security import generate_password_hash, check_password_hashhash = generate_password_hash('foobar')check_password_hash(hash, 'foobar')````models.py````Pythonclass User(db.Model):     ...        def set_password(self, password):        self.password_hash = generate_password_hash(password)        def check_password(self, password):        return check_password_hash(self.password_hash, password)``` Flask-loginFlask extension _Falsk-Login_ manages the user logged-in state.* remembers the logged in state* allow user remain logged in after closing the browser window* In `app/__init__.py````Pythonfrom flask_login import LoginManager ...login = LoginManager(app) ...``` Flask-Login - User ModelRequired items:* `is_authenticated`* `is_active`* `is_anonymous`* `get_id()`  Followers Relationship types One-to-manyusers ----< posts   (id ----- user_id)* a user has many posts; a post has one user* `user_id` gives authod of a given post* reverse: db indexes "retrieve all posts that have a user_id of X" Many-to-Manystudents --- student_teacher --- teachers* auxilary table: association table One-to-onea special case of a one-to-many Followers relationshipMany-to-Many: a user follows many users; a user has many followers* only 1 type: users. users following other users.* _self-referential_ relationship: A relationship in which instances of a class are linked to other instances of the same class> users[id] ----- followers[follower_id]>> |-------------- followers[followed_id] * `followers` table: association table* FKs are both pointing at entries in the user table* Each record in this table represnets one link between a follower user and a followed user.  DB Model representation<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10792523614925974, 'fraction_numerical': 0.0020097809338782075, 'mean_word_length': 4.97358943577431, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 44, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 3, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '420332', 'license': 'Apache-2.0', 'n_tokens_mistral': 4065, 'n_tokens_neox': 3843, 'n_words': 1710, 'path': 'Flask Tutorial.ipynb', 'repo_name': 'youtside/notebook', 'size': '14927'}