setting up databases for associations on Ruby on Rails - ruby-on-rails

First time working on ruby on rails and I have an app with the following 3 models:
class User < ActiveRecord::Base
attr_accessible :username, :name, :email, :password
has_many :comments
has_many :ideas, :inverse_of => :user
end
class Idea < ActiveRecord::Base
attr_accessible :title, :description, :rank, :user_id, :status, :privacy, :created_on, :updated_on
belongs_to :user, :inverse_of => :ideas
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :text, :rank, :user_id, :idea_id, :created_on
belongs_to :user
belongs_to :idea
end
I have a table for Comments created like:
create_table :comments do |t|
t.string :comment_id
t.string :text
t.string :rank
t.timestamps
end
I am trying to seed for these. What I'm trying to understand is how a single comment with a parent idea and a parent user is stored in the database, since the columns can only hold one parent at a time. Should I create a separate table that holds comment_id, user_id and idea_type, where a single comment gets entered in twice for each parent?
Thanks!

It sounds like you are trying to implement Comment as a join model which indicates that a particular User's comment on an Idea. If so, you should be able to accomplish that as follows:
class User < ActiveRecord::Base
attr_accessible :username, :name, :email, :password
has_many :comments
has_many :commented_ideas, :class_name => 'Idea', :through => :comments, :source => :comment
end
class Idea < ActiveRecord::Base
attr_accessible :title, :description, :rank, :user_id, :status, :privacy, :created_on, :updated_on
belongs_to :user # the user who created the Idea
has_many :comments
has_many :commented_users, :class_name => 'User', :through => :comments, :source => :user
end
class Comment < ActiveRecord::Base
attr_accessible :text, :rank, :user_id, :idea_id, :created_on
belongs_to :user
belongs_to :idea
end
create_table :comments do |t|
t.string :text
t.string :rank
t.integer :user_id
t.integer :idea_id
t.timestamps
end

Related

Translating Database Models to Models on Ruby On Rails

I am beginning Ruby On Rails through a purchase/resale platform project at school. I'm having an issue with my models when I try to translate them from my relational model.
Firstly, I've modelled my database. Here is simplified the entity-relationship model :
I've then translated it in a relational model :
Finally, I've implemented it in Ruby On Rails.
I've implemented a model Client :
class Client < ApplicationRecord
attr_accessor :name
validates :name, :presence => true
has_many :purchasings, :dependent => :destroy
has_many :sellers, :through => :purchasings
has_many :articles, :through => :purchasings
end
I've implemented a model Seller :
class Seller < ApplicationRecord
attr_accessor :name
validates :name, :presence => true
has_many :purchasings, :dependent => :destroy
has_many :sellers, :through => :purchasings
has_many :articles, :through => :purchasings
end
I've implemented a model Article
class Article < ApplicationRecord
attr_accessor :quantity
validates :quantity, :presence => true
has_one :purchasing, :dependent => :destroy
has_one :client, :through => :purchasings
has_one :seller, :through => :purchasings
end
I've implemented a model Purchasing :
class Purchasing < ApplicationRecord
attr_accessor :client_id, :seller_id, :article_id
belongs_to :client, :class_name => "Client"
belongs_to :seller, :class_name => "Seller"
belongs_to :article, :class_name => "Article"
validates :client_id, :presence => true
validates :seller_id, :presence => true
validates :article_id, :presence => true
end
I've modified the Purchasing database migration :
class CreatePurchasing < ActiveRecord::Migration[5.1]
def change
[...]
add_index :purchasings, :client_id
add_index :purchasings, :seller_id
add_index :purchasings, :article_id
add_index :purchasings, [:client_id, :seller_id], :unique => true
end
def down
[...]
end
end
I know this is incorrect because when I execute the following code on the Rails console :
cl1 = Client.create(:name => "John")
cl2 = Client.create(:name => "James")
sel1 = Seller.create(:nom => "Jack")
sel2 = Seller.create(:nom => "Jil")
a1 = Article.create(:quantity => 5)
p1 = Purchasing.new(:client => cl1, :client_id => cl1.id, :seller => sel1, :seller_id => sel1.id, :article => a1, :article_id => a1.id)
p1.save
p2 = Purchasing.new(:client => cl2, :client_id => cl2.id, :seller => sel1, :seller_id => sel1.id, :article => a1, :article_id => a1.id)
p2.save
p2.save returns true whereas an article can't be sold by a same seller and bought by two clients different.
You are adding the index on incorrect columns on purchasings table. As per the requirement, the article_id and seller_id should not get repeated ideally. So you actually need is a uniqueness constraint on seller_id, and article_id column. You can do so by creating an unique index on the composition of two columns seller_id, and article id on the database layer. You should also add the application layer validation on the purchasing model.
class Purchasing < ApplicationRecord
attr_accessor :client_id, :seller_id, :article_id
belongs_to :client, :class_name => "Client"
belongs_to :seller, :class_name => "Seller"
belongs_to :article, :class_name => "Article"
validates :client_id, :presence => true
validates :seller_id, :presence => true
validates :article_id, :presence => true
validates :article_id, uniqueness: {scope: :seller_id}
end
Now you should also write a database migration to add the unique index on these two columns.
class AddUniquenessConstraintInPurshasing < ActiveRecord::Migration
def change
add_index :purchasings, [:article_id, :seller_id], :unique => true
end
end

