DB回滚创建操作

时间:2017-01-23 01:06:21

标签: ruby-on-rails ruby forms

我尝试创建一个带有一系列检查的表单,以防止在同时创建三个模型记录期间出现重复:一个用于父级(假设它不存在),一个用于其子级(假设它不存在),并且一个用于子和用户之间的连接表(允许用户拥有自己的Song对象副本)。

  1. 在代码的当前状态下,检查看似通过,但是 服务器日志显示ROLLBACK,并且不会保存任何内容 到数据库除了父对象(艺术家)。

  2. 当我尝试使用对象的ID时,我收到错误undefined method id for nil:NilClass,或者"找不到没有ID"的对象。

  3. 以下代码在我的控制器中:

        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
    

    对不起,如果我没有足够的抽象。不确定如果没有错误消息,只是回滚(没有任何控制器中存在任何验证)。

1 个答案:

答案 0 :(得分:1)

感谢@coreyward和他指出胖模型瘦 - 控制器引理(从来不知道那是一件事),我能够切断代码并立即找到解决方案。在我的模型中,我使用了validates_uniqueness_ofscope来防止重复记录。在我的控制器中,我使用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