I am new to ROR and been trying to fumble my way through the tutorial by mike hartl( excellent read for starters i might add ). There is however something i am struggling with, my user model looks like below.
class User < ActiveRecord::Base
validates :name , :presence => true, :length => {:maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => true
end
I then open the ruby console using rails -c and create a new user
usr = User.new(:name=>"abcd",:email=>"svsvenu#gmail.com")
I then save it by using
usr.save
This created a new record in my database. So far so good.but if i type
usr.save
again, nothing happens, i look at the database ( sqlite ) and not even the last update date changed.
Another interesting thing i noticed is when i use
User.create(:name=>"abcd",:email=>"svsvenu#gmail.com"),
multiple times, there is a record created every time i run it in the console.
Can some one please explain why my save does not work and also why my uniqueness constraint is being ignored?
Thanks in advance
ActiveRecord is smart enough to understand that when you type usr.save twice in a row, the 2nd one is redundant.
usr = User.new
usr.save # usr is saved (if it was valid)
usr.save # usr is already saved and unchanged! do nothing.
usr.name = "bananas"
usr.save # usr name changed, commit the change!
When you say that a user is created in the console each time you run User.create, are you sure they're actually being created? In console you'll see a User returned each time, but the id would be nil if there had been errors in the create attempt. If you run create! instead you'd see an exception if the User had validation errors (like a duplicate email) and did not save.
Related
class User < ActiveRecord::Base
attr_accessible :email, :name
validates :name,:presence=>true,
:length=>{:maximum=>15}
validates :email,:presence=>true,
:length=>{:maximum=>15}
end
I am new to rails and the simplest of the validators are not working. I think I may be making a very silly mistake . I have a User model with 2 attributes only and when I create a new user in ruby console with wrong validations like no name or a longer name than 15 characters it gets added happily Please suggest.I am using rails version:3.2.13 and ruby version:1.9.3
If you are on rails console, be sure to type reload! after making changes to models. In this way, all changes will be reloaded in the console instance.
Moreover, are you sure you are saving these models? You should try something like this:
user = User.new(email: "john.doe#gmail.com")
user.save
If the result of the last line is false, you can view the validation errors with
p user.errors
i have a user model with a password attribute, which gets saved as password_digest.
What i want to do is have the edit user page and the edit password page separated, so that
the admin doesn't need to input the password everytime he needs to change something else. For this i have made an extra action / view.
My problem is that when i update the user info, i get password validation errors even though
i have no password field in the form. Which leads me to the conclusion that partial_updates isn't working with the password attribute (as i have already tested in the console that it is enabled, and it is)
Is this me doing something wrong, or should i approach this differently?
Similar to the railscasts approach mentioned by #normalocity, but without the need to set a pseudo-attribute in the controller, we do this (which is essentially taken from restful_authentication):
attr_accessor :password
validates :password,
:length => { :minimum => 8 },
:confirmation => true,
:presence => true,
:if => :password_required?
def password_required?
password_digest.blank? || !password.blank?
end
Basically, we assume that if the password_digest hasn't been set yet (for a new record), or if the password pseudo-attribute has been set, then that means the password is being changed and the validations have to be run. So we don't need to set a updating_password pseudo-attribute like they do in the railscasts episode.
You need to validate the password only if it's being changed. If it's not being changed, then the validation for the password field should be skipped.
Railscasts.com episode #41 shows you how to do this.
I'm just diving into Mongodb and MongoID with Rails and I find it awesome. One thing the NoSQL helps is when I can add extra fields to my model without any extra effort whenever I want:
class Page
include Mongoid::Document
include Mongoid::MultiParameterAttributes
field :title, :type => String
field :body, :type => String
field :excerpt, :type => String #Added later
field :location, :type => String #Added later
field :published_at, :type => Time
validates :title, :presence => true
validates :body, :presence => true
validates :excerpt, :presence => true
end
And this works perfectly as it should. But my question is, (sorry if this is trivial) the existing entries are blank and have no defined value for the newly added field. For example, in a sample blog application, after I've published two posts, I decide to add an excerpt and a location field to my database (refer code above). Any blog post that is published after the addition of these new fields can be made sure to have a value filled in for the excerpt field. But the posts published prior to the addition of these two new fields have null values (which is understandable why) which I cannot validate. Is there an elegant solution for this?
Thank you.
There are three basic options:
Update everything inside MongoDB to include the excerpt.
Use an after_initialize hook to add a default excerpt to existing objects when you pull them out of MongoDB.
Kludge your validation logic to only check for the existence of excerpt on new objects.
(1) requires a (possible large) time hit when you make the change but it is just a one time thing and you don't have to worry about it after that. You'd pull every Page out of MongoDB, do page.excerpt = 'some default excerpt', and then save it back to MongoDB. If you have a lot of Pages you'll want to process them in chunks of, say, 100 at a time. If you do this, you'll be able to search on the excerpt without worrying about what you should do with nulls. You can also do this inside MongoDB by sending a JavaScript fragment into MongoDB:
connection.eval(%q{
db.pages.find({}, { _id: true }).forEach(function(p) {
db.pages.update(
{ _id: p._id },
{ $set: { excerpt: 'some default excerpt' } }
);
});
})
(2) would go something like this:
after_initialize :add_default_excerpt, :unless => :new_record?
#...
private
def add_default_excerpt
self.excerpt = 'some default excerpt' unless self.excerpt.present?
end
You could move the unless self.excerpt up to the :unless if you didn't mind using a lambda:
after_initialize :add_default_excerpt, :unless => ->{ |o| o.new_record? || o.excerpt.present? }
#...
private
def add_default_excerpt
self.excerpt = 'some default excerpt'
end
This should be pretty quick and easy to set up but there are downsides. First of all, you'd have a bunch of nulls in your MongoDB that you might have to treat specially during searches. Also, you'd be carrying around a bunch of code and logic to deal with old data but this baggage will be used less and less over time. Furthermore, the after_initialize calls do not come for free.
(3) requires you to skip validating the presence of the excerpt for non-new Pages (:unless => :new_record?) or you'd have to find some way to differentiate new objects from old ones while also properly handling edits of both new and old Pages. You could also force people to supply an excerpt when they change a Page and leave your validation as-is; including a :default => '' on your field :excerpt would take care of any nil issues in views and such.
I'd go with (1) if possible. If the update would take too long and you wanted the site up and running while you were fixing up MongoDB, you could add a :default => '' while updating and then remove the :default option, restart, and manually patch up any strays that got through.
I've got a User model with three fields, :email, :display_name and :handle. Handle is created behind the scenes from the :display_name.
I'm using the following validations:
validates :display_name, :presence => :true, :uniqueness => { :message => "Sorry, another user has already chosen that name."}, :on => :update
validates :email, :presence => :true, :uniqueness => { :message => "An account with that email already exists." }
I use the handle as the to_param in the model. If the user fails the validation by submitting a :display_name that already exists, then tries to change it and resubmit the form, Rails seems to use the new handle as the validation for the email -- in other words, it assumes that the email doesn't belong to the current user and validation on the email then fails. At this point, Rails assumes that the changed display name/handle is the one to use for the look up and the update action can't complete at all, because it can't find the user based on the new handle.
Here's the update method:
def update
#user = User.find_by_handle(params[:id])
#handle = params[:user][:display_name]
#user.handle = #handle.parameterize
...
end
This problem doesn't happen when the validation first fails on a duplicate email, so I'm assuming it's something about the way I've written the update method -- maybe I should try setting the handle in the model?
maybe I should try setting the handle in the model?
^ This.
The controller isn't the place to do something like this. If it's model logic that's happening behind the scenes, beyond the user's control, why put it in controller code?
Do it instead in a before_save filter, which is guaranteed to run only after the chosen display name is determined to be available and the record is deemed valid. In this way the handle won't be changed on the cached record until it is actually committed to the db, eliminating the problem of the incorrectly generated URL.
before_save :generate_handle
...
def generate_handle
self.handle = display_name.parameterize
end
I'm having trouble seeding my database using seed.rb, specifically where table relationships are concerned.
Here's a sample of the code:
# seed.rb
user = User.find_or_create_by_login(
:login => "myname",
:email => "myname#gmail.com",
:user_type => "Admin",
:password => "admin",
:password_confirmation => "admin")
project = Project.find_or_create_by_user_id(
:user_id => user.id,
:name => "Test Project")
When project is created (along with other unrelated parameters I've left out from above), user_id is empty. How can I get this to work?
This is the strangest behavior I've seen in something so simple. In my seed file, I have about eight tables being created and some are nested 3-4 levels deep (i.e. user has_many projects; projects has_many tasks, etc.).
When I call user user as above and reference user.id multiple times after that, it only works once! I tried adding [user.reload] before each new record is created but to no avail. I don't imagine this will make sense to anyone, but are there any possibilities here? Thanks all.
I figured out what the problem was. The fields that weren't populating were not listed explicitly in attr_accessible in their respective models. The fields listed were being saved correctly.
Thank you very much for your help everyone.
The code is fine and is the correct syntax for find_or_create. As others have said the most likely problem is that the user is invalid. Trying to call user.reload would make it blow up if the user is invalid and so will kind of make the problem more apparent, but the error you'll get from it will be useless (it'll moan about not being able to find a user without an id).
Unfortunately find_or_create doesn't work as a bang method to raise exceptions if it's invalid, so the best thing to do is probably raising an error and outputting the error after attempting to create the user:
user = User.find_or_create_by_login(:login => "myname")
raise "User is invalid: #{user.errors.full_messages}" unless user.valid?
User created with success? if so..try user.reload if not. that is probably the error
Are you sure your user is saved? I think the right syntax for find_or_create_by_XX is Blog.find_or_create_by_title("Some Blog"). If you need to pass more data you need to use find_or_initialize first and set other data after that separately.
Loosely related thread: Rails find_or_create by more than one attribute?
--edit
Passing data as hash to find_or_create_by_XX seems to work too. Docs are under "Dynamic attribute-based finders" here http://apidock.com/rails/v3.0.0/ActiveRecord/Base
try this
use User.find_or_create instead of User.find_or_create_by_login.
It seems like your user object is not saved.
Or before you assign user.id do user.reload
user = User.find_or_create(
:login => "myname",
:email => "myname#gmail.com",
:user_type => "Admin",
:password => "admin",
:password_confirmation => "admin")
[user.reload]
project = Project.find_or_create_by_user_id( :user_id => user.id,
:name => "Test Project")