How to set values in rails without form field - ruby-on-rails

We have an field in the Database which should be set automatic as an UUID String. how do we that. the view doesn't contain this field because it will be autogenerated.
Our new-form will called so.
def new
#list = List.new
respond_to do |format|
format.html
end
Our create-action is here
def create
#list = List.new(params[:list])
#list = list.create!(params[:list])
end
If we try this
#list.admin_key = UUIDTools::UUID.timestamp_create().to_s
we get an validation error and the field is empty. the controller require
require 'uuidtools'
Our validation for the field is that is prencense and unique
validates :admin_key,
:presence => true,
:uniqueness => true
How did we get the admin_key into the database?

u have to do this in your model
before_validation(:on => :create) do
self.admin_key = UUIDTools::UUID.timestamp_create().to_s
end

Related

How to add default values to nested items in a Rails controller?

In the Sign up form of my Rails 6 application an Account with a nested User can be created.
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build(
:owner => true,
:language => "FR"
)
end
def create
#account = Account.new(account_params)
if #account.save
redirect_to root_url, :notice => "Account created."
else
render :new
end
end
private
def account_params
safe_attributes = [
:name,
:users_attributes => [:first_name, :last_name, :email, :password, :owner, :language]
]
params.require(:account).permit(*safe_attributes)
end
end
What is the best way to define default values on the new user here?
Right now, I use hidden_fields for the default values in my sign up form thus making them publicly available. This is of course not what I want because it's very insecure.
Is there a better way to deal with this?
I know that there's Rails' with_defaults method but I couldn't get it to work on nested items so far.
try with:
account_params[:users_attributes] = account_params[:users_attributes].with_defaults({ first_name: 'John', last_name: 'Smith'})
in first line of create action

Regex validation in rails not working: unique, alphanumeric values with blank values allowed

