update join table has_many through with additional checkboxes - mysql

if i dont find solution here i have no idea where i should looking for...
I know there is right, easy solution for it...but something i just dont understand.
I have 3 models...
Scheme.rb
class Scheme < ActiveRecord::Base
has_many :projects
has_many :custom_issue_field_definitions, through: :scheme_custom_issue_field_definitions
has_many :scheme_custom_issue_field_definitions
end
CustomIssueFieldDefinition.rb
class CustomIssueFieldDefinition < ActiveRecord::Base
has_many :schemes, through: :scheme_custom_issue_field_definitions
has_many :scheme_custom_issue_field_definitions
belongs_to :custom_issue_field
end
SchemeCustomIssueFieldDefinition.rb
class SchemeCustomIssueFieldDefinition < ActiveRecord::Base
belongs_to :scheme
belongs_to :custom_issue_field_definition
end
Join Model have 3 additional fields...with type: boolean.
I'd like to update scheme_custom_issue_field_definitions table. How controller and form should looks like?
Additional image:
Update:
In console i can update it like this:
a = Scheme.first
b = CustomIssueFieldDefinition.first
c = a.scheme_custom_issue_field_defintitions.find_or_create_by(custom_issue_field_definition: b)
c.update_attributes(visible: 1, reportable: 0, required: 0)
Next Update:
Now form looks like this (what is completely wrong):
<%= simple_form_for #scheme_new_custom_issue_field, url: configurations_scheme_path(#scheme), method: :put do |f| %>
<% #available_custom_issue_field_definitions.each do |custom_issue_field| %>
<tr>
<td><%= custom_issue_field.label %></td>
<td><%= f.input :visible, as: :boolean %></td>
<td><%= f.input :reportable, as: :boolean %></td>
<td><%= f.input :required, as: :boolean %></td>
</tr>
<% end %>
<%= f.button :submit, class: "btn btn-primary" %>
<% end %>
and schemes_controller
def update
#scheme = Scheme.find(params[:id])
#scheme_new_custom_issue_field = #scheme.scheme_custom_issue_field_definitions.
find_or_create_by(scheme_id: #scheme, custom_issue_field_definition_id: params[:custom_issue_field_definition_id])
if #scheme_new_custom_issue_field.update_attributes(scheme_custom_issue_field_definition_params)
flash[:success] = "Scheme has been successfully updated"
redirect_to :back
else
render :edit
end
end
Update Solution:

If you want to create new SchemCustomIssueFieldDefinition record in the console, I suggest this alternative:
a = Scheme.first
b = CustomIssueFieldDefinition.first
c = SchemeCustomIssueFieldDefinition.find_or_initialize_by(scheme: a, custom_issue_field_definition: b)
c.assign_attributes(visible: 1, reportable: 0, required: 0)
c.save
This will save you one less query than your current example code as this will not do have an UPDATE sql.
For your form:
<%= simple_form_for #scheme, method: :patch do |f| %>
<% #available_custom_issue_field_definitions.each do |custom_issue_field| %>
<tr>
<%= f.simple_fields_for :scheme_custom_issue_field_definitions, #scheme.scheme_custom_issue_field_definitions.find_or_initialize_by(custom_issue_field_definition: custom_issue_field) do |ff| %>
<%= ff.hidden_field :id %>
<%= ff.hidden_field :scheme_id, value: #scheme.id %>
<%= ff.hidden_field :custom_issue_field_definition_id, value: custom_issue_field.id %>
<td><%= custom_issue_field.label %></td>
<td><%= ff.input :visible, as: :boolean %></td>
<td><%= ff.input :reportable, as: :boolean %></td>
<td><%= ff.input :required, as: :boolean %></td>
<% end %>
</tr>
<% end %>
<%= f.button :submit, class: "btn btn-primary" %>
<% end %>
schemes_controller.rb:
class SchemesController < ApplicationController
# ...
def update
#scheme = Scheme.find(params[:id])
if #scheme.update(scheme_params)
flash[:success] = "Scheme has been successfully updated"
redirect_to :back
else
render :edit
end
end
# ...
private
def scheme_params
# update below if you need other scheme attributes to be updated
params.require(:scheme).permit(:id, scheme_custom_issue_field_definitions_attributes: [:id, :scheme_id, :custom_issue_field_definition_id, :visible, :reportable, :required])
end
end
scheme.rb
class Scheme < ActiveRecord::Base
# ...
accepts_nested_attributes_for :scheme_custom_issue_field_definitions_attributes
# ...
end

Related

How to update chart (Chartkick) on a page after filtering?

I have a problem. I have made some filters using ransack and they work fine, but I have no idea how to update the pie chart that I have implemented under the table that I filter. The chart is made using Chartkick. I am fairly new to ror and programing in general. Here is my index file:
<h1>Lista Przychodów</h1>
<h2><%= link_to 'Nowy Przychód', new_income_path %></h2>
<%= search_form_for #q do |f| %>
<div class="field">
<%= f.label :title_cont, "Tytuł zawiera" %>
<%= f.search_field :title_cont %>
</div>
<div class="field">
<%= f.label :text_cont, "Opis zawiera" %>
<%= f.search_field :text_cont %>
</div>
<div class="field">
<%= f.label :category_name_cont, "Kategoria zawiera" %>
<%= f.search_field :category_name_cont %>
</div>
<div class="field">
<%= f.label :amount_gteq, "Kwota pomiędzy" %>
<%= f.search_field :amount_gteq %>
<%= f.label :amount_lteq, "a" %>
<%= f.search_field :amount_lteq %>
</div>
<%= f.submit "Szukaj" %>
<div class="button">
<%= link_to 'Wyczyść filtry', request.path, class:"cancel-button" %>
</div>
<% end %>
<div style="overflow-x:auto;">
<table class="center">
<tr>
<th><%= sort_link(#q, :title, 'Tytuł', default_order: :desc) %></th>
<th><%= sort_link(#q, :text, 'Opis') %></th>
<th><%= sort_link(#q, :amount, 'Kwota') %></th>
<th><%= sort_link(#q, :category_id, 'Kategoria') %></th>
<th colspan="3"></th>
</tr>
<% #incomes.each do |income| %>
<tr>
<td><%= income.title %></td>
<td><%= income.text %></td>
<td><%= number_to_currency(income.amount, :unit => "zł", :format => "%n %u") %></td>
<td><%= Category.find(income.category_id).name %></td>
<td><%= link_to 'Pokaż', income_path(income) %></td>
<td><%= link_to 'Edytuj', edit_income_path(income) %></td>
<td><%= link_to 'Usuń', income_path(income),
method: :delete,
data: { confirm: 'Czy na pewno chcesz usunąć ten wpis?' } %></td>
</tr>
<% end %>
</table>
</div>
<%= pie_chart Income.joins(:category).group(:name).sum(:amount),suffix: " zł", thousands: ",", messages: {empty: "Nie ma wpisów"}, download: {filename: "wykres"}, title: "Podział przychodów na kategorie" %>
<footer><%= link_to 'Strona główna', controller: 'welcome' %></footer>
And my controller file if it is needed:
class IncomesController < ApplicationController
helper_method :sort_column, :sort_direction
http_basic_authenticate_with name: "admin", password: "admin", except: [:index, :show]
def index
#q = Income.search(params[:q])
#incomes = #q.result.includes(:category)
end
def show
#income = Income.find(params[:id])
end
def new
#income = Income.new
#categories = Category.all.map { |c| [c.name, c.id]}
end
def edit
#income = Income.find(params[:id])
#categories = Category.all.map { |c| [c.name, c.id]}
end
def create
#income = Income.new(income_params)
#categories = Category.all.map { |c| [c.name, c.id]}
if #income.save
redirect_to #income
else
render 'new'
end
end
def update
#income = Income.find(params[:id])
if #income.update(income_params)
redirect_to #income
else
render 'edit'
end
end
def destroy
#income = Income.find(params[:id])
#income.destroy
redirect_to incomes_path
end
private
def income_params
params.require(:income).permit(:title, :text, :amount, :category_id)
end
def sort_column
params[:sort] || "title"
end
def sort_direction
params[:direction] || "asc"
end
end
I already looked on the internet for a similar problem but didn't find anything. I would appreciate some help with that or at lest some directions what to do. I got really hooked on ror and would like to learn more about it, that's why I came here for help. Thanks in advance for answers.
If you mean the filters are applied on the table but not on the chart, this is because you are making another query for the chart only, in this line:
<%= pie_chart Income.joins(:category).group(:name).sum(:amount),suffix: " zł", thousands: ",", messages: {empty: "Nie ma wpisów"}, download: {filename: "wykres"}, title: "Podział przychodów na kategorie" %>
You should use the same instance variable that has the filtered data:
#incomes
So it should be something like:
<%= pie_chart #incomes.sum(:amount), suffix: " zł", thousands: ",", messages: { empty: "Nie ma wpisów" }, download: { filename: "wykres" }, title: "Podział przychodów na kategorie" %>

Param returns nil value when passing from form_for using patch method

I'm implementing a voting system where when the course is already upvoted/downvoted, clicking downvote/upvote will switch to that vote respectively and clicking again will delete that vote (similar to Reddit).
While voting and destroying vote perform correctly, I'm having trouble trying to update the vote with form_for and patch method, where the value of the vote_type returns nil
My Votes_Controller
class VotesController < ApplicationController
def create
#vote = Vote.new(secure_params)
#vote.course = Course.find(params[:course_id])
if #vote.save
respond_to do |format|
format.html { redirect_to :courses }
format.js
end
end
def update
vote = Vote.find_by(user: current_user)
vote.update_attribute(:vote_type, update_vote_params)
redirect_to :courses
end
def destroy
vote = Vote.find_by(user: current_user)
vote.destroy
redirect_to :courses, :notice => 'Unvoted!'
end
private
def secure_params
params.require(:vote).permit( :user_id, :vote_type )
end
def update_vote_params
params.require(:vote).permit(:vote_type)
end
end
My index.html.erb
<% if Vote.exists?(user: current_user) && Vote.find_by(user: current_user).vote_type.equal?(-1) %>
<%= form_for course.votes.build, url: course_vote_path(course, Vote.find_by(user: current_user).id), method: :patch do |f| %>
<%= f.hidden_field :vote_type, value: 1 %>
<%= f.submit 'Upvote', class: 'btn btn-default' %>
<% end %>
<% elsif Vote.exists?(user: current_user) && Vote.find_by(user: current_user).vote_type.equal?(1) %>
<%= form_for course.votes.build, url: course_vote_path(course, Vote.find_by(user: current_user).id), method: :delete do |f| %>
<%= f.submit "Upvote", class: 'btn btn-default' %>
<% end %>
<% else %>
<%= form_for course.votes.build, url: course_votes_path(course) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :vote_type, value: 1 %>
<%= f.submit 'Upvote', class: 'btn btn-default' %>
<% end %>
<% end %>
My routes.rb
Rails.application.routes.draw do
get 'vote/create'
get 'vote/destroy'
get 'courses/new'
get 'users/new'
resources :courses do
resources :votes, only: [:create, :destroy, :update]
end
resources :users
end
I expect the param to pass 1 for vote_type, however, the actual output is nil

Incorrect HTML when pre-populating nested forms in Rails 5.1

I have a Rails 5.1 app that consumes the Google Books API and I need to pre-populate nested fields in a form. There are two ways to create a Book.
Firstly, through the normal /books/new form, which accepts_nested_attributes_for :authors with a has_many: through association. Here I am using cocoon gem and everything is working great.
Secondly, a user can create a Book by searching the Google Books API using an ISBN number. This data then pre-populates a form before being submitted to the create action in the BooksController. I have managed to get this working great apart from the ability to correctly submit the nested Author data.
I currently have each author populate a field in the form, yet when I come to submit the data only the last item in the author array (in the case of a book with multiple authors) gets saved.
I believe this is because the form html has the same name and id for both fields as below. How do I get this form to submit both authors?
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
books_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
before_action :set_book, except: [:index, :new, :create, :new_book, :submit_book]
def create
#book = current_user.books.create(book_params)
#book.authors.each {|author| author.user_id = current_user.id}
if #book.save
redirect_to book_path(#book)
else
render :new
end
end
def new_book
end
def submit_book
#book = Book.new
#book.authors.new
#response = GoogleBooks.new(params[:q], #book)
end
private
def set_book
#book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :subtitle, :description, author_ids:[], authors_attributes: [:id, :name, :_destroy])
end
end
book.rb
class Book < ApplicationRecord
has_many :book_authors
has_many :authors, through: :book_authors
accepts_nested_attributes_for :authors, allow_destroy: true
validates :title, presence: true
validates_associated :authors
end
google_books.rb
class GoogleBooks
include HTTParty
base_uri 'https://www.googleapis.com/books/v1'
def initialize(isbn, book)
#query = self.class.get("/volumes?q=isbn:#{isbn}")
#book = book
end
def title
#query['items'].first['volumeInfo']['title']
end
def subtitle
#query['items'].first['volumeInfo']['subtitle']
end
def description
#query['items'].first['volumeInfo']['description']
end
def authors
#query['items'].first['volumeInfo']['authors']
#=> ['John J. Ratey', 'Richard Manning']
end
end
submit_book.html.erb
<%= form_for #book do |f| %>
<%= f.text_field :title, value: #response.title %>
<%= f.text_field :subtitle, value: #response.subtitle %>
<%= f.text_field :description, value: #response.description %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
<%= f.submit %>
Worked it out.
Was a case of changing
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
to this
<% #response.authors.each do |author| %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
Which produces the following correct HTML:
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][1][name]" id="book_authors_attributes_1_name">

Rails: Issue with recieving nested forms with has many through join

I seem to be having a problem with receiving products through my join table, it's giving me a strange error as it seems to be receiving no ID for my order. I can only assume that this is because the order has not been created yet, but I am creating the order during this step anyway, so the order doesn't have an ID yet. So this is my problem.
Here is the error I recieve:
ActiveRecord::RecordNotFound in OrdersController#create
Couldn't find Product with ID=1 for Order with ID=
Rails.root: /BillingSystem
Application Trace | Framework Trace | Full Trace
app/controllers/orders_controller.rb:10:in `new'
app/controllers/orders_controller.rb:10:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"jE2wdERoxE7PKwBhN60KAfguxwAq8qdW4wbru51SMFg=",
"order"=>{"client_id"=>"1",
"products_attributes"=>{"1368396234677"=>{"id"=>"1",
"_destroy"=>"false"}}},
"commit"=>"Create Order"}
Show session dump
Show env dump
Response
Headers:
None
New Order View:
<% if current_user %>
<div id="dashboard">
<div id="logo"></div>
<table id="go_back_link_container">
<tr>
<td>
<div class="go_back_link">
<%= link_to "<- Go Back", "/orders/view" %>
</div>
</td>
<td>
<div id="user_display">
Logged in as <%= current_user.email %>.
<%= link_to "Log out", log_out_path %>
</div>
</td>
</tr>
</table>
<%= form_for #order, method: :post do |f| %>
<% if #order.errors.any? %>
<div class="error_messages">
<% for message in #order.errors.full_messages %>
* <%= message %> <br>
<% end %>
</div>
<% end %>
<p>
<%= f.label 'Select The Client' %><br />
<%= select :order, :client_id, Client.all().collect { |c| [ (c.firstname + " " + c.surname), c.id ] } %>
</p>
<%= f.fields_for :products do |pf| %>
<% #render 'product_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Product", f, :products %>
<p class="button"><%= f.submit %></p>
<% end %>
<% flash.each do |name, msg| %>
<%= content_tag :div, "* " + msg, :id => "flash_#{name}" %><br />
<% end %>
<div id="copyright-notice"><div id="copyright_border">Copyright © Conner McCabe, all rights reserved.</div></div>
</div>
<% else %>
<script type="text/javascript">
window.location="<%= root_url %>"
</script>
<% end %>
Order Model:
class Order < ActiveRecord::Base
has_many :orderedproducts
has_many :products, through: :orderedproducts
has_one :client
attr_accessible :client_id, :order_total, :delivery_date, :products, :products_attributes
accepts_nested_attributes_for :products, :allow_destroy => true
before_save :generate_total
def generate_total
self.order_total = self.products.map(&:product_price).sum
end
end
Orders Controller:
class OrdersController < ApplicationController
def view
#orders = Order.all
end
def new
#order = Order.new
end
def create
#order = Order.new(params[:order])
if #order.save
redirect_to '/orders/view', :notice => "Order Created!"
else
render "new"
end
end
end
Product Fields Partial:
<fieldset>
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
Products Model:
class Product < ActiveRecord::Base
#This line makes these elements accessible outside of the class.
attr_accessible :product_name, :product_price, :product_quantity, :product_supplier
has_many :orderedproducts
has_many :orders, through: :orderedproducts
#These attributes ensure that the data entered for each element is valid and present.
validates_presence_of :product_name
validates_presence_of :product_price
validates_numericality_of :product_price
validates_presence_of :product_quantity
validates_numericality_of :product_quantity
validates_presence_of :product_supplier
end
Application Helper:
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Ordered Products Model:
class Orderedproduct < ActiveRecord::Base
attr_accessible :order_id, :product_id, :quantity_ordered
belongs_to :order
belongs_to :product
end
I have listed every possible file that could contain an error, I know it's a bit excessive, but it's everything that is to do with it and better I include it than not at all.
I also followed this railscast guide: http://railscasts.com/episodes/196-nested-model-form-revised
To get to where I am, I edited it slightly so that it was suitable for my application.
Thanks in advance.
We had a similar issue on a project, except the relation was singular. The problem is that ActiveRecord is looking for an existing association; something like order.products.find(1). Since order is as new record this doesn't work.
You could create your own products_attributes= method and define the correct behaviour. But I think that you could just use nested attributes for the join model (Orderedproduct) instead of Product.
class Order
accepts_nested_attributes_for :orderedproducts
end
Then adjust the form fields appropriately. In the new form
f.fields_for :products do |pf| becomes f.fields_for :orderedproducts do |pf|
In the fields partial
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %> becomes <%= f.select :product_id, Product.all().collect {|p| [ p.product_name, p.id ] } %>

Rails 3 with joins among 2 tables

I have 2 models, car and registrations.
class Car < ActiveRecord::Base
belongs_to :Registration
end
class Registration < ActiveRecord::Base
has_many :cars, :dependent => :destroy
accepts_nested_attributes_for :cars, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
In CarsController:
def index
#cars = Car.all
#cars2 = Car.all(:joins => :Registration)
end
In view:
<% #cars.each do |car| %>
<tr>
<td><%= car.twitter %></td>
<td><%= car.facebook %></td>
<td>
<% #cars2.Registration.each do |h| %> #here is my problem
<%= h.email %>
<% end %>
</td>
</tr>
<% end %>
This is my statement of cars. I am trying to print for every car owner's email. The email is in table Registration (model registration). I don't know how to make a query to database, that I want to get email from table Registrations, when column registration_id in table Cars == id column of table Registrations...
So I would like to ask you, if you have a tip, how to do...
You have got your associations in capital letters. It should be in small like this
class Car < ActiveRecord::Base
belongs_to :registration
end
Also you dont have to assign 2 variables in the controller
def index
#cars = Car.includes(:registration)
end
and in the view
<% #cars.each do |car| %>
<tr>
<td><%= car.twitter %></td>
<td><%= car.facebook %></td>
<td>
<%= car.registration.email %> #here is my problem
</td>
</tr>
<% end %>
Use this instead to fetch the email:
<%= car.registration.email %>
If you want eager loading, then use the following line in your controller instead:
#cars = Car.includes(:registration)
There's no need for #cars2.