has_many or join - what's the 'rails way' to use my table? - activerecord

I have a database that keeps track of accidents. Each accident can have multiple causes. Each cause has a friendly name in a 3rd table. What's the 'rails way' to create this association?
Here's what I have:
create_table "accidents", force: true do |t|
t.text "notes"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "causes", force: true do |t|
t.integer "accident_id"
t.integer "cause_name_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "cause_names", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
CauseName.create :name => "DUI"
CauseName.create :name => "Speeding"
Accident:
class Accident ActiveRecord::Base
has_many :causes
end
Causes:
class Cause < ActiveRecord::Base
belongs_to :accidents
has_one :cause_name
end
cause names:
class CauseName < ActiveRecord::Base
belongs_to :causes
end
It seems like to be properly "ORM"'d, I'd use it like this:
Accident.causes.first.cause_name.name #speeding
a = CauseName.find(1)
Accident.causes.first.cause_name = a #set and saved successfully
I've been trying a variety of things, but I can't seem to get my associations to work the way I'd expect. I know I'm not using it right.
I'm very new to rails and activerecord, and horrible with databases... the schema I'm working with was designed by our dba who will be doing reporting on the table, but knows nothing about Ruby or ActiveRecord.
What's the best approach in my situation? Am I even using this thing right?

I think you have your belongs_to and has_one methods placed incorrectly in your Cause - CauseName association.
Quoting the official guide:
If you want to set up a one-to-one relationship between two models,
you'll need to add belongs_to to one, and has_one to the other. How do
you know which is which?
The distinction is in where you place the foreign key (it goes on the
table for the class declaring the belongs_to association), but you
should give some thought to the actual meaning of the data as well.
The has_one relationship says that one of something is yours - that
is, that something points back to you.
So, in your case, it should be like this:
class CauseName < ActiveRecord::Base
has_one :cause # Note that I have changed :causes to singular too
end
class Cause < ActiveRecord::Base
belongs_to :accident # <- Singularized too
belongs_to :cause_name
end

In your case, I'd suggest not splitting causes into two tables. Just cram name into causes table and call it a day.
Then you can easily query like #accident.causes.first.name

Related

avoiding destroy with foreign key

I'm new in Ruby on Rails. I don't understand how rails behave using foreign Key, I've researched it for some days but I didn't get the answer.
Simple sample:
I created two tables:
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :content
t.timestamps null: false
end
end
end
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :author
t.text :content
t.references :post, index: true, foreign_key: true
t.timestamps null: false
end
end
end
My models are:
class Post < ActiveRecord::Base
has_many :comments
end
class Comments < ActiveRecord::Base
belongs_to :post
end
My doubt is: As I have a Foreign Key in my table COMMENTS (.references :post, index: true, foreign_key: true) I guess that I wouldn't be able to destroy any post which has any COMMENTS associated to them, isn't it ?
I did as above but I am still able to destroy the posts, even when I have the comments associated. How can I treat it? What am I doing wrong?
Cheers
I'd refine your migrations to use the :on_delete options on your foreign keys. It can take one of those values : :nullify, :cascade, :restrict
From what I understand, you need to set this value to :restrict on your post_id column in your comments table, so that posts with associated comments can't be deleted.
Update:
Or, you could also directly set it on the association in your Post model:
has_many :comment, dependent: :restrict_With_error
Please take a look at:
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many -> See the Options: Section
From what i understand, you dont want to destroy a post if there are associated comments?
Why not put a if statement encapsulating the delete button for a post
So something like:
psudo code
if #post.comments exists
cant delete post
else
delete post
end

Rails Associations: with self through second model

I have two models Classification and ClassificationRelationships. I want to create a hierarchy of classifications using supperclass and subclass so that each classification can have many subclasses but only one superclass.
my migrations look like this
class CreateClassifications < ActiveRecord::Migration[5.0]
def change
create_table :classifications do |t|
t.string :symbol
t.string :title
t.integer :level
t.timestamps
end
add_index :classifications, :symbol
add_index :classifications, :level
end
end
class CreateClassificationRelationships < ActiveRecord::Migration[5.0]
def change
create_table :classification_relationships do |t|
t.integer :superclass_id
t.integer :subclass_id
t.timestamps
end
add_index :classification_relationships, :superclass_id
add_index :classification_relationships, :subclass_id
add_index :classification_relationships, [:superclass_id, :subclass_id], unique: true, name: 'unique_relationship'
end
end
so far with my models I have
class ClassificationRelationship < ApplicationRecord
belongs_to :superclass, :class_name => "Classification"
belongs_to :subclass, :class_name => "Classification"
end
class Classification < ApplicationRecord
has_many :classification_relationships
has_many :subclasses, through => :classification_relationships
has_one :superclass, through => :classification_relationships
end
I read quite a few other posts but am still unsure how to finish the associations. I am pretty sure I need to specify the foreign keys but am not clear on how I should do that. Thanks for the help!
Get rid of ClassificationRelationship.
All you need is for Classification to have a parent_id which, in the root instances, is allowed to be null.
Add:
belongs_to :parent, class_name: 'Classification', foreign_key: :parent_id
def children
Classification.where(:parent_id => self.id)
end
Some operations will not be optimal. e.g. Find all descendants. That's because this will require repeated queries to find children, their children, etc...
This may not be a concern for you.
If it is, I recommend storing a path as such:
after_create :set_path
def set_path
path = parent ? "#{parent.path}#{self.id}/" : "#{self.id}/"
self.update_attributes!(:path => path)
end
Then you can do things like:
def descendants
Classification.where("classifications.path LIKE '#{self.path}%' AND classifications.path <> '#{self.path}'")
end
Of course, make sure path is indexed if you'll be doing queries like that.