Adding data in an has_many association

My project consists of People, Events and Comments.
Users can post comments ( accounts are not necessary ) and can attend events.
I'm using a has_many: through association between the people and events but whatever I try, I can't add people to events.
I don't have a clue how I can select someone and add this person to the event_people table.
The models
class EventPeople < ActiveRecord::Base
belongs_to :event
belongs_to :people
end
class Event < ActiveRecord::Base
validates :title, presence:true, length: {minimum: 3}
validates :date_from, presence:true
validates :date_to, presence:true
validates :time_from, presence:true
validates :time_to, presence:true
has_many :comments, dependent: :destroy
has_many :people, :through => :event_people
has_many :event_people
end
class People < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, length: { minimum: 5}
validates :birthdate, presence: true
has_many :comments
has_many :events, :through => :event_people
has_many :event_people
end
show method from the eventcontroller
def show
#event = Event.find(params[:id])
end
the table
create_table "event_people", force: true do |t|
t.integer "event_id"
t.integer "people_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Delete the
has_many :event_people
in your People and Event Class
and your migration should look like:
create_table "event_people" do |t|
t.belongs_to :event
t.belongs_to :people
t.timestamps
end
You can either add them individually as klausinho is suggesting, have a from for EventPeople where you'd add one person at a time.
Or the has_many association will give you the people_ids= method on events. So you can then use collection check boxes
<%= form_for #event do |f| %>
<div class="field">
<%= f.collection_check_boxes(:people_ids, People.order(:name), :id, :name)
</div>
<% end %>
Or you could use a multiple select instead.

NoMethodError during Lynda Rails 3 tutorial

