Validation in form? - ruby-on-rails

I am trying to do validation but didnot find the right way while uploading the excel
Code for view
<%= form_for :dump, :url=> import_csv_index_path, :html => { :multipart => true } do |f| %>
<% if (-----).errors.any? %>
<div id="error_explanation">
<%= pluralize((----).errors.count, "error") %> prohibited this link from being saved:
<% (---).errors.full_messages.each do |msg| %>
<li><%= msg %></li>
</div>
<% end %>
<%= f.label :'Select an Excel File:' %>
<%= f.file_field :file %>
<i href="#" id="blob_1" class="icon-info-sign" rel="popover" style="margin-top: 300px"></i><br/>
<%= submit_tag 'Submit' ,:id => 'submit',:class =>"btn btn-primary"%><br/><br/>
<% end -%>
Code for model:
class Person < ActiveRecord::Base
attr_accessible :date, :email, :name, :avatar ,:template_id
validates_presence_of :email,:message => 'Email is compulsory'
end
What do i write at place of (-----)
Thank you

I have done with the other approach so discuss here,
Code for controller:
def import
#error = []
#error = Person.import(params[:dump][:file])
redirect_to people_path, notice: "Employees imported.",:flash => { :error => "Employees( Email : invalid/blank) are not saved : #{#error.join(" , ")}" }
end
In model:
def self.import(file)
spreadsheet = open_spreadsheet(file)
#err = []
#alt = []
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
#person = Person.find_by_id(row["id"]) ||Person.new
#person.name = row["Name"].to_s
#person.date = row["Date"]
#person.email = row["Email"]
if #person.save
else
#err << {:name => row["Name"].to_s,:error => #person.errors.messages}
end
end
#err.each do |t|
#alt.push(t[:name])
end
return #alt
end
and call that as flash message...and working fine now.
Thanks

I think somewhere in your controller you have to call .validate on a new instance of your Person model with your form's data. You can save that model in an instance variable that you can put in place of the (-----).

Related

How to forward model validation errors to a view based on another model in Rails?

