validation for models in ruby on rails - ruby-on-rails

I have a model:
class HelloRails < ActiveRecord::Base
attr_accessible :filename, :filevalidate
include In4systemsModelCommon
validates :filename, presence: true
def update
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
end
end
In the view I have a text_field called as :filename. When I click the submit button it calls this update method in model to call the stored proc to execute. Now validations are not working.
I dont want to accept nil for filename. How can I do this?

It doesn't validate because you are executing sql directly:
ActiveRecord::Base.connection.execute(sql)
Validations are only run when you use the "normal" ActiveRecord methods like save and update_attributes. To run validations manually you can call valid? on your object.
Model:
def update
return false unless self.valid? # validation failed: return false
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
true
end
Then in your controller you have to check wether #object.update returns true or false and display errors in your view if necessary.
Controller:
# This is just an example. I don't know your code.
def update
#object = HelloRails.find(params[:id])
if #object.update
redirect_to somewhere
else
render :action => 'edit'
end
end

Related

How to associate a form object?

I'm trying to save a Form Object in Rails through and association like this:
document.translations_forms.save(translation_params)
And on my Document model I associated it this way:
class Document < ApplicationRecord
has_many :translations_forms
...
end
But when I run the first command above, I getting this error:
NoMethodError: undefined method `relation_delegate_class' for Document::TranslationsForm:Class
I tried declaring the TranslationFrom Object adding the Document namespace
class Document::TranslationsForm
include ActiveModel::Model
belongs_to :document
def save(params: {})
return false if invalid?
self.document.translation.create(params)
end
end
But didn't work either, my TranslationForm object is in the app/forms/translations_form.rb directory, and I'm using rails 6, what can I do to associate the model with my form object?
A form object (which is a vague term) is usually just a variation of the Decorator pattern.
So you could simply setup the form object so that it wraps an instance of the model class:
class Document
class TranslationForm
include ActiveModel::Model
attribute_reader :document
def initialize(record = nil, attributes = {})
# lets you use the form object for existing records
if record
#document = record
#document.assign_attributes(attributes)
end
#document ||= Document.new(attributes)
end
def to_model
document
end
def save
# triggers validations on the form object
if valid?
document.save
else
false
end
end
end
end
def create
#document = Document::TranslationForm.new(document_params)
if #document.save
redirect_to #document
else
render :new
end
end
def update
#document = Document::TranslationForm.new(
Document.find(params[:id]),
document_params
)
if #document.save
redirect_to #document
else
render :edit
end
end
To add a validation to the form object (instead of directly to the model) just use delegatation:
class Document
class TranslationForm
# ...
validates :foo, presence: true
delegate :errors, to: :document
delegate :foo, to: :document
end
end
delegate :errors, to: :document makes it so that your validations will add errors to the underlying model instead of the errors object of your form object.

ruby on rails: instance variable returns nil

I've been trying to push the input data from my form to the database using the create function, but the instance variable in create function keeps returning 'nil' and after the 'begin transaction', it does 'rollback transaction'
The model function works fine as I get the desired parsed data, and so does the timetables_params function. But timetables_params[:start_time] always returns 'nil' even though the timetables_params returns all the start_time values and end_time values.
How can I fix this?
Here is my controller
def index
#user = current_user
#timetables = #user.timetable.all
end
def new
#timetable = Timetable.new
end
def create
timetables_params[:start_time] = Timetable.parse_timetable_time(timetables_params, 'start')
timetables_params[:end_time] = Timetable.parse_timetable_time(timetables_params, 'end')
#timetable = Timetable.create(timetables_params)
if #timetable.save
flash[:success] = "Done"
else
render 'new'
end
end
private
def timetables_params
params.require(:timetable).permit(:user_id, :start_time, :end_time)
end
end
Here is my model
belongs_to :user
attr_accessor :user, :start_time, :end_time
def self.parse_timetable_time(hash, type)
date_string = hash["#{type}_time(1i)"].to_s + "=" + hash["#{type}_time(2i)"].to_s + "=" + hash["#{type}_time(3i)"]
Time.parse(date_string)
end
You probably have not set the user since its belongs_to :user and i dont see it anywhere in your code. That's why its rolling back and you probably required user_id in your Timetable model. My suggestion is below
Instead of:
#timetable = Timetable.create(timetables_params)
Use build:
#timetable = current_user.timetables.build(timetables_params)
Thanks for all the help above, but turns out that I just had to add the following lines in the model and modify the controller create function:
model
validates :user_id, presence: true
validates :start_time, :end_time, presence: true
controller
def create
#timetable = current_user.timetables.build(timetables_params)
if #timetable.save
flash[:success] = "Done"
else
render 'new'
end
And, I don't necessarily need to parse the datetime values, Rails does do it automatically

How do I validate update based on selected values in a key with Ruby?

I want to implement a conditional update method on a specific set of values for a key.
I want to allow updates only if the original list.permissions values (set on create) equal either "public", "viewable, or "editable". If the list.permissions value for a record does not equal one of those three acceptable values, updating the record is denied (locked).
I tried modifying the strong params in a private method in the controller file:
def list_params_validated
params.require(:list).permit(:title, permissions: ["public", "viewable", "editable"])
end
and then calling that in my update method in the same controller:
def update
list = List.find(params[:id])
if list.update(list_params_validated)
render json: list
else
render json: { errors: list.errors.full_messages }, status: :unprocessable_entity
end
end
no luck with this, any help would be most appreciated!
You can simply do this on your controller
def list_params_validated
params.require(:list).permit(:title, :permissions)
end
and in your model simply add this
validates_inclusion_of :permissions, in: %w(public viewable editable), :on => :update, :message => "value %s is invalid!"
EDIT 1
To prevent updating the record, You should add before_update callback in your model as
before_update :locked?
def locked?
return false if YourModel.find(id).persmissions == 'locked'
true
end
You can add an custom validation on your List model as following and call simple update in controller.
self.permissions value should be string like "public" or "viewable" or "editable".
validate :validate_editable, :validate_permissions:on => :update
def editable?
self.permissions != "locked"
end
private
def validate_editable
errors.add(:base, "Not Ediable!") unless editable?
end
def validate_permissions
unless ["public", "viewable", "editable"].include?(self.permissions)
errors.add(:base, "Permission denied!")
end
end

rails error added to object but not raised

I'm doing an update of a form. I can't add my validation in my model for x reason, so I'm adding an error in my projects_controller in the method update. When I update it should raise the error and render :edit but it doesn't. Here is my method
def update
#project = Project.find(params[:id])
#stuff to update
#add error if no legal_media checked, unless if creative upload its own conditions
unless has_media?(#project.legal_option.authorized_format)
#project.legal_option.authorized_format.errors[:base] << "error message"
end
if #project.update_attributes(project_params)
redirect_to brief_path(#project.order.brief)
else
render :edit
end
end
the method has_media? returns false dans when I type #project.legal_option.authorized_format.errors[:base]I have my error message ["error message"].
But when I type #project.legal_option.authorized_format.valid?, it returns true
Any idea how I could make my method raise this error?
Thank you!
UPDATE trying to do the validation in the model :
Since the beginning I want to check that if my column custom_document in legal_option isn't nil (therefore the user uploaded it in the update method of the projects_controller), then, check if there is at least one media in legal_media.
Here are my models :
class LegalOption < ActiveRecord::Base
belongs_to :project
has_one :authorized_format, class_name: "LegalMedia", foreign_key: "legal_option_id"
accepts_nested_attributes_for :authorized_format
has_attached_file :custom_document
validates_attachment :custom_document, content_type: { content_type: "application/pdf" }
end
class LegalMedia < ActiveRecord::Base
belongs_to :legal_option
def self.formats
{all_media: "Tous Media", internet: "Internet", paper: "Presse papier", object: "Objets", television: "TV", radio: "Radio", cinema: "Cinéma", poster_campaign: "Affiches", :press_relation => "Relations Presse", :plv => "Publicité sur lieux de vente", :event => 'Evènementiel'}
end
end
When I did the validation in the beginning with a validate :has_media? My LegalOption.LegalMedia because legal_option_id is nil in legal_media
in the unless block, put the line:
render :edit and return
like:
unless has_media?(#project.legal_option.authorized_format)
#project.legal_option.authorized_format.errors[:base] << "error message"
render :edit and return
end
You should add a validation to the model in order for the valid? to do what you are looking for it to do.
If you look at the docs here, you'll see that valid? just runs all the validations. It doesn't check for any errors that you manually add to the object.
Rails convention dictates that validations shouldn't be implemented in the controller but rather in the model. More specifically, update_attributes just runs valid? after assigning the attributes, which itself just runs validations defined on the model. Any errors already on the model are cleared out beforehand.
If you re-write this as a custom validation on the model, update_attributes should behave as you expect:
class Project < ActiveRecord::Base
validate :legal_option_has_media
private
def legal_option_has_media
unless has_media? legal_option.authorized_format
errors.add :base, "error message"
end
end
end

Can I access information from one associated AR object in another when both are unsaved?

Say I open a Rails (2.3.8) script console and try this:
a = Account.new(:first_name) = 'foo'
i = a.invoices.build
p i.account.first_name
Account.rb is a model object and contains:
has_many :invoices
and Invoice.rb is a model as well containing:
belongs_to :account, :validate => true
In console line 3 above, i.account is nil. I realize that i.account would not be nil if account had been saved, but I do not wish to save an account unless I can create a valid invoice for the account. And, just for kicks, the invoice validation depends on some properties of the unsaved account.
Any ideas how to make this work?
Best,
Will
I typically do this with transactions. With rails transactions you can perform db interactions and roll them back at any time if something fails to validate. For example:
in your model:
def save_and_create_invoice
Account.transaction do
#first let's save the account, this will give us an account_id to work with
return false unless self.save
invoice = self.invoices.build
#setup your invoice here and then save it
if invoice.save
#nothing wrong? return true so we know it was ok
return true
else
#add the errors so we know what happened
invoice.errors.full_messages.each{|err| errors.add_to_base(err)}
#rollback the db transaction so the account isn't saved
raise ActiveRecord::Rollback
#return false so we know it failed
return false
end
end
end
And in your controller you would call it like so:
def create
#account = Account.new(params[:account])
respond_to do |format|
if #account.save_and_create_invoice
format.html
else
format.html {render :action => "new"}
end
end
end
Note that I didn't run this code to test it, just whipped it out real quick to show an example.

Resources