Relaxing validations for 'quick create' - ruby-on-rails

I have a couple of models in my application that incorporate extensive ActiveRecord validations to ensure data quality, but because these require a lot of user input I'm also allowing users to quick create records by inputting only a fraction of the information typically required for a full create.
Is there a way to bypass a number of validations when I'm doing a 'quick create'?
Up until this point I've been doing #project.save(:validate=>false) and doing the necessary validations manually in the controller action but this is ungainly and creates redundant code. How should I go about this?

The following snippet is borrowed from another post and modified it a little:
class Project < ActiveRecord::Base
validates_uniqueness_of :project, :unless => :quick_create
attr_accessor :quick_create
end
The following snippet goes into your view
<%= submit_tag 'Submit', :name => 'project[quick_create]' %>
All the logic is in your model, you don't touch your controller at all.

Related

Associating a temporary (non-DB) model to a simple_form

I have a simple_form, that doesn't really apply to a normal model. If I have the object just set to :thing it seems to work.
However, I want it to have some validation. In other questions, I've found that this means that I NEED to have a model... I'm not sure what needs to be in that model, however. So, I made I model, but I can't figure out how to hook them up.
class ClientEmail
include ActiveModel::Validations
validate :cannot_be_present
attr_accessor :to_domain
def cannot_be_present
newDomClients = Client.where("email like :foo", {:foo => "%#{to_domain}%"})
errors.add(:base, "There cannot be any emails already in the database with the target domain" ) if newDomClients.length > 0
end
end
and the simple_form is:
= simple_form_for(#client_email, url: { action: "update" }, html: {class: "search_form", method: :put }) do |f|
.input-row
= f.input :newDomain, :label => "New domain name", :required => true
(etc)
Initially, it said that #client_email was nil, so I initialized it (which seems unlikely to be necessary given Rails...) with:
- #client_email = ClientEmail.new
but that tells me that ClientEmail doesn't have a to_key method, so I'm clearly missing a bunch of infrastructure somewhere.
If you create a form with an object that belongs to a model you can't create fields that doesn't belongs to that model. You need to create form like this in order to do that:
= simple_form_for(:client_email, url......)
if you create form with symbol like this you can create any field in this form and send to controller that you want. params hash won't change too much and you can call your special fields like this:
params[:client_email][:your_field_name]
since this field doesn't belongs to a model its better to validate it in controller.
Hope it helps.
It appears that I needed:
include Virtus.model
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
I'm not sure exactly what Virtus is. it's probably possible without it, but it's a library I already have...

Validates presence of not working in Rails 2.3.8

I have a simple Rails app that has a model that is contained in several places. I allow the updating of the model from several different controllers in Rails 2.3.8. In my model I have code that allows for the name and the description to be validated. If they are blank Active_Scaffold should be populating the div with an error message. This works in the page that is linked directly to the User's controller but if I include the user anywhere else it will only show up on the User's controller page and not on the page that they are currently on. Shouldn't Acitive_Scaffold magically redirect to the correct place?
Code:
class User < ActiveRecord::Base
validates_presence_of :name, :description, :allow_blank => false
def create_from_params(params)
#name = params[:name]
#description = params[:description]
end
As you can see nothing spectacularly hard.
EDIT: Sorry I left out the rest of the method signature
I suspect the active_scaffold views only check for errors on the model it represents. Add this error check code to any view but the user view.
<%= error_messages_for :user %>

Can't mass-assign protected attributes for creating a has_many nested model with Devise

I've watched the RailsCast, another nested attributes video, lots of SO posts, and fought with this for a while, but I still can't figure it out. I hope it's something tiny.
I have two models, User (created by Devise), and Locker (aka, a product wishlist), and I'm trying to create a Locker for a User when they sign up. My login form has a field for the name of their new Locker (aptly called :name) that I'm trying to assign to the locker that gets created upon new user registration. All I'm ever greeted with is:
WARNING: Can't mass-assign protected attributes: locker
I've tried every combination of accepts_nested_attributes and attr_accesible in both of my models, yet still nothing works. I can see from the logs that it's being processed by the Devise#create method, and I know Devise isn't smart enough to create my models how I want :)
Here's the relevant bits of my two models:
# user.rb
class User < ActiveRecord::Base
attr_accessible :username, :email, :password, :password_confirmation, :remember_me, :locker_attributes
# Associations
has_many :lockers
has_many :lockups, :through => :lockers
# Model nesting access
accepts_nested_attributes_for :lockers
end
and
# locker.rb
class Locker < ActiveRecord::Base
belongs_to :user
has_many :lockups
has_many :products, :through => :lockups
attr_accessible :name, :description
end
# lockers_controller.rb (create)
#locker = current_user.lockers.build(params[:locker])
#locker.save
I'm assuming I need to override Devise's create method to somehow get this to work, but I'm quite new to rails and am getting used to the black box "magic" nature of it all.
If anyone can help me out, I'd be incredibly thankful. Already spent too much time on this as it is :)
EDIT: I realized I omitted something in my problem. My Locker model has three attributes - name, description (not mandatory), and user_id to link it back to the User. My signup form only requires the name, so I'm not looping through all the attributes in my nested form. Could that have something to do with my issue too?
EDIT 2: I also figured out how to override Devise's RegistrationsController#create method, I just don't know what to put there. Devise's whole resource thing doesn't make sense to me, and browsing their source code for the RegistrationsController didn't help me much either.
And for bonus points: When a user submits the login form with invalid data, the Locker field always comes back blank, while the regular Devise fields, username & email, are filled in. Could this also be fixed easily? If so, how?
first, you have a typo :
attr_accessible :locker_attributes
should be plural :
attr_accessible :lockers_attributes
then, the standard way to use nested_attributes is :
<%= form_for #user do |f| %>
<%# fields_for will iterate over all user.lockers and
build fields for each one of them using the block below,
with html name attributes like user[lockers_attributes][0][name].
it will also generate a hidden field user[lockers_attributes][0][id]
if the locker is already persisted, which allows nested_attributes
to know if the locker already exists of if it must create a new one
%>
<% f.fields_for :lockers do |locker_fields| %>
<%= locker_fields.label :name %>
<%= locker_fields.text_field :name %>
<% end %>
<% end %>
and in you controller :
def new
#user = User.new
#user.lockers.build
end
def create
# no need to use build here : params[:user] contains a
# :lockers_attributes key, which has an array of lockers attributes as value ;
# it gets assigned to the user using user.lockers_attributes=,
# a method created by nested_attributes
#user = User.new( params[:user] )
end
as a side note, you can avoid building a new locker for new users in controller in different ways:
create a factory method on User, or override new, or use an after_initialize callback to ensure every new user instantiated gets a locker builded automatically
pass a specific object to fields_for :
<% f.fields_for :lockers, f.object.lockers.new do |new_locker_fields| %>
Someone helped me figure out the solution in a more "Rails 4'y" way with strong attributes & how to override Devise's sign_up_params (to catch all the data coming from my signup form).
def sign_up_params
params.require(:user).permit(:username, :email, :password, :lockers_attributes)
end
Gemfile addition: gem 'strong_parameters'
Commenting out the attr_accessible statement in my user.rb file, since apparently strong parameters eliminate the need for attr_accessible declarations.
# attr_accessible :username, :email, :password, :password_confirmation, :lockers
And the/a correct way of building a Locker before submitting the form: at the beginning of the nested form:
<%= l.input :name, :required => true, label: "Locker name", :placeholder => "Name your first locker" %>
Thanks again for all your help. I know a question like this is difficult to answer without seeing the whole codebase.

Validations that rely on associations being built in Rails

A Course has many Lessons, and they are chosen by the user with a JS drag-n-drop widget which is working fine.
Here's the relevant part of the params when I choose two lessons:
Parameters: {
"course_lessons_attributes"=>[
{"lesson_id"=>"43", "episode"=>"1"},
{"lesson_id"=>"44", "episode"=>"2"}
]
}
I want to perform some validations on the #course and it's new set of lessons, including how many there are, the sum of the lessons' prices and other stuff. Here's a sample:
Course Model
validate :contains_lessons
def contains_lessons
errors[:course] << 'must have at least one lesson' unless lessons.any?
end
My problem is that the associations between the course and the lessons are not yet built before the course is saved, and that's when I want to call upon them for my validations (using course.lessons).
What's the correct way to be performing custom validations that rely on associations?
Thanks.
looks like you don't need a custom validation here, consider using this one:
validates :lessons, :presence => true
or
validates :lessons, :presence => {:on => :create}
You can't access the course.lessons, but the course_lessons are there, so I ended up doing something like this in the validation method to get access to the array of lessons.
def custom validation
val_lessons = Lesson.find(course_lessons.map(&:lesson_id))
# ...
# check some things about the associated lessons and add errors
# ...
end
I'm still open to there being a better way to do this.

rails multistep form

id like to make a multistep form with rails using the edit and update actions. so i would like it to be like step 1 of the form, and the user fills in his name, address, and phone number. then the user clicks save and continue and he then fills out his shipping address and then clicks save and continue and fills out his billing address. i saw ryan bates version, but its not what im looking for. i would like the order to be saved after the first step so if the user doesnt finish their form, i can call them and ask them what went wrong. can anyone refer me to a tutorial or give me an example of how to make an order form using the edit and update methods?
Typically this means you'll need to put conditions on your model validations. Some subset of your validations should apply to each form page:
class User
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :street, :if => :on_page_two?
validates_presence_of :city, :if => :on_page_two?
validates_presence_of :postal_code, :if => :on_page_two?
validates_presence_of :state, :if => :on_page_two?
validates_presence_of :country, :if => :on_page_two?
validates_acceptance_of :terms_and_conditions, :if => :on_page_three?
def on_page_two?
# whatever you need to determine the page number
end
def on_page_three?
# whatever you need to determine the page number
end
end
It's not pretty but I highly recommend a pattern like this. Anything more complicated and you'll need to rewrite it when your signup flow changes.
There are different approches to this problem.
My particular prefered solution is to implement something like a "State Machine" in the model. This way, I can persist the progress of a, per example, a multistep form, without having to hasle with more actions than new/create and edit/update.
I'm currently working on a heavy long State Machine application using the State Machine gem for rails.
Hope it helps you!
There's a great Railcast on creating multi-step wizard forms, which you can find here. It uses the Wicked gem.

Resources