Ruby on Rails - Edit/Update creating new records i

2019-07-30 14:17发布

问题:

I have seen the other links to this similar question but none of them seem to work in my case.

My update action is creating new records in mysql database

My app consists of 3 models

  1. FashionModel
  2. ModelProfile and
  3. Measurement

They are defined as follows:

class FashionModel < ActiveRecord::Base
  has_secure_password
  has_one :model_profile
  has_one :measurement
  accepts_nested_attributes_for :model_profile
  accepts_nested_attributes_for :measurement
end


class ModelProfile < ActiveRecord::Base
  belongs_to :fashion_model
end


class Measurement < ActiveRecord::Base
  belongs_to :fashion_model
end

The fashion_model_controller.rb is as follows:

class FashionModelsController < ApplicationController
  def new
    @fashion_model = FashionModel.new
  end

  def create
    @fashion_model = FashionModel.new(fashion_models_sign_up_params)
    if @fashion_model.save
      flash[:success] = "Welcome to Meriad"
      session[:fashion_model_id] = @fashion_model.id
      redirect_to edit_fashion_model_path(@fashion_model)
    else
      render 'new'
    end
  end

  def edit
    @fashion_model = FashionModel.find(params[:id])
    @fashion_model.build_model_profile
    @fashion_model.build_measurement
  end

  def update
    @fashion_model = FashionModel.find(params[:id])
    if @fashion_model.update(fashion_models_edit_params)
      flash[:success] = "Saved!"
      redirect_to fashion_model_path(@fashion_model)
    else
      render 'edit'
    end
  end

  def show
    @fashion_model = FashionModel.find(params[:id])
  end

end

The fashion_model_edit_params are

def fashion_models_edit_params
    params.require(:fashion_model).permit(:first_name,
                                          :last_name,
                                          :email,
                                          :password,
                                          model_profile_attributes: [:id,
                                                                     :location,
                                                                     :bio, :gender,
                                                                     :phone_number,
                                                                     :rate,
                                                                     :profile_image,
                                                                     :birthdate],
                                          measurement_attributes: [:id,
                                                                   :feet,
                                                                   :inches,
                                                                   :bust,
                                                                   :waist,
                                                                   :hips,
                                                                   :dress,
                                                                   :shoes,
                                                                   :hair,
                                                                   :eyes])
  end

I want something on these lines:

  • The fashion model signs up to the app through new.html.erb (which is stored in the fashion_models table)

  • The index.html.erb contains a list of all fashion_models with an option to edit information (update their profile)

  • The edit.html.erb contains fields from the model_profiles table as well as the measurements table, both of which have the foreign keys to the fashion_models table.

My fashion_models/new.html.erb template is pretty straightforward containing first_name, last_name, email, password.

My fashion_models/edit.html.erb template is something like:

<%= form_for @fashion_model do |f| %>
  # fashion_model fields here

  <%= f.fields_for :model_profile do |t| %>
    # model_profile fields here
  <% end %>

  <%= f.fields_for :measurement do |t| %>
    # measurement fields here
  <% end %>

<% end %>

Now, whenever I edit a fashion_model/:id, the model_profile and measurement create a new record in the database rather than updating the existing record. Also, when I am on the Edit Profile page, none of the fields pre populate with the existing data. I have to manually enter all the data again.

First, I thought it was because of the build methods, but when I remove them, the fields_for do not display.

Appreciate any help!

回答1:

You're probably making a mess with their routes.

It works like this:

  • form_for calls @fashion_model.new_record?

  • IF True: It will submit to POST /fashion_model, and create an object.

  • IF False: It will submit to PUT /fashion_model/:id, and update an object.

As you can see, it just depends if the object already exists in the database or not. So, check again, where you are using the form_for and which object you are passing it.

For questions with the routes, see: http://guides.rubyonrails.org/routing.html



回答2:

So I finally figured out a way to do this. Hope it helps someone.

My problem was that I had 2 fields_for inside one form_for and I needed to update 2 models with to these forms.

I tired using callbacks like after_action :create_model_profile and after_action :create_measurement. And in both of these methods I tried to save fashion_model_id as the :id of the newly created fashion_model.

But since callbacks are considered as code smells, I wanted a better solution.

Instead of creating a form_for @fashion_models, I wrote a helper method as follows

# app/helpers/form_helper.rb
module FormHelper
  def setup_fashion_model(fashion_model)
    fashion_model.model_profile ||= ModelProfile.new
    fashion_model.measurement ||= Measurement.new
    fashion_model
  end
end

and in my form_for I used something like

form_for(setup_fashion_model(@fashion_model)) do |f|
  ...
end

This created a new instance of ModelProfile and Measurement if it did not persist in the database.

It then of course solved the problem of pre populating the fields which contained data.

This was it. Simple solution, but I'd been there scratching my head. Hope it helps.