I have some problem with relationship in rails. My application has the relationship between the publisher and the Website.
When I add a site to the publisher using f.collection_select relationship is working properly. However, if I create a nested routing and am using
localhost:3000/publishers/8/sites/new
<% = Link_to ("Add site", new_publisher_site_path (publisher), class "btn btn-default navbar-btn")%
def new
#publisher = Publisher.find(params[:publisher_id])
#site = Site.new
end
It does not form a relationship.
If you mean relationship between models Site and Publisher you should use has_many, has_one, belongs_to or other associations rails assosiations.
and then you can write:
def new
#publisher = Publisher.find(params[:publisher_id])
#site = #publisher.sites // sites only associated with Publisher.find(params[:publisher_id])
end
Assuming relationship between publisher and site as publisher has_many :sites, and site belongs_to :publisher :
Do this in your site controller:
def new
#publisher = Publisher.find(params[:publisher_id])
#site = #publisher.sites.new
end
def create
#publisher = Publisher.find(params[:publisher_id])
#site = #publisher.sites.new(sites_params)
if #site.save
redirect_to publishers_path
else
render 'new'
end
end
Related
I am new to Mock/Stub, In my Grape API I have following logic where I add #course to the joins association
helpers do
def fetch_site
unless current_organisation
error!("401 Unauthorized", 401)
end
#site = current_organisation.sites.find(params[:site_id])
end
def fetch_certification_course
#course = Ihub::CertificationCourse.find(params[:certification_course_id])
end
end
post do
fetch_site
fetch_certification_course
#site.certification_courses << #course
present #site.certification_courses, with: Entities::CertificationCourse
end
In Site model I have following relationship
has_many :site_certifications, class_name: 'Ihub::SiteCertification'
has_many :certification_courses, through: :site_certifications
And how do I use Mock/stub to test this API properly? Specially this part " #site.certification_courses << #course"
This is what I have done for delete join association
The code to test
#site.certification_courses.delete(certification_course)
The test
#site = stub_model(Site)
expect(#site).to receive_message_chain('certification_courses.delete')
I'm using Rails 3.2.19 and Ruby 2.1.2. I've been googling around trying to figure this out, but perhaps I'm not searching for the right thing. Anyway, I'll try and be as concise as possible.
I have a few different models that all have a name attribute. In my views I want to somehow be able to access that name attribute regardless of the instance name passed into the view. Currently my various controllers create instances of their respective models. For instance:
class PagesController < ApplicationController
def show
#page = Page.find(params[:id])
respond_to do |format|
format.html
end
end
end
-
class ProductsController < ApplicationController
def show
#product = Product.find(params[:id])
respond_to do |format|
format.html
end
end
end
While I understand I could simply re-name the instances something generic, I was wondering if there was some way of accessing any/all instances while maintaining unambiguous instance names.
Basically something like this:
page.html.haml
%h1= resources[0].name #equates to #page.name
%h2= #page.some_other_attribute
or
product.html.haml
%h1= resources[0].name #equates to #product.name
%h2= #product.price
Where in each of the above resources[0] would be either #page or #product
You will have to define a route with an additional resource_type parameter to a generic controller or otherwise just include the resource_type into the url query parameter
/resources/product/17
or
/resources/17?resource_type=product
This will allow you to do the following in the controller
class ResourcesController < ApplicationController
def show
#resource = find_resource(params)
respond_to do |format|
format.html
end
end
private
def find_resource(params)
resource_klass = {
product: Product,
page: Page
}[params[:resource_type]]
resource_klass.find(params[:id])
end
end
Another Option would be to introduce another ResourceType Entity and define a polymorphic :has_one :belongs_to association to the actual resource entity (product, page). Then always search for ResourceTypes and load the polymorphic resource entity
class ResourceType < ActiveRecord::Base
belongs_to :resource, polymorphic: true
end
class Product < ActiveRecord::Base
has_one :resource_type, as: :resource
end
class Page < ActiveRecord::Base
has_one :resource_type, as: :resource
end
product_resource_type = ResourceType.create(...)
product = Product.create(resource_type: product_resource_type)
page_resource_type = ResourceType.create(...)
page = Page.create(resource_type: page_resource_type)
ResourceType.find(product_resource_type.id).resource
=> product
ResourceType.find(page_resource_type.id).resource
=> page
I figured this out after discovering instance_variables and instance_variables_get
Those methods will return all instance variables being passed into the view. From there I discovered that the :#_assigns instance variable contained the instances that I was looking for. So I iterated over them to find if any had the name attribute.
- instance_variable_get(:#_assigns).each do |var|
- if var[1].respond_to?("name")
%h1= var[1].name
There is probably a better way of accomplishing this, so if anyone has any opinions, they are welcome.
I got the following model classes:
class Movie < ActiveRecord::Base
has_and_belongs_to_many :actors
accepts_nested_attributes_for :actors, :allow_destroy => true
...
end
and
class Actor < ActiveRecord::Base
has_and_belongs_to_many :movies
...
end
movies/_form.html.haml view contains the nested form for actors:
...
= form_for #movie do |movie_form|
= movie_form.fields_for :actors do |actor_form|
= actor_form.text_field :id, "Id"
= link_to_remove_fields "remove", actor_form
= link_to_add_fields movie_form :actors
...
= movie_form.submit 'Save'
link_to_remove_fields and link_to_add_fields are helper methods performing javascript calls, adding new fields for new actors or removing actors.
The controller:
class MovieController < ApplicationController
#POST form
def create
#movie = Movie.new(params[:movie])
...
end
#GET nearly empty form with one field for actor
def new
#movie = Movie.new
1.times {#movie.actors.build}
respond_to do |format|
format.html
format.json { render json: #movie }
end
end
end
The problem:
When 'Save' is pressed (with only one actor with id=718, for example) and the form is submitted, the #movie = Movie.new(params[:movie]) line of the controller throws the following error:
Couldn't find Actor with ID=718 for Movie with ID=
I'm sure that there is entity with id=718 in the Actor database.
The easiest thing if you are trying to associating existing actors to a new movie is not to use accepts_nested_attributes at all.
One of the things you get with your actors associations is a actor_ids, which return the ids of the associated actors, or allows you to set the list of associated actors.
To submit an array via a rails form, the name of the input must end with [], for example if you have an input with name movie[actor_ids][] and value 1, then you'll get
{'movie' => {'actor_ids' => ['1']}}
in your controller. If you want to submit a second actor, just create another input with the same name movie[actor_ids][]. You could also use a select box with the multiple attribute set, but they can be a bit unfriendly for users especially when there are more than just a few options to pick from.
Unfortunately you can't use accepts_nested_attributes_for with has_and_belongs_to_many (see Trying to use accepts_nested_attributes_for and has_and_belongs_to_many but the join table is not being populated for example). You would probably be able to build something similar with a network of has_many :x through: :ys though.
I know the rails method find_by_name but what if instead of a name I have an object? Assuming I have #car and #brand objects. How can I search for all cars that have a specific brand object.
Something like #cars = Car.find_by_brand(#brand)
I have tried #cars = #brand.cars but that only seems to pull one parent model.
Edit - More info on code
Car controller:
has_and_belongs_to_many :brands
Brands model:
attr_accessible :name
has_and_belongs_to_many :cars
Brands Controller
def create
#car = Car.find(params[:car_id])
#brand = Brand.create
#brand.assign_attributes({ :name => params[:brand][:brands][:name] })
#brand.cars << #car
if #brand.save
redirect_to #car
else
flash[:notice] = "Error!"
redirect_to #car
end
end
def findcars
#brand = Brand.find_by_name(params[:brand_name])
#cars = #brand.cars
end
View
<%= link_to brand.name, findcars_car_brand_path(#car, brand_name: brand.name), method: "get" %>
Routes
resources :cars do
resources :brands do
member { get :findcars }
end
end
Tables - There is no model for the join table
brands (name:string)
cars (name:string)
brands_cars (brand_id:integer, car_id:integer)
Let's look at findcars first. .joins(:brands) is key as it allows you to narrow the query via brand conditions ('brands.attribute' => brand_value), while building Car objects.
The SQL will look something like this:
SELECT "cars".* FROM "cars" INNER JOIN "cars_brands" ON "cars_brands"."car_id" = "cars"."id" INNER JOIN "brands" ON "brands"."id" = "cars_brands"."brand_id" WHERE "brands"."id" = 1
and the implementation like this:
# If all you care about is the #cars - as the method name indicates
def findcars
#cars = Car.joins(:brands).where('brands.name' => params[:brand_name])
end
# If you need both variables
def findcars
#brand = Brand.find_by_name(params[:brand_name])
#cars = Car.joins(:brands).where('brands.id' => #brand)
end
Without the joins, your sql looks like this:
SELECT "cars".* FROM "cars" WHERE "brands"."id" = 1
This will break as 'brands' means nothing in this context. You need to give rails a little more detail so it can build the query properly.
Now the create method:
# #brand = Brand.create
# #brand.assign_attributes({ :name => params[:brand][:brands][:name] })
# params[:brand][:brands][:name] indicates something fishy with your form
# since this form is in the context of Brand, params[:name] should suffice
#brand = Brand.create(:name => params[:brand][:brands][:name])
Does redirect_to #car mean that the form is a Car-centric form? Using *form_for* in the correct context will simplify the object creation.
For 2 great overviews, check out:
http://guides.rubyonrails.org/active_record_querying.html
http://guides.rubyonrails.org/association_basics.html
Another newbie question.
The goal: each ingredient can have zero or more unit conversions tied to it. I want to put a link to creating a new unit conversion on the page that shows a specific ingredient. I can't quite get it to work.
Ingredient Model:
class Ingredient < ActiveRecord::Base
belongs_to :unit
has_many :unit_conversion
end
Unit Conversion Model:
class UnitConversion < ActiveRecord::Base
belongs_to :Ingredient
end
Unit Conversion Controller (for new)
def new
#ingredient = Ingredient.all
#unit_conversion = #ingredient.unit_conversions.build(params[:unit_conversion])
if #unit_conversion.save then
redirect_to ingredient_unit_conversion_url(#ingredient, #comment)
else
render :action => "new"
end
end
Relevant Routes:
map.resources :ingredients, :has_many => :unit_conversions
Show Ingredient Link:
<%= link_to 'Add Unit Conversion', new_ingredient_unit_conversion_path(#ingredient) %>
This is the error:
NoMethodError in Unit conversionsController#new
undefined method `unit_conversions' for #<Array:0x3fdf920>
RAILS_ROOT: C:/Users/joan/dh
Application Trace | Framework Trace | Full Trace
C:/Users/joan/dh/app/controllers/unit_conversions_controller.rb:14:in `new'
Help! I'm all mixed up about this.
Unit Conversion Controller for new and create should be:
def new
#ingredient = Ingredient.find(params[:ingredient_id])
#unit_conversion = #ingredient.unit_conversions.build
end
def create
#ingredient = Ingredient.find(params[:ingredient_id])
#unit_conversion = #ingredient.unit_conversions.build(params[:unit_conversion])
if #unit_conversion.save
flash[:notice] = "Successfully created unit conversion."
redirect_to ingredient_unit_conversions_url(#ingredient)
else
render :action => 'new'
end
end
Also, this screencast is a nice resource for nested resources.
has_many :unit_conversion
Should be pluralized since you're calling it with
#unit_conversion = #ingredient.unit_conversions.build
your controller
def new
#ingredient = Ingredient.all
should be calling #new to setup a new Ingredient or #find to grab an existing Ingredient.
#ingredient = Ingredient.new # returns a new Ingredient
or
#ingredient = Ingredient.find(...) # returns an existing Ingredient
Which one you choose is up to your requirements.
Also, this is a typo, right?
belongs_to :Ingredient
You might want to lowercase :ingredient