ActiveAdmin No method error - ruby-on-rails

I added a couple of foreign keys to my models and to my tables and it has since broken my use of Active Admin. I'm wondering if anyone knows a work around or a fix to this issue.
schmea.rb
create_table "students", primary_key: "student_id", id: :string, force:
:cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "last_name"
t.string "first_name"
t.string "home_address"
t.string "home_city"
t.string "home_state"
t.string "home_zip"
t.string "school_year_address"
t.string "school_year_city"
t.string "school_year_zip"
t.string "room_number"
t.string "home_phone"
t.string "cell_phone"
t.boolean "new_student"
t.boolean "returning_student"
t.string "athletic_team"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "fk_rails_148c9e88f4"
end
add_foreign_key "emergency_contacts", "students", primary_key: "student_id"
add_foreign_key "students", "users"
add_foreign_key "vehicles", "students", primary_key: "student_id"
student.rb Students Model
class Student < ApplicationRecord
self.primary_key = :student_id
belongs_to :user
has_one :emergency_contact
has_one :vehicle
end
I'm getting the error, has anyone found a fix for this?
undefined method `emergency_contact_id_eq' for Ransack::Search<class: Student, base: Grouping <combinator: and>>:Ransack::Search

You have defined the assocation wrong.
class Student < ApplicationRecord
self.primary_key = :student_id
belongs_to :user
belongs_to :emergency_contact, class_name: 'User'
has_one :vehicle
end
belongs_to places the foreign key on this table and is exactly what you want. When joining you want to have the id on this table instead of having to look for records where student_id matches this record.
You also need to make sure to add a foreign key column and the correct foreign key constraint:
class AddEmergencyContactIdToStudents < ActiveRecord::Migration[5.0]
def change
add_reference :students, :emergency_contact, foreign_key: false
add_foreign_key :students, :users, column: :emergency_contact_id,
end
end
I would also strongly advise against using non standard primary keys. Prefixing the PK with student_ gives you nothing but headaches and will confuse other developers.

Related

Rails: Self-referential associations have my head spinning

I'm currently working on a small school project that utilizes Ruby on Rails and I'm having some trouble getting my self-referential associations working correctly.
Context
The intended functionality of my web app is for users to post houses/apartments for other users to search through and rent. Since I'm having issues with a specific association, I'm working with a completely stripped down version that only has two models, User and Lease.
What I'm Trying to Accomplish
Ideally, when a person first registers on the site, a User object is created to hold their information such as email and password. A User can then either post a listing or search through listings.
Once a post has been created and another user decides to rent the posted house, a Lease object is created, which holds the ID of the posting User as well as the ID of the renting user, aliased as "landlord_id" and "tenant_id" respectively.
A User should now be identified as either a User, Landlord or a Tenant (or both Landlord and Tenant) based on whether there are any Lease objects with their ID as either a Landlord or a Tenant. This identification will be used to determine whether the User can access other areas of the site.
userFoo.leases
This should give me a list of all Lease objects with which the User's ID is associated, regardless of whether it's as a Landlord or Tenant.
userFoo.tenants
This should give me a list of any User object whose ID is associated with the ID of userFoo as a Tenant through Lease, and the inverse if I ask for landlords.
The Code
User Class
class User < ApplicationRecord
has_many :tenants, class_name: "Lease", foreign_key: "landlord_id"
has_many :landlords, class_name: "Lease", foreign_key: "tenant_id"
end
Lease Class
class Lease < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
Users Table Migration
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
Leases Table Migration
class CreateLeases < ActiveRecord::Migration[6.0]
def change
create_table :leases do |t|
t.references :landlord, null: false, foreign_key: {to_table: :users}
t.references :tenant, null: false, foreign_key: {to_table: :users}
t.timestamps
end
end
end
Database Schema
ActiveRecord::Schema.define(version: 2020_10_18_005954) do
create_table "leases", force: :cascade do |t|
t.integer "landlord_id", null: false
t.integer "tenant_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_leases_on_landlord_id"
t.index ["tenant_id"], name: "index_leases_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "leases", "users", column: "landlord_id"
add_foreign_key "leases", "users", column: "tenant_id"
end
What's Wrong?
userFoo.leases
Normally a User would have_many leases by having their ID associated with a lease as "user_id." However, since I'm using "tenant_id" and "landlord_id", this command fails because it can't find "user_id" in the Leases table.
userFoo.tenants
This command gives me a list of all Lease objects where userFoo's ID is associated as "landlord_id" instead of all User objects associated with userFoo's ID as tenants. To retrieve a tenant as is, I have to use the command:
userFoo.tenants.first.tenant
Conclusion
I am having a bit of a hard time understanding these deeper, more complex associations, and I've spent some time trying to find a detailed reference on has_many that covers all the arguments, but all I can really find are small blog posts that reference the "Employees" and "Managers" example on guides.rubyonrails.com . I think one problem is that I'm not sure I'm correctly reflecting my model associations in my table schema.
I'm more than happy to teach myself if someone can point me in the right direction. I'm also open to alternative solutions but only if I can't get the functionality I want out of this setup, because my instructor specifically asked me to try it this way
Thanks in advance for any help! It's much appreciated.
as per your requirement you can try like this:
# app/models/user.rb
class User < ApplicationRecord
has_many :owned_properties, class_name: "Property", foreign_key: "landlord_id"
has_many :rented_properties, class_name: "Property", foreign_key: "tenant_id"
end
Here I have declared two associations with same table but different foreign keys.
# app/models/property.rb
class Property < ApplicationRecord
belongs_to :landlord, class_name: "User"
belongs_to :tenant, class_name: "User"
end
Here I have taken one table by using this user can post one property where landlord is the owner of a house and later you can add tenant who is taking rent to one property.
# db/migrations/20201018054951_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false, index: true
t.string :password_digest, null: false
t.timestamps
end
end
end
Above is your users table migration.
# db/migrations/20201018055351_create_properties.rb
class CreateProperties < ActiveRecord::Migration[6.0]
def change
create_table :properties do |t|
t.references :landlord, foreign_key: {to_table: :users}, null: false
t.references :tenant, foreign_key: {to_table: :users}
t.timestamps
end
end
end
Above is your properties table migration.
# db/schema.rb
ActiveRecord::Schema.define(version: 2020_10_18_055351) do
create_table "properties", force: :cascade do |t|
t.bigint "landlord_id", null: false
t.bigint "tenant_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["landlord_id"], name: "index_properties_on_landlord_id"
t.index ["tenant_id"], name: "index_properties_on_tenant_id"
end
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.string "password_digest", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email"
end
add_foreign_key "properties", "users", column: "landlord_id"
add_foreign_key "properties", "users", column: "tenant_id"
end
If you want to fetch all the owned properties of a user, use user.owned_properties.
If you want to fetch all rented properties of a user, use user.rented_properties.
^^ Here both the cases you'll get objects of Property class.
If you want to get landlord of a property, use property.landlord.
If you want to get tenant of a property, use property.tenant.
^^ Here both the cases you'll get objects of User class.
If you want you can add other attributes like: name, price, etc to properties table.
I think, this will help you. Thanks :) Happy Coding :)

Single Table Inheritance + Relationships

I'm creating a rails 5 application (a sort of job finder that connects recruiters with applicants).
Here is a part of my model configuration:
class User < ActiveRecord::Base
has_and_belongs_to_many :tag
end
class Applicant < User
has_many :experience
has_many :match
end
class Recruiter < User
has_one :company
has_many :offer
end
class Experience < ActiveRecord::Base
belongs_to :applicant, :foreign_key => "user_id"
has_one :company
end
And these are extracts from my schema file:
create_table "users", force: :cascade do |t|
t.string "type", null: false
t.string "login", limit: 40, null: false
t.string "password", limit: 500, null: false
t.bigint "company_id"
t.index ["company_id"], name: "index_users_on_company_id"
t.index ["login"], name: "index_users_on_login", unique: true
end
create_table "experiences", force: :cascade do |t|
t.string "job_name", limit: 100, null: false
t.bigint "company_id"
t.bigint "user_id", null: false
t.text "description"
t.index ["company_id"], name: "index_experiences_on_company_id"
t.index ["job_name"], name: "index_experiences_on_job_name"
t.index ["user_id"], name: "index_experiences_on_user_id"
end
add_foreign_key "users", "companies"
add_foreign_key "experiences", "companies"
add_foreign_key "experiences", "users"
An Experience is attached to the model Applicant through the table user (which contain a type field for the STI), this is why I specified "foreign_key => 'user_id'" in Experience model.
My problem is when I try to access at the first experience of an applicant, I get this error:
PG::UndefinedColumn: ERROR: column experiences.applicant_id does not exist LINE 1: SELECT "experiences".* FROM "experiences" WHERE "experiences...
I hope you can help me.
Thanks!
As stated in the docs:
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added.
Try doing:
class Applicant < User
has_many :experiences, foreign_key: :user_id
has_many :matches
end
Note that it is conventional to use the plural with the has_many association.
BTW, it's no obvious to me why you're using STI, but I'm sure there are good reasons.

Create records with associated tables in Rails

I am new to ruby on rails and don't understand how to create and save records using associated tables. I want the controller to take the data create a product record and then create as many property and product properties associated with that product. The property and product property have a one to one relationship. The product can have many properties and product properties.
Properties and product properties are coming in like this:
{"name"=>"color", "value"=>"red"}
{"name"=>"material", "value"=>"cotton"}
My controller works for the creation of the product but I am unsure how to create a loop that will build as may associated product and product properties that come in the array sent from the client.
My controller now:
class SendDataController < ApplicationController
protect_from_forgery with: :null_session
def hi
product = Product.new
product.name = params[:name]
product.upc = params[:upc].to_i
product.available_on = params[:availableon]
product.save
end
end
Below are my models:
class Product < ApplicationRecord
has_many :propertys, dependent: :destroy
has_many :product_propertys, dependent: :destroy
end
class Property < ApplicationRecord
belongs_to :product
has_one :product_property, dependent: :destroy
end
class ProductProperty < ApplicationRecord
belongs_to :property
belongs_to :product
end
Migration:
class CreateProducts < ActiveRecord::Migration[5.2]
def change
create_table :products do |t|
t.string :name
t.string :upc
t.datetime :available_on
t.timestamps
end
end
end
class CreateProductProperties < ActiveRecord::Migration[5.2]
def change
create_table :product_properties do |t|
t.string :value
t.belongs_to :property
t.belongs_to :product
t.timestamps
end
end
end
class CreateProperties < ActiveRecord::Migration[5.2]
def change
create_table :properties do |t|
t.string :name
t.belongs_to :product
t.timestamps
end
end
end
schema:
ActiveRecord::Schema.define(version: 2018_09_22_140824) do
create_table "product_properties", force: :cascade do |t|
t.string "value"
t.integer "property_id"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_product_properties_on_product_id"
t.index ["property_id"], name: "index_product_properties_on_property_id"
end
create_table "products", force: :cascade do |t|
t.string "name"
t.string "upc"
t.datetime "available_on"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "properties", force: :cascade do |t|
t.string "name"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_properties_on_product_id"
end
end
Thanks for any help you can give a new guy!
Your Product Model plurality required, has_many properties & equally has_many product_properties.
Your Property schema will need product_id as an integer. i would avoid using has_one it can get messy, just use has_many or you may require a has_many through
Your ProductProperty Model You'll also need product_id integer & property_id integer adding them as separate migration.
rails db:create add_product_id_to product_properties, product_id:integer
check the migration file product_id that the attribute is in the file
rails db:migrate
Restart server & test in the console.
Once the Models speak, instantiate a Product object, bring it across into Properties & ProductProperties through the respective controllers by setting & in turn making the SendDataController obsolete unless your logic requires this.

Rails 5 inverse of not working

I'm trying to return JSON API where a show action will
render json: user, include [:books, :friends, :comments]
Problem is, if I try to use the inverse_of in my User and Book model classes like this:
User Serializer
class UserSerializer < ActiveModel::Serializer
...
has_many :friends
has_many :books, inverse_of: :author
...
end
Book Serializer
class BookSerializer < ActiveModel::Serializer
...
belongs_to :author, class_name: "User", inverse_of: :books
...
end
I get an error:
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: books.user_id: SELECT "books".* FROM "books" WHERE "books"."user_id" = ?):
If I remove the inverse_of and has_many from my User serializer, then I don't get any errors, but then the JSON being returned does not contain the included association.
Likewise, the same happens between Comment and User models.
Am I doing something wrong ?
My DB Schema for my two models are:
User Schema
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "username"
t.string "email"
t.string "password_digest"
t.boolean "banned"
t.integer "role_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "photo"
t.boolean "email_confirmed", default: false
t.string "confirm_token"
t.string "password_reset_token"
t.boolean "show_private_info", default: false
t.boolean "show_contact_info", default: false
t.index ["role_id"], name: "index_users_on_role_id"
end
Book Schema
create_table "books", force: :cascade do |t|
t.string "title"
t.boolean "adult_content"
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "published"
t.string "cover"
t.text "blurb"
t.index ["author_id"], name: "index_books_on_author_id"
end
When I went to generate my Book model with:
rails generate model books ... author:references
It created this migration file:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.string :title
t.boolean :adult_content
t.references :author, foreign_key: true
t.timestamps
end
end
end
I assume that includes the necessary foreign key setup...
Try to change this line in your User model(user.rb):
has_many :books, inverse_of: :author
to
has_many :books, inverse_of: :author, foreign_key: :author_id
You need to tell rails what foreign_key you used if it's not the default one.And the association should be declared in your models, not serializers. In serializer you are adding keys by "has_many", inverse_of does't works here.

Rails one to many through a relationship table

I have a Company that has many Users through a join table company_user. Each user should work for only one Company. This is a 1 to many relationship.
I have looked around for this and found the solution in https://stackoverflow.com/a/7080017/883102
But I get the error
PG::UndefinedTable: ERROR: relation "companies" does not exist
LINE 5: WHERE a.attrelid = '"companies"'::regclass
When I try to create a Company. How can I solve this?
My models are
Company
class Company < ActiveRecord::Base
has_many :employments
has_many :users, :through => :employments
end
Users
class User < ActiveRecord::Base
...
end
Employment
class Employment < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
The migration for my join table is
create_table :employment do |t|
t.belongs_to :company
t.belongs_to :user
t.timestamps
end
My schema.rb
create_table "company", force: true do |t|
t.integer "rating"
t.integer "phone"
t.string "name"
t.string "address"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "employment", id: false, force: true do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_digest"
t.string "remember_token"
t.string "role"
end
Hi I found the answer here
https://stackoverflow.com/a/24318236/883102
The problem was that my table names were in the singular form, I changed these in the migration and then re-created the database. It all seems to be working fine now.
My User class ended up as
class User < ActiveRecord::Base
has_one :employment
has_one :company, :through => :employment
end
This was to allow bi-directional associations

Resources