rails: connecting model with 2 other models - ruby-on-rails

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

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.

Unidentified Method "Interests" - has_many association

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

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.

Create comments for two models from comments controller ruby on rails

Ruby on rails newbie here, I have previously been using CakePHP and wanted to allow my create comments controller to create events for two models, any help please?
My comments controller:
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(params[:comment].permit(:commenter, :body))
redirect_to event_path(#event)
end
def create
#venue = Venue.find(params[:venue_id])
#comment = #venue.comments.create(params[:comment].permit(:commenter, :body))
redirect_to venue_path(#venue)
end
My create comments view:
<h2>Add a comment:</h2>
<%= form_for([#event, #event.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
This is a classic example for polymorphic association.
There would be a bit of tweaking to get it to work right.
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
...
end
class Venue < ActiveRecord::Base
has_many :comments, as: :commentable
...
end
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
...
end
This will add to your comments Model another attribute called commentable_type so you could differ the types of comments for each Model (Venue, Event)
You would have to run a migration that looks mostly like this
def change
create_table :comments do |t|
t.integer :commenter
t.text :body
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
end
Now when you migrate you can go to your rails console and see that if you try
Venue.first.comments << Comment.create!(:body => "Body", :commenter => "Guy") # or commenter => 1 depending on your schema
It will be saved to the database as a comment and you can also do the same thing for Event
Now as for your comments controller, I would advise against creating a global comments controller and rather have follow the RESTful approach and have each controller handle his comments.
I.E
# routes.rb
resources :venues
resources :comments
end
resources :events do
resources :comments
end
This way you can both tweak your views according to each controller (venue / events), you follow the RESTful approach as you can use this with either HTML/JSON/XML, you get nicer routes
/events/1/comments # index for all the comments for event 1
/events/1/comments/new # your add a comment form
and same goes for venue.
You can find more info on associations in here http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Good luck!

Resources