https://exploreflask.com/en/latest/configuration.html

Templates

Key takeaways

  1. Sử dụng Jinja làm template engine.
  2. Standard Context: config, request, session, g, url_for(), get_flashed_messages()
  3. Có thể cân nhắc khi nào cần/ không cần autoesscape html.
  4. Jinja support filter (một dạng tương tự với các filter bên Angular), mình có thể register filter bằng @app.template_filter
  5. Streaming: Không render hết template 1 lượt mà sẽ streaming dần.

Testing Flask Applications

Key takeaways

  1. Mục đích của fixtures là viết từng đoạn code nhỏ để có thể tái sử dụng được. Một số thư viện như pytest-mock, bản chất là nó tạo ra các fixtures để có thể add vào function test.

  2. Simple fixture return value, nhưng cũng có thể là setup, yield a value and teardown.

  3. Client test tạo request tới app mà không chạy 1 live server. Sử dụng get(), post() để tương tác.

    1. Với query string: query_string={"key": "value", ...}
    2. Với form-data, gửi bằng: `client.post(‘/url’, data={“name”: “Flask”, …})
    3. Với json data: `client.post(‘/url’, json={“query”: …})
  4. Để access vào Flask’s context variables, ví dụ session, ta cần sử dụng with statement, như thế thì app và request context sẽ vẫn active sau khi making request, cho tới khi block with ends. (with client:)

  5. Test với app context hoặc request context: with app.app_context() hoặc with app.test_request_context()

Handling Application Errors

Key takeaways

  1. Có thể sử dụng sentry-sdk để push errors.

  2. Error handlers:

    1. Cần register error: cái này làm ở file application/handlers.py
    @app.errorhandler(werkzeug.exceptions.BadRequest)
    def handle_bad_request(e):
    	return 'bad request!', 400
    1. Handle các errors: HTTPException, Exception, … Nếu không có handler nào được register cho 1 exception, flask.Flask.handle_exception() sẽ được call InternalServerError.
    2. Có 2 cách để implement. 1 là dùng decorator, 2 là viết hàm handle, sau đó register_error_handler với application/ blueprint.
  3. Custom error có thể viết riêng sang class khác, sau đó raise errors trong Controller.

  4. Ở production mode, nếu errors mà không được catch thì mặc định sẽ trả về lỗi 500. Nhưng ở development mode, error sẽ được đẩy nguyên về WSGI server. Như thế lỗi sẽ được hiển thị ở development mode.

Debugging Application Errors

Key takeaways

  1. Không bao giờ bật chế độ debugger trên Production. Sử dụng logging tool (Sentry, …) or Logging để in ra lỗi.
  2. Use this command to enable --debug: flask --app hello run --debug

Logging

  1. Sử dụng module logging để in log. Có thể config output log in ra dựa vào dictConfig() : output, stream, formatter, root level, handler, …
  2. Có thể remove default Handler hoặc sử dụng logging.handlers.SMTPHandler để send email lỗi cho Admin
  3. Injecting Request Information bằng cách viết 1 class custom, eg RequestFormatter, sau đó add thêm request context từ flask.

Logging

Configuration Handling

  1. Sử dụng app.config để custom configurations: Toggle the debug mode, Setting Secret Key, ENV-specific things.
  2. Builtin Configuration Values: ENV, DEBUG, TESTING, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SECURE, APPLICATION_ROOT, MAX_CONTENT_LENGTH, PROPAGATE_EXCEPTIONS (handle error) …

https://flask.palletsprojects.com/en/2.2.x/config/#builtin-configuration-values

  1. Cách tốt hơn để config là get configuration từ file (python file, data files, ..)
  2. Config từ ENV. Prefix mặc định của app sẽ là FLASK_. dấu __ sẽ có thể được hiểu là []
  3. Some configuration is stored in instance folder.

Best Practices:

  1. Tạo application in a function, register blueprints on it.
  2. Do not write code that needs the configuration at import time.
  3. Make sure to load the configuration very early on.

Signals

Set, Gửi/ Nhận Signal?

Mình đang hiểu là các tín hiệu thông báo sẽ làm việc A, việc B, … (Được dùng trong Flask - How a Request is Handled )

Class-based Views

Mục đích là để tạo ra các views sao cho có thể tái sử dụng lại nhiều lần.

https://flask.palletsprojects.com/en/2.2.x/views/

