Unidentified Method "Interests" - has_many association - ruby-on-rails

I have a user that can create interest tags. Users have many interests and interests belong to users, very simple. I followed this (http://guides.rubyonrails.org/getting_started.html) guide to set it up but i get the error: undefined method interests for #<Interest:0x833e200>
User model:
has_many :interests
Interest model:
belongs_to :user
Database Migration:
def change
create_table :interests do |t|
t.string :iVal
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
In my view:
<%= form_for([#user, #user.interests.build]) do |fi|%>
<%= fi.label :interests %>
<%= fi.text_field :interests %>
<%= fi.submit%>
<% end %>
I've tried every solution i've found online but am yet to find anything that helps.
Any help will much appreciated, thanks.

Your form is for an interest object that belongs to the #user. Interests don't have <%= fi.text_field :interests %>
Interest, probably have a name, so a text field for the interest might be what you want in here instead:
<%= fi.text_field :name %>

Your error suggests you're attempting to call interests from the Interest class, like so: Interest.interests.
If you're interested in using a form builder for a one-to-many relationship, consider reading this documentation: http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for

Related

how would I be able to select a genre my book is in when I create a book

I don't know how to title my question, I'm sorry haha. I am building an app where users sign up as authors or reviewers. Authors upload their books which belong to genres. Users that are reviewers find the books they want to review by genre. When I create a book resource with an author user, how would I create a way of selecting multiple genres that book belongs to in the new book form??
I am learning ruby on rails development and am very early in my understanding of it. I so far have devise for my app and am using enum to create roles for users when they sign up so that they're either and author or reviewer. I've also made a users controller so that when they sign up they get redirected either to an author profile or reviewer profile. Next step is figuring out how to create genres that books belong to. Any suggestions would be helpful. Thanks
Let's start with the database. We'll create a has-and-belongs-to-many relation,
create_table :books do |t|
t.string :name
t.timestamps
end
create_table :genres do |t|
t.string :name
t.timestamps
end
create_table :books_genres do |t|
t.references :book, foreign_key: true
t.references :genre, foreign_key: true
end
add_index :books_genres, [:book_id, :genre_id], unique: true
Now to add association for the models:
# app/models/book.rb
class Book < ApplicationRecord
has_and_belongs_to_many :genres
end
# app/models/genres.rb
class Genre < ApplicationRecord
has_and_belongs_to_many :books
end
As for the User Interface, we could use standard HTML Multiple Select, but it's really not user friendly, so we'll go with checkboxes.
# config/routes.rb
resources :books # this is too much, but you will probably use other actions anyway.
# app/controllers/books_controller.rb
def new
#book = Book.new
#genres = Genre.all
end
# app/views/books/new.html.erb
<%= form_with(model: #book, local: true) do |form| %>
<%= form.label :name %>
<%= form.text_field :name %>
<% #genres.each_with_index do |genre, i| %>
<%= check_box_tag "book[genre_ids][#{i}]", genre.id %>
<%= label_tag "book[genre_ids][#{i}]", genre.name %>
<% end %>
<%= form.submit %>
<% end %>
Now we have to do some magic in the controller as we'll receive the genre_ids as a hash ({"0" => "1", "1" => "3"}), this will transform that to [1, 3]
# app/controllers/books_controller.rb
def book_params
params[:book][:genre_ids] = (params[:book][:genre_ids] || {}).values.map(&:to_i)
params.require(:book).permit(:name, genre_ids: [])
end
And finally we can have our action to create books
# app/controllers/book_controller.rb
def create
#book = Book.new(book_params)
if #book.save
redirect_to #book
else
render :new
end
end
To see any checkboxes, you'll need to have your genres in database. You can easily do this through rails console:
%w[Adventure Tragedy Fantasy].each { |g| Genre.create(name: g) }
Checkboxes might not be the best user interface for that either, you may want to google for user friendly multiple selects, but this should explain you the basics of what you're trying to achieve.

Rails ActiveRecord can't find method from many-to-many association

My application is pretty simple, I have User which can have many Videos and Video is many-to-many to Tag
Here's my model
class User < ActiveRecord::Base
def authenticate
return true
end
end
class Video < ActiveRecord::Base
belongs_to :user
end
class Tag < ActiveRecord::Base
end
class VideoTag < ActiveRecord::Base
belongs_to :video
belongs_to :tag
end
And here's my form
<%= form_for(#video, html: { class: "directUpload" }, multipart: true) do |f| %>
<% if #video.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#video.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #video.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :path %><br>
<%= f.file_field :path%>
</div>
<div class="field">
<%= f.label :tags %><br>
<%= f.text_field :tags %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But I'm getting this error.
NoMethodError in Videos#new Showing
/Users/user/MyProjects/video-archiver/app/views/videos/_form.html.erb
where line #24 raised:
undefined method `tags' for #
How can I fix this?
UPDATE
class CreateVideos < ActiveRecord::Migration
def change
create_table :videos do |t|
t.string :title
t.string :path
t.references :user, index: true
t.timestamps null: false
end
add_foreign_key :videos, :users
end
end
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :title
t.timestamps null: false
end
end
end
class CreateVideoTags < ActiveRecord::Migration
def change
create_table :video_tags do |t|
t.references :video, index: true
t.references :tag, index: true
t.timestamps null: false
end
add_foreign_key :video_tags, :videos
add_foreign_key :video_tags, :tags
end
end
The Video model should look like:
class Video < ActiveRecord::Base
belongs_to :user
has_many :video_tags
has_many :tags, through: :video_tags
end
First of all, the relationship should be setup as follows.
class User < ActiveRecord::Base
has_many :videos
def authenticate
return true
end
end
This is because if a video belongs_to a user, and you said a user has many videos, this relationship makes more sense. And while lunr is not quite answering the question, same as I unfortunately, he is right on his answer as well.
My only answer for your question is that you should use :tag not :tags on line #24.
Without more info, I don't think I can help much more.
So here is the problem with your code as I see it. Below is the offending section of code.
<div class="field">
<%= f.label :tags %><br>
<%= f.text_field :tags %>
</div>
The reason is because :tags is not a field in your database. It's simply an object. In other words, look at your migrations below.
create_table :videos do |t|
t.string :title
t.string :path
Both :title and :path are columns in your database now for your video table. But, there is no column in your database for a :tags field. A tag in your database is an object/row of your database and does not represent a single field. Each tag has a :title...
create_table :tags do |t|
t.string :title
But there is no field for :tags or :tag or anything like it. This is why you are getting the error NoMethodError. What Ruby On Rails does, or more specifically Active Records does, is link database columns to getter and setter methods in your models. So, your video model has pre made setters and getters for your :title and :path fields that are setup in the background without you having to see it first hand. That's how your forms can see that your are filling in a #video object and your :title and :path are linked to those getters and setter magically. This happens for all columns for any table that has a model as well in RoR.
So, since you don't have a single editable field in your database called :tags, Ruby On Rails doesn't have a setter and getter for it to bind to your html form. Thus, when you try to call it, it complains and says it cannot find the method that by design of RoR should be there for the form to work.
So, my suggestion to you would be to redesign your database layout a bit as suggested by lunr and I and then keep the above in mind when doing it. This part of Ruby On Rails isn't really said clearly all the time. It's right in the documentation, but takes a bit to find and truly wrap you head around so most people I have seen make this mistake early on. Once you get comfortable with it though, it's amazing how handy it all really is.
Hope this helps!

Ruby on rails: Adding 2 references of a single model to another model

I'd like to know a proper way to implement my situation here in ruby on rails 4.0.
Lets say I have 2 models named House and Order.
My Order table should have two columns from and to both referencing a house model.
What should my relations between these two models be in this case?
Note: I do not require any reference to order model from house model.
I would like to have something like this in my Order table
t.references :house, as:from (this should create a column named from and should be of type integer, index of house table
t.references :house, as:to (this should create a column named to and should be of type integer, index of house table
I would like this type of relation in order model because I want to take fields of houses in my order form something like
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :house(from) do |i| %>
... # your house forms
<% end %>
<%= f.fields_for :house(to) do |i| %>
... # your house forms
<% end %>
...
<% end %>
Is there any specific way to this in rails?
P.S : I have already read this post here but I think it does not exactly solve my problem.
Adding a Model Reference to existing Rails model
In create orders migration file:
create_table :orders do |t|
..
t.integer :from_house_id
t.integer :to_house_id
..
end
In your app/models/order.rb:
belongs_to :from_house, class_name: 'House'
belongs_to :to_house, class_name: 'House'
accepts_nested_attributes_for :from_house, :to_house
In your views:
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :from_house do |i| %>
... # your from house forms
<% end %>
<%= f.fields_for :to_house do |i| %>
... # your to house forms
<% end %>
...
<% end %>
Enjoy!
Add this answer just in case Surya's code doesn't work - I'm used to having to specify the foreign_key:
class Order < ActiveRecord::Base
belongs_to :from_house, :class_name => "House", :foreign_key => "from_id"
belongs_to :to_house, :class_name => "House", :foreign_key => "to_id"
end
Just make sure you have two attributes on Order - one being from_id and another to_id. From now on you can call order.from_house and order.to_house.

rails: connecting model with 2 other models

Hi I have the following model:
Boys have_many relationships
Girls have_many relationships
Relationship belongs to boy
Relationship belongs to girl
I have the following so far:
def create
#boy = Boy.find(current_boy.id)
#relationship = #boy.relationships.create(:relationship)
redirect_to boy_path(#boy)
end
This is my home.html.erb
<%= form_for([#boy, #boy.relationships.build]) do |f| %>
<div class="field">
<%= f.label :points %><br />
<%= f.number_field :points %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Migration file:
def change
create_table :relationships do |t|
t.integer :points
t.references :boy
t.references :girl
t.timestamps
end
add_index :relationships, :boy_id
add_index :relationships, :girl_id
end
So relationships have "points." When I submit the form, I want to specify the email of the girl as well.
How do I put that as part of the form and fix the create method so when I submit this form, I create a relationship between a boy and a girl?
Many thanks.
First, I'd consider having a Person (or whatever you prefer) model with a gender attribute. That'll greatly simplify the following:
Nest your resources:
resources :people do
resources :relationships
end
Tell the model about it:
class Person < ActiveRecord::Base
...
accepts_nested_attributes_for :relationships
...
end
In app/views/relationships/_form the form_for should take #person and #relationship.
Then, after a person is created they can navigate to /people/1/relationships#index where they can do all the CRUD stuff.
This is out of date but still useful: http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

Rails 3 Nested Resources Edit/Update method - routing error

I need some help with nested resource actions. I have three nested resources: Jobs, Questions and Answers. I am currently only trying to get the edit/update method to work for the questions controller. The relationship is as so: Jobs has_many questions and Questions belong to Jobs.
I am using the edit action on the questions and am getting an error:
No route matches "/jobs/1/questions"
and I cannot figure out why.
I currently have this code as my edit and update action in my Questions controller:
def edit
#job = Job.find(params[:job_id])
#question = #job.questions.find(params[:id])
end
def update
#job = Job.find(params[:job_id])
#question = #job.questions.find(params[:id])
if #question.update_attributes(params[:question])
redirect_to(#question)
end
end
Models:
class Job < ActiveRecord::Base
has_many :questions
class Question < ActiveRecord::Base
belongs_to :job
Routes:
resources :jobs do
resources :questions do
resources :answers
end
end
The things that I don't understand are:
a) why is it redirecting me to the questions index path, when I didn't redirect it there, and
b) It says that is not a valid route, but if I refresh that exact URL the page loads properly.
I have tried multiple options, but I can't figure out the solution.
Thanks for the help. Let me know if you need more info.
p.s. here is my rake routes : https://gist.github.com/1077134
To get you started,
In view/jobs/show.rb :
<%= link_to 'Edit', edit_jobs_path(#job) %>
In view/questions/show.rb :
<%= link_to 'Edit', edit_job_question_path(#question.job, #question) %>
In view/questions/edit.rb :
<%= link_to 'Show', job_question_path %>
What I'm showing is that the links need to have a nested pattern. If your answers had many comments, you might end up with things like:
edit_job_question_answer_comment(#job, #question, #answer, #comment)
where the #symboled variables are derived in the controller.
Hope this helps!
You may later want:
class Job < ActiveRecord::Base
has_many :questions
has_many :answer, :through => :questions
# If you want to edit the questions of a job whilst editing a job then research accepts nested attributes
#accepts_nested_attributes_for :questions, :allow_destroy => true
end
So it turns out that my issue was a little more involved than I had originally thought. My database and tables were not setup properly and they were having trouble finding the proper :ids for my resources. I had to start by normalizing my tables like so:
class CreateQuestions < ActiveRecord::Migration
def self.up
create_table :questions do |t|
t.references :job
t.text :question1
t.text :question2
t.text :question3
t.text :question4
t.text :question5
t.text :question6
t.text :question7
t.text :question8
t.text :question9
t.text :question10
t.timestamps
end
end
This set up was repetitive and dirty and it was messing up questions controller actions. So I changed it to:
def self.up
create_table :questions do |t|
t.references :job
t.text :question
t.timestamps
end
end
and created nested_forms with loops in my jobs (the parent resource) new_form view.
<%= form_for(#job) do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.fields_for :questions do |builder| %>
<%= f.label :question, "Question" %><br \>
<%= f.text_area :question, :rows => 10 %>
<% end %>
After doing this all of my controller methods were cleaner and the edit/update action was working properly.
This is how I solved the issue, it may not be the best way to do so. Also, if you have anything to add or any questions about my code, just let me know and I'll see if I can help.
Thanks!

Resources