I just started trying my hands on Ruby on rails . i have created a mode states and want that every state name must remain unique so i used
uniqueness: true
as per http://guides.rubyonrails.org/active_record_validations.html .
As the above document says that the validation is invoked automatically when object.save is called. but When i try to save the objects with same state_name value , no exception is thrown and the record is saved. Can one please help where i am doing it wrong.
Model code
class State < ActiveRecord::Base
acts_as_paranoid
validates_presence_of :state_name ,uniqueness: true
end
Controller code
def create
#stateName = params[:stateName];
#state = State.new();
#state.state_name=#stateName;
if(#state.save())
resp = { :message => "success" }
else
resp = { :message => "fail" }
end
respond_to do |format|
format.json { render :json => resp }
end
end
Thanks in advance for helping!
If you want uniqueness check, change
validates_presence_of :state_name ,uniqueness: true
to
validates :state_name, uniqueness: true
If you want uniqueness and presence check both, use
validates :state_name, uniqueness: true, presence: true
An alternate syntax is shown below, however, syntax shown above should be preferred
validates_uniqueness_of :fname
validates_presence_of :fname
Also, as per documentation, please note following with respect to usage of uniqueness: true
It does not create a uniqueness constraint in the database, so it may
happen that two different database connections create two records with
the same value for a column that you intend to be unique. To avoid
that, you must create a unique index on both columns in your database.
The validation happens by performing an SQL query into the model's
table, searching for an existing record with the same value in that
attribute.
What this means is that it is possible that if multiple users are trying to save records concurrently, there is a possibility records with duplicate state_name can get created as each save is happening on different thread
Related
I have an ActiveAdmin model registered as such:
ActiveAdmin.register MyModel do
permit_params :name, :synonyms
filter :name
index do
selectable_column
column :name
actions
end
end
I noticed that when I update an object from the ActiveAdmin interface, the synonyms are getting cleared out. Synonyms are an array field defined as such in the PostgreSQL database:
synonyms text[] DEFAULT '{}'::text[]
I tried putting the following in app/admin/my_model.rb:
controller do
def update
permitted_params[:my_model][:synonyms] = JSON.parse permitted_params[:my_model][:synonyms]
super
end
end
and I also tried it with params instead of permitted_params but that doesn't work either. Not sure why ActiveAdmin would be discarding this field. Am I doing something incorrectly? The project I'm working with uses ActiveAdmin 1.0.0.pre4 (I realize this is a dated version).
Any advice would be much appreciated. Thanks in advance!
Notes: Seems this might be a common issue?
I'm not sure if this is applicable to string arrays and I don't know about that specific version of Activeadmin, but I encountered this issue in a slightly different context.
I had a model where the array data types were decimals & integers. I had to explicitly state the type of form input to be used when editing / updating the record or nothing was passed back from the field with the update parameters. Activeadmin chose a number input by default, but it needed to be processed as a string.
form do |f|
f.inputs do
f.input :ages, as :string, :input_html => {:maxlength => '100'}
end
end
I had to set maxlength manually because for some reason it was automatically being set very short. In the model, the string then has to be converted into an actual array before being saved.
def ages= items
if items.is_a? String
items = items.split(' ')
items.each do |i|
i.to_i
end
end
super items
end
In my application I have models Post & Slides & I have:
class Post < ActiveRecord::Base
has_many :slides, inverse_of: :post
accepts_nested_attributes_for :slides, reject_if: :all_blank, allow_destroy: true
Everything works fine, only thing I need (because of how my application will work), is when a slide is created, I need to assign it to current_user or user that is creating the record.
I already have user_id in my slides table and:
class User < ActiveRecord::Base
has_many :posts
has_many :slide
end
class Slide < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
My PostsController looks like this:
def new
#post = current_user.posts.build
// This is for adding a slide without user needing to click on link_to_add_association when they enter new page/action
#post.slides.build
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Was successfully created.' }
else
format.html { render :new }
end
end
end
Any help is appreciated!
There are two ways to accomplish this:
First option: when saving the slide, fill in the user-id, but this will get pretty messy quickly. You either do it in the model in a before_save, but how do you know the current-user-id? Or do it in the controller and change the user-id if not set before saving/after saving.
There is, however, an easier option :) Using the :wrap_object option of the link_to_add_association (see doc) you can prefill the user_id in the form! So something like:
= link_to_add_association ('add slide', #form_obj, :slides,
wrap_object: Proc.new {|slide| slide.user_id = current_user.id; slide })
To be completely correct, you would also have to change your new method as follows
#post.slides.build(user_id: current_user.id)
Then of course, we have to add the user_id to the form, as a hidden field, so it is sent back to the controller, and do not forget to fix your strong parameters clause to allow setting the user_id as well :)
When I'm looking at this I see three ways to go about it, but since you're on cocoon already, I would drop the connection between user & slides - as it kind of violates good database practices (until you hit a point where you page is so popular you have to optimize of course, but that would be done differently).
You are using cocoon, but you're not utilizing the nesting of the relationship fully yet ...
The best practice would be to have cocoon's nesting create both & instead of trying to assign to current_user you call something like:
#slides = current_user.posts.find_first(param[:id]).slides
The #slides saves all the results, the .Post.find(param[:id]) finds a specific post for current_user.
Note: this is not the most optimized way & I haven't tested this, but it shows you the format of one way you can think about the relationships. You will need to hit rails console and run some tests like ...
(rails console)> #user = User.first
Next we test that there are posts available, as it's frustrating to test blanks & not get the results ...
(rails console)> #posts = #user.posts
Then we use the find method & I'm going to use Post.first just to get a working id, you can easily put "1" or any number you know is valid ...
(rails console)> #post = #posts.find(Post.first)
Finally, we go with either all slides to make sure its a valid dataset
(rails console)> #post.slides
If you want a specific slide later & have a has_many relationship just tag that find method on the .slides after.
Also one last thing - when you state earlier in there you need the current_user to be related, you can use an entry in your model.rb to create a method or a scope to get the data & allow you to link it to the current_user more easily & even drop some directed SQL query with the .where method to pull that information up if performance is an issue.
I spotted a second optimization in there ... if everything really is working - don't worry about this!
And don't forget about the strong_parameters nesting to do this fully ... Strong Param white listing
Basic format ... `.permit(:id, :something, slide_attributes: [:id, :name, :whatever, :_destroy])
I have a polymorphic lookups table with a child ContractType model. I have a before_save callback in the ContractType model that sets a category, but it doesn't seem to be working.
class Lookup < ActiveRecord::Base
validates :value, presence: true
validates :category, presence: true
end
class ContractType < Lookup
before_save { self.category = "contract_type" }
end
Then I open the rails c:
> c = ContractType.create(value: "test")
> c.errors.messages
=> { :category => ["can't be blank"] }
I don't get any errors, just a failed validation. As far as I know, the syntax looks correct, it's just that the before_save callback doesn't seem to be working...
Am I missing something obvious here?
I found the problem...validations take place BEFORE the save, so it always "failed" validation and stopped before the before_save callback took place.
The solution is to use a before_validation callback.
class ContractType < Lookup
before_validation { self.category = "contract_type" }
end
My misunderstanding of the order of events.
I know you found your solution, but think about your problem and solution logically. You are hard coding the category value. It means that the user cannot leave it blank. It will always be filled. So why have a validation for it in the first place? Just remove this:
validates :category, presence: true
In the before_save callback, you will know the value will be assigned. before_validation does have use cases. For example, a user enters a value for a currency attribute and you want to format the currency prior to validation. But in your case, before_validation is not needed.
I am trying to implement a role based access system in my rails 4 app, and I want the end user (super_admin) to have the ability to edit role assignments via the UI.
I have achieved some success but can't help feeling that there has to be a better way (Since I'm new to rails). Here is my code:
users_roles_controller.rb
# GET /user_roles/new/:id
def new
#roles = Role.all
end
# POST /user_roles/new/:id
def create
populated = params[:roles][:name].reject(&:empty?)
populated.each do |key|
#user.add_role Role.find(key).name
end
redirect_to users_path
end
And in my Form (HAML and simple_form):
= simple_form_for :roles, :url => create_user_roles_path(#user.id), :method => "post" do |f|
= f.input :name, :collection => #roles, as: :check_boxes
= f.button :submit
I'm struggling with the following:
How do I validate form entries, since there is no model?
Should I be using strong parameters and if so how do I implement on a form without a model
How do I create something similar, but with Current roles already checked? (True role management)
UPDATE
I have looked at using the reform Gem as suggested by the comments. This seems like a great solution. I am however having issues with the implementation on this case.
Let me map it out:
I have 3 tables in the database:
users
users_roles (Mapping Table with 2 Attributes : user_id & role_id {Join Table -> HABTM})
roles
I want to construct a form with all the values in the Roles model as checkboxes.The checkboxes should dictate what values are fed into the users_roles table (With relation to a specific user). What I want reform to do is validate the input of this form. This form will always display all of the values in Roles, but some/all of the boxes might be unchecked.
I have created a form folder in my app and started with the following code:
class UserRoleForm < Reform::Form
property :user__id, on: :user
property :role_id, on: :role
validates :user__id, presence: true
validates :role__id, presence: true
end
Am I going in the right direction?
Thanks for the help.
You need two things to build your form: a user's roles and the possible roles.
If I recall correctly, rolify gives your model associations ad should just let you do something like some_user.roles to return all the roles applied to some_user.
To get possible roles, try Role.all.
To combine both, try
user_roles = some_user.roles.pluck(:name) # suppose this returns ["admin"]
Role.pluck(:name).map { |r| [r, user_roles.include?(r)] }
You now have an array like this that you can use to build your form checkboxes with an each loop.
[["customer", false], ["admin", true], ["editor", false]]
Define your reform object's sync or save method to handle what to do with the submitted input, however you are handling it. You can (SHOULD) make a custom validation to verify if the submitted roles are valid roles.
When I try to register an user, it does not give me any error but just cannot save the user.
I don't have attr_accessible. I'm not sure what I am missing. Please help me.
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :email, presence: true,
uniqueness: true,
format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
validates :password, presence: true, length: {minimum: 6}
validates :nickname, presence: true, uniqueness: true
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params) # Not saving #user ...
if #user.save
flash[:success] = "Successfully registered"
redirect_to videos_path
else
flash[:error] = "Cannot create an user, check the input and try again"
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :nickname)
end
end
Log:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x5OqMgarqMFj17dVSuA8tVueg1dncS3YtkCfMzMpOUE=", "user"=>{"email"=>"example#example.com", "password"=>"[FILTERED]", "nickname"=>"example"}, "commit"=>"Register"}
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'example#example.com' LIMIT 1
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."nickname" = 'example' LIMIT 1
(0.1ms) rollback transaction
Regarding our short discussion in the comments, it appears that one or two things are happening to cause #user.save to return false:
One of the validation rules are failing
A callback within your model is returning false, thus halting processing and rolling back the transaction
There are a few quick ways to debug the validations. I figured I could describe them so you could learn a few options.
A. Change the call within the if statement to instead use the bang method of save:
if #user.save!
This will cause the app to raise an exception if validation fails, displaying the validation errors within the browser on your screen. In this particular scenario, you'd want to remember to remove the ! after you're done debugging because you probably don't want the final version of your app doing that.
Or...
B. Within the else statement, add this line:
raise #user.errors.to_yaml
This will display the validation errors within the browser on the screen. Of course, remember to remove this line after you're done debugging.
Or...
C. Within the else statement, add this line and then run the form post:
puts #user.errors.to_yaml
This will display the validation errors within your console. You'll want to remember to remove this line after you're done debugging, but it's "less worse" if you happen to forget because at least the extra info is only output to STDOUT.
You may want to try each of these just to get a little practice and to see what your options are in simple debugging scenarios like this.
High chances that error is in password confirmation. You use has_secure_password from Rails, which automagically handles password confirmation for you. And here is the problem - you don't have it before user creation. Thus just add. For details check out similar question on has_secure_password
And check, that you have password_digest:string in users table :)