$30 off During Our Annual Pro Sale. View Details »

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

  2. ssslides

  3. None
  4. 4

  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
  6. Abdur-Rahmaan Janhangeer Help people get into OpenSource People hire me

    to work on Python projects www.compileralchemy.com 6
  7. Fav foreign (https://metabob.com) World's most advanced code analysis tool? Fav

    local (https://oceandba.com) 7
  8. Fast Flask dev for Big Codebases 8

  9. The Flask Story 9

  10. Begin new project 10

  11. Code features 11

  12. Deploy 12

  13. Refine Deploy 13

  14. TEST AND PROD ENV DIFFERENT 14

  15. USE CONFIGURATION PROFILES 15

  16. ADD APP FACTORY 16

  17. APP BREAKING 17

  18. WRITE TESTS 18

  19. CODE DISORGANISED 19

  20. ORGANISE CODEBASE 20

  21. TESTS ARE BORING 21

  22. WRITE FIXTURES, WRITE SCHEMAS 22

  23. I HAVE TO START A NEW PROJECT 23

  24. HAVE A TEMPLATE 24

  25. NOW I HAVE TO USE APIS 25

  26. INTEGRATE API-FRAMEWORK 26

  27. pulls some more hair 27

  28. I WANT TO INTEGRATE CELERY 28

  29. INTEGRATE CELERY 29

  30. dev left ... 30

  31. THE NEW INTERN DOES NOT UNDERSTAND ANYTHING 31

  32. WRITE DOCS!!! 32

  33. You need something that flies in all weather 33

  34. The Application Factory 34

  35. def create_app(app_name): app = Flask(app_name) return app 35

  36. Configuration Profiles 36

  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
  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
  39. Tests 39

  40. Use Pytest Use class-based testing Define fixtures for common actions

    e.g. logout/login 40
  41. Docs 41

  42. Use Sphinx from the start 42

  43. Separate Initialisations From The Factory 43

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

    max 44
  45. app.py init.py 45

  46. init.py db = SQLAlchemy() ma = Marshmallow() login_manager = LoginManager()

    migrate = Migrate() mail = Mail() 46
  47. from init import mail def create_app(...): app = ... mail.init_app(app)

    in other files: from init import db 47
  48. Use modules for everything!!! 48

  49. app.py module1/ module2/ module3/ 49

  50. app.py modules/ module1/ module2/ 50

  51. module1/ templates/ static/ test/ models.py forms.py view.py etc etc 51

  52. Use flask to simplify your life 52

  53. @app.cli.command("jump", short_help="skip something") def jump(): # do something return 0

    # cli flask jump 53
  54. Automate blueprint registration 54

  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
  56. Automate module creation 56

  57. # pseudocode def createmodule(name): mkdir(folder) touch(folder/models.py) touch(folder/) ... # cli

    flask createmodule apple 57
  58. Form help yourself 58

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

    Conf 59
  60. Contain static in module, implement collecstatic 60

  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
  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
  63. What for celery? 63

  64. app.py init.py celery_app.py modules/ m1/ task.py 64

  65. init.py celery = Celery( __name__, broker=BaseConfig.CELERY_BROKER_URL, backend=BaseConfig.CELERY_RESULT_BACKEND, ) 65

  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
  67. celery_app.py from app import init_celery celery = init_celery() 67

  68. modules/m1/task.py from init import celery @celery.task(name="do something", bind=True) def task_do_something(self):

    68
  69. What for flask-restx? 69

  70. modules/ app.py init.py api_util.py 70

  71. init.py api = Api( version="1.0", title="MVP Data", description="Backend Part", authorizations=authorizations,

    ) 71
  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
  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
  74. Migrations to the next level 74

  75. # make flask-migrate auto detect models for folder in modules:

    import(f'modules.{folder}.models.py') 75
  76. Modular migration ideas: make flask_migrate or alembic load only specific

    classes 76
  77. Reuse base 77

  78. Use cookiecutter cookiecutter <url> Keep modules and init.py empty 78

  79. The ongoing conclusion: 79

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

  81. Why not use Django? Because Flask 81

  82. 82

  83. 83

  84. 84

  85. 85

  86. 86

  87. 87

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

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

    to me! 89