Active Record Retrieve belongs_to

I have the two following models associated:
class Post < ActiveRecord::Base
belongs_to :language
def self.are_visible
self.where(:visible => true)
end
end
class Language < ActiveRecord::Base
has_many :posts
end
Schema.rb
create_table "languages", force: true do |t|
t.string "name_de"
t.string "name_en"
end
create_table "posts", force: true do |t|
t.string "title"
t.text "description"
t.integer "language_id"
end
add_index "posts", ["language_id"], name: "index_posts_on_language_id"
How can I list all languages of all visible stores without duplicates?
I want something like this:
#languages = Post.are_visible.select(:language).uniq
But this leads to the following error
PG::UndefinedColumn: ERROR: column "language" does not exist
Of course this column does not exist, only the column language_id exists on the table.
I am wondering why this is so complicated because in C# Linq I would just write:
Repository.Posts.Where(p => p.Visible).Select(p => p.Language).Distinct()
And I would get all Locations of matching posts. But somehow I think I need to change my approach fundamentally to get this in active record.
Update: Got it working the following way:
#languages = Post.joins(:language).are_visible.uniq.pluck(:name_de)
The way you have it set up, you have only one language per post record. You don't have them related in a way you can do this with ActiveRecord; however, you can get posts by language.
#posts = #language.posts
This might make more sense to have a has and belongs to many relationship between these models.
Have you tried with scope? Then you should be able to use select or get languages of visible posts:
class Post < ActiveRecord::Base
belongs_to :language
scope -> :are_visible { where(visible: true) }
end
of course if you have a column visible in Post table which takes only boolean values.
Edit:
Try add join:
#languages = Post.joins(:language).are_visible.select(:language).uniq
Okay, so I did not get this to work so I changed my approach a bit.
#languages = Post.joins(:language).are_visible.uniq.pluck(:name_de)

rails joins with module name

Here is how to join two models
User.where(:id => 1).joins(:posts)
but how to join two models with module/namspace
#schedules= Swimming::Classschedule.joins(:Swimming::Slot).where(:date => #date)
seems not working properly (with error message)
:Swimming is not a class/module
UPDATE
I have updated to
#schedules= Swimming::Classschedule.joins(:swimming_slots).where(:date => #date)
and I do have this table
create_table :swimming_classschedules do |t|
t.integer :slot_id
t.integer :coach_id
t.integer :level_id
t.string :note
t.timestamps
end
create_table :swimming_slots do |t|
t.string :date
t.string :start
t.string :end
t.timestamps
end
Howcome I got this error
Association named 'swimming_slots' was not found; perhaps you misspelled it?
update 2
add this line to Swimming::Classschedule module
belongs_to :swimming_slots ,:class_name=>'Swimming::Slot',:foreign_key => "slot_id"
and
change joins to
#schedules= Swimming::Classschedule.joins(:swimming_slots).where(:swimming_slots =>{:date => #date})
Now it works
you pass the association name to joins. for example, if you have an association like
has_many :swimming_slots, class_name: 'Swimming::Classschedule'
then you pass swimming_slots and rails will do the joins for you.
User.joins(:swimming_slots)
UPDATE:
if slot_id refers to a record in the swimming_slots table, you should have something like
belongs_to :slot, class_name: 'Swimming::Slot'
in your class schedule model. If you have that, you should be able to do
Swimming::Classschedule.joins(:slot)

ruby on rails : how to bring changes on database to model

I have update migrate script under db/migrate, and I did a
rake db:migrate
database script before update
class CreateStudents < ActiveRecord::Migration
def change
create_table :students do |t|
t.string :firstname
t.string :lastname
t.string :account
t.timestamps
end
end
end
databse script after update
class CreateStudents < ActiveRecord::Migration
def change
create_table :students do |t|
t.string :firstname
t.string :lastname
t.string :account
t.string :address
t.string :city
t.string :state
t.string :postcode
t.string :homephone
t.timestamps
end
end
end
after I dropped the old development.sqlite3 and old schema in schame.rb.
Say I added a few columns, but in the model these columns is missing.
But my model still is
class Student < ActiveRecord::Base
attr_accessible :firstname,:lastname,:account,
end
Is there a easy way I can bring the changes in new migrate script to model ?
If you want to allow for mass assignments of the other attributes, you can just add the keys to attr_accessible
class Student < ActiveRecord::Base
attr_accessible :firstname,:lastname,:account,:address, :city, :state, :postcode, :homephone
end
However, your model still has those attributes (or columns as you call them). You just can't do a mass assignment (like create or update_attributes) without making them attr_accessible first.
It looks like maybe you did rails generate migration which isn't meant to affect your model. I believe after you create your model everything afterward has to be done manually.
If you really want to effect changes to your database and model at the same time, your best bet might be to delete your migrations and model and do a rails generate scaffold (documentation) to create your entire scaffolding from scratch.
There are no problem to add the new columns manually in the model.

Resources