Digging into Python - ChiPy Mentorship Part 3
In my last update, I went through setting up the models for Recetario. After discussing with Steve, we decided to move on to making the views work the way we want them. This involved primarily creating forms to save recipes when a user logs in. We took an iterative approach to this by first making individual forms for
Instruction, then figuring out how to combine them into one big form. I appreciated this approach because I got to see more how individual models are working together to achieve the end result. This step was a bit challenging and required a lot of scouring documentation to get it just right. I am really proud of being able to work through this and get to the core of what my app does which is saving a recipe. Below is the end result.
Let’s look at some code. In my last post I shared the models that I created, below I am creating form objects using Django’s generic form views. These are very helpful in that Django will generate fields based on the attributes for my objects:
In the above you can see that I’m importing the
ModelForm module as well as
inlineformset_factory. These will help me create the forms in my view and template. First I utilize
ModelForm to create form objects, in this case
InstructionForm. The model gets referenced along with the fields that I want to expose. Later on in my template, these fields will show up as individual input boxes. At the very end of my code I’m creating inline formsets which are like a form object but specifically use to associate models when there is a foreign key relationship. I created a formset for both
Instruction in this case since they both have a relationship to
The views were the more complex part of this. It took a few tries and unfortunately most of the examples I was able to find were for on foreign key relationship, but I’m working with two. Let’s take a look at the
RecipeCreate view which is used when a user wants to add a completely new recipe:
To begin, I am using Django generic view,
CreateView which is used specifically for making a form to create an object. In this case it is for creating a
Recipe object, the rest of the logic is so that I can associate
Instruction data to it. I referenced the model (
Recipe) as well as the fields I want to expose below it.
CreateView has methods that can be overridden.
get_success_url is used so that I can give the view somewhere to go once the object is created. I am using
reverse_lazy here to get the recipe detail URL after the object is created. It is my understanding this is used when the url details aren’t immediately known, as with a new object. Within
get_context_data this is where I am getting information from the two formsets I created. The
Instruction objects along with their attributes are assigned to data here for use in saving the form. With
form_valid, I am utilizing this data in order to get my list of ingredients and instructions. Here I use
transaction.atomic() to set up the objects to be saved, in this case multiple ingredients and instructions, so that they can be committed to the database in a single transaction.
To utilize this view, I needed to reference it in
urls.py by utilizing
as_view on the view class and passing the template that I want to use.:
Finally, in the template, I loop through the forms so that it will render the fields for the models. I have condensed the below to include only the fields for ingredients:
There are various things going on here. The main part of this is that
form.as_p will render the main
Recipe form. Further down I am looping through
for form in ingredients.forms in order to get my fields for an
ingredient_formset_row classname I used on each
tr that will allow the rows to be grouped up and passed to the
RecipeCreate view for saving the transaction.
Now I can serve up this heaping bowl of code soup in order to create tons of recipes for my app! It took some work and head scratching but I’m really happy with how this is turning out. Taking a break from all this back end work to move on to styling my templates so that they are a bit more user friendly. Stay tuned for the next update.