rails unchanged file fields got deleted on updating nested forms with carrierwave - ruby-on-rails-4

I've two models: ad and variant
Model: Ad
has_one :variant
accepts_nested_attributes_for :variant
Controller AdsController strong parameters:
params.require(:ad).permit(:title, :desc, variant_attributes: [:custom_image_1, :custom_image_2, :custom_image_3])
View ads/_form.slim
= f.fields_for :variant, #ad.variant || Variant.new do |va|
- 3.times do |i|
= va.file_field "custom_image_#{i+1}"
In view I added the nested form fields using a loop. The problem is when I create any AD, that time suppose I upload only one image in variant form (custom_image_1). Now, I came back for editing and uploaded another image on the variant (custom_image_2).
After the update, I saw that my previously uploaded custom_image_1 is deleted and only custom_image_2 is present in the database.
What is the reason behind it?
I check the params while submitting the edit form. There only contains the custom_image_2 in submitted attributes.

Hope someone will find that useful:
In my strong parameters, I need to include :id to resolve this issue.
params.require(:ad).permit(:title, :desc, variant_attributes: [:id, :custom_image_1, :custom_image_2, :custom_image_3])

Related

Rails 4: ActiveAdmin clears out an array field upon update

I have an ActiveAdmin model registered as such:
ActiveAdmin.register MyModel do
permit_params :name, :synonyms
filter :name
index do
selectable_column
column :name
actions
end
end
I noticed that when I update an object from the ActiveAdmin interface, the synonyms are getting cleared out. Synonyms are an array field defined as such in the PostgreSQL database:
synonyms text[] DEFAULT '{}'::text[]
I tried putting the following in app/admin/my_model.rb:
controller do
def update
permitted_params[:my_model][:synonyms] = JSON.parse permitted_params[:my_model][:synonyms]
super
end
end
and I also tried it with params instead of permitted_params but that doesn't work either. Not sure why ActiveAdmin would be discarding this field. Am I doing something incorrectly? The project I'm working with uses ActiveAdmin 1.0.0.pre4 (I realize this is a dated version).
Any advice would be much appreciated. Thanks in advance!
Notes: Seems this might be a common issue?
I'm not sure if this is applicable to string arrays and I don't know about that specific version of Activeadmin, but I encountered this issue in a slightly different context.
I had a model where the array data types were decimals & integers. I had to explicitly state the type of form input to be used when editing / updating the record or nothing was passed back from the field with the update parameters. Activeadmin chose a number input by default, but it needed to be processed as a string.
form do |f|
f.inputs do
f.input :ages, as :string, :input_html => {:maxlength => '100'}
end
end
I had to set maxlength manually because for some reason it was automatically being set very short. In the model, the string then has to be converted into an actual array before being saved.
def ages= items
if items.is_a? String
items = items.split(' ')
items.each do |i|
i.to_i
end
end
super items
end

Ruby on Rails - Cocoon gem, Don't Pre-populate existing/added posts in edit action