Class that acts as a view function. - Generic, Reusable or Pluggable views.

class ListView(View):
    def __init__(self, model, template):
        self.model = model
        self.template = template
 
    def dispatch_request(self):
        items = self.model.query.all()
        return render_template(self.template, items=items)
 
 
app.add_url_rule(
    "/users/",
    view_func=ListView.as_view("user_list", User, "users.html"),
)
app.add_url_rule(
    "/stories/",
    view_func=ListView.as_view("story_list", Story, "stories.html"),
)

Ta có thể sử dụng view decorator cho gọn code. Just remember that order matter.

@app.route("/users/")
@login_required
@cache(minutes=2)
def user_list():
    ...

Ta có thể custom để sử dụng kiểu shared view. Viết view chung, sau đó register view.

Application Structure and Lifecycle

Flask is a WSGI application framework. The other half of WSGI is the WSGI server.

WSGI - Web server Gateway Interface - nhận request, convert HTTP data to WSGI environ dict. After that, it calls WSGI application (Flask) with the environ. Flask route the request to View function, handler errors, … Flask translate View function return to WSGI response data, parse it to WSGI server. WSGI server creates and send an HTTP response to client.

Về mặt bản chất thì thằng WSGI này gần giống như 1 rack app bên Rails - App server :think: Có nhiệm vụ đọc thông tin từ request, pass vào Rails Application, sau đó nhận response, và convert lại về thành data để trả lại cho Web server.

Middleware là một WSGI mà wrap một WSGI app khác. (Giống với concept của Python decorators). Middleware ngoài cùng được gọi là Server. NÓ có thể modify data truyền vào, và gọi tới các WSGI app khác. Flask là application cuối cùng của chain middleware đó.

Một middleware phổ biến đó là Werkzeug’s ProxyFix, modifies request cho giống với việc nó truyền trực tiếp từ client (chứ k phải qua HTTP proxies)

Request Flow

  1. WSGI server calls the Flask object. Flask.wsgi_app()
  2. RequestContext , AppContext is created. This converts the WSGI environ dict into a Request object.
  3. The app context is pushed - current_app and g available.
  4. The appcontext_pushed signal is sent.
  5. The request context is pushed - request and session available.
  6. Load existing session data using app’s session_interface
  7. Check URL matched with route() decorator. If not match (404, 405, ..) is stored to be handled later.
  8. The request_started signal is sent.
  9. Any url_value_preprocessor() decorated functions are called.
  10. Any before_request() decorated functions are called.
  11. If no URL match (above), raised errors now.
  12. View function which is associated with  route() decorated is called.
  13. If there are any errors,  errorhandler() decorated function is called to handle errors and return response.
  14. Whatever response (before request function, view, error handler) is converted to a Response object.
  15. Any after_this_request() decorated functions are called, then cleared.
  16. Any after_request() decorated functions are called, which can modify the response object.
  17. The session is saved, persisting any modified session data using the app’s session_interface.
  18. The request_finished signal is sent.
  19. With exception, those handled now, with status code, response, … The got_request_exception signal is sent.
  20. The response object’s status, headers, and body are returned to the WSGI server.
  21. Any teardown_request() decorated functions are called.
  22. The request_tearing_down signal is sent.
  23. The request context is popped, request and session are no longer available.
  24. Any teardown_appcontext() decorated functions are called.
  25. The appcontext_tearing_down signal is sent.
  26. The app context is popped, current_app and g are no longer available.
  27. The appcontext_popped signal is sent.
Link to original

The Application Context

The application context keeps track of the application-level data during a request.

current_app and g are proxies object.

WHY

  • Nếu sử dụng app factory pattern hoặc viết reusable blueprint/ extensions thì sẽ không có app instance để mà import. Để truy cập thông tin của app instance, thì Flask cung cấp application context. Thay vì truy cập trực tiếp vào app, ta sẽ truy cập qua current_app proxy.

Tức là bình thường, nếu muốn truy vấn thông tin của application instance, ta cần truyền app vào các function. Ví dụ:

# Có proxy
from flask import Blueprint, current_app
 
bp = Blueprint('my_blueprint', __name__)
 
@bp.route('/')
def index():
    app_name = current_app.name
    # do something with app_name
    return "Hello, World!"
 
# Không có proxy
from flask import Blueprint
 
bp = Blueprint('my_blueprint', __name__)
 
def index(app):
    app_name = app.name
    # do something with app_name
    return "Hello, World!"

