我尝试创建一个带有一系列检查的表单,以防止在同时创建三个模型记录期间出现重复:一个用于父级(假设它不存在),一个用于其子级(假设它不存在),并且一个用于子和用户之间的连接表(允许用户拥有自己的Song对象副本)。
在代码的当前状态下,检查看似通过,但是
服务器日志显示ROLLBACK
,并且不会保存任何内容
到数据库除了父对象(艺术家)。
当我尝试使用对象的ID时,我收到错误undefined method id for nil:NilClass
,或者"找不到没有ID"的对象。
以下代码在我的控制器中:
class SongsController < ApplicationController
before_action :authenticate_user!
def create
@artist = Artist.find_by(name: params[:artist][:name].strip.titleize) #look for the artist
@song = Song.find_by(title: params[:artist][:songs_attributes]["0"][:title].strip.titleize)
if @artist.present? && @song.present?
@user_song = current_user.user_songs.find(@song_id)
if @user_song.present?
render html: "THIS SONG IS ALREADY IN YOUR PLAYLIST"
render action: :new
else
@user_song = UserSong.create(user_id: current_user.id, song_id: @song.id)
redirect_to root_path
end
elsif @artist.present? && !@song.present?
@song = @artist.songs.build(title: params[:artist][:songs_attributes]["0"][:title].strip.titleize, lyrics: params[:artist][:songs_attributes]["0"][:lyrics].strip)
@user_song = UserSong.create(user_id: current_user.id, song_id: @song.id)
redirect_to root_path
elsif !@artist.present?
@artist = Artist.create(name: params[:artist][:name].strip.titleize)
@song = @artist.songs.build(title: params[:artist][:songs_attributes]["0"][:title].strip.titleize, lyrics: params[:artist][:songs_attributes]["0"][:lyrics].strip)
@user_song = UserSong.create(user_id: current_user.id, song_id: @song.id)
redirect_to root_path
else
render html: "SOMETHING WENT WRONG. CONTACT ME TO LET ME KNOW IF YOU SEE THIS MESSAGE"
end
end
def index
@songs = Song.all
end
def new
@artist = Artist.new
@artist.songs.build
@user_song = UserSong.new(user_id: current_user.id, song_id: @song_id)
end
def show
@song_id = params["song_id"]
@song = Song.find(params[:id])
end
def destroy
UserSong.where(:song_id => params[:id]).first.destroy
flash[:success] = "The song has been from your playlist"
redirect_to root_path
end
def edit
@song = Song.find(params[:id])
@artist = Artist.find(@song.artist_id)
end
def update
end
private
def set_artist
@artist = Artist.find(params[:id])
end
def artist_params
params.require(:artist).permit(:name, songs_attributes: [:id, :title, :lyrics])
end
def set_song
@song = Song.find(params["song_id"])
end
end
模特:
class Artist < ApplicationRecord
has_many :songs
accepts_nested_attributes_for :songs, reject_if: proc { |attributes| attributes['lyrics'].blank? }
end
class Song < ApplicationRecord
belongs_to :artist
has_many :user_songs
has_many :users, :through => :user_songs
end
class UserSong < ApplicationRecord
belongs_to :song
belongs_to :user
end
对不起,如果我没有足够的抽象。不确定如果没有错误消息,只是回滚(没有任何控制器中存在任何验证)。
答案 0 :(得分:1)
感谢@coreyward和他指出胖模型瘦 - 控制器引理(从来不知道那是一件事),我能够切断代码并立即找到解决方案。在我的模型中,我使用了validates_uniqueness_of
和scope
来防止重复记录。在我的控制器中,我使用find_or_create_by
来达成协议。
可能涉及的人,最终代码如下:
class SongsController < ApplicationController
before_action :authenticate_user!
def create
@artist = Artist.find_or_create_by(name: params[:artist][:name].strip.titleize)
@song = @artist.songs.find_or_create_by(title: params[:artist][:songs_attributes]["0"][:title].strip.titleize) do |song|
song.lyrics = params[:artist][:songs_attributes]["0"][:lyrics].strip
end
@user_song = current_user.user_songs.find_or_create_by(song_id: @song.id) do |user_id|
user_id.user_id = current_user.id
end
redirect_to root_path
end
class Song < ApplicationRecord
validates_uniqueness_of :title, scope: :artist_id
belongs_to :artist
has_many :user_songs
has_many :users, :through => :user_songs
end
class Artist < ApplicationRecord
validates_uniqueness_of :name
has_many :songs
accepts_nested_attributes_for :songs, reject_if: proc { |attributes| attributes['lyrics'].blank? }
end
class UserSong < ApplicationRecord
validates_uniqueness_of :song_id, scope: :user_id
belongs_to :song
belongs_to :user
end