Rails 5 - Nested attributes in form, params missing or empty - ruby-on-rails

I am trying to create two associated objects from two different models but I am still getting the same error "param is missing or the value is empty: repost" I have tried to figure out why it fails for days but in vain. Any hint?
I have the Model repost
class Repost < ApplicationRecord
has_many :recurrences, inverse_of: :repost
accepts_nested_attributes_for :recurrences
def self.create_repost (twit_id)
twit = Twit.find_by_id(twit_id)
Repost.find_or_create_by(link: twit.link) do |repost|
repost.twit_id = twit.id
repost.content = twit.content
repost.image_url = twit.image_url
repost.content_url = twit.content_url
repost.click = 0
repost.like = 0
repost.retweet = 0
repost.engagement = 0
repost.number_of_publications = repost.publications.count
repost.likes_sum = 0
repost.retweets_sum = 0
repost.engagement_sum = 0
repost.recurrence = repost.recurrences.recurring
end
end
and the model Recurrence
class Recurrence < ApplicationRecord
serialize :recurring, Hash
belongs_to :repost, inverse_of: :recurrences
def self.set(repost_id, frequency)
repost = Repost.find_by_id(repost_id)
Recurrence.create do |recurrence|
recurrence.repost_id = repost.id
recurrence.recurring = frequency
recurrence.start_time = recurrence.created_at
recurrence.end_time = nil
end
end
def recurring=(value)
if RecurringSelect.is_valid_rule?(value)
super(RecurringSelect.dirty_hash_to_rule(value).to_hash)
else
super(nil)
end
end
def rule
rule = IceCube::Rule.from_hash(self.recurring)
rule
end
def schedule
schedule = IceCube::Schedule.new(self.created_at)
schedule.add_recurrence_rule(rule)
schedule
end
end
here is my Repost Controller
class RepostsController < ApplicationController
before_action :find_repost, only: [:show, :edit, :update, :destroy, :get_likes]
def index
#repost = Repost.new
if params[:filter_by]
#reposts = Repost.filter(params[:filter_by], params[:min], params[:max], params[:start_at], params[:end_at])
else
#reposts = Repost.all.order("created_at DESC")
end
end
def show
end
def new
#repost = Repost.new
end
def create
#repost = Repost.create_repost(repost_params)
redirect_to reposts_path
end
def destroy
#repost.destroy
redirect_to reposts_path
end
private
def find_repost
#repost = Repost.find(params[:id])
end
def repost_params
params.require(:repost).permit(:twit_id, recurrences_attributes: [:recurring])
end
end
and here is my view with the form
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1">
<h1 class="twit-h1">My best Tweets</h1>
<p><%= render "filter_form" %></p>
<p><% #twits.each do |twit| %></p>
<p><%= form_for #repost do |f| %></p>
<%= hidden_field_tag :twit_id , twit.id %>
<div class="twit">
<%= image_tag "#{twit.image_url}", class: "twit-image" %>
<div class="twit-body">
<% if twit.content.present? %>
<h3><%= twit.content %></h3>
<% else %>
<%= "This tweet has no text" %>
<% end %>
<% if twit.content_url.present? %>
<%= link_to "#{twit.content_url}".truncate(40), twit.content_url, class: "twit-link" %>
<% else %>
<%= "This tweet has no link" %>
<% end %>
<%= f.fields_for :recurrences do |r| %>
<h6 style="float: right;"><%= r.select_recurring :recurring %></h6>
<% end %>
<h6>
<i class="fa fa-bullhorn fa" aria-hidden="true"></i> <%= twit.engagement %>
<i class="fa fa-retweet fa" aria-hidden="true"></i> <%= twit.retweet %>
<i class="fa fa-heart fa" aria-hidden="true"></i> <%= twit.like %>
</h6>
<p>Tweeted on <%= (twit.first_date).strftime("%A, %B %d, %Y at %I:%M%p %Z") %></p>
<%= link_to "Delete", twit_path(twit), name: nil, class: "btn btn-danger btn-sm", method: :delete, data: {:confirm => 'Are you sure?'} %>
<%= f.submit "Add to your Reposts library", class: "btn btn-primary btn-sm" %>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
Update, here is my logs
Started POST "/__better_errors/923b59f8da1318ef/variables" for ::1 at 2017-02-21 15:52:56 +0100
Started POST "/reposts" for ::1 at 2017-02-21 15:54:14 +0100
Processing by RepostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"62pE3le5m99HF10RgQcd70wFHqWQWiRmQQO2ndNHT3gnceRu94Y1ttyBuSExBjr4/cxeVUtgY60GRThxdpFGJg==", "twit_id"=>"1844", "recurring"=>"{\"interval\":1,\"until\":null,\"count\":null,\"validations\":{\"day\":[1],\"hour_of_day\":1,\"minute_of_hour\":0},\"rule_type\":\"IceCube::WeeklyRule\",\"week_start\":0}", "commit"=>"Add to your Reposts library"}
Completed 400 Bad Request in 2ms (ActiveRecord: 0.0ms)
ActionController::ParameterMissing - param is missing or the value is empty: repost:
It seems my error comes from this private method in my Repost controller but I can't understand why?
def repost_params
params.require(:repost).permit(:twit_id, recurrences_attributes: [:recurring])
end
Thank you!

