Validate model without associated models - ruby-on-rails

I have a model user that has_one business
At certain times I want to validate user on its own without taking into account business.
At the moment if
user.business.valid? is false
this will make user.valid? also false even if the user record on its own is valid.
If in user.rb I do has_one :business, :validate => false it will not take business into account when validating user, but it will then always do it, which is what I dont want.
What is the syntax to call the validation on user without taking the business association into account?
If I do user.business(validate: false).valid? it will ignore the validation on business and user. The above will be true even if user is invalid.
Any ideas?
Update:
Validations:
user.rb
validates :first_name, presence: true
business.rb
validates :name, presence: true
Using rails5.0.0.rc1

Related

Partly invoke validation process on some Activerecord object attributes

I have a situation where User has_one :address and Address belongs_to :user.
I need to be able to validate the address object in these cases:
After a user has signed up, he has an option to partly fill in the address form. In this state I would like to validate for example validates :phone_number, :postal_code, numericality: true but the user can leave the field blank if he wants to.
When user is making a purchase he has to complete the address form. And all the fields have to be validated by validates presence: true + previous validations.
I understand that one approach would be to attach another parameter to the form (i.e.full_validation) and then add a custom validation method that would check for this parameter and then fully validate all attributes.
I was just wondering is there a more code efficient and easier way to do this.
So far I have only found ways to validate some attributes (seethis blog post) but I have not yet found suggestions on how to invoke part of the validation process for certain attributes.
Any help/suggestions will be appreciated :)
#app/models/user.rb
class User < ActiveRecord::Base
has_one :address, inverse_of: :user
end
#app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :user, inverse_of: :address
validates :phone_number, :postal_code, numericality: true, if: ["phone_number.present?", "postal_code.present?"]
validates :x, :y, :z, presence: true, unless: "user.new_record?"
end
--
After a user has signed up
Use if to determine if the phone_number or postal_code are present.
This will only validate their numericality if they exist in the submitted data. Whether the User is new doesn't matter.
--
When user is making a purchase
To make a purchase, I presume a User has to have been created (otherwise he cannot purchase). I used the user.new_record? method to determine whether the user is a new record or not.
Ultimately, both my & #odaata's answers allude to the use of conditional evaluation (if / unless) to determine whether certain attributes / credentials warrant validation.
The docs cover the issue in depth; I included inverse_of because it gives you access to the associative objects (allowing you to call user.x in Address).
If you give more context on how you're managing the purchase flow, I'll be able to provide better conditional logic for it.
For your first use case, you can use the :allow_blank option on validates to allow the field to be blank, i.e. only validate the field if it is not blank?.
http://guides.rubyonrails.org/active_record_validations.html#allow-blank
For both use cases, you can tell Rails exactly when to fire the validations using the :if/:unless options. This is known as Conditional Validation:
http://guides.rubyonrails.org/active_record_validations.html#conditional-validation
For Address, you might try something like this:
class Address
belongs_to :user
validates :phone_number, :postal_code, numericality: true, allow_blank: true, if: new_user?
def new_user?
user && user.new_record?
end
end
This gives you an example for your first use case. As for the second, you'll want to use conditional validation on User to make sure an address is present when the person makes a purchase. How this is handled depends on your situation: You could set a flag on User or have that flag check some aspect of User, e.g. the presence of any purchases for a given user.
class User
has_one :address
has_many :purchases
validates :address, presence: true, if: has_purchases?
def has_purchases?
purchases.exists?
end
end

Rails - What's the proper way to use scopes to check if a record exists for the current day only

In our website we will have a search history feature so users can view and retrieve their last x number of searches for the current day.
I would like to check that the user hasn't already entered the same keyword for the current day before creating a new record. These records would be kept in the db for a few days before being removed so if I just validate the uniqueness of the keyword and the user entered that keyword in the past, the record would not be created.
Below is how I have my model and controller setup. Bear with me, I'm still learning about rails and scopes.
MODEL
class UserLog < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :query_type, presence: true
validates :keyword, presence: true
validates :url, presence: true
validates_uniqueness_of :id
scope :user_searches, -> (user = nil) {where(user_id: user).order(created_at: :desc)}
scope :today_only, -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
end
I believe I could add these checks in my model that would do what I want.
validates_uniqueness_of :keyword, scope: :keyword, conditions: -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
OR THIS?
validates_uniqueness_of :keyword, conditions: -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
And the controller
# to save user query in db
if query_valid (other checks in controller)
UserLog.create(user_id: current_user.id, query_type: query_type, keyword: query_value, url: request.fullpath)
end
And to get records to display on user request
#recent_searches = UserLog.user_searches(current_user).today_only.limit(15)
The whole Time.now.beginning_of_day..Time.now.end_of_day sounds overcomplicated to me. How about you store created_on just like created_at, but a Date, not a DateTime. Your uniqueness scope becomes much easier, similarly creation could be:
current_user.logs.where(keyword: query_value, created_on: Date.today).first_or_create(other_fields)
I'm assuming user has_many :logs, for readability. Instead of UserLog.create(user_id: current_user.id, ...

Limit queries in custom Rails4 model validation

