2017-01-23 3 views
0

Я пытаюсь создать форму с серией проверок, чтобы предотвратить дублирование при одновременном создании трех записей модели: один для родителя (если он не существует), один для его child (при условии, что он не существует), и один для таблицы соединения между дочерним элементом и пользователем (чтобы позволить Пользователю иметь собственную копию объекта Song).DB rolls back on create action

  1. В текущем состоянии кода, проверяет, казалось бы пройти, но журналов сервера показывают ROLLBACK, и ничего не будет сохранено в базу данных, КРОМЕ родительского объекта (художник).

  2. Когда я пытаюсь использовать идентификаторы объекта, я получаю сообщение об ошибке undefined method id for nil:NilClass или «не удалось найти объект без идентификатора».

Следующий код в мой контроллер:

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? && [email protected]? 

     @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 [email protected]? 

     @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 

Извините, если я не абстрагируется достаточно. Не совсем уверен, как, учитывая, что нет сообщения об ошибке, просто откат (без каких-либо проверок, присутствующих в любом из контроллеров).

+1

Вот почему никто с любым опытом работы с Rails расскажет вам, не сбрасывать тонны логики в контроллеры. Существует множество стратегий для обработки сложных, вложенных и чрезмерно ограниченных данных, но StackOverflow не является местом, чтобы узнать о них. Как бы то ни было, этот вопрос будет долгим, чтобы ответить, потому что вы не разделили его достаточно далеко, чтобы было легко ответить. Если ничего другого, я рекомендую удалить код с комментариями для ясности. – coreyward

+0

Gotcha. Мои извинения. Я немедленно отредактирую его. – michaelsking1993

+0

Я попытался изо всех сил снять его. Еще раз извините. – michaelsking1993

ответ

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