Rails 4.2.1, Ruby 2.2.1 and PostgreSQL 9.3
I have region and pattern models
class Region < ActiveRecord::Base
has_many :patterns, dependent: :destroy
accepts_nested_attributes_for :patterns, reject_if: :all_blank, allow_destroy: true
end
class Pattern < ActiveRecord::Base
belongs_to :region
has_attached_file :picture
validates_attachment_content_type :picture, content_type: "image/png"
end
And nested form in slim format with cocoon gem
= simple_form_for(#region) do |f|
= f.simple_fields_for :patterns do |pattern|
= render 'pattern_fields', f: pattern
.links
= link_to_add_association 'add pattern', f, :patterns
_pattern_fields.html.slim view
.nested-fields
.form-inline
= f.input :picture, as: :file
= link_to_remove_association f, class: 'btn btn-default btn-xs' do
.glyphicon.glyphicon-remove
I can easily create region with multiple patterns, but when I click edit, file fields are empty.
Is there nice way to properly work with file fields and rails forms with paperclip?
Related
I'm using Rails 6 + cocoon gem with vanilla js. I'm creating a website with quizzes, and quizzes, questions to them and answers to questions should be created on one page. Because of that i used double nested forms:
class Test < ApplicationRecord
belongs_to :author, class_name: 'Teacher'
belongs_to :article, optional: true
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions, reject_if: :all_blank
end
class Question < ApplicationRecord
belongs_to :test
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers, reject_if: :all_blank
validates :title, presence: true
end
class Answer < ApplicationRecord
belongs_to :question
validates :content, presence: true
validate :validate_max_answer_amount, on: :create
scope :correct, -> { where(correct: true) }
private
def validate_max_answer_amount
errors.add :base, :invalid_amount, message: 'Too many answers!' if question.answers.count >= 5
end
end
The problem lies precisely in the display of fields when they are added using cocoon. For example:
Can't post here images, because i have 1 rep, so check here please
Initial form - 1st screenshot
Form after adding answers - 2nd screenshot (expected that new fields would be lower than old one)
Form after adding questions - 3rd screenshot (expected that new fields would be lower than old one & that new fields would contain field for the first answer)
My View files:
tests/_form.html.slim:
=render 'shared/errors', object: #test
=form_with model: [:teacher, #test], local: true do |f|
=f.number_field :author_id, class: 'hidden'
=f.number_field :article_id, class: 'hidden'
.mb-3.add-questions
p
| Add questions:
.mb-3
=f.fields_for :questions do |q|
=render 'teacher/questions/question_fields', f: q
br
br
=link_to_add_association 'Add one more question', f, :questions, partial: 'teacher/questions/question_fields'
=f.submit 'Submit', class: 'btn btn-outline-success btn-lg mb-4'
questions/_question_fields.html.slim:
.mb-3.row
.col-sm-2.col-form-label
=f.label :title, 'Title'
.col-sm-10.col-form-label
=f.text_field :title, class: 'form-control'
=f.fields_for :answers do |a|
=render 'teacher/answers/answer_fields', f: a
=link_to_add_association 'Add one more answer', f, :answers, partial: 'teacher/answers/answer_fields'
answers/_answer_fields.html.slim:
.mb-1.row
.col-sm-1
.col-sm-2.col-form-label
=f.label :content, 'Enter content'
.col-sm-8.col-form-label
=f.text_field :content, class: 'form-control'
.col-sm-1.col-form-label
=f.check_box :correct, {class: 'form-check-input'}, true, false
I tried to remove =render 'teacher/answers/answer_fields' in questions/_question_fields.html.slim, but it didn't help with the problem with the questions. That is, new questions appeared anyway without an answer field.
I haven't figured out how to try to solve the problem with adding fields for answers (so that new fields appear below the old ones), so I hope for your help!
How do I build a nested form for objects using multiple table inheritance in rails? I am trying to make a nested form to create an object using a model with a has_many relationship to another set of models that feature multi-table inheritance. I am using formtastic and cocoon for the nested form and the act_as_relation gem to implement the multiple table inheritance.
I have the following models:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
end
class Book < ActiveRecord::Base
acts_as :product, :as => :producible
end
class Pen < ActiveRecord::Base
acts_as :product, :as => :producible acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
end'
For this example, the only unique attribute that book has compared to other products is an author field. In reality, I have a number of unique attributes for book which is why I chose multi-table inheritance over the more commonplace single table inheritance.
I am trying to create a nested form that allows you to create a new store with products. Here's my form:
<%= semantic_form_for #store do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<h3>Books/h3>
<div id='books'>
<%= f.semantic_fields_for :books do |book| %>
<%= render 'book_fields', :f => book %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add book', f, :books %>
</div>
<% end %>
<%= f.actions :submit %>
<% end %>
And the book_fields partial:
<div class='nested-fields'>
<%= f.inputs do %>
<%= f.input :author %>
<%= link_to_remove_association "remove book", f %>
<% end %>
</div>
I get this error:
undefined method `new_record?' for nil:NilClass
Based on reading the issues on the github page for act_as_relation, I thought about making the relationship between store and books more explicit:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
has_one :book
accepts_nested_attributes_for :book, :allow_destroy => true, :reject_if => :all_blank
end
class Book < ActiveRecord::Base
belongs_to :store
acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
has_many :books, :through => :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
accepts_nested_attributes_for :books, :allow_destroy => true, :reject_if => :all_blank
end
Now, I get a silent error. I can create new stores using the form, and cocoon allows me to add new book fields, but when I submit the store gets created but not the child book. When, I go through the `/books/new' route, I can create a new book record that spans (the products and books table) with no problem.
Is there a workaround to this problem? The rest of the code can be found here.
Maybe you could:
Build the books relation manually on your stores_controller#new action
#store.books.build
Store manually the relation on you stores_controller#create action
#store.books ... (not really confident on how to achieve it)
Keep us posted.
You might want to consider creating your own form object. This is a RailsCast pro video, but here are some of the examples in the ASCIIcast:
def new
#signup_form = SignupForm.new(current_user)
end
This signup form can include relations to your other objects, just as you would in your original controller code:
class SignupForm
# Rails 4: include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
validates_presence_of :username
validates_uniqueness_of :username
validates_format_of :email, with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
validates_length_of :password, minimum: 6
def persisted?
false
end
def subscribed
subscribed_at
end
def subscribed=(checkbox)
subscribed_at = Time.zone.now if checkbox == "1"
end
def generate_token
begin
self.token = SecureRandom.hex
end while User.exists?(token: token)
end
end
Here is the link to the RailsCast. Getting a pro membership might be worth your time. I have been getting lucky with a membership through www.codeschool.com where you can get 'prizes' when you finish courses:
RailsCast:
http://railscasts.com/episodes/416-form-objects
I have a nested form using the cocoon gem that gests 5 classes deep. They are all belongs_to and has_many except one that is the 2nd to most nested class with a has_one and belongs_to association. Whenever I edit it, it gets deleted and recreates an instance (not what I want) any ideas?
class FirmwaresController < ApplicationController
def index
#firmwares = Firmware.all
end
def new
#firmware = Firmware.new
end
def edit
#firmware = Firmware.find(params[:id])
end
end
class Setting < ActiveRecord::Base
belongs_to :menu_item
has_many :selections, dependent: :destroy
has_many :dependencies
attr_accessible :kind, :name, :placement, :selections_attributes
accepts_nested_attributes_for :selections, reject_if: :all_blank, allow_destroy: true
validates_presence_of :kind
end
class MenuItem < ActiveRecord::Base
belongs_to :menu
belongs_to :dependency
has_one :setting, dependent: :destroy
attr_accessible :dependency_id, :menu_for, :name, :placement, :setting_attributes
accepts_nested_attributes_for :setting, reject_if: :all_blank, allow_destroy: true
validates_presence_of :name
end
_menu_items.html.haml
.row-fluid
.input-prepend
= f.input :name, label: "Menu Item Name"
.input-append
= f.input :placement, label: "Order in Menu (X.Y)", as: :string
= f.simple_fields_for :setting do |settings_form|
= render 'setting_fields', f: settings_form
%p
= link_to_add_association "Has Setting?", f, :setting, class: "btn btn-primary btn-small add_setting_link"
= link_to_remove_association 'Remove Menu Item', f, class: "btn btn-danger btn-small remove_menu_item"
_setting_fields.html.haml
.row-fluid
.input-prepend
= f.input :kind, label: "Type", collection: setting_kinds, input_html: {class: "setting_type"}
.input-append
= link_to_remove_association 'Remove Setting', f, class: 'btn btn-danger btn-small remove_setting_link'
.setting_selection{style: "display: none;"}
= f.simple_fields_for :selections do |selections_form|
= render 'selection_fields', f: selections_form
%p= link_to_add_association 'Add Selection Option', f, :selections, class: "btn btn-primary btn-small"
I assume the has_one association you're referring to is the has_one :setting in MenuItem. If so, you might try adding update_only: true to your accepts_nested_attributes_for :setting options. From the documentation (emphasis mine):
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include the record's :id value. Otherwise a new record will be instantiated and used to replace the existing one. However if the :update_only option is true, the nested attributes are used to update the record’s attributes always, regardless of whether the :id is present.
This question is old, but this could help some people:
The reason the association was always recreated for me, was that I had forgotten to include :id in the permitted parameters of phone_number_attributes! If you forget to do that, rails wont get the id and will recreate a new record to replace the old one.
def user_params
params.require(:user).permit(
:avatar,
:remove_avatar,
{ id_photo_attributes: [:photo, :remove_photo] },
{ phone_number_attributes: [:id, :phone_number] } # dont forget :id here!
)
end
I'm using active_admin and carrierwave gems. Have two simple models:
class Image < ActiveRecord::Base
attr_accessible :gallery_id, :file
belongs_to :gallery
mount_uploader :file, FileUploader
end
class Gallery < ActiveRecord::Base
attr_accessible :description, :title, :file, :images_attributes
has_many :images
accepts_nested_attributes_for :images, allow_destroy: true
mount_uploader :file, FileUploader
end
Now my active_admin form for Gallery looks like this:
form do |f|
f.inputs "Gallery" do
f.input :title
end
f.has_many :images do |ff|
ff.input :file
end
f.actions
end
Now I can upload one file, click "Add New Image" and upload another one. Instead of it, I'd like to click "Add new Image", select multiple files and upload them all at once. Any idea how can I implement it?
For a Gallery form with multiple image uploads you can try this
admin/galleries.rb
form do |f|
f.inputs "Gallery" do
f.input :name
end
f.has_many :images do |ff|
ff.input :file
end
end
In model/gallery.rb:
attr_accessible :images_attributes
In model/gallery.rb (add after relations):
accepts_nested_attributes_for :images, :allow_destroy => true
Hi
I just can not find out what is wrong with my code. I have two models Items and Images and relation between them
class Item < ActiveRecord::Base
attr_accessible :category_id, :user_id, :title, :description, :published, :start_date, :end_date, :images_attributes
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :reject_if => :all_blank, :allow_destroy => true
mount_uploader :name, ImageUploader
end
class Image < ActiveRecord::Base
belongs_to :item
mount_uploader :image, ImageUploader
attr_accessible :item_id, :name
end
I call the 3 instances of Carrierwave in items_controller.rb to add 3 images to the created item
def new
#item = Item.new
3.times { #item.images.build }
end
The view form looks like this :
<%= f.fields_for :images do |builder| %>
<p> <%= builder.text_field :name %> </p>
<% end %>
Which resoults in this randered code :
<input id="item_images_attributes_0_name" name="item[images_attributes][0][name]" type="file">
When Add and save the new item i get object data saved instead of file name ( suit.jpg ) in my database :
--- !ruby/object:ActionDispatch::Http::UploadedFile
content_type: image/jpeg
headers: |
Content-Disposition: form-data; name="item[images_attributes][0][name]"; filename="suit.jpg"
Content-Type: image/jpeg
original_filename: suit.jpg
tempfile: !ru
Screen shot from database table below :
https://lh3.googleusercontent.com/_USg4QWvHRS0/TXDT0Fn-NuI/AAAAAAAAHL8/91Qgyp5jK3Q/carrierwave-objest-database-saved.jpg
Is anyone who have an idea how to solve it ?
I had a similar issue, I wanted to create my own filenames (identifiers) so I defined a filename method on the uploader (in your case ImageUploader)
e.g.
def filename
#name ||= "foo"
end
First things first, Your mounting your uploader to a column named :image but from your pic of your db you dont have a column with said name.
1: Make a column for images called image ("since thats what your uploading.")
rails g migration add_image_to_images image:string
rake db:migrate
2: Update your model's attr_accessible to use the new column.
class Image < ActiveRecord::Base
belongs_to :item
mount_uploader :image, ImageUploader
attr_accessible :item_id, :name, :image
end
3: Update your view
<%= f.fields_for :images do |builder| %>
<p>
<%= builder.text_field :name %>
<%= builder.file_field :image %>
</p>
<% end %>
4: remove the unused mount from the Item class.
class Item < ActiveRecord::Base
attr_accessible :category_id, :user_id, :title, :description, :published, :start_date, :end_date, :images_attributes
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :reject_if => :all_blank, :allow_destroy => true
end
I left :name in place on Image for use as an arbitrary value you can add to your image.
Also by abstracting your image model like you did I would also assume you want to keep track of the image order so maybe an extra column for that would be a good idea too.
Hope this helps.