Flat vs Nested with make:controller#
Some controller names describe only one clear model shape. Others can describe more than one valid data structure, and that is where this chapter gets interesting.
In the last chapter we used -m in the simplest possible case:
flask make:controller RecipeController --crud -m
That command works cleanly because RecipeController points to one obvious
model: Recipe.
But not every controller name is that simple. In the last chapter we also saw that longer names can carry different meanings:
one data structure with a multi-word name
a namespace that organizes part of the app
a nested resource relationship
This chapter picks up from there. We already know that -m tells
Flask-Commands to generate model classes from the controller name. Now we need
to decide what kind of model shape Flask-Commands should generate when the name
could reasonably mean more than one thing.
To make that decision, Flask-Commands looks at the controller name segments and
compares them to the registered models. From there, it determines the possible
model-generation options. This is where you come in and guide the command with
one of two options: --flat or --nest. These options let you tell
Flask-Commands which model story the controller name should tell.
Flat or Nest with the -m Option#
Flat or Nest with the ``-m`` Option Unwatched
What the structure choice means inside the controller-first workflow so you can pick the layout that matches your app instead of accepting a default blindly.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
When you add the --crud flag you are telling Flask-Commands to build
RESTful scaffolding for the data structure. That means build:
controller methods
route handlers
templates for the
GETactions
In addition, we have seen that the -m flag adds model generation to that
process. Putting this together means that the command:
flask make:controller RecipeIngredientController --crud -m
asks Flask-Commands to generate model names from
RecipeIngredientController with RESTful scaffolding. In the prior
chapter, we ran this command without the -m option and with Recipe
as a registered model.
If we run this command in a new project the generated model name from
RecipeIngredientController can tell two different model stories:
flat:
RecipeIngredientnested:
Recipe -> Ingredient
So Flask-Commands prompts you to choose.
The exact prompt depends on which models are already registered.
If Recipe is not registered yet, Flask-Commands sees multiple child-like
segments:
Detected multiple child-like segments:
Recipe, Ingredient
1) (flatten resource model) = RecipeIngredient
2) (generate the following models) = Recipe, Ingredient
Choose model structure (1/2, flat/nest): [1]:
In this case, the flat choice generates one model, RecipeIngredient. The
nested choice generates two models, Recipe and Ingredient.
If Recipe is already registered, the prompt changes:
Detected nested models:
Recipe
1) (flatten resource model) = RecipeIngredient
2) (nested generated model) = Ingredient
Choose model structure (1/2, flat/nest): [1]:
Now Flask-Commands recognizes Recipe as an existing parent model. The flat
choice still generates RecipeIngredient, but the nested choice only
generates the missing nested structure Ingredient.
In both cases, -m is what creates the flat-or-nested question. Registered
models help Flask-Commands understand which words are already part of your app
and which words should be used to generate a model.
If you choose flat, Flask-Commands keeps the words together:
model generated:
RecipeIngredientcontroller class:
RecipeIngredientControllerURL shape:
/recipe-ingredients/<int:recipe_ingredient_id>
If you choose nested, Flask-Commands builds the relationship:
model story:
Recipe -> Ingredientcontroller class:
RecipeIngredientControllerURL shape:
/recipes/<int:recipe_id>/ingredients/<int:ingredient_id>
You can skip the prompt with either --flat or --nest:
flask make:controller RecipeIngredientController --crud -m --flat
flask make:controller RecipeIngredientController --crud -m --nest
Therefore, Flask-Commands can build out complicated data
structures under multiple setups (a registered or not registered Recipe).
If the new data structure contains the name
of an existing data structure, choose --flat and keep the words together
as one model. Conversely, if the controller name is describing a parent-child
relationship, choose --nest and let the relationship show in the
generated RESTful scaffolding.
Let’s revisit a command we showed in the prior chapter and use these new options to shorten the command.
Choose Flat for One Multi-Word Model#
Choose Flat for One Multi-Word Model Unwatched
How --flat keeps a multi-word controller resource together as one model-backed structure when the words describe a single thing.
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 previous chapter we used ShoppingListController as an example of a
controller whose model name needs more than one word.
A ShoppingList is one data structure. It is not a Shopping model with a
nested List model under it.
In Single Data Structures that are Multiple Words, we solved the problem by naming the model directly:
flask make:controller ShoppingListController --crud --model ShoppingList
That works because --model ShoppingList explicitly tells Flask-Commands
that ShoppingList should stay together as one model.
Without that instruction, Shopping becomes a namespace and List
becomes the RESTful resource, which is not the story we want for a
shopping list.
Now that we have -m and --flat, we can let Flask-Commands generate
the multi-word model from the controller name:
flask make:controller ShoppingListController --crud -m --flat
This command says:
--crud: build the RESTful controller, routes, and templates-m: generate the model from the controller name--flat: keepShoppingListtogether as one model
The generated structure is flat:
model class:
ShoppingListmodel file:
app/models/shopping_list.pycontroller class:
ShoppingListControllerroute package:
app/routes/shopping_liststemplate folder:
app/templates/shopping_listsURL shape:
/shopping-lists/<int:shopping_list_id>
The rule to remember here is:
To keep all the words before
Controllertogether as one data structure use--flatwith the-mgenerator flag.
Build Nested Resources One Level at a Time#
Build Nested Resources One Level at a Time (make:controller) Unwatched
Why building nested controller resources from the top down keeps generated parents, children, routes, and prompts easier to reason about.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
Now that ShoppingList is registered as one flat model, we can build nested
resources under it.
Suppose your cooking app wants to organize a shopping list by store, and then track ingredients inside each store.
That relationship looks like this:
ShoppingList -> Store -> Ingredient
I recommend building the relationship one level at a time:
flask make:controller ShoppingListController --crud -m --flat
flask make:controller ShoppingListStoreController --crud -m --nest
flask make:controller ShoppingListStoreIngredientController --crud -m --nest
As we have seen, the first command creates the top-level flat resource:
model:
ShoppingListcontroller:
ShoppingListControllerroutes:
/shopping-lists
The second command sees ShoppingList as a registered parent and generates
the next child:
parent model:
ShoppingListgenerated model:
Storecontroller:
ShoppingListStoreControllerroutes:
/shopping-lists/<int:shopping_list_id>/stores
The third command now sees both ShoppingList and Store as registered
parents and generates the next child:
parent models:
ShoppingListandStoregenerated model:
Ingredientcontroller:
ShoppingListStoreIngredientControllerroutes:
/shopping-lists/<int:shopping_list_id>/stores/<int:store_id>/ingredients
It is tempting to think that --nest will always split every remaining word
into its own model, especially because earlier we saw that
RecipeIngredientController --crud -m --nest can generate both Recipe
and Ingredient when neither model exists yet.
But once ShoppingList is registered, Flask-Commands has a parent to anchor
the relationship. If you skip the middle command and run:
flask make:controller ShoppingListController --crud -m --flat
flask make:controller ShoppingListStoreIngredientController --crud -m --nest
Flask-Commands reads the second command as a multi-word data structure like this:
registered parent:
ShoppingListgenerated child:
StoreIngredient
So you get:
ShoppingList -> StoreIngredient
not:
ShoppingList -> Store -> Ingredient
There is one more shortcut you might be tempted to try. What if you skip straight to the longest controller name?
flask make:controller ShoppingListStoreIngredientController --crud -m --nest
If none of those models are registered yet, Flask-Commands has no registered parent chain to anchor on. In that case, the nested choice generates each segment as its own model:
Shopping -> List -> Store -> Ingredient
That is probably not what you meant. A ShoppingList should be one
two-word data structure, not separate Shopping and List models.
Also notice that this command only creates one CRUD controller:
ShoppingListStoreIngredientController. The generated CRUD surface is for
the final nested resource, Ingredient. It does not give you separate CRUD
controllers for Shopping, List, or Store.
Flask-Commands uses the registered parents as an anchor and, when you provide
the option --nest, it takes the remaining word segments that come
after the registered parent chain and combines them to generate the new
child resource. Consequently, if we want Store to be its own parent in
the chain, we have to build up in order by generating and registering Store
before generating Ingredient under it.
The takeaway pattern to remember is:
Build nested resources one command at a time, from the top down. Run the parent resource command first, then run the next child resource command under that parent. Repeat this pattern for each child resource in the nested chain.
Each command registers the next model before the following command needs it.
Because every command includes --crud, every level also receives its own
controller, routes, and templates.
Combine Namespaces with Nested Model Generation#
Combine Namespaces with Nested Model Generation Unwatched
How controller namespaces and nested model generation can work together when part of the name organizes the app and part describes data relationships.
This video slot is planned for the series. Once a YouTube video is linked, it will expand and play directly on this page.
You can combine namespaces, nested resources, RESTful scaffolding, and generated models. The key is still to build the registered model chain in order.
In the previous chapter we introduced a multi-word namespace (review in
sections
Multi-Word Namespace and
Public Pages and Private Tools)
with TestKitchenRecipeController. Let’s keep using that idea and build a
deeper staff-only workflow inside the test kitchen namespace.
Suppose test kitchen users need to manage recipe steps and tips before a recipe is published.
That relationship looks like this:
TestKitchen / Recipe -> CookStep -> Tip
Start by creating the top-level model-backed resource:
flask make:controller RecipeController --crud -m
Public Index and Show Only Pages
In a more realistic application, you would just create a view page showing all the recipes and a single recipe resource for the public using the following two commands.
flask make:view recipes.index -rcm
flask make:view recipes.show -rc
Then create the test kitchen CRUD controller for the already existing
Recipe model:
flask make:controller TestKitchenRecipeController --crud
Now you can generate nested children inside the test kitchen namespace:
flask make:controller TestKitchenRecipeCookStepController --crud -m --nest
flask make:controller TestKitchenRecipeCookStepTipController --crud -m --nest
The first nested staff command sees:
namespace:
TestKitchenregistered parent:
Recipegenerated child:
CookStep
So it builds:
model:
CookStepcontroller:
TestKitchenRecipeCookStepControllerroutes:
/test-kitchen/recipes/<int:recipe_id>/cook-steps
The second nested staff command sees:
namespace:
TestKitchenregistered parents:
RecipeandCookStepgenerated child:
Tip
So it builds:
model:
Tipcontroller:
TestKitchenRecipeCookStepTipControllerroutes:
/test-kitchen/recipes/<int:recipe_id>/cook-steps/<int:cook_step_id>/tips
The rule is the same as before:
namespaces come before the registered model chain
registered models become parents
--nestgenerates the next child--crudgives each controller its RESTful scaffolding
As you have already seen, order matters.
First build the top-level model. Then add the namespaced controller around that model. Once the namespace is in place, generate each nested child one level at a time so the whole nested chain lives under that namespace.
That wraps up the controller-first path. We started with the controller name,
used --crud when we wanted all the RESTful actions, used -m when we
wanted model generation, and used --flat and --nest when the
controller name carries more than one possible meaning. The last part
was the key to let you choose whether Flask-Commands should keep words
together as one model or build a nested relationship from the registered
model chain.
Next we will look at the same choice from the model-first side. Instead of starting with a controller name and asking for a model, we will start with a model name and ask Flask-Commands to build the RESTful structure around it.