I am just working on a simple rails project in which the models have these many relationships between them :
A author can have many posts
A post can have many comments
likes and dislikes belongs to each post
Now, I have rendered the authors data ( in json ) and the output which I am getting is this :
As we can say that it is rendering only author and post data ( neither comments nor likes/dislikes ).
I am very new to RubyOnRails. So, Whatever I have tried so far is this below :
Controller :
class AuthorsController < ApplicationController
def show
#auth = Author.find_by(id: params[:id])
render json: #auth
end
end
Models :
class Author < ApplicationRecord
has_many :posts
end
class Comment < ApplicationRecord
belongs_to: post
end
class Dislike < ApplicationRecord
belongs_to: post
end
class Like < ApplicationRecord
belongs_to: post
end
class Post < ApplicationRecord
has_many :comments
end
Serializers :
class AuthorSerializer < ActiveModel::Serializer
attributes :id, :name, :age
has_many :posts
end
class CommentSerializer < ActiveModel::Serializer
attributes :id, :content, :username
end
class DislikeSerializer < ActiveModel::Serializer
attributes :id, :dislikecount
end
class LikeSerializer < ActiveModel::Serializer
attributes :id, :likecount
end
class PostSerializer < ActiveModel::Serializer
attributes :name, :content
has_many :comments, serializer: CommentSerializer
end
schema.rb :
ActiveRecord::Schema.define(version: 2020_03_25_091544) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "authors", force: :cascade do |t|
t.string "name"
t.integer "age"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "comments", force: :cascade do |t|
t.string "content"
t.string "username"
t.bigint "post_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["post_id"], name: "index_comments_on_post_id"
end
create_table "dislikes", force: :cascade do |t|
t.integer "dislikecount"
t.bigint "post_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["post_id"], name: "index_dislikes_on_post_id"
end
create_table "likes", force: :cascade do |t|
t.integer "likecount"
t.bigint "post_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["post_id"], name: "index_likes_on_post_id"
end
create_table "posts", force: :cascade do |t|
t.string "name"
t.string "content"
t.bigint "author_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["author_id"], name: "index_posts_on_author_id"
end
end
Now, I just want to render complete data of a author ( Means, The expected output must include author detail + post details + comments + likes + dislikes ) in json form.
I have searched a lot to overcome this issue, but could not resolve this issue.
You need to make 2 changes in your code -
class Post < ApplicationRecord
has_many :comments
has_many :likes
has_many :dislikes
belongs_to :author
end
class PostSerializer < ActiveModel::Serializer
attributes :name, :content
has_many :comments, serializer: CommentSerializer
has_many :likes
has_many :dislikes
end
Related
I'm building a Rails app that has modals Outage, Service, Note and User.
Service has a boolean attribute is_down. By default, is_down is false. When the attribute is updated to true meaning the service goes down, an Outage should be created and a Note should also be created with User, automated.
This all happens in one update of the is_down attribute. If Service goes back up, the outage remains intact but now has an end_time.
Here is the 'story line`:
Service model:
class Service < ApplicationRecord
has_many :outages
has_many :notes
# This is where I'm confused
is_down
if self.is_down
Outage.create(start_time: Time.now, reason: nil)
Note.create(user_id: 1, entry: "Outage began at #{Time.now}", service_id: self.id)
end
end
end
Outage model:
class Outage < ApplicationRecord
belongs_to :service
has_many :notes
has_many :users, through: :notes
end
Note modal (a join table between Outage and User)
class Note < ApplicationRecord
belongs_to :outage
belongs_to :user
end
and User model:
class User < ApplicationRecord
has_many :notes
has_many :outages, through: :notes
end
Outage is more like a story line where during the outage, users can enter notes about what they've learned.
Here is the schema:
enable_extension "plpgsql"
create_table "notes", force: :cascade do |t|
t.text "entry"
t.boolean "is_public", default: true
t.bigint "outage_id"
t.bigint "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["outage_id"], name: "index_notes_on_outage_id"
t.index ["user_id"], name: "index_notes_on_user_id"
end
create_table "outages", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.text "reason"
t.boolean "is_recurring", default: false
t.string "frequency", default: "None"
t.bigint "service_id"
t.index ["service_id"], name: "index_outages_on_service_id"
end
create_table "services", force: :cascade do |t|
t.string "name"
t.boolean "is_down", default: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "notes", "outages"
add_foreign_key "notes", "users"
add_foreign_key "outages", "services"
end
Besides the initial question of automated creation on update of Service attribute, is_down, is this also a good way to go about implementing this?
I would suggest looking into the lifecycle callbacks for ActiveRecord. You can add an after_save callback to your Service class that checks to see if is_down has changed and then create or close an Outage
class Service < ApplicationRecord
has_many :outages
...
after_save :create_or_update_outage, if: is_down_changed?
...
private
def create_or_update_outage
if is_down
outages.create
else
outages.where(end_time: nil).last.update(end_time: Time.now)
end
end
I am trying to make an association that is not working.
I have the following scope:
ActiveRecord::Schema.define(version: 2020_04_05_125812) do
create_table "accounts", force: :cascade do |t|
t.string "social_network"
t.string "name_account"
t.integer "person_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["person_id"], name: "index_accounts_on_person_id"
end
create_table "lists", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "lists_people", id: false, force: :cascade do |t|
t.integer "list_id", null: false
t.integer "person_id", null: false
end
create_table "people", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "posts", force: :cascade do |t|
t.string "post_text"
t.date "date"
t.string "link"
t.integer "account_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["account_id"], name: "index_posts_on_account_id"
end
add_foreign_key "accounts", "people"
add_foreign_key "posts", "accounts"
end
I wish I could consult, for example:
I want to consult the person named "Test_name"
That person belongs to lists, which is a collection of people. In addition, that person has accounts and those accounts have Posts.
class List < ApplicationRecord
has_and_belongs_to_many :people
end
class Person < ApplicationRecord
has_and_belongs_to_many :lists
end
class Account < ApplicationRecord
has_many :posts
belongs_to :person
end
class Post < ApplicationRecord
belongs_to :account
end
How could I have a return like the one below:
List | Name | social_network
1 | Test_name | facebook
2 | Test_name | twitter
All the queries I make, either return the wrong type, or return only the list.
Problems such as:
List | Name | social_network
1 | Test_name | facebook
2 | Test_name | twitter
1 | Second_name | twitter
And I don't want to see the data "second_name"
I try this:
#lists = List.from(
Person.left_outer_joins(:list).where('people.name LIKE ?', "Renata Rempel"),
:list
)
but, doesn't works =/
To start off with you want to setup a many to many association between Person and List. This can be done with has_and_belongs_to_many but there are many reasons why has_many through: may be a better choice. The primary one is that it will let you add features like keeping track of banned users or when a user joined a list.
# rails g model list_membership member:belongs_to user:belongs_to
class ListMembership < ApplicationRecord
belongs_to :member, class_name: 'Person'
belongs_to :list
end
We then have to fix the foreign key in the association:
class CreateListMemberships < ActiveRecord::Migration[6.0]
def change
create_table :list_memberships do |t|
t.belongs_to :list, null: false, foreign_key: true
t.belongs_to :member, null: false, foreign_key: { to_table: :people }
t.timestamps
end
# can be a good idea to add a compound index
# add_index [:list_id, :member_id], unique: true
end
end
class Person < ApplicationRecord
has_many :list_memberships, foreign_key: :member_id
has_many :lists, through: :list_memberships
has_many :accounts
has_many :posts, through: :accounts
end
class List
has_many :list_memberships
has_many :members,
through: :list_memberships
end
Your from query will not work as your actually selecting rows from people but you just alias the table lists. That won't magically select the right data! If you really wanted to use from you would do:
List.from(
List.joins(:members).where("people.name LIKE ?", "Renata Rempel"),
:lists
).eager_load(members: :posts)
If you want to create a bunch of lists with a random number of members in your seed file you can just do:
ids = 10.times.map do
Person.create!(name: Faker::Name.name).id
end
lists = 10.times.do
List.create!(member_ids: ids.sample(2))
end
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.
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'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.