I have two models, Job and Survey. I am using the PaperTrail gem to track changes that occur on the models, and I want to save the job_id onto the version every time it’s created.
# app/models/job.rb
class Job < ActiveRecord::Base
has_paper_trail :ignore => [:id, :created_at, :updated_at],
:meta => { :resource_id => self.id }
has_one :survey
end
# app/models/survey.rb
class Survey < ActiveRecord::Base
has_paper_trail :ignore => [:id, :created_at, :updated_at],
:meta => { :resource_id => self.job_id }
belongs_to :job
end
# app/db/schema.db
# versions
create_table "versions", force: :cascade do |t|
t.string "item_type", null: false
t.integer "item_id", null: false
t.string "event", null: false
t.string "whodunnit"
t.text "object"
t.datetime "created_at"
t.text "object_changes"
t.integer "resource_id"
end
# surveys
create_table "surveys", force: :cascade do |t|
t.string "survey"
t.integer "job_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
When I run this code and try to save a new record, I get the following error:
undefined method `job_id' for #<Class:0x007fd53d70c0a0>
How do I correctly fetch the foreign job_id key from inside my Survey model?
Solved by using :job_id instead of self.job_id.
Related
I have a table that needs to reference another table without using the id column for the foreign key.
I have these two models:
class MatchReference < ApplicationRecord
belongs_to :summoner, :foreign_key => 'accountId'
end
class Summoner < ApplicationRecord
has_many :match_references
end
I created this migration to link them together with a Summoner.accountId:
class AddAccountIdToMatchReferences < ActiveRecord::Migration[5.1]
def change
add_reference :match_references, :summoner, column: :accountId, foreign_key: true
end
end
Note that the foreign key that match_references should use is summoners.accountId and NOT summoners.id
I get this schema after running the migrations:
create_table "match_references", force: :cascade do |t|
t.string "lane"
t.bigint "gameId"
t.integer "champion"
t.string "platformId"
t.integer "season"
t.integer "queue"
t.string "role"
t.bigint "timestamp"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "summoner_id"
t.index ["summoner_id"], name: "index_match_references_on_summoner_id"
end
create_table "summoners", force: :cascade do |t|
t.bigint "profileIconId"
t.string "name"
t.integer "summonerLevel"
t.datetime "revisionDate"
t.bigint "league_id"
t.bigint "accountId"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
and I want to be able to do something like:
s = Summoner.last
s.match_references.last.gameId # => 123456789
m = MatchReference.new
m.summoner_id = s.accountId
m.game_id = 1234
m.champion = 123
...
m.save!
But I get an error saying:
ActiveRecord::RecordInvalid: Validation failed: Summoner must exist
from /Users/hebrongeorge/.rvm/gems/ruby-2.4.1/gems/activerecord-5.1.4/lib/active_record/validations.rb:78:in `raise_validation_error'
This should work:
class Summoner < ApplicationRecord
self.primary_key = 'accountId'
has_many :match_references, :foreign_key => 'accountId'
end
class MatchReference < ApplicationRecord
belongs_to :summoner
end
I am making a Band application where a Venue has many Events and Bands through Events.
I realized that in my form for creating an event can only hold one band_id
but I want to have many bands because it only makes sense to do so.
This is my Schema
ActiveRecord::Schema.define(version: 20170817180728) do
create_table "bands", force: :cascade do |t|
t.string "name"
t.string "genre"
t.string "image"
t.boolean "explicit_lyrics"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "events", force: :cascade do |t|
t.string "name"
t.text "date"
t.boolean "alcohol_served"
t.string "image"
t.integer "venue_id"
t.integer "band_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "venues", force: :cascade do |t|
t.string "name"
t.string "city"
t.string "state"
t.boolean "family_friendly"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
These are my models
class Venue < ApplicationRecord
has_many :events
has_many :bands, through: :events
end
class Event < ApplicationRecord
belongs_to :venue
belongs_to :band
end
class Band < ApplicationRecord
has_many :events
end
I am fairly new to rails this is a practice web app. I want to be able to be able to show multiple band_ids to my event.
Would I just keep repeating t.band_id in my form??
You'll want to specify a foreign key relationship in your migration that reflects the Active Record associations you've set up in your models using belongs_to instead of a data type. This way, you'll get a references from one table to another, or in your case, from one table to two others, which is how one table with two one-to-many relationships is set up.
class CreateEvents < ActiveRecord::Migration
def change
create_table :venues do |t|
t.string :name
t.string :city
t.string :state
t.boolean :family_friendly
t.string :image
t.timestamps
end
create_table :bands do |t|
t.string :name
t.string :genre
t.string :image
t.boolean :explicit_lyrics
t.timestamps
end
create_table :events do |t|
t.belongs_to :venue, index: true # Look here!
t.belongs_to :band, index: true # and here!
t.string :name
t.text :date
t.boolean :alcohol_served
t.string :image
t.timestamps
end
end
end
Ruby 2.3.0, Rails 4.2.4, PostgreSQL 9.5
UPDATE: added activerecord-import code below.
Does anyone know how to make these associations hold, so that a model's table attributes can be referenced in another view? Similar to another Q&A (Rails has_many through aliasing with source and source_type for multiple types), where I have investors, companies, and transactions.
I've tried associations like the below (has_many ... through ...), but I'm failing to get ActiveRecord to recognize the connection among the 3 models & tables. Seeding the db:
The way data gets into these tables is via a csv file having 3 columns. I use roo-xls to extract each into an array of arrays.
My activerecord-import gem-based code (each *_val is an array of 1000s of arrays):
icol = [:name]
ccol = [:name]
tcol = [:investor_name, :company_name, :percent_owned]
investor_val = [["i1"],["i2"]] # just showing 2 arrays for brevity
company_val = [["c1"],["c2"]] # ""
transaction_val = [["i1","c1","pct1"],["i2","c2","pct2"]] # ""
Investor.import icol, investor_val, :validate => false
Company.import ccol, company_val, :validate => false
Transaction.import tcol, transaction_val, :validate => false
Import works, but when I check the transactions table, both company_id and investor_id are nil after executing the activerecord-import .import. I of course would like them to contain the foreign keys for the company and investor model records.
My models are below.
Class Company < ActiveRecord::Base
has_many :investors,
:through => :transactions
has_many :transactions
end
Class Investor < ActiveRecord::Base
has_many :companies,
:through => :transactions
has_many :transactions
end
Class Transaction < ActiveRecord::Base
belongs_to :company
belongs_to :investor
end
Transactions migration (others left out for brevity)
class CreatePositions < ActiveRecord::Migration
def change
create_table :positions do |t|
t.string :investor_name
t.string :company_name
t.string :percent_owned
t.belongs_to :company, index: true
t.belongs_to :manager, index: true
t.timestamps null: false
end
end
end
My schema, where I've added references to the belongs_to (transactions) table.
ActiveRecord::Schema.define(version: 20160128224843) do
create_table "companies", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "investors", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "transactions", force: :cascade do |t|
t.string "investor_name"
t.string "company_name"
t.float "percent_owned"
t.integer "investor_id"
t.integer "company_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "transactions", ["investor_id"], name: "index_transactions_on_investor_id", using: :btree
add_index "transactions", ["company_id"], name: "index_transactions_on_company_id", using: :btree
I have a simple one to many relationship mapping users to posts. Here is the relevant part of the schema:
create_table "users", :force => true do |t|
t.string "username"
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password"
t.string "status", :default => "User"
t.string "img_url"
t.text "bio"
t.integer "num_posts", :default => 0
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "posts", :force => true do |t|
t.string "subject"
t.text "body"
t.integer "user_id"
t.integer "section_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
And here are the models:
class User < ActiveRecord::Base
attr_accessible :bio, :email, :first_name, :img_url, :last_name, :num_posts, :password, :status, :username
has_many :posts
has_many :comments
end
class Post < ActiveRecord::Base
attr_accessible :body, :section_id, :subject, :tag_ids, :user_id
belongs_to :user
belongs_to :section
has_and_belongs_to_many :tags
has_many :comments
end
I go into the rails console and create a new user doing User.create(attributes) and a new post doing Post.create(some attributes), assinging them to p1 and u1, respectively, then I do p1.user = u1.
Now when I do p1.user, I get a u1 object. Moreover, I can see that the user_id key is set to the key of u1 in the DB. However, when I do u1.posts, I get an empty list. How can I get a list of all of the posts that belong to a given user?
Ideally, when creating posts, you can create like this:
user.posts.create!({attributes})
Here in your case it could be the problem with association caching. Try
u1.reload.posts
I am using the PublicActivity gem and this was created in my database
schema.db
create_table "activities", :force => true do |t|
t.integer "trackable_id"
t.string "trackable_type"
t.integer "owner_id"
t.string "owner_type"
t.string "key"
t.text "parameters"
t.integer "recipient_id"
t.string "recipient_type"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I am able to track all posts that are happening inside my website by using this inside the post.rb model
class Post < ActiveRecord::Base
include PublicActivity::Model
tracked except: :destroy, owner: ->(controller, model) { controller && controller.current_user }
end
By doing this, I can get the owner_id. What do I need to set recipient: to make it grab the value? Right now recipient_id is nil.
I want to ultimately use something like this in my view <%= link_to activity.recipient_id.name %> to get the name of the recipient where the activity was made to
I'm trying to set it to either the post_id or user_id but I'm getting an undefined local variable or method error.
Here's the table of the model that I'm tracking
create_table "postcomments", :force => true do |t|
t.text "content"
t.integer "user_id"
t.integer "post_id"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.text "comment_content"
end
I put this in the Model
assuming recipient_id is the user of tracked object. Otherwise change model.user as per your application
tracked recipient: ->(controller, model) { model && model.user }