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

Fast Flask Dev For Big Codebases

Fast Flask Dev For Big Codebases

Abdur-Rahmaan Janhangeer

November 24, 2022
Tweet

More Decks by Abdur-Rahmaan Janhangeer

Other Decks in Technology

Transcript

  1. Fast Flask dev

    for

    Big Codebases

    View Slide

  2. ssslides

    View Slide

  3. View Slide

  4. 4

    View Slide

  5. Python Mauritius UserGroup
    (pymug)
    More info: mscc.mu/python-mauritius-usergroup-pymug/
    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
    5

    View Slide

  6. Abdur-Rahmaan Janhangeer
    Help people get into OpenSource
    People hire me to work on Python projects
    www.compileralchemy.com
    6

    View Slide

  7. Fav foreign (https://metabob.com)



    World's most advanced code analysis tool?
    Fav local (https://oceandba.com)

    7

    View Slide

  8. Fast Flask dev

    for

    Big Codebases
    8

    View Slide

  9. The Flask Story
    9

    View Slide

  10. Begin new project


    10

    View Slide

  11. Code features


    11

    View Slide

  12. Deploy


    12

    View Slide

  13. Refine Deploy


    13

    View Slide

  14. TEST AND PROD ENV DIFFERENT
    14

    View Slide

  15. USE CONFIGURATION PROFILES
    15

    View Slide

  16. ADD APP FACTORY
    16

    View Slide

  17. APP BREAKING
    17

    View Slide

  18. WRITE TESTS
    18

    View Slide

  19. CODE DISORGANISED
    19

    View Slide

  20. ORGANISE CODEBASE
    20

    View Slide

  21. TESTS ARE BORING
    21

    View Slide

  22. WRITE FIXTURES, WRITE SCHEMAS
    22

    View Slide

  23. I HAVE TO START A NEW PROJECT
    23

    View Slide

  24. HAVE A TEMPLATE
    24

    View Slide

  25. NOW I HAVE TO USE APIS
    25

    View Slide

  26. INTEGRATE API-FRAMEWORK
    26

    View Slide

  27. pulls some more hair
    27

    View Slide

  28. I WANT TO INTEGRATE CELERY
    28

    View Slide

  29. INTEGRATE CELERY
    29

    View Slide

  30. dev left ...
    30

    View Slide

  31. THE NEW INTERN DOES NOT UNDERSTAND
    ANYTHING
    31

    View Slide

  32. WRITE DOCS!!!
    32

    View Slide

  33. You need something that flies in all
    weather
    33

    View Slide

  34. The Application Factory
    34

    View Slide

  35. def create_app(app_name):

    app = Flask(app_name)



    return app
    35

    View Slide

  36. Configuration Profiles
    36

    View Slide

  37. 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

    37

    View Slide

  38. 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
    38

    View Slide

  39. Tests
    39

    View Slide

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

    View Slide

  41. Docs
    41

    View Slide

  42. Use Sphinx from the start
    42

    View Slide

  43. Separate Initialisations From The Factory
    43

    View Slide

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

    View Slide

  45. app.py

    init.py

    45

    View Slide

  46. init.py
    db = SQLAlchemy()

    ma = Marshmallow()

    login_manager = LoginManager()

    migrate = Migrate()

    mail = Mail()

    46

    View Slide

  47. from init import mail

    def create_app(...):

    app = ...

    mail.init_app(app)

    in other files: from init import db
    47

    View Slide

  48. Use modules for everything!!!
    48

    View Slide

  49. app.py

    module1/

    module2/

    module3/

    49

    View Slide

  50. app.py

    modules/

    module1/

    module2/

    50

    View Slide

  51. module1/

    templates/

    static/

    test/

    models.py

    forms.py

    view.py

    etc

    etc

    51

    View Slide

  52. Use flask to simplify your life
    52

    View Slide

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

    def jump():

    # do something

    return 0

    # cli

    flask jump

    53

    View Slide

  54. Automate blueprint registration
    54

    View Slide

  55. 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)

    55

    View Slide

  56. Automate module creation
    56

    View Slide

  57. # pseudocode

    def createmodule(name):

    mkdir(folder)

    touch(folder/models.py)

    touch(folder/)

    ...

    # cli

    flask createmodule apple

    57

    View Slide

  58. Form help yourself
    58

    View Slide

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

    class Meta:

    model = Conf

    59

    View Slide

  60. Contain static in module, implement
    collecstatic
    60

    View Slide

  61. 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

    61

    View Slide

  62. 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

    62

    View Slide

  63. What for celery?
    63

    View Slide

  64. app.py

    init.py

    celery_app.py

    modules/

    m1/

    task.py

    64

    View Slide

  65. init.py
    celery = Celery(

    __name__,

    broker=BaseConfig.CELERY_BROKER_URL,

    backend=BaseConfig.CELERY_RESULT_BACKEND,

    )

    65

    View Slide

  66. 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

    66

    View Slide

  67. celery_app.py
    from app import init_celery

    celery = init_celery()

    67

    View Slide

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

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

    def task_do_something(self):

    68

    View Slide

  69. What for flask-restx?
    69

    View Slide

  70. modules/

    app.py

    init.py

    api_util.py

    70

    View Slide

  71. init.py
    api = Api(

    version="1.0",

    title="MVP Data",

    description="Backend Part",

    authorizations=authorizations,

    )

    71

    View Slide

  72. 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")

    72

    View Slide

  73. app.py
    from init import api

    from api_util import add_api

    def create_app(...):

    app = ...

    api.init_app(app)

    add_api(api)

    73

    View Slide

  74. Migrations to the next level
    74

    View Slide

  75. # make flask-migrate auto detect models

    for folder in modules:

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

    75

    View Slide

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

    View Slide

  77. Reuse base
    77

    View Slide

  78. Use cookiecutter
    cookiecutter

    Keep modules and init.py empty
    78

    View Slide

  79. The ongoing conclusion:
    79

    View Slide

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

    View Slide

  81. Why not use Django?
    Because Flask
    81

    View Slide

  82. 82

    View Slide

  83. 83

    View Slide

  84. 84

    View Slide

  85. 85

    View Slide

  86. 86

    View Slide

  87. 87

    View Slide

  88. And, that's what shopyo is!
    88

    View Slide

  89. Like it? Appreciate a mail at: arj.python[@]gmail[.]com
    Means a lot to me!
    89

    View Slide