associated models in rails 3 - ruby-on-rails-3

I think I may be missing something here or my understanding of rails model association isnt quite there yet (still learning). I have two models, a recipe and a ingredient. A recipe has many ingredients, and when i fill my form in i want to post recipe and ingredient params to their respective dbs
Recipe Model
class Recipe < ActiveRecord::Base
attr_accessible :dish_name, :country_of_origin, :difficulty, :preperation_time
has_many :ingredients
end
Ingredients Model
class Ingredient < ActiveRecord::Base
belongs_to :recipe
attr_accessible :ingredients
end
recipe controller
def new
#recipe = Recipe.new
end
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.valid?
#recipe.save
redirect_to recipes_path, :notice => "Recipe sucessfully created."
else
flash.now.alert = "Please ensure all fields are filled in."
render :new
end
end
Ingredients Controller
def new
#ingredient = recipes.find(params[:recipes_id].ingredients.new
#recipe_id = params[:recipe_id]
end
Form
<%= form_for #recipe do |f| %>
<%= f.label :dish_name, "Dish Name" %>
<%= f.text_field :dish_name, :placeholder => "Enter Dish Name" %>
more fields here(wanted to keep as short as poss)
<%= f.label :ingredients, "Ingredients" %>
<%= f.text_field :ingredients , :class => :ingred, :placeholder => "Enter Ingredient Here" %><br>
<% end %>
Apologies for the size of the code, is anything else needed to see why i cannot create a recipe?

To start with, does your Ingredient model include a foreign key column like "recipe_id"?
Also, if the ingredients for a given recipe are just stored in a single text string, why not just make that a column in your recipe model instead of creating a second model? Alternatively, if you are going to have multiple ingredients tied to a single recipe, you should look into forms with nested model attributes. There's a good rails cast on it here.
Hope this helps.

Your code should be something link this...
---MODELS---
class Recipe < ActiveRecord::Base
attr_accessible :dish_name, :country_of_origin, :difficulty, :preperation_time, :ingredients_collection
has_many :ingredients
def ingredients_collection
self.ingredients.collect(&:name).join(" ,")
end
def ingredients_collection(ingredients)
ingredients.each do |ingredient|
i = Ingredient.create(:name => ingredient)
self.ingredients << i
end
end
end
class Ingredient < ActiveRecord::Base
belongs_to :recipe
attr_accessible :name # you should have some valid attribute name as it would conflict with the ingredients which is a method defined by the association (:has_many) in the instance of the Recipe...
end
----CONTROLLER----
def new
#recipe = Recipe.new
end
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save # no need to call the valid the validations would take care of it by themselves
redirect_to recipes_path, :notice => "Recipe sucessfully created."
else
flash.now.alert = "Please ensure all fields are filled in."
render :new
end
end
no need to define the controller for the ingredients...you would be defining the ingredient from the particular Recipe from the recipe instance only...
---FORM---
<%= form_for #recipe do |f| %>
<%= f.label :dish_name, "Dish Name" %>
<%= f.text_field :dish_name, :placeholder => "Enter Dish Name" %>
more fields here(wanted to keep as short as poss)
<%= f.label :ingredients, "Ingredients" %>
<%= f.text_field :ingredients_collection , :class => :ingred, :placeholder => "Enter Ingredient Here" %><br>
<% end %>
I would still suggest that you study about the virtual attributes and I guess it would fit in perfectly given your situation and needs...
Read about the virtual attributes from http://railscasts.com/episodes/16-virtual-attributes

Related

ROR - Trying to create objects using nested forms

