Ruby on rails。在不同视图中的嵌套模型的表单

时间:2016-02-13 12:43:16

标签: ruby-on-rails devise simple-form nested-resources

我一直在与此作斗争一段时间,我无法理解。我有一个使用设计的用户模型。用户可以上传歌曲,添加YouTube视频等。

我正试图让用户在设计edit registrations视图中添加/删除歌曲和视频。

视频上传很好,但由于歌曲是播放列表的嵌套资源,属于用户,我想我已经糊里糊涂了。

音乐在相应的页面上使用相同的表单上传,但不是来自设计registration edit视图。

路线:

  devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions" }

  resources :videos

  resources :playlists do
    resources :songs
  end

设计注册控制器:

   def edit
     @song = Song.new
     @video = Video.new
   end

表格设计编辑注册:

<div id="user-music-box">
  <p class="p-details-title"> Upload Music </p>
<%= simple_form_for [@user.playlist, @song] do |f| %>
<%= f.file_field :audio %>
<%= f.button :submit %>
<% end %>
</div>

<div id="user-video-box">
  <p class="p-details-title"> Add videos </p>
<%= simple_form_for @video do |f| %>
<%= f.input :youtubeurl %>
<%= f.button :submit %>
<% end %>
</div>

正如我所说,视频(这是一个youtube url字符串)创建并保存没有问题。完全相同的歌曲形式,基本上似乎只是更新用户registration。歌曲信息显示在服务器日志中,但不存在playlist_id,也没有任何内容被保存。

歌曲控制器:

def new
      if user_signed_in?
      @song = Song.new
      if current_user.playlist.songs.count >= 5
        redirect_to user_path(current_user)
        flash[:danger] = "You can only upload 5 songs."
      end
      else
     redirect_to(root_url)
     flash[:danger] = "You must sign in to upload songs"
   end
  end

  def create
    @song = current_user.playlist.songs.new song_params
    @song.playlist_id = @playlist.id
    if @song.save
        respond_to do |format|
          format.html {redirect_to user_path(current_user)}
          format.js
        end
    else
      render 'new'
    end
  end

Playlist.rb

class Playlist < ActiveRecord::Base
  belongs_to :user
  has_many :songs, :dependent => :destroy
  accepts_nested_attributes_for :songs
end

song.rb

class Song < ActiveRecord::Base
  belongs_to :user
  belongs_to :playlist
  has_attached_file :audio
  validates_attachment_presence :audio
  validates_attachment_content_type :audio, :content_type => ['audio/mp3','audio/mpeg']
end

1 个答案:

答案 0 :(得分:1)

除非您通过songs / playlists通过accepts_nested_attributes_for,否则您不应该使用registrations#edit。我将详细介绍两种方法,以实现您想要的目标:

嵌套属性

#app/models/user.rb
class User < ActiveRecord::Base
  has_many :videos

  has_many :playlists
  has_many :songs, through: :playlists

  accepts_nested_attributes_for :videos
end

#app/models/playlist.rb
class PlayList < ActiveRecord::Base
  belongs_to :user
  has_and_belongs_to_many :songs
end

#app/models/song.rb
class Song < ActiveRecord::Base
  has_and_belongs_to_many :playlists
end

这一点的重要性在于,要正确使用它,您可以直接编辑@user对象,通过fields_for帮助程序传递嵌套属性:

#config/routes.rb
devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions" }

#app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < ApplicationController
  before_action :authenticate_user!, only: [:edit, :update]

  def edit
    @user = current_user
    @user.playlists.build.build_song
    @user.videos.build
  end

  def update
    @user = current_user.update user_params
  end

  private

  def user_params
    params.require(:user).permit(:user, :attributes, videos_attributes: [:youtubeurl], playlists_attributes: [song_ids: [], song_attributes: [:title, :artist, :etc]])
  end
end

这将允许您使用:

#app/views/users/registrations/edit.html.erb
<%= form_for @user do |f| %>
  <%= f.fields_for :videos do |v| %>
    <%= v.text_field :youtubeurl %>
  <% end %>
  <%= f.fields_for :playlists do |p| %>
    <%= p.collection_select :song_ids, Song.all, :id, :name %>
    <%= p.fields_for :song do |s| %>
      <%= f.text_field :title %>
    <% end %>
  <% end %> 
  <%= f.submit %>
<% end %>

这将为您提供表单,您可以从中为videos创建playlistssongs@user }。

<强>独立

另一个选项是单独创建对象。

这种方式优于nested attributes,没有技术上的理由;你这样做是为了确保你的路线顺序正确等等。

作为一个注释,你需要记住路线!=模型结构。你可以拥有你想要的任何路线,只要它们为你的模型定义一个好的模式:

# config/routes.rb
authenticated :user do #-> user has to be logged in
  resources :videos, :playlists, :songs #-> url.com/videos/new
end

# app/controllers/videos_controller.rb
class VideosController < ApplicationController
  def new
    @video = current_user.videos.new
  end

  def create
    @video = current_user.videos.new video_params
    @video.save
  end

  private

  def video_params
    params.require(:video).permit(:youtubeurl)
  end
end

# app/views/videos/new.html.erb
<%= form_for @video do |f| %>
  <%= f.text_field :youtubeurl %>
  <%= f.submit %>
<% end %>

上述内容需要重复VideosController PlaylistsSongs