Rails 5 inverse of not working - ruby-on-rails

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.

Related

Unitialized constant User:Bookings when trying to add data into a join table

I have a User table and a Booking Table that is linked by a create_join_table what holds the user id and booking ids. When a user books a room, i need the id of both the user and new booking to go into that. I am getting the error above and im not sure why.
I have looked online and saw something similar, their class names were plural however I don't think I have that.
booking.rb
class Booking < ApplicationRecord
enum room_type: ["Basic Room", "Deluxe Room", "Super-Deluxe Room", "Piton Suite"]
has_many :join_tables
has_many :users, through: :join_tables
end
user.rb
class User < ApplicationRecord
has_secure_password
validates :email, format: {with: URI::MailTo::EMAIL_REGEXP}, presence: true, uniqueness: true
has_many :join_tables
has_many :bookings, through: :join_tables
end
join_table.rb
class JoinTable < ApplicationRecord
belongs_to :users
belongs_to :bookings
end
bookings_controller.rb
def create
#booking = Booking.create(booking_params)
current_user.bookings << #booking ##Where the error happens
db/schema
ActiveRecord::Schema.define(version: 2019_12_13_181019) do
create_table "bookings", force: :cascade do |t|
t.integer "room_type"
t.date "check_in"
t.date "check_out"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "join_tables", force: :cascade do |t|
t.integer "users_id"
t.integer "bookings_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["bookings_id"], name: "index_join_tables_on_bookings_id"
t.index ["users_id"], name: "index_join_tables_on_users_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
I have just tried to reproduce your problem and I have a similar exception
irb(main):003:0> User.first.bookings
NameError (uninitialized constant User::Bookings)
but, when I change
belongs_to :users
belongs_to :bookings
to
belongs_to :user
belongs_to :booking
in app/models/join_table.rb everything works as expected.
This is how I created the JoinTable model
$ rails generate model JoinTable
class CreateJoinTables < ActiveRecord::Migration[6.0]
def change
create_table :join_tables do |t|
t.references :user
t.references :booking
t.timestamps
end
end
end
As you can see in the belongs_to docs, it is used in the singular form most of the time.

How to get fields from has_many :throught association

I have a many-to-many association throught RoomsUsers model and in this model i have a role field, association works well but i can't access this field.
My schema looks like:
create_table "messages", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "room_id"
end
create_table "rooms", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "rooms_user_id"
end
create_table "rooms_users", force: :cascade do |t|
t.integer "user_id"
t.integer "room_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "role"
t.integer "last_checked"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "mail"
t.integer "rooms_user_id"
end
User model:
class User < ApplicationRecord
has_secure_password(validations: false)
has_many :messages
has_many :rooms_users
has_many :rooms, through: :rooms_users
accepts_nested_attributes_for :rooms_users
attr_accessor :register, :mail_confirmation, :login
end
Room model:
class Room < ApplicationRecord
has_many :rooms_users
has_many :users, through: :rooms_users
accepts_nested_attributes_for :rooms_users
has_many :message
end
RoomsUsers model:
class RoomsUsers < ApplicationRecord
belongs_to :user
belongs_to :room
end
And i am trying to get role field from first user's room.
User.first.rooms.first.role
It give's me NoMethodError (undefined method `role' for #). What's wrong?
You're trying to access role field in the rooms table, but it is in rooms_users table. Should be:
User.first.rooms_users.first.role
And remove rooms_user_id from rooms and users table, you don't need it
If you want to access "role" field through Rooms model, you will need to change the place of your "role" field from rooms_users table to rooms table. Doing it you can access "role" using User.first.rooms.first.role.
However if you want to keep "role" field in rooms_users table, so you will need to use User.first.rooms_users.first.role as Vasilisa has already mentioned.
t.integer "rooms_user_id" are not necessary in rooms and users tables. The has_many used in rooms and users are already linking rooms_users with them.

Has_many through query

I have 2 models (Books and Authors) and a third table joining them (has_many through association).
I am trying to implement search in my app and run a query on both tables. My query looks like this and I cannot figure out the problem:
Book.includes(:authors, :author_books).where("books.title LIKE ? OR authors.name = LIKE ?", "%#{book}%", "%#{book}%")
This is the error that I get running it:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "authors"
SELECT "books".* FROM "books" WHERE (books.title LIKE '%Harry%' OR authors.name = LIKE '%Harry%')
Here is my schema of the three tables:
create_table "author_books", force: :cascade do |t|
t.bigint "author_id"
t.bigint "book_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["author_id"], name: "index_author_books_on_author_id"
t.index ["book_id"], name: "index_author_books_on_book_id"
end
create_table "authors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "books", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "rating"
t.string "critics"
t.float "price"
end
author_book.rb
class AuthorBook < ApplicationRecord
validates_presence_of :author, :book
belongs_to :author
belongs_to :book
end
author.rb
class Author < ApplicationRecord
validates :name, uniqueness: true
has_many :author_book
has_many :books, through: :author_book
end
book.rb
class Book < ApplicationRecord
validates :title, uniqueness: true, :case_sensitive => false
has_many :author_book
has_many :authors, through: :author_book
has_many :categories, through: :category_book
def self.search_book(book)
if book
Book.joins(:authors, :author_books).includes(:authors, :author_books).where("books.title LIKE ? OR authors.name = LIKE ?", "%#{book}%", "%#{book}%")
end
end
end
I call this search_book method in my book controller like so:
def search
#books = Book.search_book(params[:book])
end
Some help, please?
Thanks!
From the docs
If you want to add conditions to your included models you’ll have to
explicitly reference them.
That said, you need to add references(:authors) to your query like below to resolve the error
Book.includes(:authors, :author_books).where("books.title LIKE ? OR authors.name = LIKE ?", "%#{book}%", "%#{book}%").references(:authors)
Update:
Can't join 'Book' to association named 'author_books'; perhaps you
misspelled it?
You should replace has_many :author_book with has_many :author_books and through: :author_book with through: :author_books
You forgot to join authors and author_books to your relation. includes loads both :author and :author_books but in separate queries.
Try this:
Book.joins(:authors, :author_books).includes(:authors, :author_books).where("books.title LIKE ? OR authors.name = LIKE ?", "%#{book}%", "%#{book}%")

ActiveAdmin No method error

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.

All Rails active record foreign keys are nil after import via activerecord-import gem

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

Resources