I'm trying to create objects using nested forms in my bookings/new view. I've got a Flight model, a Booking model and a Passenger model.
The Flight model has_many :passengers and has_many :bookings
The Booking model belongs_to :flight and has_many :passengers
The Passenger model ``belongs_to :bookingandbelongs_to :flight`
Here is my view bookings/new.html.erb:
<h1>Create new booking</h1>
For flight <%= #flight.id %>: <%= #flight.departure_airport.code %> - <%= #flight.arrival_airport.code %>
on: <%= #flight.departure_date %><br><br>
<%= form_with model: #booking, local: true do |f| %>
<%= f.hidden_field :flight_id, value: #flight.id %>
<%= f.fields_for :passengers do |passenger| %>
<%= passenger.hidden_field :flight_id, value: #flight.id %>
<%= passenger.label :name %><br>
<%= passenger.text_field :name %><br>
<%= passenger.label :email %><br>
<%= passenger.email_field :email %><br><br>
<% end %>
<%= f.submit %>
<% end %>
Here is my Bookings Controller:
class BookingsController < ApplicationController
def new
#flight = Flight.find(params[:flight_id])
#num_of_pass = params[:num_of_pass].to_i
#booking = Booking.new
#num_of_pass.times do
#booking.passengers.build
end
end
def create
#flight = Flight.find(params[:booking][:flight_id])
#booking = #flight.bookings.build(booking_params)
if #booking.save
puts "SAVED"
else
puts #booking.errors.full_messages
puts "NOT SAVED"
end
end
def show
#booking = Booking.find(params[:id])
end
private
def booking_params
params.require(:booking).permit(:flight_id, passengers_attributes:[:name, :email, :flight_id])
end
end
The error I get is that "the passengers booking can't be blank"
If I build the booking, then save it, then build the passengers, it works fine, but I'm trying to use the strong parameters that are nested.
The problem seems to be that the booking model isn't being saved prior to creating the passengers.
I've been banging my head against this for hours and hoping someone can give me some insight
Thanks
My problem was that I had validations in my Passenger model. It wouldn't build because I had validates :booking_id, presence: true
Does anyone know how to keep the validations, but also allow it to build?

Rails Form Objects with multiple nested resources

7 Patterns to Refactor Fat ActiveRecord Models - here is a great article about different refactoring approaches using PORO. Under the 3rd caption there is a Form Object pattern, which I really liked and already implemented in one of the projects. There is only an example using one nested resource, but I would like to implement this pattern for multiple nested resources. Maybe someone here had already dealt with this? I don't necessarily need any code examples, just the basic idea would be fine.
Update
Consider this example. I have two models.
class Company
has_many :users
accepts_nested_attributes_for :users
end
class User
belongs_to :company
end
In case of one nested user for company using Form Object Pattern I would write the following:
<%= form_for #company_form do |f| %>
<%= f.text_field :name %>
<%= f.text_field :user_name %>
<%= f.submit %>
<% end %>
Form Object
class CompanyForm
include Virtus
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :company, :user
def user
#user ||= company.users.build
end
def company
#company ||= Company.new
end
def submit(params={})
company.name = params[:name]
user.name = params[:user_name]
persist!
end
private
def persist!
company.save!
user.save!
end
end
But what if I have a form, where a company with multiple users can be created. The usual approach is to write it like this, using nested_form:
<%= nested_form_for #company do |f| %>
<%= f.text_field :name %>
<%= fields_for :users, do |user_form| %>
<%= user.form.text_field :name %>
<% end %>
<%= f.link_to_add "Add a user", :users %>
<%= f.submit %>
<% end %>
What I am asking is how do I implement that Form Object Pattern in this case?
the rails fields_for helper checks for a method in this format: #{association_name}_attributes=
so, if you add this method to CompanyForm:
def users_attributes=(users_attributes)
# manipulate attributes as desired...
#company.users_attributes= users_attributes
end
def users
company.users
end
the fields_for generators will generate the nested users fields for a CompanyForm as if it were a Company. the above could be rewritten as a delegation since nothing is happening in the methods:
delegate :users, :users_attributes=, :to => :company, :prefix => false, :allow_nil => false

Rails nested form on HABTM: how to prevent duplicate entry?

I have simple app with 3 tables in DB and 'many-to-many' relationships.
# Model Employee
class Employee < ActiveRecord::Base
has_and_belongs_to_many :phonenumbers
accepts_nested_attributes_for :phonenumbers, :allow_destroy => true
attr_accessible :last_name, :first_name, :middle_name, :phonenumbers_attributes
end
# Model Phonenumber
class Phonenumber < ActiveRecord::Base
has_and_belongs_to_many :employees
attr_accessible :number
accepts_nested_attributes_for :employees
end
I have 'employees_phonenumbers' join-table with 'employee_id' and 'phonenumber_id' columns.
# View
<%= form_for #employee, :url => { :action => :create } do |f| %>
<%= f.label "Last name" %>
<%= f.text_field :last_name %>
<%= f.label "First name" %>
<%= f.text_field :first_name %>
<%= f.label "Middle name" %>
<%= f.text_field :middle_name %>
<%= f.fields_for :phonenumbers do |phonenumber| %>
<%= phonenumber.label "Phone number" %>
<%= phonenumber.telephone_field :number %>
<% end %>
<%= f.submit "Create" %>
<% end %>
# Controller
def create
#employee = Employee.new(params[:employee])
#employee.save ? (redirect_to :action => :index) : (render "new")
end
Now if I create a user: 'John' with phone number '555', it's OK.
But if I want to create a user 'Larry' with the same phone number '555', there's a dublicate of '555' entry in the DB.
How do I prevent this?
UPDATE: My logic is: If there is number '555', then do not create a new one, use existing. If there is no such a number, then create a new one and use it.
in employee.rb:
before_save :get_phonenumbers
def get_phonenumbers
self.phonenumbers = self.phonenumbers.collect do |phonenumber|
Phonenumber.find_or_create_by_number(phonenumber.number)
end
end
I have found its working
You can use rails validation to check uniqueness of record.
In your model phonenumber.rb put following line,
validates_uniqueness_of :column_name
It will ensure that Phonenumber will have unique phone_numbers only.
Now in controller you can check phone_number from params and if number is already exist then we will delete nested attributes fron params so that Phonenumber record wont generate.
def create
#phone_number = Phonenumber.where(:number=>params[:employee][:phonenumber][:number])
if #phone_number.any?
params[:employee].delete(:phonenumber)
#employee = Employee.new(params[:employee])
if #employee.save?
#employee.phonenumber = #phone_number.first
redirect_to :action => :index
else
render "new"
end
else
#employee = Employee.new(params[:employee])
#employee.save ? (redirect_to :action => :index) : (render "new")
end
end

how to insert same data into two tables using nested forms

I am using nested forms to insert data into two tables (user and address).I want to have my users email id in both the table, but the user should enter the email id once. Here is my current view
<%= form_for #user do |f| %>
<%= f.text_field(:name, :size => 20) %>
<%= f.text_field(:email, :size => 20) %>
<%= f.fields_for :address do |r| %>
<%= r.text_field(:street, :size => 20) %>
<%= r.text_field(:city, :size => 20) %>
<% end %>
<%= f.submit "Create" %>
<% end %>
In my nested section "address" i once again want the email field, but i don't want a repeated text field for email. How can i use the email section inside the nested form "address"? I have tried using hidden form fields, but it didn't worked. Can somebody help please.
EDIT
Controller(Only question related parts)
class UsersController < ApplicationController
def new
#domain = Domain.find(params[:id])
#user = #domain.users.build
#title = "Add User"
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Welcome!"
redirect_to manageDomain2_path(:id => #user.domain_id)
else
#title = "Add User"
redirect_to addNewUser_path(:id => #user.domain_id)
end
end
I have tried with:
#user = User.new(params[:user][:address_attributes].merge!(:email => params[:user][:email]))
When using:
#user = User.new(params[:user][:address].merge!(:email => params[:user][:email]))
I am getting error "Undefined method merge".
User Model (Question related portion only)
class User < ActiveRecord::Base
attr_accessible: :email, :domain_id, :address_attributes
belongs_to :domain
has_one :address
accepts_nested_attributes_for :address
end
Address Model (Full)
class Address < ActiveRecord::Base
attr_accessible :user_id, :email, :street, :city
end
When i use
belongs_to :user
in address model i get syntax error. So i have tried without using it. the user_id, street, city is getting proper values but email email field is not getting any thing.
You can just add it to the params hash from your controller:
params[:user][:address].merge!(:email => params[:user][:email])

how to update multiple models with rails form

I have following two models
class Office < ActiveRecord::Base
has_many :locations, :dependent => :destroy
end
class Location < ActiveRecord::Base
belongs_to :office
end
I have a new.html.erb for the office model and the below code in OfficeController
def create
#office = Office.new(params[:deal])
if #office.save
redirect_to office_url, :notice => "Successfully created office."
else
render :action => 'new'
end
end
How can I add fields for Location model in new.html.erb of Office?
I want to be able to have fields for locations in the same form.
You'll have to use nested attributes to do this. Fortunately, Rails makes it pretty easy. Here's how to do it:
First, signify to Office that you're giving it Location fields as well by adding this line:
accepts_nested_attributes_for :location.
Now, in new.html.erb, add the fields that you want. Say we want to have city and state:
<%= f.fields_for :location do |ff| %>
<%= ff.label :city %>
<%= ff.text_field :city %>
<%= ff.label :state %>
<%= ff.text_field :state %>
<% end %>
That's it!