Search code examples
ruby-on-railsmodel-view-controllermodelform-with

How to use form_with in Rails for multiple models that don't have controllers


I am trying to create recipe app that users can take. Part of the app is an admin users getting to create the recipes and instructions.

  • There is a User model/table with all the users. This has a controller.

  • There is also a recipe table with all the recipes created.

  • Finally there is an instructions table that are each associated with a recipe

What I am trying to make happen is the admin user visits a page to create a recipe. This page also has a list of existing recipes. The user can click on one of the recipes which brings the user to an edit page where they can edit the instructions for the recipe. The edit page shows a list of instructions. You can also add an instruction.

The problem is the form_with on the recipe create page is showing an error:

undefined method `recipes_path'

I don't have a recipe controller. The only controller is the user controller. Inside this controller I have defined an admin action a create_recipe action an edit_recipe action and an update_recipe action:

def admin
        @recipe = Recipe.new(
            recipe_params
        )

        @Recipes = Recipe.all

    end

    def create_recipe

        if @recipe.save
            redirect_to root_url
        else
            render 'new'
        end

    end

    def edit_recipe
        @recipe = Recipe.find(params[:id])
    
        @recipe_instructions = RecipeInstructions.where(recipe_id: @recipe.id) 

    end

    def update_recipe
        @recipe = Recipe.find(params[:id])
        @recipe_instructions = RecipeInstructions.where(recipe_id: @recipe.id) 


        if @recipe.update(recipe_params)
           redirect_to root_url
        else
           render :edit
        end

The routes look like this:

  get 'admin', to: 'users#admin', as: :admin 
  get 'recipes/:id/edit', to: 'users#edit_recipe', as: :edit_recipe_instructions
  post 'recipes/:id/update', to: 'users#update_recipe', as: :update_recipe_instructions

Here is the view of the recipe page:

<div id = "signup-form-container">
        <%= form_with model: @recipe do |newRecipe| %>

        
                    <h3 class="signup-form-heds"> <%= newRecipe.label :recipe_name, "Recipe name" %></h3>
                    <%= newRecipe.text_field :recipe_name, class: "form-inputs form-text-inputs"%>
                
                <%= newRecipe.submit "SAVE", class: "btns submit-btns", id: "signup-submit-btn" %>
            <% end %>
        </div>

Though I get the error. I'm also worried a similar error will happen with the instructions page because I'm using a similar form, but with multiple models.

<%= form_with model: [@recipe, @recipe_instructions], url: update_recipe_instructions_path(@recipe) do |newRecipe| %>


            
                     <%= newRecipe.fields_for :recipe do |recipeData| %>
                       <h3 class="signup-form-heds"> <%= recipeData.label :recipe_name, "Recipe name" %></h3>
                      <%= recipeData.text_field :recipe_name, class: "form-inputs form-text-inputs"%>
                    <% end %>


                     <%= newRecipe.fields_for :recipe_instructions do |recipeInstructions| %>
                       <h3 class="signup-form-heds"> <%= recipeInstructions.label :instruction, "Instruction" %></h3>
                      <%= recipeInstructions.text_field :recipe_instructions , class: "form-inputs form-text-inputs"%>
                    <% end %>

                    
        
                <%= newRecipe.submit "SAVE", class: "btns submit-btns", id: "signup-submit-btn" %>
            <% end %>
        </div>

I'm thinking it's the fact that the models don't have controllers that's causing the issue?


Solution

  • <div id = "signup-form-container">
            <%= form_with model: @recipe do |newRecipe| %>
    
            
                        <h3 class="signup-form-heds"> <%= newRecipe.label :recipe_name, "Recipe name" %></h3>
                        <%= newRecipe.text_field :recipe_name, class: "form-inputs form-text-inputs"%>
                    
                    <%= newRecipe.submit "SAVE", class: "btns submit-btns", id: "signup-submit-btn" %>
                <% end %>
            </div>
    

    When you pass model: @recipe in the form, the resulted form action is '/recipes', that is the path to create a new recipe, but you don't have a path to create a recipe, or at least that's what I think, you're showing only three routes, one route to the admin, one to show the edit form and one to update the recipe, this last must be a patch or put route instead of post.

    Create a new route like:

    post '/recipes', to: 'users#create' # this must be a post route because you will create a new recipe
    

    Despite this could resolve your problem, this isn't the correct way to do what you are doing.

    In the Users controller you are creating a recipe, this is incorrect. In your project, you could have two controllers, one for the Users and one for the Recipes.

    Also, you could create the routes with:

    resources :users
    resources :recipes
    

    This a quickly way to create the 7 basic routes index, show, new, edit, create, update and destroy. See Rails routing from the outside

    If you have two different tables, one for recipes and one for instructions, in your Recipe model must have:

    has_many :instructions
    

    and in your Instructions model you must have:

    belongs_to :recipe
    

    Of course, you also must have the required Foreign Keys to make the relation between this two tables. And then, when you create a new recipe could do something like:

    @recipe.instructions << params[:instructions]
    

    See Active Record Associations