I have a relatively simple web application that is written in Python using the Flask microframework. I've really enjoyed Flask's ease of use, however, as the app has grown larger it has started to become unwieldy having all of my actions (and utility functions) in a single file. My views.py is about 700 lines of code and I'd really like to break things out into more discrete units. How should I restructure my code?
Flask Application Structure – How to Structure a Larger Flask Application
flaskpythonrefactoring
Related Solutions
In the development server docs, they state there are issues with calling run() and automatically reloading code:
This works well for the common case but it does not work well for development which is why from Flask 0.11 onwards the flask method is recommended. The reason for this is that due to how the reload mechanism works there are some bizarre side-effects (like executing certain code twice, sometimes crashing without message or dying when a syntax or import error happens).
They claim the CLI doesn't suffer from this problem.
The first commit that seems to touch on this issue is this: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f
And there Armin Ronacher wrote:
It is not recommended to use this function for development with automatic reloading as this is badly supported. Instead you should be using the
flask
command line script'srunserver
support.
As mentioned by Aaron Hall, it seems like the use of run() could be problematic due to the fact that all objects which are instances of classes defined in the modules being replaced won't be reinstantiated, and whenever a module is reloaded, the modules it imports aren't reloaded as well.
The details about this may be found for Python 3 at: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib
It states:
As with all other objects in Python the old objects are only reclaimed after their reference counts drop to zero.
Other references to the old objects (such as names external to the module) are not rebound to refer to the new objects and must be updated in each namespace where they occur if that is desired.
When a module is reloaded, its dictionary (containing the module’s global variables) is retained. Redefinitions of names will override the old definitions, so this is generally not a problem. If the new version of a module does not define a name that was defined by the old version, the old definition remains.
So, by creating a new process and killing the old one, you naturally eliminate all obsolete references.
Also, Flask's CLI uses the 'click' module, making it very easy to add custom commands, but most importantly, besides fixing the reloading bug, the CLI offers a standardised way to run applications and add custom commands. This sounds like a very good thing, because it makes familiarity with Flask more transferable between different teams and applications, rather than having multiple ways to do the same thing.
It seems like a genuine way to make Flask more in accordance to the Zen of Python:
There should be one-- and preferably only one --obvious way to do it.
This is a pretty common problem for scientists. I've seen it a lot, and it always stems by the fact that programming is something you pick on the side as a tool to do your job.
So your scripts are a mess. I'm going to go against common sense and say that, assuming you're programming alone, this is not so bad! You're never going to touch most of what you write ever again, so spending too much time to write pretty code instead of producing "value" (so the result of your script) isn't going to do much to you.
However, there is going to be a time where you need to go back to something you did and see exactly how something was working. Additionally, if other scientists will need to review your code, it's really important for it to be as clear and concise as possible, so that everyone can understand it.
Your main problem is going to be readability, so here's a few tips for improving:
Variable names:
Scientists love to use concise notations. All mathematical equations usually use single letters as variables, and I wouldn't be surprised to see lots and lots of very short variables in your code. This hurts readability a lot. When you'll go back to your code you're not going to remember what those y, i and x2 represent, and you'll spend a lot of time trying to figure it out. Try instead naming your variables explicitly, using names that represent exactly what they are.
Split your code into functions:
Now that you renamed all your variables, your equations look terrible, and are multiple lines long.
Instead of leaving it in your main program, move that equation to a different function, and name it accordingly. Now instead of having a huge and messed up line of code, you'll have a short instructions telling you exactly what's going on and what equation you used. This improves both your main program, as you don't even have to look at the actual equation to know what you did, and the equation code itself, as in a separate function you can name your variables however you want, and go back to the more familiar single letters.
On this line of thought, try to find out all the pieces of code that represent something, especially if that something is something you have to do multiple times in your code, and split them out into functions. You'll find out that your code will quickly become easier to read, and that you'll be able to use the same functions without writing more code.
Icing on the cake, if those functions are needed in more of your programs you can just make a library for them, and you'll have them always available.
Global variables:
Back when I was a beginner, I thought this was a great way for passing around data I needed in many points of my program. Turns out there are many other ways to pass around stuff, and the only things global variables do is giving people headaches, since if you go to a random point of your program you'll never know when that value was last used or edited, and tracking it down will be a pain. Try to avoid them whenever possible.
If your functions need to return or modify multiple values, either make a class with those values and pass them down as a parameter, or make the function return multiple values (with named tuples) and assign those values in the caller code.
Version Control
This doesn't directly improve readability, but helps you do all the above. Whenever you do some changes, commit to version control (a local Git repository will be fine enough), and if something doesn't work, look at what you changed or just roll back! This will make refactoring your code way easier, and will be a safety net if you accidentally break stuff.
Keeping all this in mind will allow you to write clear and more effective code, and will also help you find possible mistakes faster, as you won't have to wade through gigantic functions and messy variables.
Best Answer
There are multiple ways to structure your application:
For all these topics there are entries in the pattern section of the Flask documentation.