My goal is to validate my form and making sure all fields filled out in my form exists and not blank. if ALL these condition's attributes first_name, last_name, date_of_birth exists AND values are not blank it can search the database; otherwise, it just return to the search page with an error telling the user that all fields need to be filled out. Im doing this validation for my backend.
I currently having this object received from filling out a form in my view:
pry(#<RegistrantsController>)> #q.conditions
=> [Condition <attributes: ["first_name"], predicate: matches, values: ["John"]>, Condition <attributes: ["last_name"], predicate: matches, values: ["Smith"]>]
As you see I haven't filled out the date_of_birth in my form that's why it is not in this array but basically that's why I want to validate this.
how can I loop through and implement this condition?
Use the validations provided by ActiveModel just like you would in normal CRUD actions:
class SearchForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :first_name, :string
attribute :last_name, :string
attribute :date_of_birth, :date
validates :first_name, :last_name,
:date_of_birth, presence: true
end
class SomeController
def search
#search_form = SearchForm.new(search_params)
if #search_form.valid?
# do something
else
flash.now[:error] = "Oh noes..."
render :some_kind_of_view
end
end
private
def search_params
params.require(:q)
.permit(:first_name, :last_name, :date_of_birth)
end
end
In the model is where you should do your validations.
class Model
validates :first_name, :last_name, presence: true
end
then is the controller for that model you should be able to create a conditional:
def create
#user = User.new(user_params)
if #user.save
//....
else
render json: #user.errors.full_messages, status: 422
end
end
def user_params
params.require(:user).permit(:first_name, :last_name, :date_of_birth)
end
Related
I have a simple contact form that accepts the following fields (all should be required): Name, Email, phone, and message.
I also want to validate the email address.
Users should be given a response on whether or not the form submitted successfully, or if there are errors.
if so, display specific errors on the view.
This form is not connected to any database model. I'm not saving submissions. Only mailing.
I have a POST route set to contact_form in my PagesController
In my PagesController I have
def contact_form
UserMailer.contact_form(contact_form_params).deliver
end
In my UserMailer class I have:
def contact_form(params)
#formParams = params;
#date = Time.now
mail(
to: "support#example.com",
subject: 'New Contact Form Submission',
from: #formParams[:email],
reply_to: #formParams[:email],
)
end
This mails successfully, but there's no validation. I need to only run the mail block if validation passes. then return a response to the user.
Since I have no Model, I'm not sure how to do this. All the answers I see tell people to use validates on the ActiveRecord model.
With the few answers:
(note I've updated my params)
class UserMailerForm
include ActiveModel::Validations
def initialize(options)
options.each_pair{|k,v|
self.send(:"#{k}=", v) if respond_to?(:"#{k}=")
}
end
attr_accessor :first_name, :last_name, :email, :phone, :message
validates :first_name, :last_name, :email, :phone, :message, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end
def contact_form
#form = UserMailerForm.new(contact_form_params)
if #form.valid?
UserMailer.contact_form(contact_form_params).deliver
else
logger.debug('invalid')
logger.debug(#form.valid?)
end
end
This sends mail when valid. However, I'm still unsure about sending info to the user
You can make UserMailer a model and use validations on that.
class UserMailer
include ActiveModel::Model # make it a model
include ActiveModel::Validations # add validations
attr_accessor :name, :email, :phone, :message
validates :name, :email, :phone, :message, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
def send_mail(subject:, to:)
mail(
to: to,
subject: subject,
from: email,
reply_to: email,
)
end
end
Then use it like any other model.
def UserMailersController < ApplicationController
def new
#user_mailer = UserMailer.new
end
def create
#user_mailer = UserMailer.new(params)
if #user_mailer.valid?
#user_mailer.send_mail(
to: "support#example.com",
subject: 'New Contact Form Submission',
)
else
# Use #user_mailer.errors to inform the user of their mistake.
render 'new'
end
end
end
If you have multiple forms associated with UserMailer, you can make separate classes to validate each Form's inputs and then pass them along to UserMailer. You'd likely still want validations on UserMailer regardless.
You can use ActiveModel::Validations on your PORO the same way AR does this.
class MyFormObject
include ActiveModel::Validations
def initialize(options)
options.each_pair{|k,v|
self.send(:"#{k}=", v) if respond_to?(:"#{k}=")
}
end
attr_accessor :name, :email, :phone, :message
validates :name, presence: true
# and so on...
end
Sorry I'm new to rails but can't wrap my head around this one.
I have an Order object with various attributes - no references
In my controller I can print out the attributes individually via their attr_accessor and see them in the console via puts.
But when I call .inspect they are all nil! any suggestions?
class Order < ApplicationRecord
attr_accessor :name, :email, :phone, :date, :dessert_type, :size, :quantity, :dessert, :comments, :total
validates :name, :date, :quantity, presence: true
validates :quantity, numericality: { only_integer: true, greater_than: 0}
validate :contact_provided?
private
def contact_provided?
if :email.blank? || :phone.blank?
errors.add(:base, "Please provide either phone or email so we can contact you!")
end
end
end
Controller
def create_order
puts "create_order object"
#order = Order.new order_params
if #order.valid?
puts #order.inspect
#everything is null here
#order.attributes.each do |attr_name, attr_value|
puts "#{attr_name}: #{attr_value}"
end
#this prints out fine!
puts "dessert: #{#order.dessert}"
end
end
Parameters
Parameters: {"utf8"=>"✓", "authenticity_token"=>"randomtoken", "order"=>{"name"=>"jim", "email"=>"test#email.com", "phone"=>"12345678", "dessert_type"=>"Cake", "size"=>"25.0", "dessert"=>"Chocolate Caramel", "date"=>"2018-04-15", "quantity"=>"1", "comments"=>""}, "commit"=>"Submit Order"}
Any insight much appreciated!
That's because this line:
attr_accessor :name, :email, :phone, :date, :dessert_type, :size, :quantity, :dessert, :comments, :total
is overriding the Order attributes in the way Rails works with them. As working with Rails you don't need that declaration, so you can remove them as attr_accessor.
As Order is an ActiveRecord model, then the getters and setters are already generated by ActiveRecord for all of your object attributes.
What you're doing right now is defining all of your attributes, with the attr_accessor as virtual attributes which are attributes on the model that don't persist in the database, just "an attribute not corresponding to a column in the database".
One thing that confuses me the most is when doing validation in one model with two controllers. I have a login system which register and logs users in. There both use the same model but both does not use the same amount of HTML widgets. One controller contains password, retype password, user name, first & second name and so on. The second controller uses only the user name and password fields. How would you do validation in the same model for this situation?
Thank you
here is the controller that register new users:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/cool'
else
#user = Newuser.new
#user.valid?
#user.errors.messages
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
second controller:
class LoginsController < ApplicationController
before_filter :authorize
def index
#rentals = Rental.where(user_id: current_user.id).limit(5)
#buys = Buy.where(user_id: current_user.id).limit(5)
#users = User.where(id: current_user.id)
#buyGames = BuyGame.where(user_id: current_user.id).limit(5)
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to '/logout'
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(account_params)
redirect_to '/cool'
else
render 'edit'
end
end
private
def account_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
Here is my model:
class User < ApplicationRecord
has_secure_password
end
One way to go is to remove validations from the model and put them in form objects. For this case, you'll have two form objects, each with its own set of validations. And you use the appropriate one in respective controllers. Something along these lines:
# logins_controller
def update
login_form = FormObjects::LoginForm.new(login_params)
if login_form.valid?
redirect_to '/cool'
else
render 'edit'
end
end
# users controller
def create
signup_form = FormObjects::SignupForm.new(user_params)
if signup_form.save
redirect_to '/cool'
else
render 'new'
end
end
# signup_form
module FormObjects
class SignupForm
include ::ActiveMode::Model
validate_presense_of :email, :password, :password_confirmation, :address, :whatever_else
def save
# create user here
end
end
end
# login_form
module FormObjects
class LoginForm
include ::ActiveMode::Model
validate_presense_of :email, :password
end
end
You can simply specify validations on actions, that is:
validates :first_name, presence: true, on: :create # which won't validate presence of first name on update or any other action
I believe the trick you are looking for is to define validation actions on create/update of the model. Something roughly along these lines:
class User < ActiveRecord::Base
# These are example validations only; replace with your actual rules.
validates :password, confirmation: true
validates_presence_of :username
validates :first_name, presence: true, format: {with: /.../}, on: create
validates :last_name, presence: true, format: {with: /.../}, on: create
end
...However, I am unclear why you would want to do this in your specific example. It would be advisable to always run all validation checks on fields like first_name, to help maintain data integrity.
I have a user model which consists of 8-10 attributes.
I tried to use form object concept to extract out the validations stuffs into another UserForm Class.
FYI I am using Rails 4 :)
My controller :
class UsersController < ApplicationController
def create
#user = UserForm.new(user_params)
#user.save
end
def user_params
# Granted permission for all 10 attributes.
params.require(:user).permit(:first_name, :last_name, :email....)
end
end
My custom class looks like this:
class UserForm < ActiveModel::Validator
# like this i have 10 attributes
attr_accessor :first_name, :last_name, :email, ....
#validation for all 10 attributes
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
#I think this is a bad idea, putting all 10 attributes.
#User.create(first_name: first_name, email: email, .... )
# what better solution we can have here ?
end
end
Now everything seems quite good so far. Just I am confused how to get all attributes saved directly with User.create (in persist! method) rather than manually assigning each and every value ?
UserFrom.create(user_params)
Also, why not just User.create(user_params) ?
have you looked into "Virtus" gem. it makes dealing with Form object really easy.
https://github.com/solnic/virtus
class UserForm < ActiveModel::Validator
include Virtus.model
attr_accessor :user
attribute :first_name, String
attribute :last_name, String
attribute :email, String
and so on..
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
#user = User.create(self.attributes)
end
end
This my code:
class OrdersController
def create
#order = Order.new(params[:order])
if #order.purchase
work = GATEWAY.store(credit_card, options)
result = work.params['billingid']
current_user.update_attributes(:billing_id => result)
end
end
end
billingid is returned by running GATEWAY.store(credit_card, options)
I am trying to save this returned billingid into :billing_id column in User Model. Is it not possible to update attribute of User model from a that is not UsersController?
Simply put, is it not possible to update an attribute of model #1 from a controller of model #2?
Thanks
UPDATE:
With the help of the men below, I was able to verify two things:
1. result = work.params ['billingid'] returns string
2. That I am able to save into a different model from any controller
However, even though I have attr_accessible :billing_id I am still unable to save the result into billing_id column of User table. I was successful in saving the result in a store_name column of a Store table, so I don't know what it is about User model that is preventing me from saving.
I ran,
#mystore = Store.find(current_user)
#mystore.store_name = result
#mystore.save
and it was successful. But,
#thisuser = User.find(current_user)
#thisuser.billing_id = result
#thisuser.save
This fails even though attr_accessible is set correctly. What else could prevent from saving certain attributes other than attr_accessible? Thanks everyone!
UPDATE 2: User Model
require 'digest'
class User < ActiveRecord::Base
has_one :store
has_many :products
attr_accessor :password
# attr_accessible was commented out completely just to check as well. Neither worked
attr_accessible :name, :email, :password, :password_confirmation, :username, :billing_id
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
username_regex = /^([a-zA-Z0-9]{1,15})$/
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
end
UPDATE FINAL: SOLUTION
using #thisusers.errors, I was able to find out that it was trying to validate the presence of password during this request. Once I commented it out, it saved without an issue. I am unsure why this is happening, but I will take it from here. Thanks everyone esp. dmarkow!
There should be no issue updating any number of models from a controller.
Make sure that work.params['billingid'] actually contains a value.
Your User model may have some attributes marked as attr_accessible (since you have current_user, I assume you have authentication, and this often means needing to protect your model's attributes by default). If this is the case, that means that only those attributes can be changed by mass assignment (e.g. using update_attributes). Either add billing_id to the list of attributes that are attr_accessible, or don't use mass assignment. (Instead, you would just do current_user.billing_id = result and then current_user.save)
Edit: The problem wound up being a validation error on the User model. Always make sure to check the user.errors when user.save returns false.