My application has models Campaign & Post, I have:
class Campaign < ActiveRecord::Base
has_many :posts, inverse_of: :campaign
accepts_nested_attributes_for :posts, reject_if: :all_blank, allow_destroy: true
class Post < ActiveRecord::Base
belongs_to :campaign
My form:
= simple_form_for(#campaign) do |f|
= f.error_notification
= f.input :title
#posts
= f.simple_fields_for :posts do |post|
= render 'post_fields', f: post
.links
= link_to_add_association 'Add Post', f, :posts, wrap_object: Proc.new {|post| post.user_id = current_user.id; post }
I use Cocoon gem for nested_forms.
When I go to my campaigns#edit view, I can see all posts that were already added to a campaign (natural behavior of the gem), and I can add new posts to my campaign and/or edit existing posts .
I have also a button that has this param: add_to: 'existing_campaign' and what I am trying to achieve is, if my link has ?add_to=existing_campaign, I don't want to show/Pre-populate any of the posts that were already added to campaign, so user can only add new posts to the campaign
My link_to looks like:
= link_to 'Add Post', edit_campaign_path(campaign, add_to: 'existing_campaign'),
short explain: if edit link has param ?add_to=existing_campaign, I don't Pre-populate already added posts, if param doesn't exists, I Pre-populate posts
How can I achieve this?
Set up an attr_accessor in campaign to control whether or not existing posts should be seen...
class Campaign < ActiveRecord::Base
attr_accessor hide_posts
...
end
Set the value in your edit method
class CampaignsController < ApplicationController
def edit
#campaign.hide_posts = params[:add_to] == 'existing_campaign'
...
end
Ensure the temporary variable is in your strong parameters (so that redisplay after failed update remembers to hide posts)
def campaign_params
params.require(:campaign).permit( :hide_posts, ...
Now on your view you can do...
= f.hidden_field :hide_posts
= f.simple_fields_for :posts do |post|
= render('post_fields', f: post) unless #campaign.hide_posts && post.object.persisted?
Updating with more specifics ...
You as the design need to decide if you want to move the logic to a helper. If you aren't aware of skinny controller / fat model or another strategy for organizing your codebase read this ... Link. For now, I'm going to assume you will use a helper.
The theory & some nitpicky things ...
What's going to happen with skinny controller is you need that parameter accessible in the view logic (but that would be insecure and violate the rails way). Since you shouldn't just expose params to the view, you instead are passing the message containing the parameter's value. Object orientation teaches us to use pass messages. Rails says to use instances variables (#something) in the controller and the view has access to those.
We need to setup the conditional logic next. One of the Rails ways is to use helpers to remove extraneous logic from the view or make it readable. This qualifies as something that unless you have another reason I'm not aware of - this should be in a helper. I would be tempted here to just test for boolean and call another partial for the extra view you make.
Which means I have to assume your tests will change too (if not done in a standard way, you have to include to get access to that method/object).
Specific to your question
link_toon the page calling the nested form should be true/false...
= link_to 'Add Post', edit_campaign_path(campaign, show_posts: false)
Your controller will have #show_posts in whatever action of the campaign controller you are using (edit or new usually). You need to set #show_posts = params[:show_posts]
Write the helper ...
helpers/campaign_helper.rb
def showPosts?(show_posts)
testPart == true ? 'only_comment' : 'post_fields'
end
A new partial ... which is basically the same, but drops the simple_fields_for loop which populates the other posts.
Your _form partial will change from what you had above to ...
= f.simple_fields_for :posts do |post|
= render 'posts_fields', f: post
To ...
...
#posts
= render showPosts?(#show_posts), f: post
...
Update, I tested all the parts & got it working with your exact syntax - I ended up using ternary operator in the helper.

Rails 4: strong_params,nested_attributes_for and belongs_to association trouble

I really can't get my head around Rails 4 strong parameters, belongs_to association and form with fields_for.
Imagine I have model for quoting some price:
class Quote < ActiveRecord::Base
belongs_to :fee
accepts_nested_attributes_for :fee
Now, I have seeded some fees into the db, and have put some radiobuttons on my form_for #quote using fields_for. The values of the radiobuttons are simply ids of the records.
Here is the troubling part, the controller:
def create
#quote = Quote.new(quote_params)
...
end
def quote_params
params.require(:quote).permit(:amount_from, fee_attributes: [:id])
end
From my understanding, automagically Rails should fetch fee record with some id, but there is some mystic error instead.
params hash is: "quote"=>{"amount_from"=>"1200", "fee_attributes"=>{"id"=>"1"}}
Log tail:
Completed 404 Not Found in 264ms
ActiveRecord::RecordNotFound (Couldn't find Fee with ID=1 for Quote with ID=)
app/controllers/quotes_controller.rb:14:in `create'
I really don't understand what is going on here, have read Rails association guide, googled for hour for all info, but to no avail.
What I want to achieve here is to understand the correct "Rails way" to fetch some associations for new Quote object using some params I've put in the form.
Guess I got nested_attributes_for wrong, somehow thought it would call Fee.find automagically.
I've opted for ditching fields_for helpers from the form and rendering fields manually like
radio_button_tag 'fee[id]', fee.id
Then in controller I have 2 params methods now:
def quote_params
params.require(:quote).permit(:amount_from)
end
def fee_params
params.require(:fee).permit(:id)
end
And my action looks like
def create
#quote = Quote.new(quote_params)
#quote.fee = Fee.find(fee_params[:id])
...
Any additions on best practices when one has to handle lots of different objects with not so straight init logic are welcome.

Mongoid records embedded in resource in active admin are not displayed

I have the following models
class User::ActiveAdmin::Partner < User::ActiveAdmin::Base
embeds_many :bonuses, class_name: 'User::Bonus'
end
and
class User::Bonus
include Mongoid::Document
embedded_in :partner, class_name: 'User::ActiveAdmin::Partner'
end
and then I register Bonuses in Active Admin
ActiveAdmin.register User::Bonus, as: 'Bonuses' do
config.filters = false
permit_params :number, :order_id
controller do
def scoped_collection
if current_admin_user.is_a? User::ActiveAdmin::Partner
current_admin_user.bonuses.page(params[:page]).per(10)
else
super
end
end
end
the collection is not empty (I have created a couple of bonuses), but in ActiveAdmin index page I see, that there are NO BONUSES. And nothing I can do to make it displayed properly. I have noticed, that User::Bonus table is empty, even if a partner does have any, but as I know, this is the way it works, and this is OK. So how can I make my table to be displayed? Thanks.
The problem in method ActiveAdmin::Helpers::Collection#collection_size. You are using old version of activeadmin-mongoid. Try update activeadmin-mongoid.
In rails4 branch, collection_size isn't correct. You should override this method in your app like here: https://github.com/elia/activeadmin-mongoid/blob/master/lib/active_admin/mongoid/helpers/collection.rb

Rails 4 - Column exists but does not update

I have recently added a field "tag" to my blog app built in Rails 4. Below you can see the field appearing in the Edit view:
But once I return to the Show view after editing, this does not appear:
When I check the database directly I can definitely see it exists:
sqlite> PRAGMA table_info(POSTS);
0|id|INTEGER|1||1
1|title|varchar(255)|0||0
2|body|text|0||0
3|created_at|datetime|0||0
4|updated_at|datetime|0||0
5|slug|varchar(255)|0||0
6|tag|varchar(255)|0||0
Can anyone suggest what is going on or how to troubleshoot this?
Rails 4 uses strong parameters by default. This means you have to explicitly whitelist params you wish to mass assign.
When adding a new attribute to a model, you have to remember to update the permitted params in you controller.
For example, in your case, you would need to make sure :tags are added like so:
class PostController < ActionController::Base
def update
post = Post.find(params[:id])
post.update(post_params)
redirect_to post
end
private
def post_params
params.require(:post).permit(:title, :body, :tag)
end
end