What is wrong and how can I fix it?
I get the following error when I run me = AdminUser.find(1) then run me.section.edits
NoMethodError: undefined method `section' for #<AdminUser:0x007fa539ee0558>
from ...gems/activemodel-3.2.13/lib/active_model/attribute_methods.rb:407:in `method_missing'
from ...gems/activerecord-3.2.13/lib/active_record/attribute_methods.rb:149:in `method_missing'
My code
create_section_edits.rb
class SectionEdit < ActiveRecord::Base
attr_accessible :title, :body, :name, :position
belongs_to :editor, :class_name => "AdminUser", :foreign_key => 'admin_user_id'
belongs_to :section
end
admin_user.rb
class AdminUser < ActiveRecord::Base
attr_accessible :title, :body, :username, :first_name, :last_name
has_and_belongs_to_many :pages
has_many :section_edits
scope :named, lambda {|first,last| where(:first_name => first, :last_name => last)}
end
section.rb
class Section < ActiveRecord::Base
attr_accessible :title, :body, :name, :position
belongs_to :page
has_many :section_edits
end
section_edit.rb
class SectionEdit < ActiveRecord::Base
attr_accessible :title, :body, :name, :position
belongs_to :editor, :class_name => "AdminUser", :foreign_key => 'admin_user_id'
belongs_to :section
end
AdminUser has no relationship with sections, but with section_edits only.
So, instead of
me.section.edits
You need to use
me.section_edits
I think what you are missing is the fact that Admins can have multiple sections through section_edits.
Your association needs to look like this
class AdminUser < ActiveRecord::Base
attr_accessible :title, :body, :username, :first_name, :last_name
has_and_belongs_to_many :pages
has_many :section_edits
has_many :sections, through: :section_edits
scope :named, lambda {|first,last| where(:first_name => first, :last_name => last)}
end
Note the has_many through allows you to call me.sections

Rails :inverse_of Error

class User < ActiveRecord::Base
attr_accessible :email, :password, :picture, :username
has_many :entries, :class_name => "Entry", :inverse_of => :user
has_many :sent_messages, :inverse_of => :sender, :class_name => "Message", :foreign_key => "sender_id"
has_many :recieved_messages, :inverse_of => :target, :class_name => "Message", :foreign_key => "target_id"
has_many :mails, :inverse_of => :user
...
end
class Message < ActiveRecord::Base
attr_accessible :message, :seen, :title
belongs_to :sender, class_name => "User", :inverse_of => :sent_messages
belongs_to :target, class_name => "User", :inverse_of => :recieved_messages
end
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.references :sender
t.references :target
t.string :title
t.string :message
t.boolean :seen
t.timestamps
end
end
end
I have these two models. When I try to get user's sent messages (user.sent_messages) I get an error like this:
Could not find the inverse association for sent_messages (:sender in Message)
It's my second day using Rails so it may be trivial, but I don't see it.
PS: If I delete the :inverse_of => :sender from class User, the user.sent_messages works but as I understand :inverse_of is important.
PPS: Found the error! In the Message class I was using class_name instead of :class_name.
That's why i prefer to use the new style hash wherever possible
class Message < ActiveRecord::Base
attr_accessible :message, :seen, :title
belongs_to :sender, class_name: 'User', inverse_of: :sent_messages
belongs_to :target, class_name: 'User', inverse_of: :recieved_messages
end
Though as with many things, it's often a question of personal taste :)

Rails 3 has_many through checkbox form doesn't (does not) work

I have been stuck on this for a day now. I've heard all of this talk of Rails being able to handle easy complexities like this (although this isn't/shouldn't be complex).
Story: User can have many advanced degrees. I want to be able to create this association using a has_many through relationship and use checkboxes in my view.
Models:
class User < ActiveRecord::Base
has_many :user_degree_lists
has_many :degrees, :through => :user_degree_lists, :source => :advanced_degree, :dependent => :destroy
end
class AdvancedDegree < ActiveRecord::Base
attr_accessible :value, :description
has_many :user_degree_lists
end
class UserDegreeList < ActiveRecord::Base
belongs_to :user
belongs_to :advanced_degree
end
ActiveRecord:
class CreateUserDegreeLists < ActiveRecord::Migration
def self.up
create_table :user_degree_lists do |t|
t.integer :user_id
t.integer :advanced_degree_id
t.timestamps
end
add_index :user_degree_lists, :user_id
add_index :user_degree_lists, :advanced_degree_id
add_index :user_degree_lists, [:user_id, :advanced_degree_id], :unique => true
end
def self.down
drop_table :user_degree_lists
end
end
View:
<%= form_for(#user, :html => {:autocomplete => 'off', :id => "sign_up_user" }) do |f| %>
...
<% for advanced_degree in AdvancedDegree.find(:all)%>
<%= check_box_tag "user[advanced_degree_ids][]", advanced_degree.id, #user.degrees.include? (advanced_degree.id) %>
<%= f.label :advanced_degrees, advanced_degree.description %>
...
<% end %>
Once the form is submitted, all user fields are updated, but the :user_degree_lists relationship is not created.
What am I doing wrong here?
Not sure if you solved this already, but one thing I spotted: shouldn't the class User have 'has_many :advanced_degrees' versus 'has_many :degrees'? Might want to try that without the source on it (unless you're trying for something polymorphic), that's how I did something similar.
1) I would rename "UserDegreeList" to "UserDegree" since this is a join table.
2) "AdvancedDegree.find(:all)" can be "AdvancedDegree.all".
3) I agree with the previous comment and it should be renamed to "has_many :advanced_degrees"
4) To solve the issue, try adding this to User:
accepts_nested_attributes_for :advanced_degrees, :allow_destroy => true, :reject_if => :all_blank
You need to make sure that attr_accessible has the attr you are setting in the check boxes.
class Zone < ActiveRecord::Base
attr_accessible :name, :active, :user_ids
has_many :user_zones
has_many :users, :through => :user_zones
end
class User < ActiveRecord::Base
attr_accessible :name, :zone_ids
has_many :user_zones
has_many :zones, :through => :user_zones
end

Resources