The create and update methods of my controller are not working as expected. One of my attributes is a coupon code. I want the coupon code to be:
Unique but allow blank or nil values
Use regex to restrict codes to uppercase letters, numbers and underscores
Currently, I'm having a few issues. My model is called ClassPrice and it has two attributes: :coupon_code & :cost_in_cents. The issues are:
When I try to create a new ClassPrice with a blank :coupon_code, it reloads the new action, gives me the message that I succeeded, but the new code is not actually saved.
When I try to update an existing ClassPrice with a blank :coupon_code, it gives me the validation error and doesn't save the change: "ERROR: only allows upppercase letters, numbers or underscores."
Here is my controller:
class ClassPrice < ActiveRecord::Base
has_many :class_section_prices
has_many :class_sections, through: :class_section_prices
after_initialize :init
validates :coupon_code, uniqueness: true, allow_blank: true, allow_nil: true
validates :coupon_code, format: { with: /\A[A-Z0-9_]+\z/,
message: "ERROR: only allows upppercase letters, numbers or underscores." }
def cost_in_dollars
if self.amount_in_cents == 0
"FREE"
else
"$#{amount_in_cents / 100.0}0"
end
end
def virtual_price_in_dollars
amount_in_cents.to_d / 100 if amount_in_cents
end
def virtual_price_in_dollars=(dollars)
self.amount_in_cents = dollars.to_d * 100 if dollars.present?
end
private
def init
self.coupon_code ||= ""
self.amount_in_cents ||= 0
end
end
And here is my model:
class Admin::ClassPricesController < ApplicationController
layout 'admin'
before_action :full_authenticate
def full_authenticate
authenticate_user!
if !current_user.is_admin?
flash[:alert] = "Access denied."
redirect_to root_url
end
end
def index
#class_prices = ClassPrice.all
end
def new
#class_price = ClassPrice.new
end
def create
if #class_price = ClassPrice.create(class_price_params)
redirect_to new_admin_class_price_path(#class_price), notice: 'New class price created.'
else
render action: "new"
end
end
def edit
#class_price = ClassPrice.find(params[:id])
end
def update
#class_price = ClassPrice.find(params[:id])
if #class_price.update_attributes(class_price_params)
redirect_to edit_admin_class_price_path(#class_price), notice: 'Class Price was successfully updated.'
else
render action: "edit"
end
end
def destroy
#class_price = ClassPrice.find(params[:id])
if #class_price != nil
#class_price.destroy
redirect_to admin_class_prices_path(#class_section), notice: 'Class Price was deleted.'
else
redirect_to admin_class_prices_path(#class_section), notice: 'Class Price not found.'
end
end
private
def class_price_params
params.require(:class_price).permit(:class_price_id, :amount_in_cents, :coupon_code, :virtual_price_in_dollars)
end
end
Any idea where the problem is?
There couple of issues that I see in your code:
allow_blank: true will allow value to be nil or empty string, so you don't have to use also allow_nil if you have allow_blank
You are missing allow_blank in format validation.
Get rid of after_initialize callback. It is firing up every time you instantiate ClassPrice object. Also when you fetch correct record from database it will have blank coupon_code and amount_in_cents set to 0
Create action is not correct. ClassPrice.create will always return an object no matter if it was saved to the database or not. And this object will always evaluate to true. You should split this into 2 lines, first build an object and the save it.
So your validations should look like this
validates :coupon_code, uniqueness: { allow_blank: true },
format: { with: /\A[A-Z0-9_]+\z/, message: "ERROR: only allows upppercase letters, numbers or underscores.", allow_blank: true }
And your create action:
def create
#class_price = ClassPrice.new(class_price_params)
if #class_price.save
redirect_to new_admin_class_price_path(#class_price), notice: 'New class price created.'
else
render action: "new"
end
end
I hope I didn't miss anything.

rendering the partials in controller after the validation check

I have two partial views for two different sign up forms. On my home page , based on the link one clicks on, I'm rendering respective form.(views/application/index)
= link_to 'Mentor', new_user_path(user_role: true), :class =>'btn'
= link_to 'Mentee', new_user_path, :class =>'btn'
In views/users/new.html.haml , I'm checking the user role and redirecting to the respective form.
- if params[:user_role]
= render 'mentor'
- else
= render 'mentee'
In the user model I've added validation like this.
class User < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#cisco.com/i
validates :cisco_email, :presence => true,
:format => { :with => email_regex,}
validates :work_city, :presence => true
end
So, when there is any invalid field I want to direct to the same form with a flash message. My controller looks like this.
class UsersController < ApplicationController
def index
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user]) # Not the final implementation!
if #user.save
flash[:success] = "Welcome to the CSG Mentoring Tool!"
redirect_to #user
else
flash[:notice] = "Error regsitering."
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
end
end
end
When an invalid field entry is there, it is redirecting to 'mentee' page no matter on which page the error is made. Also the entire css styling gets changed and flash is also not displayed
Why this is not working?
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
params[:user][:user_role] is nil.
You can check it using lots of way:
Above your if condition raise params[:user].inspect
Why its nil?
Reason of this is You are passing new_user_path(user_role: true) user_role true, but user_role is not true in mentor form.
params[:user_role] will not set user_role = true field in mentor form.
Set user_role
<%=f.hidden_field :user_role, value: params[:user_role] %>
If its supposed to be true for mentor always
<%=f.hidden_field :user_role, value: true %>
By default flash will make them available to the next request, but sometimes you may want to access those values in the same request.
Reference
This works with redirection
flash[:success] = "Welcome to the CSG Mentoring Tool!"
This will work with render
flash.now[:success] = "Welcome to the CSG Mentoring Tool!"

How to validate foreign keys in Rails validations?

In my Rails app I have users who can have many projects which in turn can have many invoices.
How can I make sure that a user can only create an invoice for one of his projects and not for another user's projects?
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:inclusion => { :in => ????????? }
end
Thanks for any help.
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
end
I think that shouldn't be on a validation. You should ensure the project the user selected is one his projects.
You could do something on your controller like:
project = current_user.projects.find params[:project_id]
#invoice = Invoice.new(project: project)
# ...
Your create action could look something like this.
def create
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project = current_user.projects.find params[:invoice][:project_id]
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
project_id is "sensitive" attribute - so remove it from attr_accessible. You are right that you should not believe params from the form and you must check it.
def create
#invoice = current_user.invoices.build(params[:invoice])
# #invoice.project_id is nil now because this attr not in attr_accessible list
#invoice.project_id = params[:invoice][:project_id] if current_user.project_ids.include?(params[:invoice][:project_id])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
If user tries to hack your app and change project_id to not owned value then method create render partial new with invalid #invoice. Do not forget to leave the validation of project_id on presence.
If you get exception Can't mass-assign protected attributes... there are several ways what to do. The simplest ways are:
1. remove line from environment configs (development, test, production)
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
2. Reject sensitive parameters from params before assigning.
# changes in method create
def create
project_id = params[:invoice].delete(:project_id)
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project_id = project_id if current_user.project_ids.include?(project_id)
...
end
OK, luckily I managed to come up with a solution of my own this time.
I didn't make any changes to my controller ("let's keep 'em skinny"), but added a validation method to my model instead:
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:numericality => { :only_integer => true },
:inclusion => { :in => proc { |record| record.available_project_ids } }
def available_project_ids
user.project_ids
end
end
I am not sure if this is good or bad coding practice. Maybe someone can shed some light on this. But for the moment it seems pretty safe to me and I haven't been able to hack it in any way so far.

How can I manipulate a decimal or integer column in a callback before saving to database

I am having a model Factsheet which has some decimal columns, for entering some $ values(price). I need to be able to enter values like $1,000,000.00 and calculate the original value from them before saving. If I do the save, before any formatting, it will save it as 1.00 as "$1,000,000.00".to_f = 1.0 I was trying to do a before_save callback, and extract the digits and dots only from the column before saving it. Here is how I went about it:
class Factsheet < ActiveRecord::Base
before_save :convert_to_decimal
def convert_to_decimal
self.shopping_center_size = decimal_value(shopping_center_size)
self.cam = decimal_value(cam)
self.tax = decimal_value(tax)
#... some other manipulations
end
def decimal_value(string)
string ? string.gsub(/[^\d\.]/, "") : string
end
end
But, this code is not working as Rails convert the parameters into BigDecimal (I believe it performs to_f to the string). So I am getting NoMethod gsub for BigDecimal when I execute this code. I know I can manipulate the params in the controller itself before saving, or pass the params to a class method in the model and manipulate it there. But, both these options doesnt seem right. Is there anyway, I can manipulate them in the callback itself?
Controller:
def FactsheetsController < ApplicationController
def new
#factsheet = Factsheet.new
end
def create
#factsheet = Factsheet.new(params[:factsheet])
if #factsheet.save
flash[:notice] = "Factsheet created successfully"
redirect_to #factsheet
else
render :action => "new"
end
end
def edit
#factsheet = Factsheet.find(params[:id])
end
def update
#factsheet = Factsheet.find(params[:id])
if #factsheet.update_attributes(params[:factsheet])
flash[:notice] = "Factsheet updated successfully"
redirect_to #factsheet
else
render :action => "edit"
end
end
end
View:
_form.html.erb
# Here user should be able to enter values like $1,000,000.00
# and it should be correctly saved to database as 1000000.0
<%= form.text_field :cam %>
<%= form.text_field :tax %>
You can override the set method of each field in the model, like this:
def shopping_center_size=(num)
self[:shopping_center_size] = decimal_value(num)
end
def cam=(num)
self[:cam] = decimal_value(num)
end
def tax=(num)
self[:tax] = decimal_value(num)
end

Resources