Related

rails params permitted: false when updating

I have a site where uses have a profile and can add an art to their profile and the month_started, year_started, and rank in that art. I am able to add the art but I cannot get update to work. I'm doing it from within the users folder on the show page. I render the add_art form on the profile page and it submits to the user_arts_controller's create action. For edit_art I am trying to use the edit.html.erb page inside users and it pulls in the information from the #art_user table to populate the fields, but when I submit it acts like it's updating and goes back to the set page and gives a success message but nothing gets updated. I used byebug and it shows all the params but says permitted: false.
Here is my form:
<% vars = request.query_parameters %>
<% #add = vars['add'] %>
<% if #add == "1" %>
<% art = "Judo" %>
<% else %>
<% art = "Brazillian Jiu Jitsu" %>
<% end %>
<div class="container">
<div class="row center form">
<div class="col-sm-12 col-md-10">
<div class="card-panel">
<h3>Edit <%= art %> info.</h3>
<%= form_with(model: #art, url: art_edit_path(art_id: #add), class: "shadow p-3 mb-3 rounded text-dark", local: true) do |f| %>
<div class="form-group row">
<div class="col-md-3 col-sm-12 col-form-label">
<%= f.label :month_started %>:<span class="text-danger">*</span>
</div>
<div class="col-md-9 col-sm-12">
<%= f.select :month_started, Date::MONTHNAMES[1..12], {prompt: true}, {class: 'form-control'} %>
</div>
</div>
<diva class="form-group row">
<div class="col-md-3 col-sm-12 col-form-label">
<%= f.label :year_started %>:<span class="text-danger">*</span>
</div>
<div class="col-md-9 col-sm-12">
<%= f.select :year_started, Date.today.year-70 .. Date.today.year, {prompt: true, order: [:year]}, {class: 'form-control'} %>
</div>
</diva>
<% #rank = Rank.where(art_id: #add) %>
<div class="form-group row">
<div class="col-md-3 col-sm-12 col-form-label">
<%= f.label :rank %>:<span class="text-danger">*</span>
</div>
<div class="col-md-9 col-sm-12">
<%= f.collection_select :rank_id, #rank, :id, :rank, {:prompt=>true}, {:class=>'form-control'} %>
</div>
</div>
<div class="form-group row text-right">
<div class="col-md-8 col-sm-12"></div>
<div class="col-md-4 col-sm-12">
<%= f.submit (#add == "1" ? "Edit Judo" : "Edit Brazilian Jiu Jitsu"), class: "profile_btn" %>
</div>
</div>
<% end %>
</div>
</div>
</div>
</diva>
Here is the user_arts_controller.rb:
class UserArtsController < ApplicationController
def create
art_to_add = Art.find(params[:art_id])
unless current_user.arts.include?(art_to_add)
UserArt.create(art: art_to_add, user: current_user, month_started: params["month_started"], year_started: params["year_started"], rank_id: params["rank_id"])
flash[:notice] = "You have successfully enrolled in #{art_to_add.art}"
redirect_to ("/users/#{current_user.username}?cat=comp")
else
flash[:notice] = "Something went wrong!"
redirect_to ("/users/#{current_user.username}")
end
end
def edit
#art = UserArt.find_by(user_id: current_user.id, art_id: 1)
if #art
#my_rank = Rank.find(id: #art.rank_id)
end
end
def update
byebug
#user_art = UserArt.where(id: params[:id])
if #user_art.update(art_params)
flash[:notice] = "You have successfully updated your art"
redirect_to ("/users/#{current_user.username}?cat=comp")
else
flash[:notice] = "Something went wrong!"
redirect_to ("/users/#{current_user.username}")
end
end
private
def art_params
params.require(:user_arts).permit(:user_id, :art_id, :month_started, :year_started, :rank_id)
end
end
Here are my routes:
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: 'registrations', omniauth_callbacks: 'users/omniauth_callbacks' }
root 'pages#index'
get 'events', to: 'pages#events'
get 'features', to: 'pages#features'
get 'pricing', to: 'pages#pricing'
get 'about', to: 'pages#about'
get 'contact', to: 'pages#contact'
get 'privacy', to: 'pages#privacy'
get 'terms', to: 'pages#terms'
get 'remove', to: 'pages#remove'
delete 'logout', to: 'sessions#destroy'
resources :users, except: [:new], param: :username
resources :clubs, param: :club_username
post 'club_join', to: 'user_clubs#create'
post 'art_add', to: 'user_arts#create'
post 'art_edit', to: 'user_arts#edit'
patch 'art_edit', to: 'user_arts#update'
end
Here is what I get when I use byebug and enter params:
(byebug) params
<ActionController::Parameters {"_method"=>"patch", "authenticity_token"=>"UY4RG1D0N0IHcJeyftn44WmAgsdslKJScD+GSJPbAUEEjlJLiYLd2htK4RBTfrP+YPUXP5EFB5pQc98OmQPPtg==", "user_art"=>{"month_started"=>"January", "year_started"=>"1988", "rank_id"=>"14"}, "commit"=>"Edit Judo", "art_id"=>"1", "controller"=>"user_arts", "action"=>"update"} permitted: false>
(byebug)
Any help is greatly appreciated.
Thanks
I think you have an extra s in the definition of your allowed params. Since you are getting this:
"user_art"=>{"month_started"=>"January", "year_started"=>"1988", "rank_id"=>"14"}
You should check in this way:
def art_params
params.require(:user_art).permit(:user_id, :art_id, :month_started, :year_started, :rank_id)
end
Just a final comment. Take a look at the params you're permitting, I guess user_id shouldn't be one of them.
Let me know if it works

Aggregation on Many to many relations activerecord

I want to make ES aggregation on many to many activerecord model on rails but no way
My model :
class Foo < ApplicationRecord
searchkick
def search_data
{
bar: bar
}
end
has_many :bars
end
I have tried many solution but always get
{"active"=>{"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]}}
The answer is add this :
def search_data
Attributes.merge(
bar_names: bar.map(&:name)
)
end
Here more about how to work with associated models, here is:
Item.rb (model):
# Relations
belongs_to :brand
## many-to-many relations between Items and Textures
has_many :item_attribute_for_textures
has_many :textures, through: :item_attribute_for_textures, :class_name => 'ItemAttribute::Texture'
# Searchkick
searchkick
def search_data
{
full_item_title: full_item_title,
brand: brand.try(:title),
texture: textures.map(&:title)
}
end
ItemController.rb:
def index
args = {}
args[:brand] = params[:brand] if params[:brand].present?
args[:texture] = params[:texture] if params[:texture].present?
query = params[:busca].presence || "*"
#items = Item.search query, where: args,
aggs: {
full_item_title: {},
brand: {},
texture: {}
}
end
and view index.html.erb:
<div class="row">
<div class="col-sm-2" style="font-size: 0.75rem">
<h5>Brands</h5>
<div>
<% #items.aggs["brand"]["buckets"].sort_by{ |b| b["key"] }.each do |bucket| %>
<div>
<% if params[:brand] == bucket["key"].to_s %>
<strong><%= bucket["key"] %></strong>
<% else %>
<%= link_to bucket["key"], request.params.merge(brand: bucket["key"]) %>
<% end %>
(<%= bucket["doc_count"] %>)
</div>
<% end %>
</div>
<hr>
<h5>Texture</h5>
<div>
<% #items.aggs["texture"]["buckets"].sort_by{ |b| b["key"] }.each do |bucket| %>
<div>
<% if params[:texture] == bucket["key"].to_s %>
<strong><%= bucket["key"] %></strong>
<% else %>
<%= link_to bucket["key"], request.params.merge(texture: bucket["key"]) %>
<% end %>
(<%= bucket["doc_count"] %>)
</div>
<% end %>
</div>
<hr>
</div>
</div>

ElasticSearch Range Aggregation no output results with no error

Hello I've got this problem with the rails Elascticsearch range aggregations, it seems right as there's no error output but then again it also doesn't aggregate.
Heres my controller
def results
min_price = params[:min_price] if params[:min_price].present?
max_price = params[:max_price] if params[:max_price].present?
price_ranges = [{to: max_price}, {from: min_price, to: max_price}, {from: min_price}]
#results = Item.search(params[:q], aggs: {item_final_price: {ranges: price_ranges}}, page: params[:page], per_page: 10) if params[:q].present?
end
and my model
class Item < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
# Item.import
searchkick callbacks: :async, highlight: [:item_name]
def search_data
{
item_name: item_name,
item_details: item_details,
item_final_price: item_final_price,
item_av_rating: item_av_rating
}
end
end
and my views
<%= form_tag results_path, method: :get, enforce_utf8: false, id: "q_filter" do %>
<section class="widget widget-categories">
<%= hidden_field_tag :q, params[:q] %>
<h3 class="widget-title">Price Range</h3>
<div class="form-group">
<label>Price Between</label>
<%= number_field_tag :min_price, params[:min_price], class: "form-control form-control-sm", placeholder: "Min Price" %>
</div>
<div class="form-group">
<label>And</label>
<%= number_field_tag :max_price, params[:max_price], class: "form-control form-control-sm", placeholder: "Max Price" %>
</div>
<%= button_tag(type: "submit", name: nil, class: "btn btn-outline-primary btn-sm btn-block") do %>
Filter Search
<% end %>
</section>
<% #results.each do |item| %>
<%= item.item_name %>
<% end %>
Try to target this in your view
#results.response["aggregations"]["item_final_price"]["buckets"]
If you're running in development, can you throw a binding.pry and see what the result keys are? The data should be there if the query returns results.

How to filter out certain objects in a table with certain ids? (Rails 4, Postgresql)

before I ask my question, I will provide some context.
I have 3 Models which interact with each other.
Scoreboard has many Teams. Team belongs to Scoreboard.
The following use a through table.
Scoreboard many-to-many with User.(let's call this Scoreboard.followers)
Team many-to-many with User.(Let's call this Team.members)
I have a Team_Members Controllers with the following method:
Team_member#new Method:
def new
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = Team.find(params[:team_id])
#team_members = #team.members
#followers = #scoreboard.followers.search(params[:search]).paginate(page: params[:page], per_page: 50)
end
What I'm attempting to do is display all the Scoreboard followers in my new.html.erb view. I can search through them and add members to a Team that is associated to the same Scoreboard.
Here's is the new.html.erb view:
<%= form_tag(new_scoreboard_team_team_member_path, :method => "get", id: "member-search-form", autocomplete: "off") do %>
<div class="row new-member-field">
<div class="col-xs-12 col-sm-6">
<%= text_field_tag :search, params[:search], placeholder: "Filter Members by Name", class:"form-control" %>
</div>
</div>
<% end %>
<% if #followers.any? %>
<% #followers.each do |follower| %>
<div class="row member-follower-div" id="follower_<%=follower.id%>">
<div class="col-xs-10 col-sm-5 member-prof-link">
<%= link_to follower.name, user_path(follower.id) %>
</div>
<div class="col-xs-2 col-sm-1 member-add">
<%= link_to (add_scoreboard_team_team_member_path(#scoreboard,#team, follower)), method: :post, remote: true, :data => {:disable_with => ".."} do %>
<i class="fa fa-plus-square fa-lg" aria-hidden="true"></i>
<% end %>
</div>
</div>
<% end %>
<div class="row">
<div class="col-xs-12 col-sm-6 new-member-pages">
<%= will_paginate #followers %>
</div>
</div>
<% end %>
Okay now for the question. The problem arises in the controller method. The #team.members and #scoreboard.followers both source objects from the Users table. How would I filter out the User objects from the #scoreboard.followers that exist in the #team.members? I don't want to display #followers on my view that are a part of the #team.members collection. Also, as a bonus, I would like to alphabetically order the followers by user.name.
I tried the .where("users.id NOT IN (?)", #team_members.pluck(:id)) but if #team_members collection is null, no results are displayed on the page.
I solved it like so:
def new
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = Team.find(params[:team_id])
#team_members = #team.members
if (#team_members.any?)
#wonderful_humans = #scoreboard.favourited_by.where("users.id NOT IN (?)", #team_members.pluck(:id)).search(params[:search]).sort_by{ |a| a.name.downcase }
else
#wonderful_humans = #scoreboard.favourited_by.search(params[:search]).sort_by{ |a| a.name.downcase }
end
#followers = #wonderful_humans.paginate(page: params[:page], per_page: 50)
end

Setting up pagination with will_paginate properly

I have been trying to setup pagination properly but I am having trouble following any tutorial and getting it to work properly.
What I basically want setup is that when a user chooses the number of companies they want to appear on a page, the following loop with paginate the given number of companies on each page. If the user doesn't choose a specific number of companies/page, there would be a default value that would be set.
If anyone could maybe give me a push in the right direction, that would be much appreciated.
View
<ul class="list-group">
<%= #comps.find_each do |comp| %>
<li class="list-group-item">
<div class="row">
<div class="span12">
<p style="font-size: 1.5em"><strong><%= link_to comp.name, comp %></strong></p>
<hr class="divider">
<div class="row">
<div class="span6">
<p><strong>Primary Field:</strong> <%= comp.primary_field %></p>
<p><strong>Description:</strong> <%= truncate(comp.description, length: 150) { link_to "Read More", comp, :target => "_blank"}%>
</p>
</div>
<div class="span5">
<% if signed_in? %>
<p><% if !(current_user.favorites.include? comp) %>
<%= link_to 'Favorite',
favorite_company_path(comp, type: "favorite"),
class: "btn btn-primary",
method: :put%>
<% else %>
<%= link_to 'Unfavorite',
favorite_company_path(comp, type: "unfavorite"),
class: "btn btn-danger",
method: :put%>
<% end %></p>
<% end %>
<p><strong>Website:</strong> <%= auto_link(comp.website, :html => { :target => '_blank' })%></p>
<p><strong>Location:</strong> <%= comp.address %></p>
</div>
</div>
</div>
</div>
</li>
<% end %>
<%= will_paginate #comps%>
</ul>
Controller (Where the list page is handled)
def filter
if ((params[:field] != "") and (params[:field] != "All Fields") and (params[:field] != nil))
# This will apply the filter
#comps = Company.where(primary_field: params[:field])
else
# This will return all records
#comps = Company.all
end
end
def list
#fields = ##primary_field
filter
#comps = #comps.paginate(:page => params[:page], :per_page => params[:number_of_companies] ||= 5)
end
Model for the list page
class DynamicPages < ActiveRecord::Base
self.per_page = 5
end
Since you are using will_paginate. You could do in this way.
Post.paginate(:page => params[:page], :per_page => params[:number_of_companies] ||= 10)
Here params[:number_of_companies] is the number of companies a user has selected and 10 is the
default page value.
class Post
self.per_page = 10
end
Also, your code snippet <%= #comps.find_each do |comp| %>, should be this <% %> instead of <%= %>

Resources