Nested resources¶
To demonstrate a RESTful nesting, let’s suppose we are designing a cooking
website and you want to list all your recipes. That’s the index action.
This means we need:
a
recipes.indexviewa
Recipemodela
RecipeControllercontroller class to handle the logic of what is viewed
To create the index page and wire up the route, controller, and model:
flask make:view recipes.index --route /recipes --controller RecipeController --model Recipe
This one (really lonnnnng….) command creates five files and updates three files 😮
Created files¶
app/templates/recipes/index.html— the recipes index viewapp/controllers/recipe_controller.py— the controller classapp/routes/recipes/__init__.py— the recipes blueprint packageapp/routes/recipes/routes.py— the recipes routes fileapp/models/recipe.py— the Recipe model
Updated files¶
app/controllers/__init__.py— registersRecipeControllerapp/models/__init__.py— registersRecipeapp/__init__.py— registers the recipes blueprint increate_app
You didn’t think I was going to make you keep putting everything in mains did you?
Now you know why I wrote this package — that’s a lot to wire up just to get one
model-backed view.
But wait, it get’s better!! The above is just too much to type so Flask-Commands can infer the route, controller, and model based off of the dotted name. That means this really long command can be written as:
flask make:view recipes.index -rcm
If you use -r, -c, and -m (in any order), Flask-Commands assumes the
standard setup above and does exactly the same thing.
To create the show page for a single recipe, you could write:
flask make:view recipes.show --route '/recipes/<int:recipe_id>' --controller RecipeController
Or, using the generators:
flask make:view recipes.show -rc
Notice, we didn’t include -m here. This is because the Recipe model already
exists. In this case, the only new file is the view template — the
route and controller class are updated to include the show logic. If you
forgot to remove the -m then you would receive a warning saying
that the recipe model already exists and was left alone (which is what you
want, expecally if you have gone in and made changes to the model to include
specific columns).
Nesting Models¶
This is where the package really shines ☀️! Let’s continue with our cooking website example and suppose we are going to allow users to make comments on the recipes. This is a one-to-many relationship (a single recipe might have many comments).
flask make:view recipes.comments.index --route '/recipes/<int:recipe_id>/comments' --controller RecipeCommentController --model Comment
When you run that command, Flask-Commands sets up the nested comments view and
route under recipes, creates the RecipeCommentController, and builds the Comment
model. The key part of the story is that the comments blueprint gets registered
inside the recipes blueprint (in app/routes/recipes/__init__.py). So weired
🤪, who would have thought to register a blueprint in another blueprint!!!!
That is one of the cool things I love about the Flask framework it’s not
opinionated which gives you the freedom to try new things. Ok your saying,
that’s great but why would I do this? By register the comments blueprint
in the recipes blueprint we get to use the dotted naming convention when
reference a route such as
url_for('recipes.comments.index', recipe_id=1).
Again the above is a lot to type and I don’t know that people will remember all the formating. Because I want to make everyone’s life easier (myself included) the generates come to save the day. Here is the shortened command that produces the same behavior.
flask make:view recipes.comments.index -rcm
Let’s dive a little deeper down this rabbit hole with another relationship. Suppose that on our cooking website we allow users to upload images when they make a comment. Three level, what Recipes → Comments → Images that’s hurts my brain just thinking about it 🧐. Here is how I would think about this.
Ok what do we need to have o have Recipes → Comments → Images? Let’s see, we need a new Image model, an image controller, an images blueprint, and images view folder for file like create.html. O ya, and I want to make sure I wire up the blueprint in such a way that I get my dotted naming convention to work like so
url_for('recipes.comments.images.something', recipe_id=#, comment_id=#)
The cool thing that Flask-Commands does for you is handle the nesting for you.
In order to have the dotted naming convention we register the images blueprint
inside comments at app/routes/recipes/comments/__init__.py, and comments is already
registered in recipes, which is registered with the application. That chain
gives you natural dot notation (similar to SQLAlchemy’s relationships) when you
reference the view as recipes.comments.images, so your url_for looks
like url_for('recipes.comments.images.index', recipe_id=1, comment_id=1).
Here is the simple command that sets everything up:
flask make:view recipes.comments.images.index -rcm
That’s it that is all you have to remember tell Flask-Commands that you want a view and how the structure/relationship should look with dots and throw in your generated flags of route -r, controller -c, and model -m (then preso 🪄 everything is built for you). But wait, it get’s better with controllers because we can make multiple views with one command!!!!