I created a data import page to centralize the feature, and remove numerous import controllers.
I have a DataImport object to grab parameters for the DataImports controller.
The controller identifies the required importation model, and kicks the importation script. If it is successfull, it renders imported objects index, if not, it comes back to the data import form.
As you can see, in case of validation failure, the save method from the organisation_import.rb model returns the error messages list. How can I pass this list of errors to the #data_import instance so that when rendering back the new view, errors are listed ?
Thanks for your help!
Model: data_import.rb
class DataImport
include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :file, :playground_id, :object_type_id
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
end
View: new.html.erb
<% provide(:title, (t('DataImport'))) %>
<% provide :page_heading do %>
<%= t('DataImport') %>
<% end %>
<%= render partial: "shared/error_list", locals: { errors: #data_import.errors } %>
<div class="row mat-form-row">
<div class="col-md-12"> <h3><%= t('DataImport') %></h3>
</div>
</div>
<%= form_for #data_import, html: {id: "input_form"} do |f| %>
<div class="row mat-form-row">
<div class="mat-form-field col-md-3">
<%= f.label :object_type, t('ObjectType'), class: "mat-form-field-label" %>
<%= f.collection_select :object_type_id, qdm_object_types('import'), :id, :name, {}, { class: "mat-input-element" } %>
</div>
</div>
<div class="row mat-form-row">
<div class="mat-form-field col-md-3">
<%= f.label :file, t('SourceFile'), class: "mat-form-field-label" %>
<%= f.file_field :file, class: "mat-input-element" %>
</div>
</div>
<br/>
<div class="row">
<div class="mat-form-field col-md-3">
<%= f.label :playground, t('Playground'), class: "mat-form-field-label" %>
<%= f.collection_select :playground_id, list_of_playgrounds, :id, :translation, {}, { class: "mat-input-element" } %>
</div>
</div>
<div class="mat-button-group">
<%= link_to t('Cancel'), root_path, method: :get, class: "mat-stroked-button mat-button-base" %>
<%= submit_tag(t('Submit'), :onclick => "submitform();", class: "mat-flat-button mat-button-base mat-primary" ) %>
</div>
<% end %>
Controller: data_imports_controller.rb
class DataImportsController < ApplicationController
# Check for active session
before_action :authenticate_user!
def new
#data_import = DataImport.new
end
def create
#data_import = DataImport.new(params[:data_import])
if #data_import.file.nil?
render :new, notice: t('FileNameCannotBeEmpty')
end
objects = Parameter.find(#data_import.object_type_id).name.downcase.pluralize
objects_import = "#{objects}Import".classify.constantize
#imported = objects_import.new(file: #data_import.file, playground: #data_import.playground_id)
if #imported.save
redirect_to "/#{objects}", notice: t('ImportedObjects')
else
render :new
end
end
end
Example of organisations_import.rb model:
class OrganisationsImport
include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :file, :playground
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
def save
if imported_organisations.map(&:valid?).all?
imported_organisations.each(&:save!)
true
else
imported_organisations.each_with_index do |column, index|
column.errors.full_messages.each do |message|
errors.add :base, "Row #{index+2}: #{message}"
end
end
false
end
end
def imported_organisations
#imported_organisations ||= load_imported_organisations
end
def load_imported_organisations
spreadsheet = self.open_spreadsheet(file)
spreadsheet.default_sheet = 'Organisations'
header = spreadsheet.row(1)
(2..spreadsheet.last_row).map do |i|
record = Organisation.new
record.playground_id = $Unicity ? 0 : playground # If only one tenant, then the organisations belong to Governance
record.code = spreadsheet.cell(i,1).to_s
record.name = spreadsheet.cell(i,6)
record.parent_code = spreadsheet.cell(i,2).to_s # before_save action sets the parent_id
record.organisation_level = spreadsheet.cell(i,3) # overwriten by the before_save action
record.created_by = 'admin'
record.updated_by = 'admin'
record.owner_id = 1 # Administrator
record.status_id = Parameter.find_by_name("New").id if Parameter.exists?(:name => ("New"))
# Add description translations
next_cell = 6
ApplicationController.helpers.list_of_languages.order(:property).each do |translation|
record.description_translations.build(field_name: 'name', language: translation.property, translation: spreadsheet.cell(i,next_cell))
next_cell += 1
record.description_translations.build(field_name: 'description', language: translation.property, translation: spreadsheet.cell(i,next_cell))
next_cell += 1
end
puts "####################### test ####################################"
puts record.attributes
record
end
end
def open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path, csv_options: {col_sep: ";"})
# when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
end
You can append error messages to your #data_import object's errors.
#imported.errors.full_messages.each{|msg| #data_import.errors[:base] << msg}

Search method Ruby

I made this method for a search method in ruby with a form.
I think, that this code can be simplify. What do u think ?
And I have an issue. When a user does not enter anything in the form, the results display all the tips in the database.
But when a user combines two searches, for example a keywords and a country, the result is all the tips that have the keywords as well as all the tips that have the country concerned. Except what I'm looking for is for the two criteria to be combined.
Search.rb
require 'pry'
class Search < ApplicationRecord
def self.search(keywords, category_id,city,country)
table_of_ids_country = []
table_of_ids_city = []
table_of_ids_title = []
table_of_ids_cats = []
two_searches_ids = []
merged_table_of_tips_ids = []
#results = []
tips_by_country = Tip.where(["country like?",country]) if country.present?
tips_by_city = Tip.where(["city like?",city]) if city.present?
tips_by_keyword = Tip.where(["title LIKE?","%#{keywords}%"]) if keywords.present?
tips_by_cat = Category.where(["id = :id",{id:category_id}]) if category_id.present?
two_searches = Tip.where(["title LIKE?",keywords]) if keywords.present? && Category.where(["id = :id",{id:category_id}]) if category_id.present?
if tips_by_country != nil
tips_by_country.each do |tip|
tip.id
table_of_ids_country << tip.id
end
end
if tips_by_city != nil
tips_by_city.each do |tip|
tip.id
table_of_ids_city << tip.id
end
end
if tips_by_keyword != nil
tips_by_keyword.each do |tip|
tip.id
table_of_ids_title << tip.id
end
end
if two_searches != nil
two_searches.each do |tip|
tip.id
two_searches_ids << tip.id
end
end
if tips_by_cat != nil
Category.find(tips_by_cat[0].id).tips.each do |tip|
table_of_ids_cats << tip.id
end
end
merged_table_of_tips_ids = [table_of_ids_title, table_of_ids_cats,table_of_ids_city,table_of_ids_country,two_searches_ids].flatten
merged_table_of_uniq_tips_ids = merged_table_of_tips_ids.uniq
merged_table_of_uniq_tips_ids.each do |tip|
result = Tip.find(tip)
#results << result
binding.pry
end
return #results
end
end
searches_controller
require 'pry'
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
#search = Search.search(#search.keywords, #search.category_id,#search.city,#search.country)
#Permet d'envoyer les paramètres au model search et à les réutilisé dans la méthode self.search
end
private
def search_params
params.require(:search).permit(:keywords,:category_id,:id,:city,:country)
end
end
my form :
<%=form_for #search do |f| %>
<div class="form-group">
<%= f.label :keywords, "Mots-clés" %>
<%= f.text_field :keywords, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :city, "Mots-clés" %>
<%= f.text_field :city, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :country, "Mots-clés" %>
<%= f.text_field :country, class: "form-control", placeholder: "plongée, randonnée, temple..." %>
</div>
<div class="form-group">
<%= f.label :category_id, "Catégories" %><br>
<%= f.collection_select :category_id, Category.all, :id, :name, :include_blank => true %>
</div>
<div class="form-group d-flex justify-content-center">
<%= f.submit "Rechercher", class: "btn btn-primary" %>
</div>
<%end%>
class Search < ApplicationRecord
def self.search(keywords, category_id,city,country)
ids = if keywords.present? && category_id.present?
Tip.keywords(keywords).pluck(:id) & Category.category_id(category_id).pluck(:id)
elsif [keywords, category_id, city, country].any?(&:present?)
Tip.country(country)
.city(city)
.keywords(keywords)
.pluck(:id)
else
[]
end
Tip.find(ids.uniq)
end
end
class Category < ApplicationRecofd
scope :category_id, ->(category_id) {
where(["id = :id",{id:category_id}])
}
end
class Tip < ApplicationRecord
scope :country, ->(country) {
where("country like?",country) if country.present?
}
scope :city, ->(city) {
where("city like?",city) if city.present?
}
scope :keyword, ->(keywords) {
tips = tips.where("title LIKE?","%#{keywords}%") if keywords.present?
}
end
also notice I'm doing a union on category ids, so keep in mind
2.5.1 :002 > [1,2] && [2,3]
=> [2, 3]
2.5.1 :003 > [1,2] & [2,3]
=> [2]

Multiselect is not saving

I`m trying to pass related params into a model with multiselect list. I need to assing classes to lecturers by choosing a lector when creating new class entry. I checked the db and got nothing in lecturers_id column. This is my form:
<%= f.select(:lecturers_id, Lecturer.all.collect {|l| [ l.name, l.id ] }, {}, { :multiple => true }) %>
My controller:
def subject_params
params.require(:subject).permit(:name, :credit, :number_of_hours, :lecturers_id => [])
end
def create
#subject = Subject.new(subject_params)
#subject.save
redirect_to "/subjects" and return
end
And my view:
<% #lecturers.each do |lect| %>
<tr>
<td class='col-md-3'><%= lect.name %></td>
<td class='col-md-3'>
<% lect.subjects do |subj| %>
<%= subj.name %>
<% end %>
</td>
<% end %>
Assuming the association is that Subject has_many :lecturers then you would change lecturers_id to lecturers_ids:
def subject_params
params.require(:subject).permit(:name, :credit, :number_of_hours, :lecturers_ids => [])
end
Form:
<%= f.select(:lecturers_ids, ...
In case of Subject has_many :lecturers. Your form should be
<%= f.select(:lecturer_ids, Lecturer.all.collect {|l| [ l.name, l.id ] }, {}, { :multiple => true }) %>
and your subject_params should be
def subject_params
params.require(:subject).permit(:name, :credit, :number_of_hours, :lecturer_ids => [])
end

how to create an array in a helper and then loop through in a view

I have a poll, and I've created a helper to return a JSON object of all the results which works like so:
module PollVotesHelper
def poll_results_json(poll)
#poll_results = Array.new
#poll_results << {
:total_votes => poll.poll_votes.length,
:options => poll.poll_options.collect { |poll_option|
{
:id => poll_option.id,
:title => poll_option.title,
:vote_percentage => '33%',
:vote_count => poll_option.poll_votes.length
}
}
}
#poll_results.to_json
end
end
Then in the view I want to call it and loop through the options and output the title, etc...
<% poll_results_json(#poll)['options'].each do |poll_option| %>
<%= poll_option['id'] %>
<%= poll_option['title'] %>
<%= poll_option['vote_percentage'] %>
<%= poll_option['vote_count'] %>
<% end %>
The view is erroring. What's the right way to loop through the resulting JSON object?
Thanks
Your problem is, you want to iterate through JSON data, not a Ruby array. This is because to_json just returns a Ruby string with valid JSON inside. So instead of converting your whole Poll objects into JSON, you should just return the values as JSON:
<% #poll.poll_options.each do |poll_option| %>
<%= poll_option.id.to_json %>
<%= poll_option.title.to_json %>
<%= '33%'.to_json %>
<%= poll_option.poll_votes.length.to_json %>
<% end %>
I don't know why your are pushing Hash into an Array(#poll_results).Instead try something like
module PollVotesHelper
def poll_results_json(poll)
#poll_results = {
:total_votes => poll.poll_votes.length,
:options => poll.poll_options.collect { |poll_option|
{
:id => poll_option.id,
:title => poll_option.title,
:vote_percentage => '33%',
:vote_count => poll_option.poll_votes.length
}
}
}
#poll_results.to_json
end
end
The value returned from the method will be a string.first you need to parse json to process it.
in your view try like
<% JSON.parse(poll_results_json(#poll))['options'].each do |poll_option| %>
<%= poll_option['id'] %>
<%= poll_option['title'] %>
<%= poll_option['vote_percentage'] %>
<%= poll_option['vote_count'] %>
<% end %>

Rails custom validations with multi-model forms

I'm having problems with my rails custom validations.
def validates_hsp_program(*attr_names)
options = attr_names.extract_options!
regex = '^('
err = ''
$CetConfig.program.each do |key, val|
regex << val.to_s << '|'
err << $CetConfig.program_prefix + " " + val.to_s + ", "
end
regex.chomp!('|')
regex << ')$'
regex = Regexp.new(regex)
validates_each attr_names do | record, attr_name, value |
exit 1
unless value.nil? or value =~ regex
record.errors.add(attr_name, 'must be one of ' + err.chomp(", "));
end
end
end
The problem is that for debugging purposes I added exit 1 as I wasn't getting the error message for invalid date for that field. However, it never exits. This is the same thing I do in all my other custom validators. The only difference I can see is that this one is the second model of a multi-model form and all the others are on the first model... What am I doing wrong?
My Model
class ProfileProgram < ActiveRecord::Base
set_table_name "profile_program"
set_primary_key "STUDENT_ID"
belongs_to :profile_core, :primary_key => "STUDENT_ID", :foreign_key => "STUDENT_ID"
validates_presence_of :program
validates_hsp_program :program
end
Action from my controller
def create
#pc = ProfileCore.new(params[:profile_core])
#pp = ProfileProgram.new(params[:profile_program])
#pc.student_type = $CetConfig.student_type.application
#pc.AGENT_ID = current_agents_staff.AGENT_ID
year = #pc.contract.to_s
case #pp.program
when 10 then
sd = year + '-09-01'
ed = year + '-06-30'
when 51 then
sd = year + '-08-15'
ed = year + '-06-30'
when 52 then
sd = year + '-01-15'
ed = year + '-06-30'
when 12 then
sd = year + '-01-15'
ed = year + '-01-14'
else
sd = nil
ed = nil
end
#pc.start_date = Date.parse(sd) unless sd.nil?
#pc.end_date = Date.parse(ed) unless ed.nil?
#pc.program_status = $CetConfig.student_status.apply
if #pc.valid? and #pp.valid?
ProfileCore.transaction do
#pc.save!
#pp.save!
end
redirect_to(students_path(#pc.STUDENT_ID))
else
render :action => 'new'
end
end
My view (element_block is a helper that just stuffs the label and field into the right tags for the dl)
<% form_for :profile_core, #pc, :url => { :controller => 'core', :action => 'create'} do |f| %>
<%= error_messages_for :object => [ #pc, #pp ] %>
<dl>
<%= element_block f.label(:contract, 'Contract Year'), f.contract_year_select(:contract) %>
<% fields_for :profile_program do |pp| %>
<%= element_block pp.label(:program, 'Program'), pp.hsp_program_select(:program) %>
<% end %>
<%= element_block f.label(:passport_number, 'Passport Number'), f.text_field(:passport_number) %>
<%= element_block f.label(:passport_country, "Country that issued the student's passport"), f.countries_select(:passport_country) %>
<%= element_block f.label(:passport_expires, 'Passport Expiration Date'), f.text_field(:passport_expires, :class => 'datepicker') %>
<%= element_block f.label(:last_name, 'Last Name (as on passport)'), f.text_field(:last_name) %>
<%= element_block f.label(:first_name, 'First Name (as on passport)'), f.text_field(:first_name) %>
<%= element_block f.label(:middle_name, 'Middle Name (as on passport)'), f.text_field(:middle_name) %>
<%= element_block f.label(:other_names, 'Other Names'), f.text_field(:other_names) %>
<%= element_block f.label(:residence_street_address, 'Street Address'), f.text_field(:residence_street_address) %>
<%= element_block f.label(:residence_city, 'City'), f.text_field(:residence_city) %>
<%= element_block f.label(:residence_province, 'Province'), f.text_field(:residence_province) %>
<%= element_block f.label(:residence, 'Country'), f.text_field(:residence) %>
<%= element_block f.label(:residence_postal_code, 'Postal Code'), f.text_field(:residence_postal_code) %>
<%= element_block f.label(:birthdate, 'Date of Birth'), f.text_field(:birthdate, :class => 'datepicker', :id => "student_birth_date") %>
<%= element_block f.label(:citizenship, 'Country of Citizenship'), f.countries_select(:citizenship) %>
<%= element_block f.label(:birth_city, 'Birth City'), f.text_field(:birth_city) %>
<%= element_block f.label(:nationality, 'Nationality'), f.countries_select(:nationality) %>
<%= element_block f.label(:gender, 'Gender'), f.gender_select(:gender) %>
<%= element_block f.label(:email, 'Email'), f.text_field(:email) %>
<%= element_block f.label(:desires_esl, 'Does the student wish to participate in CLEP?'), f.bool_yes_no_select(:desires_esl) %>
<%= element_block f.label(:may_pay_tuiton, 'Willing to pay tuition'), f.yes_no_select(:may_pay_tuition) %>
</dl>
<div class="submit"><%= submit_tag("Proceed to Step Two") %></div>
<% end %>
This is how I ended up taking care of it with accepts_nested_attributes_for
Primary Model. (Irrelevant code replaced with ellipses)
class ProfileCore < ActiveRecord::Base
set_table_name "profile_core"
set_primary_key "STUDENT_ID"
belongs_to :agents_profile, :primary_key => "AGENT_ID", :foreign_key => "AGENT_ID"
has_one :profile_program, :primary_key => "STUDENT_ID", :foreign_key => "STUDENT_ID"
accepts_nested_attributes_for :profile_program
...
end
Secondary Model
class ProfileProgram < ActiveRecord::Base
set_table_name "profile_program"
set_primary_key "STUDENT_ID"
belongs_to :profile_core, :primary_key => "STUDENT_ID", :foreign_key => "STUDENT_ID"
validates_presence_of :program
validates_hsp_program :program
end
New action in the controller. (Create was irrelevant)
def new
#pc = ProfileCore.new
#pp = #pc.build_profile_program
end
View
<% form_for #pc, :url => { :controller => 'core', :action => 'create'} do |f| %>
<%= f.error_messages %>
<dl>
<%= element_block f.label(:contract, 'Contract Year'), f.contract_year_select(:contract) %>
<% f.fields_for :profile_program do |pp| %>
<%= element_block pp.label(:program, 'Program'), pp.hsp_program_select(:program) %>
<% end %>
<%= element_block f.label(:passport_number, 'Passport Number'), f.text_field(:passport_number) %>
...
<% end %>
It's not correct custom validation implementation, try something like that:
http://marklunds.com/articles/one/312
or http://chrisblunt.com/blog/2009/04/18/rails-writing-dry-custom-validators/

Resources