Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced Flask: Recipes For An All-weather Craft

Advanced Flask: Recipes For An All-weather Craft

Abstract: Flask is favoured for prototyping. It is easy to set up and run. However, choosing Flask as your main 'cheval de bataille', be it as a company or individual, requires solid grounding. Flask lets you choose your own ingredients, which lights up the joy of coding but bites if not being careful. This talk covers the standard techniques not to be missed as well as new audacious ones used to help manage BIG codebases.

Abdur-Rahmaan Janhangeer

October 21, 2021
Tweet

More Decks by Abdur-Rahmaan Janhangeer

Other Decks in Programming

Transcript

  1. Advanced Flask: Recipes For An All-weather Craft

    View Slide

  2. PyCon Sweden

    View Slide

  3. 3

    View Slide

  4. Python Mauritius UserGroup (pymug)
    More info: mscc.mu/python-mauritius-usergroup-pymug/
    pymug.com
    Why Where
    codes github.com/pymug
    share events twitter.com/pymugdotcom
    ping professionals linkedin.com/company/pymug
    all info pymug.com
    tell friends by like facebook.com/pymug
    4

    View Slide

  5. Abdur-Rahmaan Janhangeer

    (Just a Python programmer)
    twitter: @osdotsystem

    github: github.com/abdur-rahmaanj
    compileralchemy.com
    5

    View Slide

  6. FlaskCon organising member + Flask Community Workgroup member
    6

    View Slide

  7. Advanced Flask: Recipes For An All-weather Craft
    7

    View Slide

  8. The Flask Story
    8

    View Slide

  9. Begin new project


    9

    View Slide

  10. Code features


    10

    View Slide

  11. Deploy


    11

    View Slide

  12. Refine Deploy


    12

    View Slide

  13. TEST AND PROD ENV DIFFERENT
    13

    View Slide

  14. USE CONFIGURATION PROFILES
    14

    View Slide

  15. ADD APP FACTORY
    15

    View Slide

  16. APP BREAKING
    16

    View Slide

  17. WRITE TESTS
    17

    View Slide

  18. CODE DISORGANISED
    18

    View Slide

  19. ORGANISE CODEBASE
    19

    View Slide

  20. TESTS ARE BORING
    20

    View Slide

  21. WRITE FIXTURES, WRITE SCHEMAS
    21

    View Slide

  22. I HAVE TO START A NEW PROJECT
    22

    View Slide

  23. HAVE A TEMPLATE
    23

    View Slide

  24. NOW I HAVE TO USE APIS
    24

    View Slide

  25. INTEGRATE API-FRAMEWORK
    25

    View Slide

  26. pulls some more hair
    26

    View Slide

  27. I WANT TO INTEGRATE CELERY
    27

    View Slide

  28. INTEGRATE CELERY
    28

    View Slide

  29. dev left ...
    29

    View Slide

  30. THE NEW INTERN DOES NOT UNDERSTAND ANYTHING
    30

    View Slide

  31. WRITE DOCS!!!
    31

    View Slide

  32. You need something that flies in all weather
    32

    View Slide

  33. The Application Factory
    33

    View Slide

  34. def create_app(app_name):

    app = Flask(app_name)



    return app
    34

    View Slide

  35. Configuration Profiles
    35

    View Slide

  36. class Config:

    TESTING = False

    class ProductionConfig(Config):

    FAV_FLOWER = 'rose'

    class DevelopmentConfig(Config):

    FAV_FLOWER = 'sunflower'

    class TestingConfig(Config):

    FAV_FLOWER = 'moonlight petal'

    TESTING = True

    36

    View Slide

  37. from config import ProductionConfig

    from config import DevelopmentConfig

    from config import TestingConfig

    profiles = {

    'development': DevelopmentConfig(),

    'production': ProductionConfig(),

    'testing': TestingConfig()

    }

    def create_app(profile):

    app = Flask(__name__)

    app.config.from_object(profiles[profile])

    return app
    37

    View Slide

  38. Tests
    38

    View Slide

  39. Use Pytest
    Use class-based testing
    Define fixtures for common actions e.g. logout/login
    39

    View Slide

  40. Docs
    40

    View Slide

  41. Use Sphinx from the start
    41

    View Slide

  42. Separate Initialisations From The Factory
    42

    View Slide

  43. Use a file for initialisations
    Reduces import errors to a max
    43

    View Slide

  44. app.py

    init.py

    44

    View Slide

  45. init.py
    db = SQLAlchemy()

    ma = Marshmallow()

    login_manager = LoginManager()

    migrate = Migrate()

    mail = Mail()

    45

    View Slide

  46. from init import mail

    def create_app(...):

    app = ...

    mail.init_app(app)

    in other files: from init import db
    46

    View Slide

  47. Use modules for everything!!!
    47

    View Slide

  48. app.py

    module1/

    module2/

    module3/

    48

    View Slide

  49. app.py

    modules/

    module1/

    module2/

    49

    View Slide

  50. module1/

    templates/

    static/

    test/

    models.py

    forms.py

    view.py

    etc

    etc

    50

    View Slide

  51. Use flask to simplify your life
    51

    View Slide

  52. @app.cli.command("jump", short_help="skip something")

    def jump():

    # do something

    return 0

    # cli

    flask jump

    52

    View Slide

  53. Automate blueprint registration
    53

    View Slide

  54. 1. name each blueprint: foldername_blueprint
    2. In app.py
    # pseudocode

    for folder in modules:

    blueprint = import(f'modules.{folder}.view.{folder}_blueprint')

    app.register(blueprint)

    54

    View Slide

  55. Automate module creation
    55

    View Slide

  56. # pseudocode

    def createmodule(name):

    mkdir(folder)

    touch(folder/models.py)

    touch(folder/)

    ...

    # cli

    flask createmodule apple

    56

    View Slide

  57. Form help yourself
    57

    View Slide

  58. Use flask-wtf
    Use wtforms-alchemy
    class ConfForm(ModelForm):

    class Meta:

    model = Conf

    58

    View Slide

  59. Contain static in module, implement collecstatic
    59

    View Slide

  60. app.py

    static/ # served from here in production

    modules/

    m1/

    static/

    file.png
    m2/

    static/

    file.png
    collectstatic just collects all assets in all modules and add it under
    app.py

    static/

    modules/

    m1/

    file.png

    m2/

    file.png

    60

    View Slide

  61. define function serve_asset(module, resource_path)

    e.g. serve_asset('m1', 'file.png')

    if mode is dev serve from /modules/m1/static/file.png

    i.e /modules/module_name/m1/static/path/to/file.png

    if mode is prod serve from /static/modules/m1/file.png (assumes collecstatic run before)

    i.e /static/modules/module_name/path/to/file.png

    61

    View Slide

  62. What for celery?
    62

    View Slide

  63. app.py

    init.py

    celery_app.py

    modules/

    m1/

    task.py

    63

    View Slide

  64. init.py
    celery = Celery(

    __name__,

    broker=BaseConfig.CELERY_BROKER_URL,

    backend=BaseConfig.CELERY_RESULT_BACKEND,

    )

    64

    View Slide

  65. app.py
    from init import celery

    def init_celery(app=None):

    app = create_app(os.getenv("FLASK_ENV", ""))

    app = app or create_app()

    celery.conf.update(app.config)

    class ContextTask(celery.Task):

    """Make celery tasks work with Flask app context"""

    def __call__(self, *args, **kwargs):

    with app.app_context():

    return self.run(*args, **kwargs)

    celery.Task = ContextTask

    return celery

    65

    View Slide

  66. celery_app.py
    from app import init_celery

    celery = init_celery()

    66

    View Slide

  67. modules/m1/task.py
    from init import celery

    @celery.task(name="do something", bind=True)

    def task_do_something(self):

    67

    View Slide

  68. What for flask-restx?
    68

    View Slide

  69. modules/

    app.py

    init.py

    api_util.py

    69

    View Slide

  70. init.py
    api = Api(

    version="1.0",

    title="MVP Data",

    description="Backend Part",

    authorizations=authorizations,

    )

    70

    View Slide

  71. api_util.py #
    from modules.some.api import some_ns

    from modules.some2.api import some2_ns

    # ns: namespace

    from init import api_v1

    api_v = api_v1 # /api/v1

    def add_api(api):

    api.add_namespace(some_ns, path=api_v + "/someapi")

    api.add_namespace(some2_ns, path=api_v + "/someapi2")

    71

    View Slide

  72. app.py
    from init import api

    from api_util import add_api

    def create_app(...):

    app = ...

    api.init_app(app)

    add_api(api)

    72

    View Slide

  73. Migrations to the next level
    73

    View Slide

  74. # make flask-migrate auto detect models

    for folder in modules:

    import(f'modules.{folder}.models.py')

    74

    View Slide

  75. Modular migration ideas:
    make flask_migrate or alembic load only specific classes
    75

    View Slide

  76. Reuse base
    76

    View Slide

  77. Use cookiecutter
    cookiecutter

    Keep modules and init.py empty
    77

    View Slide

  78. The ongoing conclusion:
    78

    View Slide

  79. https://github.com/shopyo/shopyo
    Star it to show support!
    79

    View Slide

  80. Helped you? appreciate a mail at: [email protected]
    Really means a lot to me!!!!!
    80

    View Slide