I've got two tables/models ( Users
and Concerts
) that has a join-table model of Posts
. This is for a 'ticketmaster' style marketplace so a User
can make a Post
on any given Concert
, and a Concert
can also have info on a given User
through the Post
that this User
made.
The problem is that I have user duplicates on my /concerts and concert duplicates on my users; I'm not sure why either. Below are the JSON output of /concerts and /users.
/concerts is here:
{
"id": 45,
"date": "2023-01-19T00:00:00.000Z",
"location": "Brooklyn Steel",
"image": "https://i.imgur.com/SmFrzTC.jpg",
"artist_id": 33,
"artist": {
"id": 33,
"name": "Adele",
"image": "https://i.imgur.com/zmGbfKS.jpg",
"genre": "Pop"
},
"posts": [],
"users": [
{
"id": 257,
"username": "onlineguy1",
"email": "eusebia_larson@wilderman.co"
},
{
"id": 257,
"username": "onlineguy1",
"email": "eusebia_larson@wilderman.co"
},
{
"id": 273,
"username": "L0V3MUSIC",
"email": "lulu_lemke@johns.name"
}
]
},
For /users, it looks like this and you can see the issue more:
{
"id": 257,
"username": "onlineguy1",
"email": "eusebia_larson@wilderman.co",
"posts": [],
"concerts": [
{
"id": 45,
"date": "2023-01-19T00:00:00.000Z",
"location": "Brooklyn Steel",
"image": "https://i.imgur.com/SmFrzTC.jpg",
"artist_id": 33
},
{
"id": 45,
"date": "2023-01-19T00:00:00.000Z",
"location": "Brooklyn Steel",
"image": "https://i.imgur.com/SmFrzTC.jpg",
"artist_id": 33
},
{
"id": 46,
"date": "2024-05-23T00:00:00.000Z",
"location": "Mao Livehouse",
"image": "https://i.imgur.com/CghhYym.jpg",
"artist_id": 33
},
{
"id": 46,
"date": "2024-05-23T00:00:00.000Z",
"location": "Mao Livehouse",
"image": "https://i.imgur.com/CghhYym.jpg",
"artist_id": 33
},
{
"id": 47,
"date": "2023-04-29T00:00:00.000Z",
"location": "Madison Square Garden",
"image": "https://i.imgur.com/0gd1dD0.jpg",
"artist_id": 33
},
{
"id": 47,
"date": "2023-04-29T00:00:00.000Z",
"location": "Madison Square Garden",
"image": "https://i.imgur.com/0gd1dD0.jpg",
"artist_id": 33
},
]
},
Below are my post model, my user model, my concert model FWIW.
class User < ApplicationRecord
has_secure_password
validates_uniqueness_of :username, presence: true
# validates :username, presence: true, uniqueness: true
validates :password, length: { minimum: 8, maximum: 254}
validates_presence_of :email
validates_format_of :email, with: URI::MailTo::EMAIL_REGEXP
# validates :my_email_attribute, email: true, presence: true
has_many :posts
has_many :concerts, through: :posts
end
class Post < ApplicationRecord
belongs_to :user
belongs_to :concert
validates :body, presence: true
validates :tickets, presence: true, numericality: { greater_than: 0 }
end
class Concert < ApplicationRecord
belongs_to :artist
has_many :posts
has_many :users, through: :posts
end
If anybody's got a step in the right direction, I'll gladly take it because I can't figure it out. Been poring through docs but I've psyched myself out somewhere
EDIT: to include my Controllers, Serializers, and route.
Also, controllers here below, starting with Post:
class PostsController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
def index
posts = Post.all
render json: posts
end
def show
post = Post.find_by!(id: params[:id])
render json: post, status: 200
end
def create
post = Post.create!(new_post_params)
render json: post, status: 201
end
# ## made this one to not-render duplicates but still rendered duplicates
# def create
# ## links the proper user to the post
# correct_user = User.find_by!(id: params[:user_id])
# ## links the proper concert to the post
# correct_concert = Concert.find_by!(id: params[:concert_id])
# newPost = Post.create!(
# id: params[:id],
# body: params[:body],
# tickets: params[:tickets],
# for_sale: params[:for_sale],
# concert_id: correct_concert.id,
# user_id: correct_user.id
# )
# render json: newPost, status: 201
# end
def update
post = Post.find_by!(id: params[:id])
if session[:user_id] === post[:user_id]
post.update!(
body: params[:body],
tickets: params[:tickets]
)
render json: post, status: 200
end
end
def destroy
post = Post.find_by!(id: params[:id])
if session[:user_id] === post[:user_id]
post.destroy
head :no_content
end
end
private
def new_post_params
params.require(:concert_id, :user_id, :for_sale, :tickets, :body)
end
def render_unprocessable_entity_response(invalid)
render json: { errors: invalid.record.errors.full_messages }, status: :unprocessable_entity
end
def render_not_found_response(invalid)
render json: { error: invalid.message }, status: :not_found
end
end
And here's for Users:
class UsersController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
def index
users = User.all
render json: users
end
## get '/me'
def show
user = User.find_by!(id: session[:user_id]) ## changed it to User.find_by! for it to work
render json: user, status: 200
end
def create
user = User.create!(signup_user_params)
session[:user_id] = user.id
render json: user, status: :created
end
# # the original show
# def show
# user = User.find_by(id: session[:user_id])
# if user
# render json: user, status: 200
# else
# render json: user.errors.full_messages, status: :unprocessable_entity
# end
# end
# # the original create
# def create
# user = User.create(signup_user_params)
# if user.valid?
# session[:user_id] = user.id
# render json: user, status: :created
# else
# render json: user.errors.full_messages, status: :unprocessable_entity
# end
# end
# # update a specific user
# def update
# if user.update(user_params)
# render json: user
# else
# render json: user.errors, status: :unprocessable_entity
# end
# end
# # delete a specific user
# def destroy
# user.destroy
# end
private
def signup_user_params
params.permit(:username, :password, :password_confirmation, :email)
end
def render_unprocessable_entity_response(invalid)
render json: { errors: invalid.record.errors.full_messages }, status: :unprocessable_entity
end
def render_not_found_response(invalid)
render json: { error: invalid.message }, status: :not_found
end
end
And here's Concert:
class ConcertsController < ApplicationController
def index
concerts = Concert.all
render json: concerts
end
def show
concert = Concert.find_by!(id: params[:id])
render json: concert, status: 200
end
## finish after the duplicates issue
def create
## find the proper artist, and link the proper artist
end
end
Here's the Serializers:
class ConcertSerializer < ActiveModel::Serializer
attributes :id, :date, :location, :image, :artist_id
belongs_to :artist, serializer: ArtistSerializer
has_many :posts, serializer: PostSerializer
has_many :users, through: :posts, serializer: UserSerializer
end
class PostSerializer < ActiveModel::Serializer
attributes :id, :body, :for_sale, :tickets, :concert_id, :user_id
belongs_to :user, serializer: UserSerializer
belongs_to :concert, serializer: ConcertSerializer
end
class UserSerializer < ActiveModel::Serializer
attributes :id, :username, :email
has_many :posts, serializer: PostSerializer
has_many :concerts, through: :posts, serializer: ConcertSerializer
end
Here's routes.rb:
Rails.application.routes.draw do
#& Defines the root path route ("/")
#& root "articles#index"
##~ FOR THE ARTIST-CONCERTS-VENUES DISPLAYS
#& getting all the artists-concerts-users
get '/artists', to: "artists#index"
get '/artists/:id', to: "artists#show"
get '/concerts', to: "concerts#index"
get "/users", to: "users#index"
##~ FOR THE POSTS GET/CREATION/EDITS/DELETION
get '/posts', to: "posts#index"
post '/new_post', to: "posts#create"
patch '/update_post/:id', to: "posts#update"
delete '/delete_post/:id', to: "posts#destroy"
##~ THE LOGIN/LOGOUT ROUTES
#& to create a new user outright
post "/new_user", to: "users#create"
#& to login our user
post "/login", to: "sessions#create"
#& to keep the user logged in
get "/me", to: "users#show"
#& to log the user out
delete "/logout", to: "sessions#destroy"
##~ SESSION & COOKIES INFO
#& shows session_id and sessions info
get "/show_session", to: "application#show_session"
#& displays cookies
get "/cookies", to: "application#show_cookies"
# Routing logic: fallback requests for React Router.
# Leave this here to help deploy your app later!
get "*path", to: "fallback#index", constraints: ->(req) { !req.xhr? && req.format.html? }
end
The fact that posts: []
is shown I am going to assume is for brevity becuase there can be no users
for a Concert
without posts
having elements.
Your issue is that you join User
and Concert
through Post
, and it is reasonable to assume that a User
may post more than once about a Concert
is it not?
Given your current relationships and the fact that you are using ActiveModel::Serializer
you are going to have to Override the Association method to return only distinct User
/ Concerts
.
For Example:
class ConcertSerializer < ActiveModel::Serializer
attributes :id, :date, :location, :image, :artist_id
belongs_to :artist, serializer: ArtistSerializer
has_many :posts, serializer: PostSerializer
has_many :users, through: :posts, serializer: UserSerializer do
object.users.distinct
end
end
Note: I am not sure how this does not end up in a circular dependency as it appears it should (I don't use this library for APIs)
I managed to solve this by using the distinct
property when defining my models.
class Concert < ApplicationRecord
belongs_to :artist
has_many :posts
has_many :users, -> { distinct }, through: :posts
end
By using --> {distinct}, I only got distinct (ie no repeats) objects rendered back in the JSON. Whether or not this is the most optimal way, I can't speak on but it definitely solved my original problem so I'm answering this question myself. You can read more here if you're stuck in the same boat.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.