Flat vs Nested with make:model#
Some model names describe one obvious structure. Others can describe more than one valid resource shape, and that is where this chapter gets interesting.
In the last chapter we used make:model with --crud to build a simple
model-backed resource. That workflow is easy to read when the model is one
word like Recipe.
flask make:model Recipe --crud
However, not every model name is this simple. As we have seen before, we might
have multiple words describing a single model, like ShoppingList. Other
times, the words might describe a nested relationship, like
Recipe -> Ingredient.
This chapter explains how make:model handles these multi-word scenarios from the model-first side.
Choose Flat or Nested with --crud#
Choose Flat or Nested with ``--crud`` Unwatched
How make:model --crud handles a multi-word model name when it could represent either one flat resource or a nested model chain.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
In the prior chapter we saw that adding --crud to make:model
instructs Flask-Commands to build the model and wire RESTful scaffolding
around it.
In the last chapter you saw this with a simple one-word model, Recipe. In
that case, there is only one structure choice. When you provide make:model
with a multi-word segment, we introduce a bit of ambiguity as we will see in
this section.
If you are following this tutorial from the controller chapters, this section will feel familiar as it mirrors the Flat or Nest with the -m Option section, but from the model-first side.
In the controller-first workflow, we explored the multi-segment controller with the following command:
flask make:controller RecipeIngredientController --crud -m
This command asks Flask-Commands to create a controller, generate a model from the controller name, and wire RESTful scaffolding around the result.
The model-first version of the above command is shorter:
flask make:model RecipeIngredient --crud
This reads more directly:
Make a
RecipeIngredientmodel-backed resource and wire RESTful scaffolding around it.
The command is easier to read and remember; however, the multi-word segments introduce a naming question.
should the word segments stay together as one model?
or should the route wiring show a parent-child relationship based on the word segments?
This uncertainty results in the following terminal prompt:
Detected nested model structure:
1) (flatten model) = RecipeIngredient
2) (nested model chain) = Recipe -> Ingredient
Choose model structure (1/2, flat/nest) [1]:
In this case, Flask-Commands can create one flat model,
RecipeIngredient, or it can generate the nested model chain,
Recipe -> Ingredient.
If Recipe is already registered, then the prompt becomes:
Detected nested model structure:
1) (flatten model) = RecipeIngredient
2) (nested leaf model) = Ingredient
Choose model structure (1/2, flat/nest) [1]:
Now Flask-Commands recognizes Recipe as an existing parent model. The flat
choice still keeps RecipeIngredient together, but the nested choice only
needs to generate the missing child model, Ingredient.
Skipping the Prompt with --flat or --nest#
Skipping the Prompt with ``--flat`` or ``--nest`` Unwatched
How to bypass the interactive structure prompt when you already know whether the generated resource should be flat or nested.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
If you know which structure you want, you can skip the prompt by using
either --flat or --nest.
Choosing Flat for One Multi-Word Model#
Choosing Flat for One Multi-Word Model Unwatched
How --flat keeps a model name like ShoppingList together and generates the matching controller, routes, templates, and URL shape.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
Use --flat when the words describe one model:
flask make:model ShoppingList --crud --flat
Make Controller Equivalence
Recall this is the model-first equivalent of what we saw in Choose Flat for One Multi-Word Model with the following command:
flask make:controller ShoppingListController --crud -m --flat
In one command you end up with the flat ShoppingList resource, which is
fully wired up with a new controller, routes, and views.
model class:
ShoppingListmodel file:
app/models/shopping_list.pycontroller class:
ShoppingListControllercontroller file:
app/controllers/shopping_list_controller.pyroute package:
app/routes/shopping_lists/template folder:
app/templates/shopping_lists/URL shape:
/shopping-lists
Choosing Nest for a Parent-Child Resource#
Choosing Nest for a Parent-Child Resource Unwatched
How --nest turns a name like RecipeIngredient into a child resource under Recipe, including the nested controller, route package, and URL shape.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
Use --nest when the words describe a relationship:
flask make:model RecipeIngredient --crud --nest
Make Controller Equivalence
That is the model-first equivalent of:
flask make:controller RecipeIngredientController --crud -m --nest
In this one command you tell Flask-Commands to make an Ingredient model as
a nested resource under Recipe and to wire RESTful resources around
Ingredient.
model class:
Ingredientmodel file:
app/models/ingredient.pycontroller class:
RecipeIngredientControllercontroller file:
app/controllers/recipe_ingredient_controller.pyroute package:
app/routes/recipes/ingredientstemplate folder:
app/templates/recipes/ingredients/URL shape:
/recipes/<int:recipe_id>/ingredients/<int:ingredient_id>
This happens independently of Recipe. If Recipe is not built then
Flask-Commands also builds out the missing parent models, in addition to the
child model, Ingredient. While this is helpful, it leaves the parent
resource without RESTful routes because the RESTful scaffolding is only
applied to the child model. In order for Recipe to also have all the
RESTful routes you would need to run make:model on Recipe first before
running make:model on the combined RecipeIngredient.
Why do we need to nest?
You might ask, ‘Why do we even need to nest? I want an ``Ingredient`` model so why not just run ``flask make:model Ingredient –crud``?
The answer is, you can if you don’t care about having routes that show the nested relationship. This approach would result in URLs like these:
/recipes/<int:recipe_id>/ingredients/<int:ingredient_id>
instead of
/recipes/<int:recipe_id>/recipes/<int:recipe_id>/ingredients/<int:ingredient_id>
Use make:controller for Namespacing#
Use ``make:controller`` for Namespacing Unwatched
Why namespaces are controller and route organization rather than model structure, and why make:controller is the clearer tool for that job.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
Namespaces are different from nested models.
A namespace is not a data structure. It is an organizing prefix for part of the application. Because of that, namespacing belongs more naturally to the controller and route shape than to the model shape.
We saw this in
Multi-Word Namespaces and
Public Pages and Private Tools
with TestKitchenRecipeController.
The important idea is that TestKitchen is not a model. It is a section of
the application where test kitchen users can manage recipes.
So if we want this structure:
TestKitchen / Recipe
I would rely on make:model for the Recipe model and make:controller for the TestKitchen namespace. In other words, I would use the following commands:
flask make:model Recipe
flask make:controller TestKitchenRecipeController --crud
The first command creates the model Recipe.
The second command creates a namespaced RESTful controller around the already
registered Recipe model.
Flask-Commands reads TestKitchenRecipeController like this:
TestKitchenis the namespaceRecipeis the registered model-backed resourceControllertells Flask-Commands this is controller structure
So the URL shape becomes:
/test-kitchen/recipes/test-kitchen/recipes/<int:recipe_id>
There is no TestKitchen model, and there is no
<int:test_kitchen_id> route parameter. TestKitchen is just the namespace
that organizes this part of the application.
As we will see in the next section, once the namespace exists we can return
to make:model to add additional child models.
Build Nested Resources One Level at a Time#
Build Nested Resources One Level at a Time (make:model) Unwatched
How to grow nested model-first resources from the top down so each registered parent is available before adding the next child.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
Now let’s build multiple child resources on top of our TestKitchen / Recipe
structure.
Suppose test kitchen users need to manage recipe steps and tips before a recipe is published. In addition, suppose the public can only see a list of all the published recipes and view each individual recipe.
That overall structure looks like this for the test kitchen users:
TestKitchen / Recipe -> CookStep -> Tip
For the public, the URLs look like this:
/recipesall recipes/recipes/<int:recipe_id>an individual recipe
To build this out from start to finish in a new project I would use the following commands:
flask make:model Recipe
flask make:view recipes.index -rc
flask make:view recipes.show -rc
flask make:controller TestKitchenRecipeController --crud
flask make:model TestKitchenRecipeCookStep --crud --nest
flask make:model TestKitchenRecipeCookStepTip --crud --nest
The first command creates the top-level model
registered model:
Recipe
The second and third commands build out the wired up views for showing all recipes and an individual recipe.
controller:
RecipeControllerURL shape:
/recipesURL shape:
/recipes/<int:recipe_id>
The fourth command adds the RESTful actions around the namespaced
Recipe model:
namespace:
TestKitchenregistered model:
Recipecontroller:
TestKitchenRecipeControllerURL shape:
/test-kitchen/recipes
Then we return to make:model for the first child model, CookStep:
flask make:model TestKitchenRecipeCookStep --crud --nest
Flask-Commands reads that command like this:
namespace:
TestKitchenregistered parent:
Recipegenerated child model:
CookStep
So it builds:
model:
CookStepcontroller:
TestKitchenRecipeCookStepControllerURL shape:
/test-kitchen/recipes/<int:recipe_id>/cook-steps
Then we add the next child model, Tip:
flask make:model TestKitchenRecipeCookStepTip --crud --nest
Now Flask-Commands sees both Recipe and CookStep as registered parents:
namespace:
TestKitchenregistered parents:
RecipeandCookStepgenerated child model:
Tip
So it builds:
model:
Tipcontroller:
TestKitchenRecipeCookStepTipControllerURL shape:
/test-kitchen/recipes/<int:recipe_id>/cook-steps/<int:cook_step_id>/tips
The order matters.
If you skip the CookStep model generation and jump straight to:
flask make:model TestKitchenRecipeCookStepTip --crud --nest
Flask-Commands does not know that CookStep should be a parent model to
Tip. Instead, it will read the remaining words as one child model and
generate CookStepTip.
So the pattern is:
Build the structure from the top down. Use
make:controllerfor the namespace surface, then return tomake:model --crud --nestwhen you are ready to add one additional child at a time.