当前位置:网站首页>[flask] official tutorial -part3: blog blueprint, project installability

[flask] official tutorial -part3: blog blueprint, project installability

2022-07-06 01:27:00 Coriander Chrysanthemum

Preface article :
The official tutorial (Tutorial)-part1: Project layout 、 Application settings 、 Define and access databases
The official tutorial (Tutorial)-part2: The blueprint - View 、 Templates 、 Static files

blog The blueprint

Next, we use the related technology of writing and verifying the blueprint model to write the blueprint of the blog . Blogs should list all posts , Allow login users to create posts , And allow the post author to edit or delete it .

When implementing each view , Please keep the development server running . When saving changes , Please try going to URL And test it .

The blueprint

Define the blueprint and register in the application factory . Use app.register_blueprint() Import and register blueprints from the factory . Before returning to the application , Put the new code at the end of the factory function .

flaskr/__init__.py

def create_app():
    app = ...
    # existing code omitted

    from . import blog
    app.register_blueprint(blog.bp)
    app.add_url_rule('/', endpoint='index')

    return app

Different from the authentication blueprint , There is no blog blueprint url_prefix. So the indexed view will be in /, Create views in /create, And so on . The blog is Flaskr The main function of , So it makes sense that blog index will become the main index .

however , As defined below index The endpoint of the view will be blog.index. Some authentication views refer to common index endpoints . app.add_url_rule() Set the endpoint name 'index' And / Link associated , In order to url_for('index') or url_for('blog.index') Can work , Generate the same /URL.

In another application , You may give a blog blueprint url_prefix And define a separate indexed view in the application factory , Be similar to hello View . that index and blog.index Endpoint and URL It will be different .

index page

The index will show all posts , The most recent is before . Use JOIN So that the author information in the user table can be used in the results .
flaskr/blog.py

@bp.route('/')
def index():
    db = get_db()
    posts = db.execute(
        'SELECT p.id, title, body, created, author_id, username'
        ' FROM post p JOIN user u ON p.author_id = u.id'
        ' ORDER BY created DESC'
    ).fetchall()
    return render_template('blog/index.html', posts=posts)

flaskr/templates/blog/index.html

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Posts{% endblock %}</h1>
  {% if g.user %}
    <a class="action" href="{
     { url_for('blog.create') }}">New</a>
  {% endif %}
{% endblock %}

{% block content %}
  {% for post in posts %}
    <article class="post">
      <header>
        <div>
          <h1>{
   { post['title'] }}</h1>
          <div class="about">by {
   { post['username'] }} on {
   { post['created'].strftime('%Y-%m-%d') }}</div>
        </div>
        {% if g.user['id'] == post['author_id'] %}
          <a class="action" href="{
     { url_for('blog.update', id=post['id']) }}">Edit</a>
        {% endif %}
      </header>
      <p class="body">{
   { post['body'] }}</p>
    </article>
    {% if not loop.last %}
      <hr>
    {% endif %}
  {% endfor %}
{% endblock %}

When the user logs in , The title block adds a link to create the view . When the user is the author of the post , They will see the view pointing to the post update “ edit ” link .loop.last yes Jinja for Special variables available in the loop . It is used to display a line after every post except the last one , To visually separate them .

Create a post

create How views work and auth register Same view . Or display the form , Either verify the published data and add the post to the database or display errors .

You wrote before login_required Decorator for blog view . Users must log in to access these views , Otherwise they will be redirected to the login page .

flaskr/blog.py

@bp.route('/create', methods=('GET', 'POST'))
@login_required
def create():
    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None

        if not title:
            error = 'Title is required.'

        if error is not None:
            flash(error)
        else:
            db = get_db()
            db.execute(
                'INSERT INTO post (title, body, author_id)'
                ' VALUES (?, ?, ?)',
                (title, body, g.user['id'])
            )
            db.commit()
            return redirect(url_for('blog.index'))

    return render_template('blog/create.html')

flaskr/templates/blog/create.html

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}New Post{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="title">Title</label>
    <input name="title" id="title" value="{
     { request.form['title'] }}" required>
    <label for="body">Body</label>
    <textarea name="body" id="body">{
   { request.form['body'] }}</textarea>
    <input type="submit" value="Save">
  </form>
{% endblock %}

to update

Both updating and deleting views need to pass id Get the post and check whether the author matches the login user . To avoid duplicate code , You can write a function to get the post and call it from each view .

flaskr/blog.py

def get_post(id, check_author=True):
    post = get_db().execute(
        'SELECT p.id, title, body, created, author_id, username'
        ' FROM post p JOIN user u ON p.author_id = u.id'
        ' WHERE p.id = ?',
        (id,)
    ).fetchone()

    if post is None:
        abort(404, f"Post id {
      id} doesn't exist.")

    if check_author and post['author_id'] != g.user['id']:
        abort(403)

    return post

abort() Will trigger a return HTTP Special exception of status code . It needs an optional message to display the error , Otherwise, use the default message .404 Express “ Not found ”,403 Express “ prohibit ”. (401 Express “ unaccredited ”, But you redirect to the login page instead of returning to that status .)

