Rails 4 strong params and nested form - ruby-on-rails

I'm building a simple app (Ruby 2.0.0 and Rails 4) where a user can create a project and for each project create multiple screens. When creating a screen the user can upload a screenshot, that refer to a its own model (I do this to handle multiple versions of the same screen).
When creating the screen, the screenshot doesn't seem to be created because of a permission problem. Here's the server log:
Processing by ScreensController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kezaGADsaLmY/+zozgbjEe5UfdeqRPg58FCf1qzfHxY=", "screen"=>{"project_id"=>"24", "name"=>"Billing", "description"=>"This is the page where a user will enter their credit card information", "screenshot"=>{"image"=># <ActionDispatch::Http::UploadedFile:0x007fcdce25b2c0 #tempfile=#<Tempfile:/var/folders/pv/srwrv0qj35b0hsxkt42l_z500000gn/T/RackMultipart20131007-91790-tewse9>, #original_filename="fb-banner.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"screen[screenshot][image]\"; filename=\"fb-banner.png\"\r\nContent-Type: image/png\r\n">}}, "commit"=>"Create Screen"}
Unpermitted parameters: screenshot
These are my models:
Screen
class Screen < ActiveRecord::Base
belongs_to :project
has_many :screenshots
validates :name, presence: true
accepts_nested_attributes_for :screenshots
end
Screenshot
class Screenshot < ActiveRecord::Base
belongs_to :screen
end
This is my screens_controller:
class ScreensController < ApplicationController
before_action :set_screen, only: [:show, :edit, :update, :destroy]
def index
#screens = Screen.all
end
def show
end
def new
#screen = Screen.new(:project_id => params[:project_id])
#screen.screenshot.build
end
def edit
end
def create
#screen = Screen.create(screen_params)
if #screen.save
flash[:notice] = "A new screen has been added to this project"
redirect_to [#screen.project]
else
render :action => 'new'
end
end
def update
#screen = Screen.find(params[:id])
if #screen.update_attributes(screen_params)
flash[:notice] = "The screen has been successfully updated"
redirect_to [#screen.project]
else
render :action => 'edit'
end
end
def destroy
#screen = Screen.find(params[:id])
#screen.destroy
flash[:notice] = "Successfully destroyed screen"
redirect_to [#screen.project]
end
private
def set_screen
#screen = Screen.find(params[:id])
end
def screen_params
params.require(:screen).permit(:project_id, :name, :description, screenshot_attributes: [ :id, :screen_id, :image, :version ])
end
end
And finally this is the form:
<%= form_for #screen, :html => { :multipart => true } do |f| %>
<% if #screen.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#screen.errors.count, "error") %> prohibited this screen from being saved:</h2>
<ul>
<% #screen.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :project_id %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :screenshot do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I hope this is enough to help me spot the problem. I'm a newbie when it comes to programming, so any help is more than welcome.

I recently worked through something similar, and this is what seemed to work...
Change your fields_for to plural:
<%= f.fields_for :screenshots do |s| %>
And also, make your params
def screen_params
params.require(:screen).permit(:project_id, :name, :description, screenshots_attributes: [ :id, :screen_id, :image, :version ])
end
Also, you need to update your new action to make screenshots plural, like so:
def new
#screen = Screen.new(:project_id => params[:project_id])
#screen.screenshots.build
end

Related

has_many join form with collection checkboxes not saving more than one checkbox value

I am working on a form for a editorial calendar app. I have two things going out that are pretty similar and not working.
Working with 3 models: Platforms, Posts and Calendars. They are join tables. Platform <=> Post, Post <=> Calendars
Post/new & Post/edit form:
<div class="container">
<div class="form-field">
<%= form_for #post do |f| %>
<%= f.label :title %>
<%= f.text_field :title, required: true %> <br>
Title is required.
</div>
<div class="form-field">
<%= f.label :content%>
<%= f.text_area :content%>
</div>
<div class="form-field">
<%= f.label :link %>
<%= f.text_field :link %>
</div>
<div class="file-field">
<%= f.label :picture%>
<%= f.file_field :picture, id: :post_picture%>
</div>
<div class="file-field">
<%= f.label :finalized %>
<%= f.radio_button :finalized , true%>
<%= f.label :finalized, "Yes" %>
<%= f.radio_button :finalized, false %>
<%= f.label :finalized, "No" %>
</div>
<%= f.hidden_field :user_id %> <br>
<div class="form-field">
<%= f.fields_for :platform_attributes do |platform| %>
<%= platform.label :platform, "Social Platforms"%>
<%= platform.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<h4> Or Create a new platform: </h4>
<%= platform.label :platform, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
</div>
<% end %>
<%= f.submit%>
<% end %>
</div>
My post controller is handling the checkboxes issue, and the "schedule post" issue. It will only allow me to schedule for one calendar, and it does not save the updates and add additional calendars.
Posts Controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :schedule_post, :destroy]
def new
#posts = current_user.posts.select {|p| p.persisted?}
#post = current_user.posts.build
#platforms = Platform.all
end
def edit
#calendars = current_user.calendars
#platforms = Platform.all
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to post_path(#post)
else
redirect_to new_post_path
end
end
def update
#post.update(post_params)
if #post.save
redirect_to post_path(#post), notice: 'Your post has been updated.'
else
redirect_to edit_post_path(#post)
end
end
def schedule_post
#calendar_post = CalendarPost.new(calendar_post_params)
if #calendar_post.save
binding.pry
redirect_to post_path(#post)
else
render 'show'
end
end
private
def set_post
#post = Post.find(params[:id])
end
def set_calendars
#calendars = current_user.calendars
end
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_attributes => [:platform_ids, :name])
end
def calendar_post_params
params.require(:calendar_post).permit(:post_id, :calendar_id, :date, :time)
end
end
I want the user to be able to add a post to multiple platforms and multiple calendars because of the versatility of what someone may need.
I also have my setter in my Post model.
class Post < ApplicationRecord
has_many :calendar_posts
has_many :calendars, through: :calendar_posts
has_many :platform_posts
has_many :platforms, through: :platform_posts
belongs_to :user
def platform_attributes=(platform_attributes)
if platform_attributes['platform_ids']
platform_attributes.platform_ids.each do |id|
platform = Platform.find(id: id)
self.platforms << platform
end
end
if platform_attributes['name'] != ""
platform = Platform.find_or_create_by(name: platform_attributes['name'])
self.platforms << platform
end
end
thoughts? why are they not saving to more than one calendar or more than one platform if they choose to have more than one?
Here is the updated code... and more of what I know about these changes and what is happening.
My submit button is not working for some odd reason on my form, so I'm trying to get the params submitted but it won't even route to give me params even if I raise them, nothing is happening.
On the form you can choose checkboxes or add in a platform. If you add in a platform it creates that one but it does not also save the other ones you selected. If you go to edit the post, and click submit with changes, no page loads at all and nothing is happening in log. It's just idle.
<%= f.fields_for :platform_attributes do |platform| %>
assumes you are creating one platform... it says "these are the fields for this platform"
but platform_ids is intended to be a selection of a set of platforms... and probably should be outside of the fields_for section (which should only surround the name field).
try something like the following:
<div class="form-field">
<%= f.label :platform_ids, "Social Platforms"%>
<%= f.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<%= f.fields_for :platform_attributes do |platform| %>
<h4> Or Create a new platform: </h4>
<%= platform.label :name, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
<% end %>
<%# end fields_for %>
</div>
Also you'll need to update permit/require appropriately eg
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_ids, :platform_attributes => [:name])
end
Note: not tested - bugs are left as an exercise for the reader ;)

Rails passing params to different model and then saving it not working

I have a 'post' controller in that I have two variable title and body which I am passing through strong parameters.But I need to use two other variable which are path and name which are in different model name 'Document'..And also I am saving the content in database ..but unable to do so..getting this error view [posts/_form.html.erb]
undefined method `name' for #
[posts_controller]
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post = find_params
end
def create
#post = Post.create(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
Document.new(params,:files=>[])
end
def find_params
Post.find(params[:id])
end
end
[post/_form.html.erb]
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<%= f.label :name %> <br>
<%= f.text_field :name %><br>
<br>
<br>
<%= f.label :path %><br>
<%= f.file_field :path %><br>
<%= f.submit %>
<% end %>
[document.rb]
class Document < ActiveRecord::Base
validates :name, presence: true
validates :path, presence: true
validates :resource_type, presence: true
validates :resource_id, presence: true
mount_uploader :path, PathUploader
validates :name, presence: true
# def self.abc
# params.permit(:name,:path)
# end
def initialize(params,file)
params=file[:name]
#params.permit(name =>:name,path =>:path)
end
end
undefined method `name' for #
You're referencing a non-existent attributes for your Post form:
<%= form_for #post,html: { multipart: true } do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<%= f.submit %>
<% end %>
Remove :name & :path references.
--
If you want to pass "extra" attributes to another model, you need to use accepts_nested_attributes_for or set the params separately to your "primary" model:
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :documents
accepts_nested_attributes_for :documents
end
#app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :post
end
This will allow you to pass the documents as "nested" attributes of your Post model:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
#post.documents.build
end
def create
#post = Post.new post_params
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body, documents_attributes: [:name, :path])
end
end
#app/views/posts/_form.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.fields_for :documents do |d| %>
<%= d.text_field :name %>
<%= d.text_field :path %>
<% end %>
<%= f.submit %>
<% end %>
So undefined method on a model will indicate that, well, the method doesn't exist on the model. Want to see a model's methods? Post.methods. However, in this example, the column name is not defined on the model., and you're trying to tell Post that it has a name. What you need to do is nest your parameters.
While there is a ton of cleaning up that might want to focus on first, your answer is found in the accepts_nestable_attributes_for class methods, as shown here, http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html, and strong_params documentation as shown here, http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
In your case, you want to create a new document from a post. Your permitted params hash will look like this,
params.require(:post).permit(:title, :body, :document_attributes => [:name])
Ensure that document_attributes is singular; if a person has_many pets (for example), then you'd have pets_attributes.
In your form, something that often trips people up is the builder.
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.fields_for #post.document do |document_field| %>
<%= document_field.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
Make sure that you're telling ERB that <%= f.fields_for %>, not just <% f.fields_for %>.

Rails form not passing image with carrierwave

I've currently got an upload form that takes an epub file, parses it using epubinfo and then displays in another form to validate the information we're extracting is correct. We then submit this new upload from this form.
This first form (upload.html.erb) seems to work exactly as I'd expect, extracting all the information I required, including the cover image and displays it appropriately in the edit/verification form (new.html.erb). However, the issue lies in this second form (new). Upon posting it doesn't retain the cover image that was displayed, instead not saving anything, even though any other changed (or unchanged) data in this form posts just as it should.
To note, when the upload form posts, public/tmp gets two files, the epub and the png, and upon posting the new form, the epub tmp goes away and gets put into public, however the png stays in public/tmp.
Ruby 2.1.1p76
Rails 4.1
CarrierWave 0.10.0
/views/pages/upload.html.erb
<%= form_for Book.new, :url => new_book_path do |f| %>
<div class="form-group">
<%= f.label :book, "Upload an epub" %>
<%= f.file_field :book, class: "form-control" %>
<%= f.hidden_field :book_cache %>
</div>
<br />
<div class="form-group">
<%= f.button "Upload", class: "btn btn-primary", data: {disable_with: "Uploading..."} %>
</div>
<% end %>
/app/controllers/books_controller.rb (somewhat sanitized):
class BooksController < ApplicationController
before_action :redirect_to_book, only: [:index]
before_action :set_book, only: [:show, :edit, :update, :destroy, :download]
before_action :is_mod, only: [:edit, :update, :destroy]
def create
#books = Book.new(book_params)
if #books.author.nil? && book_params[:author_name].present?
#books.author = Author.where(name: book_params[:author_name]).first_or_create
end
if #books.series.nil? && book_params[:series_name].present?
#books.series = Series.where(name: book_params[:series_name]).first_or_create
end
if book_params[:is_public].present?
else
#books.is_public == true
end
if #books.save
redirect_to book_path(#books), success: "The book was successfully saved!"
else
redirect_to new_book_path
end
end
def new
#authors = Author.all
#series = Series.all
#books = Book.new(book_params)
#books.extract_epub_info
end
private
def set_book
#books = Book.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to "/404"
end
def redirect_to_book
redirect_to book_path(params[:book_id]) if params[:book_id]
end
def book_params
params.require(:book).permit(:book, :cover, :title, :isbn, :summary, :is_public, :series_nbr, :author_id, :author_name, :series_id, :series_name, :tag_list, :book_cache)
end
def is_mod
unless current_mod
redirect_to book_path, error: "You're not allowed to edit books."
end
end
end
/app/uploaders/book_uploader.rb
class BookUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{mounted_as}"
end
def filename
"#{model.id}.epub"
end
end
/app/uploaders/cover_uploader.rb
class CoverUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{mounted_as}"
end
def filename
"#{model.id}.png"
end
end
/app/models/book.rb
class Book < ActiveRecord::Base
belongs_to :author
belongs_to :series
has_many :downloads
has_many :flags
has_many :ratings
has_many :raters, :through => :ratings, :source => :users
validates :title, :author_id, presence: true
attr_accessor :author_name, :series_name
acts_as_taggable
scope :public_books, ->{ where(is_public: true) }
mount_uploader :book, BookUploader
# validates_presence_of :book
mount_uploader :cover, CoverUploader
# validates_presence_of :cover
searchable do
text :title
text :summary
text :isbn
integer :is_public
end
def epub
#epub ||= EPUBInfo.get(book.file.path)
end
def extract_epub_info
if epub.creators.first.name.include? ","
self.author = Author.where(name: epub.creators.first.name).first_or_create
else
parts = epub.creators.first.name.split
first = parts.first
last = parts[1..-1].join(" ")
self.author = Author.where(name: "#{last}, #{first}").first_or_create
end
self.title = epub.titles.first
self.summary = epub.description
self.is_public = true
self.isbn = epub.identifiers.find{ |i| i.scheme == "ISBN" }.try(:identifier)
if epub.cover.present?
self.cover = epub.cover.tempfile
else
end
end
end
And the edit form that displays the cover appropriately but doesn't post when you submit the form.
/views/books/new.html.erb:
<%= form_for #books, url: books_path, html: {multipart: true} do |f| %>
<% if #books.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#books.errors.count, "error") %> prohibited this author from being saved:</h2>
<ul>
<% #books.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<br/><br/>
<div class="alert alert-warning">
<strong>Please validate that all of this information is correct before saving the book!</strong>
</div>
<br/>
<% if #books.cover.present? %>
<div align="center">
<%= image_tag #books.cover %>
</div>
<% end %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :author %>
<%= f.collection_select(:author_id, #authors.order("name ASC"), :id, :name, {:include_blank => true} ) %>
OR New Author Name:
<%= f.text_field :author_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :isbn %>
<%= f.text_field :isbn, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :summary %>
<%= f.text_area :summary, class: "form-control", size: "20x10" %>
</div>
<div class="form-group">
<%= f.label :series_nbr %>
<%= f.text_field :series_nbr, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :series %>
<%= f.collection_select(:series_id, #series.order("name ASC"), :id, :name, {:include_blank => true} ) %>
OR New Series Name:
<%= f.text_field :series_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :tag_list %>
<%= f.text_field :tag_list, class: "form-control" %>
</div>
<%= f.hidden_field :book_cache %>
<div class="form-group">
<%= f.label :cover, "Upload a cover" %>
<%= f.file_field :cover, class: "form-control" %>
</div>
<br/>
<div class="form-group">
<%= f.hidden_field :is_public, :value => "1" %>
<%= f.submit class: "btn btn-primary", data: { confirm: "Are you sure all of this information is correct?\rYou will not be able to edit it later." } %>
</div>
<% end %>
EDIT for routes 11/2 # 9:20ET
routes.rb
App::Application.routes.draw do
root "pages#home"
# All pages
get "browse" => "books"
get "browse/new" => "books#newest"
get "browse/popular" => "books#popular"
get "browse/random" => "books#random"
get "faq" => "pages#faq"
get "empowerment" => "pages#empowerment"
get "legal" => "pages#legal"
get "pages/catalog"
get "search" => "search#search"
get "search/search"
get "upload" => "pages#upload"
get "download" => "pages#download"
get "dmca" => "pages#dmca"
# Catalog Routes
get "catalog" => "catalog#index", defaults: {format: :atom}
get "catalog/search" => "catalog#search", defaults: {format: :atom}
%w{ newest popular random author title serie tag }.each do |section|
get "catalog/#{section}", controller: :catalog, action: section, defaults: {format: :atom}
end
get "catalog/author/:id" => "catalog#authored_by", defaults: {format: :atom}
get "catalog/tag/:tag" => "catalog#tagged", defaults: {format: :atom}
# Normal Model stuff
resources :authors
resources :series
resources :books do
collection do
get "tagged/:tag", action: :tagged, as: :tagged
post "new", action: :new, as: :new_book
end
member do
get :download
end
end
resources :tags
# Stuff for Mods and management
devise_for :mods
namespace "admin" do
resources :mods
end
devise_scope :mod do
get "mods" => "devise/sessions#new"
end
# Lists for dropdowns
get "authorlist" => "books#authorlist"
get "serieslist" => "books#serieslist"
get "taglist" => "books#taglist"
# Let's be nice and support old urls.
get "book/show/id/:id" => "books#show"
get "serie/:id" => "series#show"
get "author/:id" => "authors#show"
get "browse/author/:id" => "authors#show"
end
I'm not sure what else would be needed. I feel that what I'm missing is simple, but after staring at it for so long I've just stopped making any progress. I know that the cover image shows up in new.html.erb because it displays in the image_tag in the form, however upon post it doesn't save the param.
Halp! :(
/views/pages/upload.html.erb -- missing multipart: true
<%= form_for Book.new, html: { multipart: true } do |f| %>
I don't think you need to supply the url argument unless you've set up your routes/models incorrectly. You should change this in your other forms as well.
/app/controllers/books_controller.rb
You're completely missing edit/update methods. Is this by design? Why? Is this why you're supplying the url option in the forms? This promotes confusion.
Have you viewed the contents of book_params to see if the image data is being received correctly? What about checking to see if the actual image files have been created? If they are created, perhaps they're just not accessible (check file permissions).
FURTHER EDIT: I can't determine how things are routed based on what you've shown. Please show your routes as well, and clearly describe which actions result in the unwanted consequences.
i.e., "I want to create a new book, but the uploaded image isn't saved" (meaning it doesn't exist in the database and there's no actual image anywhere on the disk).
or
"I can create a book and save the image, but upon viewing the EDIT page, the image doesn't show up"

unable to create or update a belongs_to record in Rails 4

I'm new to Rails and struggling to get my belongs_to association right. I have an app where a painting belongs to an artist and an artist can have_many paintings. I can create and edit my paintings, however I can not edit or create artists except through the console. Through much Googling I feel I have got myself turned around. Any help would be much appreciated!
Here's my routes.rb file:
MuseumApp::Application.routes.draw do
resources :paintings
resources :paintings do
resources :artists
resources :museums
end
root 'paintings#index'
end
Here's my paintings Controller
def show
#painting = Painting.find params[:id]
end
def new
#painting = Painting.new
##artist = Artist.new
end
def create
safe_painting_params = params.require(:painting).permit(:title, :image)
#painting = Painting.new safe_painting_params
if #painting.save
redirect_to #painting
else
render :new
end
end
def destroy
#painting = Painting.find(params[:id])
#painting.destroy
redirect_to action: :index
end
def edit
#painting = Painting.find(params[:id])
end
def update
#painting = Painting.find(params[:id])
if #painting.update_attributes(params[:painting].permit(:title, :image)) #safe_params
redirect_to #painting
else
render :edit
end
end
Here's the form in my paintings view:
<%= form_for(#painting) do |f| %>
<fieldset>
<legend>painting</legend>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :image %>
<%= f.text_field :image %>
</div>
<%= form_for([#painting,#painting.create_artist]) do |f| %>
<div>
<%= f.label :Artist %>
<%= f.text_field :name %>
</div>
</fieldset>
<%= f.submit %>
<% end %>
<% end %>
Artists Controller:
class ArtistsController < ApplicationController
def index
#artists = Artist.all
#artists = params[:q] ? Artist.search_for(params[:q]) : Artist.all
end
def show
#artist = Artist.find params[:id]
end
def new
#artist = Artist.new
end
def create
#painting = Painting.find(params[:painting_id])
#artist = #painting.create_artist(artist_params)
redirect_to painting_path(#painting)
end
def destroy
#artist = Artist.find(params[:id])
#Artist.destroy
redirect_to action: :index
end
def edit
#artist = Artist.find(params[:id])
end
def update
#painting = Painting.find(params[:painting_id])
#artist = #artist.update_attributes(artist_params)
redirect_to painting_path(#painting)
end
end
private
def artist_params
params.require(:artist).permit(:name)
end
Index view:
<h1> Hello and Welcome to Museum App</h1>
<h3><%= link_to "+ Add To Your Collection", new_painting_artist_path %></h3>
<%= form_tag '/', method: :get do %>
<%= search_field_tag :q, params[:q] %>
<%= submit_tag "Search" %>
<% end %>
<br>
<div id="paintings">
<ul>
<% #paintings.each do |painting| %>
<li><%= link_to painting.title, {action: :show, id:painting.id} %> by <%= painting.artist_name %></li>
<div id = "img">
<br><%= link_to (image_tag painting.image), painting.image %><br>
</div>
<%= link_to "Edit", edit_painting_path(id: painting.id) %>
||
<%= link_to 'Destroy', {action: :destroy, id: painting.id},method: :delete, data: {confirm: 'Are you sure?'} %>
<% end %>
</ul>
</div>
In your case you should use accepts_nested_attributes_for and fields_for to achieve this.
Artist
has_many :paintings, :dependent => :destroy
accepts_nested_attributes_for :paintings
Painting
belongs_to :artist
And also you should try creating artist with paintings like this
form_for(#artist) do |f| %>
<fieldset>
<legend>Artist</legend>
<%= f.label :Artist %>
<%= f.text_field :name %>
<%= fields_for :paintings, #artist.paintings do |artist_paintings| %>
<%= artist_paintings.label :title %>
<%= artist_paintings.text_field :title %>
<%= artist_paintings.label :image %>
<%= artsist_paintings.text_field :image %>
</fieldset>
<%= f.submit %>
<% end %>
Note:
You should be having your Artist Controller with at least new,create,edit and update methods defined in it to achieve this.
Edit
Try the reverse
Artist
has_many :paintings, :dependent => :destroy
Painting
belongs_to :artist
accepts_nested_attributes_for :paintings
form_for(#painting) do |f| %>
<fieldset>
<legend>Painting</legend>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :image %>
<%= f.text_field :image %>
<%= fields_for :artists, #painting.artists do |ff| %>
<%= ff.label :Artist %>
<%= ff.text_field :name %>
</fieldset>
<%= f.submit %>
<% end %>
Put this form in paintings views.

partial form for nested models in rails 3

I have two models pages and author, here is the code of model pages:
edit -1
now my models are as follows:
class Page < ActiveRecord::Base
validates :title, :presence => true
belongs_to :author
end
author model:
class Author < ActiveRecord::Base
has_many :pages
end
and my form is as follows:
<%= form_for(#page) do |f| %>
<% if #page.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#page.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #page.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.fields_for :author do |fields| %>
<%= f.label :author %><br />
<%= fields.text_field :author %>
<% end %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :reference %><br />
<%= f.select(:reference,[['google',1],['yahoo',2],['MSN',3],['Ask',4]]) %>
</p>
<%= f.submit "Submit" %>
<% end %>
and controller :
class PagesController < ApplicationController
def index
#total = Page.count
#pages = Page.find(:all)
end
def show
#page = Page.find(params[:id])
end
def new
#page = Page.new
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to pages_path, :notice => "The data has been saved!"
else
render "new"
end
end
def edit
#page = Page.find(params[:id])
end
def update
#page = Page.find(params[:id])
if #page.update_attributes(params[:page])
redirect_to pages_path, :notice => "Your post has been updated!"
else
render "edit"
end
end
def destroy
#page = Page.find(params[:id])
#page.destroy
redirect_to pages_path, :notice => "Your page has been deleted!"
end
end
Now when i am submitting the form it is giving me this error:
ActiveRecord::AssociationTypeMismatch in PagesController#create
Author(#40328004) expected, got ActiveSupport::HashWithIndifferentAccess(#32291496)
Rails.root: C:/rorapp
Application Trace | Framework Trace | Full Trace
app/controllers/pages_controller.rb:19:in `new'
app/controllers/pages_controller.rb:19:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"hzBlXsdUjEDDCLp036R8bJBwep6BdATSvJPNwt0M8Dg=",
"page"=>{"title"=>"",
"body"=>"",
"author"=>{"author"=>""},
"email"=>"",
"reference"=>"1"},
"commit"=>"Submit"}
Show session dump
Show env dump
Response
Headers:
None
and one more problem if I add accepts_nested_attributes_for :author to my Page model, then the field is not displayed at all. I really want to accomplish this.. any help?
Apparently this doesn't work in reverse with the association. I'm sorry for the mistake. You could do the following, but then your pages controller is acting on the author, which isn't really appropriate. You could make an author controller though, and include fields_for :pages, to have the author and the first page created at the same time.
class PagesController < ApplicationController
def new
#author = Author.new
#page = #author.pages.new
end
def create
#author = Author.create(params[:author])
end
end
class Author < ActiveRecord::Base
has_many :pages
accepts_nested_attributes_for :pages
end
class Page < ActiveRecord::Base
belongs_to :author
end
<%= form_for(#author, :url => pages_url) do |f| %>
<%= f.text_field :author %>
<%= f.fields_for :pages do |fields| %>
<%= fields.text_area :body %>
<% end %>
<% end %>

Resources