Về bản chất thì đây là 1 Local Proxy, giúp chúng ta access vào application instance dễ dàng hơn.

Lifetime

  • Khi request đến, Flask push application context và request context. Khi request end, nó lại pop thông tin này ra.
  • Nếu muốn access application_context outside, nên đặt nó trong block with. Eg: with app.app_context():

g object

  • Mục đích: Lưu trữ common data during a request or CLI command.
  • Data này chỉ lưu được trong request, muốn share giữa cá request cần lưu thông tin vào session hoặc database.

Common cases của thằng g object này là:

  • get_X() : Create resource X nếu nó không tồn tại, sau đó cache nó vào g.X
  • teardown_X(): Close/ deallocates nếu resource tồn tại. Nó sẽ được register với teardown_appcontext() handler.

Case hay được dùng là cho db. Tất cả các request tới db trong 1 request sẽ dùng chung 1 connection.

The Request Context

Thằng Application Context thì lưu trữ data ở tầng application-level, còn Request Context thì sẽ lưu trữ data ở tầng request-level. Cả 2 thằng này đều lưu data tồn tại ở trong request.

Mục đích

Vì một worker (thread, process, coroutine) chỉ handle 1 request tại 1 thời điểm, nên request data có thể lưu ở dạng global.

View functions, error handlers, and other functions trong 1 request có thể truy cập vào request proxy, trỏ tới request object.

Push a context manually

  1. Thường thì dùng khi mình cần chạy test, và cần access vào request context.
  2. Sử dụng app.test_client() hoặc with app.test_request_context()

How it works

  1. Request đến, Flask.wsgi_app() được gọi, convert WSGI environ dict thành Request Object.
  2. AppContext được tạo, push vào context stack. (Mỗi request sẽ có 1 context stack). Lúc này, current_appg object available.
  3. Trong quá trình gọi request, nếu cần truy cập vào requestsession object, RequestContext sẽ được push vào stack, making request & session available.
  4. Khi các context này được push vào stack, các proxies phụ thuộc vào nó sẽ được active, trỏ vào context mới nhất ở trong stack

Signals

Các signals được gửi:

  1. request_started before before_request()
  2. request_finished after function after_request() is called
  3. got_request_exception is sent when exception begins to be handled, but before an errorhandler() is looked up or called.
  4. request_tearing_down teardown_request() is called.

https://flask.palletsprojects.com/en/2.2.x/reqcontext/

Modular Applications with Blueprints

Check all urls with app.url_map.

HOW

Khai báo 1 blueprint, define name, url prefix nếu cần. Register vào application với command app.register_blueprint(XXX.bp).

Ta có thể register 1 blueprint này vào 1 blueprint khác (nesting blueprints.)

Blueprint Resources

  1. Resource folder: single_page.root_path.
  2. Static files: admin = Blueprint('admin', __name__, static_folder='static')
  3. Template folder: Use options template_folder='template' khi khởi tạo.
  4. url building: url_for('admin.index')
  5. Error handlers: @simple_page.errorhandler(404)

Notes

  • Application Object: Center object của Flask application. (Flask(__name__)). Nhiệm vụ handle incoming request, routing them tới view functions, return response.

  • Extensions: Các package riêng lẻ có thể cài và integrate với Flask. Vd: Flask-SQLAlchemy. Flask làm theo kiểu modular design nên rất dễ để add extra functionality.

  • Blueprints: Way to organize related views and other code into separated modules. Một application to có thể chia thành nhiều blueprints nhỏ, trong đó bao gồm cả routes, views, templates, … Sau đó có thể register blueprints này với application object, making those routes available.

Link to original

Extensions

Extensions là các extra packages mà add thêm functions vào cho Flask application. Ví dụ: Sending email, connect to DB, REST API support, …

Có thể search package với tagged: Framework::Flask.

Để dùng extensions thì follow theo guide hướng dẫn. Nhưng đại đa số là bọn nó sẽ pull their own configuration từ app.config và pass application instance vào để khởi tạo.

Ví dụ:

from flask_foo import Foo
foo = Foo()
 
app = Flask(__name__)
app.config.update(
	FOO_BAR='baz'
)
 
foo.init_app(app)

Command Line Interface

Development Server

Working with the Shell

Patterns for Flask

Security Considerations

Deploying to Production

Using async and await

Questions

  • Application Factories là gì?
  • super() trong Python