Trying Ruby on Rails I am fiddling around making a small application. So far I like the rails way. I have a model Administration which has a Manager and an Organisation.
I want to make sure - using validations - that the manager assigned to the adminisration is associated to the organisation the administrator belongs to.
I have a working validation, but my gut-feeling says it's expensive on queries.
class Administration < ActiveRecord::Base
belongs_to :organisation
belongs_to :manager, :class_name => "User", :foreign_key => 'manager_id'
validates :code, numericality: true
validates :manager_id, :presence => true
validates :organisation_id, :presence => true
validates :code, uniqueness: { scope: :organisation_id, message: 'BB: Code already in use' }
validate :manager_belongs_to_organisation
def manager_belongs_to_organisation
errors.add(:base, 'BB: Manager does not exist') unless Organisation.find(self.organisation_id).users.include?(User.find(self.manager_id))
end
end
Any thoughts on this matter?
One way to work around this issue is to assign only manager to Administration. organization_id is then automatically inserted using before_save callback:
class Administration
...
before_save :update_organization_id
...
def update_organization_id
self.organization_id = self.manager.organization_id
end
end
It looks like there are more problems with your data model.
Please don't forget about single responsibility principle. In your Administration model your check relations beetween Manager and Organization.
This is bad idea in general.
Is your manager has only one organization? If so, better solution is to store only manager for Administration, and call administration.manager.organization when you need it. And add validation to your Manager model:
validates :organization, presence: true

Rails - new User builds dependent Email and validates both?

I'm building a quick Rails project that allows users to manage their email addresses. Users can have many emails, but one (and only one) of those emails has to be marked as 'primary' (for login), and a user cannot exist without a primary email.
I've been struggling to get this to work right - it seems so circular to me. I need to build a User, and then the Email, but I don't want to save the User into the database unless the Email is valid, which it won't be until the User is saved (because of the validates :user, presence: true constraint).
Accepts nested resources for doesn't seem to work with .new (works fine with .create), and if my Email fails its validations, the User still shows as valid.
Been having a difficult time trying to find good resources (or SO questions) for building/validating multiple/dependent models from a single form.
What's the most Rails way to do this?
User
has_many :emails
has_one :primary_email, -> { where(primary: true) }, class_name: "Email"
accepts_nested_attributes_for :primary_email
validates :first_name, presence: true
validates :last_name, presence: true
validates :birthday, presence: true
validates :password_digest, presence: true
Email
belongs_to :user
validates :user, presence: true
validates :address, presence: true, uniqueness: {
case_sensitive: false
}
UsersController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
# do something
else
# show #user.errors
end
end
private
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:birthday,
:password,
:password_confirmation,
:primary_email_attributes => [:address]
)
end
EDIT
The Email model also contains the following fields:
label = string, eg. 'Personal', 'Work', etc
primary = boolean, whether it's marked as primary email or not
confirmation_code = autogenerated on creation, used to confirm ownership
confirmed = boolean, whether it's been confirmed or not
class User
user has_many :emails
user has_one :primary_email, -> { where(primary: true) }, class_name: "Email", autosave: true
after_initialize {
build_primary_email if new_record?
}
end
class Email
# use gem https://github.com/balexand/email_validator
validates :my_email_attribute, :email => true
end
So after a user initialized its building a primary_email so that record is already associated, or at least it will be if it can be saved. the autosave is working pretty cool - if the primary-email can't be saved due validation error, the user can't neither. should work out of the box, im in a bus right now, can't check it. cheers
futher information: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
If validations for any of the associations fail, their error messages will be applied to the parent. That means, the Parent Model (in your case User) is having errors, and thats why the saving is not possible! that's what you are looking for.
I would store a primary email as a common field and additional emails some another way. I would prefer to store additional emails in another field too that is Array rather than in an associated table. You shouldn't store a primary email in another table. Just imagine, every time you need authorize user or just get his email you will perform an extra request to db.
Meant to post this months ago.
The solution, keeping users and emails normalized across different models without storing a primary email as an attribute on the user, is to use inverse_of:
User.rb
class User < ActiveRecord::Base
has_many :emails, inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :emails
validates :emails, presence: true
end
Email.rb
class Email < ActiveRecord::Base
belongs_to :user, inverse_of: :emails
validates :user, presence: true
end
This allows validations to be performed using in-memory objects, rather than via database calls (ie the object associations are being validated, rather than the presence of an id/record in the database). Therefore they both pass validation and can both be saved in the same transaction.
See: https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations

Having trouble with Terms of Service and Mongoid

Hey I have a simple rails app setup with a devise model named worker and a regular model named user.
Worker:
has_one :user, as: :account, autobuild: true
accepts_nested_attributes_for :user
User:
belongs_to :account, polymorphic: true
accepts_nested_attributes_for :account
On my user model I have a boolean field for :tos with a default to :false. When my user creates a new devise account on the sign_up page I want to take in the tos checkbox input as worker[user_attributes][tos] and on the user model validate the acceptance and store the value as true on the model for the :tos field.
My checkbox:
%input{ name: 'worker[user_attributes][tos]', type: 'checkbox', value: '1' }
My user validation:
validates_acceptance_of :tos, allow_nil: false, on: :create
For some reason tho it doesn't seem that the validation works, is there any configuration I have to make to devise to make this happen? If i take out the validation the account is just created and tos remains false in the database and if I add the validation and check the box it says validation failed.
Instead of saving the tos for each user,remove it from your migration then do the following
attr_reader :tos
validates_acceptance_of :tos

Resources