Is there anyway for me to identify whether an object/record is dirty before saving and which fields are changed in Rails?
Example
Suppose I have a Person model and Person has a property called name and age. In the db, Person with id 1 is named "John" with age 20.
p = Person.find 1
p.name #John
p.age #20
now, when I change his name from John to Nathan, is there any way for me to identify
the the object is changed (dirty)
and which fields got changed
Now I know the answer for the first one. If I change his name to Nathna, I can do the following
p.name = "Nathan"
p.changed? #true
However, is there anyway for me to identify which field was changed? May be a method that returns an array of fields that got changed?
p.dirty_fields #[:name]
See http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changes, specifically changed.
Related
I am using a ActiveRecord::Base to get some values from database. The result of this query is below:
Result:
{"parent_id"=>"4", "id"=>"3"}
{"parent_id"=>"10", "id"=>"23"}
{"parent_id"=>"13", "id"=>"29"}
{"parent_id"=>"15", "id"=>"35"}
Now, I need to get a id from parent_id. Example, I want to find how is the id associate to parent_id.
It's possible to do it?
Update
I have several hashes from PG::Result. I need to use the first key (parent_id) to get the second key (id).
Example: I need to know how is the id from parent_id = 4. In this case, will be 3.
I think the I need something like this:
"4" => 3
"10" => 23
"13" => 29
I hope I understood your question correctly.
I'm assuming parent_id and id are fields that belong to some model and the result you posted is a result of a query to that model.
Say, you store the result of your query in a variable named res and the input value for parent_id is pid, then try the following. It should give you the id value corresponding to the parent_id submitted as input variable named pid.
res.find_by(parent_id: pid).id
Hope that helps.
I would like to understand why in Rails 4 (4.2.0) I see the following behaviour when manipulating data in a join table:
student.student_courses
returns all associated records of courses for a given user;
but the following will save changes
student.student_courses[0].status = "attending"
student.student_courses[0].save
while this will not
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
Why is that, why are those two working differently, is the first one the correct way to do it ?
student.student_courses[0] and student.student_courses.find(1) are subtly different things.
When you say student.student_courses, you're just building a query in an ActiveRecord::Relation. Once you do something to that query that requires a trip to the database, the data is retrieved. In your case, that something is calling [] or find. When you call []:
student.student_courses[0]
your student will execute the underlying query and stash all the student_courses somewhere. You can see this by looking at:
> student.student_courses[0].object_id
# and again...
> student.student_courses[0].object_id
# same number is printed twice
But if you call find, only one object is retrieved and a new one is retrieved each time:
> student.student_courses.find(1).object_id
# and again...
> student.student_courses.find(1).object_id
# two different numbers are seen
That means that this:
student.student_courses[0].status = "attending"
student.student_courses[0].save
is the same as saying:
c = student.student_courses[0]
c.status = "attending"
c.save
whereas this:
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
is like this:
c1 = student.student_courses.find(1)
c1.status = "attending"
c2 = student.student_courses.find(1)
c2.save
When you use the find version, you're calling status= and save on entirely different objects and since nothing was actually changed in the one that you save, the save doesn't do anything useful.
student_courses is an ActiveRecord::Relation, basically a key => value store. The find method would only work on a model
I try to construct a username based on name given. Since many people are named "John" I need to somehow check for this and create it by following a +1 count.
I'm a bit lost in how to iterate ActiveRecord to find in this case a username called john, then if already exists try john1 and if not available try john2 and so on. I can suppose it would use while but I have no idea how to iterate it for this case.
Any idea how to do this?
In order to not make multiple trips to the database, get a list of users that match the name, check the count of users, append the count to the name, and then check the already loaded collection for the new name.
It would look something like this:
name = params[:name]
users = User.where(User.arel_table[:name].matches("#{params[:name]}%")).to_a
count = users.length
name_without_collision = if count > 0
loop do
break "#{name}#{count}" unless users.any? { |u| u.name == "#{name}#{count}" }
count += 1
end
else
name
end
Since you're calling to_a on the ActiveRecord::Relation, you don't have to worry about accidentally sending multiple queries to the database, since it is being converted to a standard Ruby array.
I have a count column in my tags table. I wanna increment tag count if a tag is just added to the post while updating, and it's already inside db. I added this to my post model:
before_update :increment_tag
def increment_tag
db_post = Post.find_by_id(self)
self.tags.each do |tag|
unless db_post.tags.include? tag
tag.update_attribute("count", tag.count + 1)
end
end
end
I get the post from db and test if the current tag is already in db, if it is, nothing happens, if it's not there, it should update count field. But for some reason this doesn't work.
You should not have a Count column in a tag. You should in stead set up your models propperly so you could do the following:
db_post.tags.count
If you do it right, you can get this in your tag:
tag.post.tags.count
If your aim is to find how many times the tag is used in a post, in total, you can simply count the instances in the TagToPostColumn (if you got one), which you need to sine this is a many-to-many relation.
Then you do:
TagToPostColumn.where(tag_id: someTag.id).count
count is a standard attribute, and you should never have to keep track of this yourself, unless you actually need a column called count that tracks something other than the models you have in the database. But then it is a good idea to name it something else than count, since it can lead to ambiguous attributes.
Also, i find it very strange that you are doing this:
db_post = Post.find_by_id(self)
Why are you not just using the self parameter, in stead of doing a db lookup to find the post you already have.
I'm just trying to increment a record by 1 starting at 2000, when a new record is created upon clicking on the create action to create a record:
if resource_model == Student then #resource.testing_id = id + 2000 end
So if the record has an id of 1, I assume that the testing_id will be 2001. But instead it returns:
2147483647 (maximum mysql limit?)
Any suggestions on how to address this? Thanks.
You can't know record ID during create. ID is known after saving record do database.
You can't relay on ID to give you values like 1, 2, 3 ... and so on.
Don't store value like ID+2000, becouse you can get it at any time by calculating id+2000.
You can get next testing_id by something like this:
if resource_model == Student then
#resource.testing_id = Student.first(:order => "testing_id DESC").testing_id + 1
end
But if two processes at the same time will fetch the same value then you will have duplicate testing_id.
Object.idf is a (deprecated) method that returns the Ruby object ID, which uniquely identifies the object. Rails has overridden id so that in models it refers to the database primary key. I'm going to take a guess that your code snippet is from a controller, and that's why id is returning a strange and large number: It's the Ruby object id of the controller, not the primary key of the object. Give id a receiver, e.g. #resource.id, and see how that works.
2147483647 = 2 ^(32-1)
Could you show some of your code here?
From what i'm guessing here is that you are comparing apples with strawberries :P.
I think you are adding a "1" on a string so that means for example:
2000 + 1 = 20001
20001 + 1 = 200001
So if you do that a couple of times this means you will get to the maximum size of an int (21475483647). I don't know this for a 100% sure, but this has to do with the way you ask your question and the way you don't post code etc...
I hope this edit was helpfull tho.