This one is driving me crazy. I've got a nested relationship between two models in my project, and I decided I did not want it to be shallow, since the child object (years) has no meaning outside the context of the parent (festivals).
So I sort of de-shallowed the relationship wherever I could find a reference to it, but I find myself unable to access the page to create a new child object.
Here's the url as I understand it should be: /festivals/1/years/new
from routes.rb:
resources :festivals do
resources :years
end
From years_controller.rb:
# GET festivals/1/years/new
# GET festivals/1/years/new.json
def new
#festival = Festival.find(params[:festival_id])
#year = #festival.years.build
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #year }
end
end
And the button users press to get to the New page (on the Show page for the parent object):
<%= link_to 'Add Year', new_festival_year_path(#festival), :class => 'btn' %>
That takes the user to the correct URL, but I get:
No route matches {:action=>"show", :controller=>"years", :festival_id=>#<Festival id: 7, name: "Improganza", founded: nil, logo: "", mission: "This is that one that people spend a lot of money t...", city: "Honolulu", state_code: "HI", country_code: "US", created_at: "2013-07-26 14:49:19", updated_at: "2013-07-26 14:49:19">}
I created a new Rails project and set up scaffolds using Akria Matsuda's nested_scaffold gem, just to compare that output with my code... the resulting files look as I've shown here. I have no idea what I might be missing.
Just for good measure, the output of my rake routes:
festival_years GET /festivals/:festival_id/years(.:format) years#index
POST /festivals/:festival_id/years(.:format) years#create
new_festival_year GET /festivals/:festival_id/years/new(.:format) years#new
edit_festival_year GET /festivals/:festival_id/years/:id/edit(.:format) years#edit
festival_year GET /festivals/:festival_id/years/:id(.:format) years#show
PUT /festivals/:festival_id/years/:id(.:format) years#update
DELETE /festivals/:festival_id/years/:id(.:format) years#destroy
festivals GET /festivals(.:format) festivals#index
POST /festivals(.:format) festivals#create
new_festival GET /festivals/new(.:format) festivals#new
edit_festival GET /festivals/:id/edit(.:format) festivals#edit
festival GET /festivals/:id(.:format) festivals#show
PUT /festivals/:id(.:format) festivals#update
DELETE /festivals/:id(.:format) festivals#destroy
GET /festivals(.:format) festivals#index
POST /festivals(.:format) festivals#create
GET /festivals/new(.:format) festivals#new
GET /festivals/:id/edit(.:format) festivals#edit
GET /festivals/:id(.:format) festivals#show
PUT /festivals/:id(.:format) festivals#update
DELETE /festivals/:id(.:format) festivals#destroy
Try this:
<%= link_to 'Add Year', new_festival_year_path(#festival.id, :class => 'btn' %>
or
<%= link_to 'Add Year', new_festival_year_path({festival_id: #festival.id}, :class => 'btn' %>
according to the error you're getting
:festival_id=>#<Festival id: 7, name: "Improganza", founded: nil, logo: "", mission: "This is that one that people spend a lot of money t...", city: "Honolulu", state_code: "HI", country_code: "US", created_at: "2013-07-26 14:49:19", updated_at: "2013-07-26 14:49:19">}
the router is getting your whole festival param as the input for :festival_id
I think you are merging together the #new and #year actions in the years_controller and that might be causing some problems.
# GET festivals/1/years/new
# GET festivals/1/years/new.json
def new
#festival = Festival.find(params[:festival_id])
#year = #festival.years.build
end
def create
#festival = Festival.find(params[:festival_id])
#year = #festival.years.create(...)
#...fill in the rest of the method...
end
You also should update your link:
<%= link_to 'Add Year', new_festival_year_path(festival_id: #festival), :class => 'btn' %>
I created a short quiz on nested resources that might be helpful.
The answer was fairly silly. In my Rails server log (which I need to train myself to pay more attention to), I saw the some lines indicating a problem in line 63 of my _form.html.erb partial.
That line was:
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
festival_year_path(#festival), :class => 'btn' %>
Oops. Why I ever decided the "Cancel" button should take you to a year (that, of course, would not exist) is beyond me. I changed it to festival_path(#festival) and it's all good.
Thanks, everyone, for your help. I'm a newcomer to StackOverflow and to Rails in general. It really makes me feel welcome that I got such quick responses!
Related
I'm having some weird problems with rails params. I have two models: Page and NewsItem.
Page: has_many :news_items
and
NewsItem: belongs_to :page
In my form for creating new news_item I have a list of radio buttons with which I can select a page to which newly created item will belong.
Everything is working correctly so far. What is bugging me is:
When I select first option (page has no parent -> value passed should be nil) the value actually passed in parameters for page_id is "on"!
So I have few questions:
Since I need to do some checking of the value of params for proper redirecting, I'm wondering can I count on page_id using value "on" to represent nil? Is this a standard way in rails to represent nil or is this specific to my setup? Is there a chance that this will change in future versions?
As you can see in hash from console on the bottom of the post, after selecting nil as a value to pass as foreign key (page_id), the value actually set as page_id is not nil but 0. Is this a good way of stating that this item does not belong to a page, or should I try to force the value to be nil? Should I be passing a value of zero instead of nil?
Is there a chance for a table to have a row with primary id of zero?
I'm running this in development, on rails 4.0.4 with sqlite
Here is the part of the form with radio buttons:
<ul>
<li>
<%= f.radio_button :page_id, nil %>
<%= f.label :page_id, t('page_no_parent'), value: nil %>
</li>
<% #parents.each do |page| %>
<li>
<%= f.radio_button :page_id, page.id %>
<%= f.label :page_id, page.name, value: page.id %>
</li>
<% end %>
</ul>
Here is the actual params hash from request:
Started PATCH "/hr/admin/news_items/37" for 127.0.0.1
Processing by Admin::NewsItemsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"blablablablablaabllba",
"commit"=>"Save content to page", "news_item"=>{"name"=>"test new",
"title"=>"", "body"=>"", "author"=>"", "page_id"=>"on"},
"locale"=>"hr", "id"=>"37"}
And here is item hash from console:
2.0.0p353 :124 > NewsItem.find(37)
NewsItem Load (0.3ms) SELECT "news_items".* FROM "news_items" WHERE "news_items"."id" = ? LIMIT 1 [["id", 37]]
=> #<NewsItem id: 37, name: "test new", url: nil, title: "", body: "", author: "",
page_id: 0, user_id: 7, created_at: "nvm", updated_at: "nvm">
Please note that I tried to google this, but couldn't find anything...
UPDATE:
The way i realized this was happening was because I have following routes:
resources :pages do
resources :news_items, except: [:index, :show]
end
resources :news_items
I was trying to enable CRUD for :news_items both with and without page_id in url. So after a delete I was trying to redirect to a proper route depending on values in params.
if params[:news_item].key? :page_id and not params[:news_item][:page_id].nil?
admin_page_path(Page.find(params[:news_item][:page_id]))
else
admin_news_items_path
end
So if :page_id is not nil route to corresponding page, else route to NewsItem#index.
But I kept getting an error saying:
ActiveRecord::RecordNotFound in Admin::NewsItemsController#update
Couldn't find Page with id=on
If I pass zero instead of nil, everything works as expected. Is this a good way to represent that a row does not have foreign key?
I'm trying to make a simple movie database using Rails 4.0.0 as a learning project. I'm particularly interested in using scaffolding as much as possible, as this is one of the features that drew me to RoR in the first place.
Yes, I do realize the potential risks (Box 1.2.Scaffolding: Quicker, easier, more seductive), but I promise I won't have my project go public before I really understand what's going on beneath the hood. Right now I'm more in "evaluating technologies for my next super duper project"-mode.
Here's what I've got so far:
rails g scaffold Person name:string
rails g scaffold Movie name:string
Now, I could've done something like
rails g scaffold Movie name:string person_id:integer
instead, but I want a movie to be associated with both a director and an actor. (Next step is to make an association that relates multiple actors to a single movie, but I'm not quite there yet.)
So, I headed over to the blog post Creating Multiple Associations With the Same Table, describing pretty much what I need. It's a somewhat old post, so things might have changed now - I don't know. Anyway. This how I changed the models:
class Person < ActiveRecord::Base
has_many :movies
end
and
class Movie < ActiveRecord::Base
belongs_to :director_id, :class_name => 'Person', :foreign_key => 'person_id'
belongs_to :actor_id, :class_name => 'Person', :foreign_key => 'actor_id'
end
and finally the magical
rake db:migrate
Starting the WEBrick by running rails s in the console, I open my browser and start registering people
The time has come to start registering movies. According to previous questions here and here I have to make a migration script in order to create the necessary database fields. So this is what I did:
rails g migration AddPersonIdsToMovies director_id:integer actor_id:integer
I also updated the app/views/movies/_form.html.erb to
<%= form_for(#movie) do |f| %>
<% if #movie.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#movie.errors.count, "error") %> prohibited this movie from being saved:</h2>
<ul>
<% #movie.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :director_id %><br>
<%= f.select :director_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="field">
<%= f.label :actor_id %><br>
<%= f.select :actor_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When I create a new movie, the view shows up fine and the select inputs works fine. However, the data in the director and actor field isn't persisted. I ran rails console and looked at the newly created movie:
irb(main):004:0> mov = Movie.first
Movie Load (0.2ms) SELECT "movies".* FROM "movies" ORDER BY "movies"."id"
ASC LIMIT 1 => #<Movie id: 1, name: "No such movie", created_at:
"2013-08-02 17:02:12", updated_at: "2013-08-02 17:02:12",
director_id: nil, actor_id: nil>
which is kind'a disappointing with no director or actor info.
Update
Based on #Mattherick's suggesition, I edited the private part of the movies_controller.rb to this:
# Never trust parameters from the scary internet, only allow the white list through.
def movie_params
params.require(:movie).permit(:name, :director_id, :actor_id)
end
Unfortunately, when I post a new movie I get
Person(#70319935588740) expected, got String(#70319918738480)
Extracted source:
# POST /movies.json
def create
#movie = Movie.new(movie_params)
respond_to do |format|
if #movie.save
and the request data goes as
{"utf8"=>"✓",
"authenticity_token"=>"???",
"movie"=>{"name"=>"Romantic Comedy",
"director_id"=>"2",
"actor_id"=>"1"},
"commit"=>"Create Movie"}
Update 2
I tried to create a new Movie in the rails console, like this:
irb(main):001:0> movie = Movie.new(name: "My fine movie", director_id: "1", actor_id: "2")
ActiveRecord::AssociationTypeMismatch: Person(#70311109773080) expected, got String(#70311102311480)
which is what you'd expect from the POST to the controller. This made me test what happened if I excluded the quotation marks for director_id and actor_id. So I did
irb(main):005:0> movie = Movie.new(name: "My fine movie", director_id: 1, actor_id: 2)
ActiveRecord::AssociationTypeMismatch: Person(#70282707507880) expected, got Fixnum(#70282677499540)
Still using the console, I decided to create an actor and a director
director = Person.new(name: "Woody Allen")
director.save
actor = Person.new(name: "Arnold Schwarzenegger")
actor.save
and then I did
movie = Movie.new(name: "I'd like to see that", director_id: director, actor_id: actor)
movie.save
which worked like a charm (output omitted for brevity). So the whole question boils down to "How can I pass a Person as the argument to director_id and actor_id through the web interface?"
If I had a single field in Movies called person_id: integer, I believe that rails would've inferred that I'm not trying to pass a string containing the id of a person, but rather I'm trying to pass an entire person object.
Update 3
I tested my suspicion that rails understands how to deal with posts when the foreign key column is named after the pattern [table]_id. So I created a new project with a Continent model and a Country model, where rails g scaffold Country name:string continent_id:integer. I changed my Country view to include
<div class="field">
<%= f.label :continent_id %><br>
<%= f.select :continent_id, Continent.all.collect {|x| [x.name, x.id]} %>
</div>
instead of the default numeric field. The continent_id is still posted a string:
Started POST "/countries" for 127.0.0.1 at 2013-08-03 10:40:40 +0200
Processing by CountriesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"???", "country"=>{"name"=>"Uganda", "continent_id"=>"4"}, "commit"=>"Create Country"}
yet rails understood that continent_id was the identifier of an entry in the Continent table.
Sadly, the inferrer mechanism doesn't work in my original case, as I have two associations from Movie to the Person table. I need to somehow make sure rails understand that there is a mapping from director_id to a Person.
Update 4
According to some sources, it seems as I need to refine the Person model further. So I did
class Person < ActiveRecord::Base
has_many :directed_movies, :class_name => 'Movie', :foreign_key => 'director_id'
has_many :acted_movies, :class_name => 'Movie', :foreign_key => 'actor_id'
end
but still not fixing my problems.
I'm kind'a lost. Can anyone give me a few pointers on what I'm missing here? Can anyone help me map from director_id to person_id? Thanks!
Ok, so I finally got it. I don't think this is the correct way to go about this, but at least it solved the problem. As I mentioned in update 2, I was able to create a Movie object in the irb, and so I asked the question "How can I pass a Person as the argument to director_id and actor_id through the web interface?"
According to the sources here and elsewhere, rails should've understood the has_many and belongs_to class methods. However, I just can't seem to get it to work.
So I hacked the create method in movies_controller.rb to read like this:
def create
director = Person.where(:id => movie_params[:director_id]).first
actor = Person.where(:id => movie_params[:actor_id]).first
#movie = Movie.new(name: movie_params[:name], director_id: director, actor_id: actor)
respond_to do |format|
if #movie.save
format.html { redirect_to #movie, notice: 'Movie was successfully created.' }
format.json { render action: 'show', status: :created, location: #movie }
else
format.html { render action: 'new' }
format.json { render json: #movie.errors, status: :unprocessable_entity }
end
end
end
This is certainly not as elegant as I'd like it to be, and I don't think it is the RoR way to do things. Unfortunately it is the only thing I got working so far, but if anyone else can make a multiple association to the same model using rails 4, please do give me a heads up! :)
I have a rails app with working reports that have tags. In the Report/Index.html.erb I want the user to be able to sort the reports by selecting a tag. They may only select one tag at a time so I feel that a select box would work best. I currently have this:
<%= select("preferences", :tag_with,
["Politics", "Technology", "Entertainment", "Sports", "Science", "Crime",
"Business", "Social", "Nature", "Other"], :prompt => "Filter Feed by:" )%>
I have a working preferences controller with a method call tag_with that updates the current tag. This code, however, only generates the select box. I want it to be that when the user selects one of the tags, it calls the tag_with method from the preferences controller.
I generated a series of link_to lines that complete the task, however I would really like a select box.
<%= link_to "Politics", :action => "tag_with", :tag => "Politics", :controller =>"preferences" %>
<%= link_to "Entertainment", :action => "tag_with", :tag => "Entertainment", :controller =>"preferences" %>
<%= link_to "Science", :action => "tag_with", :tag => "Science", :controller =>"preferences" %>
<%= link_to "Technology", :action => "tag_with", :tag => "Technology", :controller =>"preferences" %>
And so on for each tag. This works fine but is bulky and undesirable. Is there a way to do the same thing through a select box?
In your reports.js.coffee file, or whatever other js file you want.
jQuery ->
$('select#preferences').change ->
$.get 'preferences/tag_with',{ term: $('option:selected', this). val() }
Or, if you want to use regular javascript:
$(function(){
$('select#preferences').change( function() {
$.get('preferences/tag_with',{term: $('option:selected',this).val()});
});
});
A link is a GET request. The jQuery .change() method fires whenever someone makes a change. The $.get method sends a GET request to a URL and can pass data (the second argument). This data becomes your params hash, so in the example above you would get:
params[:term] #=> the value attribute of whatever option was selected by the user
See the jQuery docs on .change() and $.get() for more help.
Update
For this to refresh the page, the easiest thing would be to extract the table that you want changed into a partial, let's assume it's called _report.html.erb. The partial should look something like this:
<div id="report">
<%= render #report %>
</div>
*Note: render #report is just short for render :partial => 'report'. See http://guides.rubyonrails.org/layouts_and_rendering.html*
In your preferences controller, tag_with option you should be sure to set the #report object (or whatever else is delivering the data to your partial).
Then you should make a file called views/preferences/tag_with.js.erb and put something like this in it:
$('div#report').html('<%= escape_javascript( render #report ) %>');
This will update the report container with the new content.
How do you deal with form_for's when the routes are namespaced? I am getting some weird route errors that I really expect to get.
For example, let's say you have a controller called Admin::CompaniesController in
your :admin namespace in your routes.rb:
namespace :admin do
resources :companies
end
Most things work just fine, but I get an error when I render a new form. Here's the code:
<%= simple_form_for(#company, :url => admin_company_path(#company)) do |f| %>
And here's the error message:
ActionView::Template::Error: No route matches {:action=>"show", :controller=>"admin/companies", :id=>#<Company id: nil, name: nil, phone_number: nil, address: nil, postal_code: nil, is_enabled: true, courses_created: 0, province_id: nil, theme_id: nil, payment_plan_id: nil, created_at: nil, updated_at: nil>}
How can I get rails to play nice here? I obviously want one url for edits, and another for new forms. Usually, I'd never even have to put :url in my form_for statements, but because of the nesting, I am forced to.
I have no idea what to do here now, at least not elegantly.
Try using simple_form_for([:admin, #company]) do |f|
I believe I just have to pluralize the path at the end of the path, like this:
<%= simple_form_for(#company, :url => admin_companies_path(#company)) do |f| %>
This is not what I would have expected. I just guessed at it. This is not a valid route or anything, but it seems to work for puts and posts.
I'm building a fairly simple recipe site to learn RoR, and I've been following the getting started guide, except that I've exchanged posts for recipes and comments for ingredients.
I got all the way to deleting a comment (ingredient) http://edgeguides.rubyonrails.org/getting_started.html#deleting-comments
now i'm getting an error
undefined method `recipe' for #
The line which in the partial which is causing the problem is here
<%= link_to 'Delete Ingredient', [ingredient.recipe_id, ingredient],
:confirm => 'Are you sure',
:method => :delete %>
The controller method (which I don't think has any effect, but I'm not completely sure) is
def destroy
#recipe = Recipe.find(params[:recipe_id])
#ingredient = #recipe.ingredient.find(params[:id])
#ingredient.destroy
redirect_to post_path(#recipe)
end
I use 'recipe_id' in the link_to because when I output the debug, it doesn't have a 'recipe' attribute, but has a recipe_id attribute.
The output of the debug is
--- !ruby/object:Ingredient
attributes:
id: 3
ingredient: testing
amount: 10
measure: "10"
description: "10"
recipe_id: 2
created_at: 2010-09-06 22:16:17.599217
updated_at: 2010-09-06 22:16:17.599217
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
new_record: false
previously_changed: {}
readonly: false
I'm assuming the [ingredient.recipe_id, ingredient] is simply a hash of the variables??
Is that correct? Am I coming at this from the wrong angle?
Shot in the dark, are your associations proper ? Kinda like:
class Ingredient < ActiveRecord::Base
belongs_to :recipe
...
That will give you
#ingredient.recipe
Hope that helps.