Defined check_author Parameters , So that this function can be used to get posts without checking the author . If you write a view to display a single post on the page , It will be useful , Users are not important , Because they didn't modify the post .

flaskr/blog.py

@bp.route('/<int:id>/update', methods=('GET', 'POST'))
@login_required
def update(id):
    post = get_post(id)

    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None

        if not title:
            error = 'Title is required.'

        if error is not None:
            flash(error)
        else:
            db = get_db()
            db.execute(
                'UPDATE post SET title = ?, body = ?'
                ' WHERE id = ?',
                (title, body, id)
            )
            db.commit()
            return redirect(url_for('blog.index'))

    return render_template('blog/update.html', post=post)

Different from the view you are currently writing , The update function accepts a parameter id. This corresponds to <int:id>. A real URL Look like /1/update. Flask Will capture 1, Make sure it's a int, And use it as id Parameter passing . If you don't specify int: But use <id>, It will be a string . To generate the updated page URL, You need to url_for() Pass on id, So that it knows what to fill in :url_for('blog.update', id=post['id']). This is also on the top index.html In file .

Creating and updating views look very similar . The main difference is that the update view uses a post Object and a UPDATE Query instead of a INSERT. Through some clever refactoring , You can use a view and template for these two operations , But for this tutorial , It will be clearer to separate them .

flaskr/templates/blog/update.html

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Edit "{
   { post['title'] }}"{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="title">Title</label>
    <input name="title" id="title" value="{
     { request.form['title'] or post['title'] }}" required>
    <label for="body">Body</label>
    <textarea name="body" id="body">{
   { request.form['body'] or post['body'] }}</textarea>
    <input type="submit" value="Save">
  </form>
  <hr>
  <form action="{
     { url_for('blog.delete', id=post['id']) }}" method="post">
    <input class="danger" type="submit" value="Delete" onclick="return confirm('Are you sure?');">
  </form>
{% endblock %}

This template has two form. The first is to publish the edited data to the current page (/<id>/update). Another form contains only one button , And specify an operation attribute published to the delete view . This button uses some JavaScript Show a confirmation dialog before submitting .

Pattern { { request.form['title'] or post['title'] }} Used to select the data displayed in the form . When the form has not been submitted , The original release data will appear , But if invalid form data is published , You want to display it so that the user can fix the error , Therefore use request.form Instead of .request Is another variable automatically available in the template .

Delete

Deleting a view does not have its own template , The delete button is update.html Part of , And release to the /<id>/deleteURL. Because there is no template , It only handles POST Method , Then redirect to the indexed view .

flaskr/blog.py

@bp.route('/<int:id>/delete', methods=('POST',))
@login_required
def delete(id):
    get_post(id)
    db = get_db()
    db.execute('DELETE FROM post WHERE id = ?', (id,))
    db.commit()
    return redirect(url_for('blog.index'))

Start the service test

Because the previous service was not shut down , And use the development mode , Then you can directly access .

After logging in, it has the following effects :
 Insert picture description here
So let's click “New” You can create an article :
 Insert picture description here
After clicking save , Just jump to the front page of the blog :
 Insert picture description here
For this blog , We can choose “Edit”, Then go to the following page :
 Insert picture description here
We can modify it , It can also be deleted . Later readers can try it by themselves .

The project can be installed

Making your project installable means that you can build a distribution file and install it in another environment , Just like you install in the project environment Flask equally . This makes deploying your project the same as installing any other library , So you use all the Standards Python Tools to manage all content .

The installation also brings others from the tutorial or as new Python Users may not have obvious benefits , Include :

  • at present ,Python and Flask Learn how to use flaskr The package is just because you are running from the project directory . Installation means that you can import it no matter where you run it .
  • You can manage the dependencies of a project just like any other package , therefore pip install yourproject.whl Will install them .
  • Testing tools can isolate your testing environment from your development environment .

Project description

setup.py The file describes your project and the files that belong to it .

setup.py

from setuptools import find_packages, setup

setup(
    name='flaskr',
    version='1.0.0',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    install_requires=[
        'flask',
    ],
)

packages tell Python Which package directories to include ( And what they contain Python file ). find_packages() These directories will be found automatically , So you don't have to enter them . To include other files , For example, static and template directories , Please set up include_package_data. Python Need another one called MANIFEST.in To explain what other data is .

MANIFEST.in

include flaskr/schema.sql
graft flaskr/static
graft flaskr/templates
global-exclude *.pyc

This tells Python Copy static and template directories and schema.sql Everything in the document , But exclude all bytecode files .

Project installation

Use pip Install your project in a virtual environment .

pip install -e .

This tells pip Find... In the current directory setup.py And install it in editable or development mode . Editable mode means that when you change local code , If you change the metadata about the project ( For example, its dependencies ), Then just reinstall .
 Insert picture description here

You can observe that the project is now installed pip list.

 Insert picture description here
up to now , There is no change in the way you run the project . FLASK_APP Still set to flaskr also flask run Still running the application , But you can call it from anywhere , Not just flask-tutorial Catalog

原网站

版权声明
本文为[Coriander Chrysanthemum]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060125461989.html