I'm creating a form_for in which one of the field fetches the drop-down list from the database. I'm interpolating the data to display the string but I want to store its id back into other database which is linked with my form.
class FlightsController < ApplicationController
def new
#flight = Flight.new
#airplane = #flight.airplane
#options = Airport.list
end
def create
#flight = Flight.new(flight_params)
if #flight.save!
flash[:success] = "Flight created successfully."
redirect_to #flight
else
flash[:danger] = "Flight not created."
redirect_to :new
end
end
private
def flight_params
params.require(:flight).permit(:name, :origin, :destination, :depart, :arrive, :fare, :airplane_id)
end
end
<%= form_for(#flight) do |f| %>
...
<div class="row">
<div class="form-group col-md-6">
<%= f.label :origin %>
<%= f.select :origin, grouped_options_for_select(#options), { include_blank: "Any", class: "form-control selectpicker", data: { "live-search": true } } %>
</div>
</div>
...
<% end %>
class Airport < ApplicationRecord
def self.list
grouped_list = {}
includes(:country).order("countries.name", :name).each do |a|
grouped_list[a.country.name] ||= [["#{a.country.iso} #{a.country.name}", a.country.iso]]
grouped_list[a.country.name] << ["#{a.iata} #{a.name} (#{a.city}, #{a.country.name})", a.id]
end
grouped_list
end
end
class Flight < ApplicationRecord
belongs_to :origin, class_name: "Airport"
belongs_to :destination, class_name: "Airport"
belongs_to :airplane
has_many :bookings, dependent: :destroy
has_many :passengers, through: :bookings
end
The following error is showing,
Airport(#69813853361360) expected, got "43" which is an instance of String(#47256130076180)
The output of Airport.list when run in a console is shown below:
=> {"India"=>[["IN India", "IN"], ["AGX Agatti Airport (Agatti, India)", 3], ["IXV Along Airport (Along, India)", 5], ["AML Aranmula International Airport (Aranmula, India)", 6], ["IXB Bagdogra International Airport (Siliguri, India)", 50]]}
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+Z8+rkrJkkgaTznnwyTd/QjEoq3kR4ZmoUTp+EpM+320fNFg5rJm+Izx1zBODo/H7IIm3D+yg3ysnVUPmy7ZwQ==", "flight"=>{"name"=>"Indigo", "origin"=>"49", "destination"=>"11", "depart"=>"2019-02-21T21:30", "arrive"=>"2019-02-22T01:30", "fare"=>"2500", "airplane_id"=>"3"}, "commit"=>"Create Flight"}
I tried using to_i but it didn't work.
if you're interpolating a string with space delimiter you can try this.
'1 one'.split(' ').first.to_i
grouped_options_for_select is sending a.id as string value. Convert it to integer in your create action.
def create
#flight = Flight.new(flight_params)
#flight.origin = #flight.origin.to_i ## <== add this line
if #flight.save!
...
Convert string into integer in rails:
user_id = "123"
#user_id = user_id.to_i
puts #user_id
#user_id = 123
Convert integer into string in rails:
user_id = 456
#user_id = user_id.to_s
puts #user_id
#user_id = "456"
Convert column type string into integer in rails migration :
def change
change_column :webinars, :user_id, :integer, using: 'user_id::integer'
end
Convert column type integer into string in rails migration:
def change
change_column :webinars, :user_id, :string, using: 'user_id::string'
end
Your problem is not integer versus string, your problem is (and the error is telling you) the field is expecting an Airport object, but it's getting an airport id...
<%= f.select :origin, grouped_options_for_select(#options), { include_blank: "Any", class: "form-control selectpicker", data: { "live-search": true } } %>
You're trying to select origin which is an airport object. You really are just returning the ID of an airport object (origin_id).
Change it to
<%= f.select :origin_id, grouped_options_for_select(#options), { include_blank: "Any", class: "form-control selectpicker", data: { "live-search": true } } %><%= f.select :origin, grouped_options_for_select(#options), { include_blank: "Any", class: "form-control selectpicker", data: { "live-search": true } } %>
And don't forget to update your flight_params
def flight_params
params.require(:flight).permit(:name, :origin_id, :destination, :depart, :arrive, :fare, :airplane_id)
end
I would guess you'll have a similar problem with destination
Related
I'm fairly new to rails, so excuse my ignorance. But I currently have a controller which stores "Scan Results" for a theoretical virus scanner.
The Scan Model, has_many Detections, which is a record which holds a Sha256 hash and a type.
the code looks as follows
model/scan.rb
class Scan < ApplicationRecord
has_many :detections, class_name: "detection", foreign_key: "d_id", :dependent => :destroy
accepts_nested_attributes_for :detections
validates :hostname, presence: true, uniqueness: { case_sensitive: true }, length: {maximum: 50, minimum: 1}, if: :hostname_not_empty
def hostname_not_empty
if ( self.hostname != '' || self.hostname.nil? )
return true
end
return errors.add(:scansErr, "Hostname is empty")
end
end
model/detections
class Detection < ApplicationRecord
belongs_to :scan
validates :hash, presence: true, length: {maximum: 64, minimum:64}, format: { with: /\b[A-Fa-f0-9]{64}\b/ }
def new ()
end
def new_record
end
end
when I try creating a template for adding new scans to the db, I'm getting this error hash is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
I'm using the following template to try and render it all.
views/dashboard/form.html.haml
...
= form_for :scans, url: dashboard_scan_create_path(params.to_unsafe_h.slice(:hostname)), :html => {:class => 'flex w-full flex-col scan-form' } do |s|
%div.flex.w-full.relative
#{s.text_field :hostname, :class => 'input border border-gray-400 appearance-none rounded w-full px-3 py-3 pt-8 pb-2 my-2 focus focus:border-indigo-600 focus:outline-none active:outline-none active:border-indigo-600' }
#{s.label :hostname, :class => 'label absolute mb-0 -mt-2 pt-4 pl-3 leading-tighter text-gray-400 text-base mt-2 cursor-text'}
%div.detections.flex.w-full.my-2{ 'data-controller' => 'detection-form' }
%div.text-xl.font-bold Detections
%template{"data-target" => "detection-form.template"}
= s.fields_for :detections, Detection.new, child_index: "NEW_RECORD" do |d_form|
= render "detection_fields", form: d_form
...
views/dashboard/detections-fields.html.haml
= content_tag :div, class: "detection-fields" do
.input.detection-field-input.d-flex.justtify-content-between.mb-2
.col-11.pl-0
= form.fields_for(:detection) do |detection_form|
= detection_form.text_field :type
= detection_form.text_field :hash
.col-1
= link_to "delete", "#", data: { action: "nested-form#remove_association" }
= form.hidden_field :_destroy, as: :hidden
can anyone help me figure out what I am doing wrong.
The error is clear in that ActiveRecord::Core has defined a method hash and your model, Detection, has a conflicting attribute called hash.
The action here is to rename the hash attribute of your model to something that isn't already implemented/reserved.
As an example, if you change the model attribute (and the associated code that uses it) to be sha256 or sha it will avoid the conflict.
First, sorry for my bad English. I'm still learning.
I have 3 tables in my DB:
Problem
has_many :registers
has_many :solutions, through : :registers
Solution
has_many :problems
has_many :problems, through : :registers
Register
belongs_to: problem
belongs_to :solution
The system is working well. I am able to insert new data in all of the 3 tables.
In the views for the table/model Register, to select problems and solutions, I make use of collection_select, like this:
= form_for #register do |f|
.field
= f.label :problem_id
= collection_select( :register, :problem_id, #problems, :id, :name, {}, { :multiple => false })
.field
= f.label :solution_id
= collection_select( :register, :solution_id, #courses, :id, :name, {}, { :multiple => false })
.field
= f.label :entry_at
= f.datetime_select :entry_at
.actions = f.submit
The problem only appears when I try to add this validation to Register:
validates_uniqueness_of :student_id , scope: :course_id
Then I get:
> undefined method `map' for nil:NilClass
> = collection_select( :register, :problem_id, #problems, :id, :name, {}, { :multiple => false })
And I dont know why.
So, I tried to do the validation by the controller:
def create
#register = Register.new(register_params)
problem_id = #register.problem_id
solution_id = #register.solution_id
if Register.exists?(['problem_id LIKE ? AND solution_id LIKE ?', problem_id, solution_id ])
render 'new'
else
#register.save
respond_with(#register)
end
end
But the error remains.
I believe that the cause is the collection_select, but I don't know how to solve it.
Saying one more time, I am able to persist date in all the 3 DB tables. But when I try to avoid duplication, the error appears.
This is how I solve this problem:
def create
#register = register.new(register_params)
#if #register.save
# respond_with(#register)
#else
# #register = register.all
# render :new
#end
problem_id = #register.problem_id
solution_id = #register.solution_id
if register.exists?(['problem_id LIKE ? AND solution_id LIKE ?', problem_id, solution_id ])
#register = register.new
#solutions = Solution.all
#problems = Problem.all
flash[:error] = "Problem alread in the register for this solution"
render 'new'
else
#register.save
respond_with(#register)
end
end
The context is as follows, I have entities that can have multiple roles. These roles are manageable by the user.
For example, Entity named "Lipsum" may be "Cashier and Salesperson". So, this is a relation many_to_many.
So I have my 3 models: Entity, type_entity and entity_by_type
class Entity < ActiveRecord::Base
has_many :entity_by_types
has_many :type_entities, :through => :entity_by_types
accepts_nested_attributes_for :entity_by_types
end
class EntityByType < ActiveRecord::Base
belongs_to :entity
belongs_to :type_entity
end
class TypeEntity < ActiveRecord::Base
has_many :entity_by_types
has_many :entities, :through => :entity_by_types
end
I have an ordinary CRUD for entity types.
Now, in the CRUD of entities, I have a field Select-Option Multiple. In which the user chooses has 1 or more types, the entity that is creating.
Then my Controller Entity is as follows:
class Logistics::EntitiesController < ApplicationController
def index
#type_entities = TypeEntity.all
render layout: false
# I use this for show All entities by TypeEntity in my view index
end
def show
end
def new
#type_entities = TypeEntity.all
#entity = Entity.new
render layout: false
end
def create
entity = Entity.new(entity_parameters)
if entity.save
flash[:notice] = "Succesfull!."
redirect_to :action => :index
else
flash[:error] = "Error."
redirect_to :action => :index
end
end
def edit
#entity = Entity.find(params[:id])
#type_entities = TypeEntity.all
#action = 'edit'
render layout: false
end
def update
entity = Entity.find(params[:id])
entity.update_attributes(entity_parameters)
flash[:notice] = "Succesfull."
redirect_to :action => :index
end
def destroy
#entity = Entity.destroy(params[:id])
render :json => #entity
end
private
def entity_parameters
params.require(:entity).permit(:name, :surname, entity_by_types_attributes: [:id, :entity_id, :type_entity_id])
end
end
And my partial form (for method create and Update) is:
= simple_form_for([:namespace, #entity], html: {class: 'form-horizontal' }) do |f|
= f.input :name, placeholder: "Nombre", input_html: { class: 'form-control' }, label: false
= f.input :surname, placeholder: "Apellidos", input_html: { class: 'form-control' }, label: false
%select.select2#type-entity-select{:name => "entity[entity_by_types_attributes][type_entity_id][]", :style => "width:100%;padding: 0;border: none;", :multiple => true}
- #type_entities.each do |tent|
%option{value: "#{tent.id}"}
= tent.name
But, when I click in button submit, and "type_entity_id" have 1 or more values; in my database only display a 1 record where, entity_id is OK, however type_entity_id is NULL.
Moreover only view a 1 record, when should see 1 or more records, depending on the number of types of choice in the form.
The problem here is the way of pass type_entity_id in form of array. So, How I can do that?
P.D
The following is how the params go to my controller:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ASD"1231+Dssr6mRJcXKh9xHDvuVDmVl4jnwIilRBsuE=", "entity"=>{"name"=>"Lorem", "surname"=>"Ipsum", "entity_by_types_attributes"=>{"type_entity_id"=>["1", "4"]}}}
Try this:
def entity_parameters
params.require(:entity).permit(:name, :surname, entity_by_types_attributes: [:id, :entity_id, {:type_entity_id => []}])
end
Edit:
In your form and in def entity_parameters replace type_entity_id with type_entity_ids
Thus, the parameter will refer to a set (array) not to a single object. These are the generic method syntaxes:
Model.associate_id = some integer
Model.associate_ids = an array (for a has_many relation)
I got a problem defining new dynamic ProductFilters for Spree 2.0.4
My customer has got Categories (e.g. Taxons). A Product belongs to one category and has maximum 8 Properties BUT the Properties also depend on the Category each Product is in AND the Position of the Property is also important.
My Solution was to extend the Database in an unnormalized way:
module Spree
Taxon.class_eval do
belongs_to :prop1, class_name: "Spree::Property",
foreign_key: "p1_id"
belongs_to :prop2, class_name: "Spree::Property",
foreign_key: "p2_id"
belongs_to :prop3, class_name: "Spree::Property",
foreign_key: "p3_id"
belongs_to :prop4, class_name: "Spree::Property",
foreign_key: "p4_id"
belongs_to :prop5, class_name: "Spree::Property",
foreign_key: "p5_id"
belongs_to :prop6, class_name: "Spree::Property",
foreign_key: "p6_id"
belongs_to :prop7, class_name: "Spree::Property",
foreign_key: "p7_id"
belongs_to :prop8, class_name: "Spree::Property",
foreign_key: "p8_id"
attr_accessible :prop1, :prop2, :prop3, :prop4, :prop5, :prop6, :prop7, :prop8
def properties
prop = []
prop << prop1
prop << prop2
prop << prop3
prop << prop4
prop << prop5
prop << prop6
prop << prop7
prop << prop8
return prop
end
def applicable_filters
fs = []
fs << Spree::Core::ProductFilters.price_filter if Spree::Core::ProductFilters.respond_to?(:price_filter)
#fs << Spree::Core::ProductFilters.brand_filter if Spree::Core::ProductFilters.respond_to?(:brand_filter)
fs
end
end
end
So that I'm able to get the possible properties in a taxon for the corresponding product. I now made 8 Filters for each taxon property (prop1 .. prop8) because some values are numerical and should be handled different from text values, so even that's not completely DRY I came to the following solution:
module Spree
module Core
module ProductFilters
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop1_any do |*opts|
conds = opts.map {|o| ProductFilters.selective_prop1_filter[:conds][o]}.reject {|c| c.nil?}
scope = conds.shift
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Spree::Product.where(scope)
end
def ProductFilters.selective_prop1_filter(taxon = nil, locale = 'en')
return if taxon.nil? #||= Spree::Taxon.find_by_permalink!("categories")
property = taxon.prop1
scope = Spree::ProductProperty.where(:property_id => property, :locale => locale).
joins(:product => :taxons).
where("#{Spree::Taxon.table_name}.id" => [taxon] + taxon.descendants).
scoped
brands = scope.pluck(:value).uniq
{
:name => property.presentation,
:scope => :selective_prop1_any,
:labels => brands.sort.map { |k| [k,k] }
}
end
end
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop2_any do |*opts|
conds = opts.map {|o| ProductFilters.selective_prop2_filter[:conds][o]}.reject {|c| c.nil?}
Rails.logger.debug conds.inspect
scope = conds.shift
Rails.logger.debug scope.inspect
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Rails.logger.debug scope.inspect
Spree::Product.where(scope)
end
# ... other filters cut out for brevity
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop8_any do |*opts|
[..]
end
def ProductFilters.selective_prop8_filter(taxon = nil, locale = 'en')
[..]
end
end
Spree::Product.add_search_scope :price_range_any do |*opts|
conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject {|c| c.nil?}
scope = conds.shift
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Spree::Product.joins(:master => :default_price).where(scope)
end
def ProductFilters.format_price(amount)
Spree::Money.new(amount)
end
def ProductFilters.price_filter
v = Spree::Price.arel_table
conds = [ [ Spree.t(:under_price, :price => format_price(10)) , v[:amount].lteq(10)],
[ "#{format_price(10)} - #{format_price(15)}" , v[:amount].in(10..15)],
[ "#{format_price(15)} - #{format_price(18)}" , v[:amount].in(15..18)],
[ "#{format_price(18)} - #{format_price(20)}" , v[:amount].in(18..20)],
[ Spree.t(:or_over_price, :price => format_price(20)) , v[:amount].gteq(20)]]
{ :name => Spree.t(:price_range),
:scope => :price_range_any,
:conds => Hash[*conds.flatten],
:labels => conds.map {|k,v| [k,k]}
}
end
end
end
end
Due to the fact, that even the values of the Properties should be localized, a made a column locale in the ProductProperties table. My selective filters pass the locale Variable to retrieve the correct ProductProperty.
Because of the MVC restrictions not being able to pass the locale from session[:locale] and the current taxon to the model I overwrote the original display logic which is using the applicable_filters method for the taxon in the CONTROLLER(!) like so:
TaxonsController.class_eval do
def show
#taxon = Taxon.find_by_permalink(params[:id])
return unless #taxon
if #taxon
#filters = []
#filters << Spree::Core::ProductFilters.selective_prop1_filter(#taxon, locale) unless #taxon.prop1.nil?
[...]
#filters << Spree::Core::ProductFilters.selective_prop8_filter(#taxon, locale) unless #taxon.prop8.nil?
#filters.concat(#taxon.applicable_filters)
else
#filters = Spree::Core::ProductFilters.all_taxons
end
p = params.merge(:taxon => #taxon.id)
#searcher = Spree::Config.searcher_class.new(params)
#searcher.current_user = try_spree_current_user
#searcher.current_currency = current_currency
#products = #searcher.retrieve_products
end
end
And here's the mainly left original view code:
<% unless #filters.empty? %>
<%= form_tag '', :method => :get, :id => 'sidebar_products_search' do %>
<% params[:search] ||= {} %>
<%= hidden_field_tag 'per_page', params[:per_page] %>
<% #filters.each do |filter| %>
<% labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]} %>
<% next if labels.empty? %>
<div class="" data-hook="navigation">
<h5 class="filter-title"> <%= filter[:name] %> </h5>
<% labels.each do |nm,val| %>
<% label = "#{filter[:name]}_#{nm}".gsub(/\s+/,'_') %>
<label for="<%= label %>" class="checkbox" style="display:block;"><%= nm %><input type="checkbox"
id="<%= label %>"
name="search[<%= filter[:scope].to_s %>][]"
value="<%= val %>"
<%= params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s) ? "checked" : "" %> />
</label>
<% end %>
</div>
<% end %>
<%= submit_tag Spree.t(:search), :name => nil, :class => 'button' %>
<% end %>
<% end %>
The display works like it should: Depending on the taxon the user is in, the values from ProductProperties in the session[:locale] are fetched and displayed correctly, but now there comes the Problem:
The Search doesn't work work anymore. Even the not modified :price_range_any filter does not work. All Products belonging to the current taxon are always displayed. The search hash in params hash is built correct by the form search => {"price_range_any":["10.00 € EUR - 15.00 € EUR"]}
I've no idea. If I switch back to the original files by removing the overwrites everything is working.
How can I get my filters to run correctly?
How do I write a data in table event to json file?
Please see this code:
In model event.rb
class Event < ActiveRecord::Base
attr_accessible :name, :event_description, :start_at, :end_at, :status, :eventable_id
has_event_calendar
belongs_to :eventable, polymorphic: true
after_save :write_json
end
def write_json
Event.all.each do |event|
#eventJson = {
"id" => event.id,
"start" => event.start_at,
"end" => event.end_at,
"title" => event.name,
"body" => event.event_description,
"status" => event.status
}
end
File.open("public/event.json","w") do |f|
f.write(#eventJson.to_json)
end
end
In file Json there's one record, but in table event there are many records. How do I write all records from table event to event.json file after saving the record?
public/event.json
{"id":35,"start":"2013-03-28T00:00:00Z","end":"2013-03-28T00:00:00Z","title":"1345edrewrewr","body":"123124","status":"Confirm"}
The problem is that you assign a value to #eventJson in a loop so the previous values are lost. You should use an array:
def write_json
events_json = []
Event.all.each do |event|
event_json = {
"id" => event.id,
"start" => event.start_at,
"end" => event.end_at,
"title" => event.name,
"body" => event.event_description,
"status" => event.status
}
events_json << event_json
end
File.open("public/event.json","w") do |f|
f.write(events_json.to_json)
end
end
In this case, you might want to use map instead of each -- it's much cleaner.
Given that you said the method is in the model, this is how it would look.
class Event < ActiveRecord::Base
...
def self.write_json
record_json = self.all.map{ |record| { self.name => record.attributes } }.to_json
File.open("#{Rails.root}/#{(self.name.underscore)}.json", "w") do |f|
f.write record_json
end
end
end
You could do it in the way below:
def write_json
File.open('public/event.json', 'w') { |f| f.write(Event.all.to_json) }
end
If you want to save specific fields, you can do it in this way:
def write_json
File.open('public/event.json', 'w') do |f|
f.write(Event.select(:id, :start, :end, :title, :body, :status).to_json)
end
end