Create or Update Model with JSON?

Having issue updating a model with JSON. I simply “model.attributes=json”, which does update the attributes but not the id, or uuid in this case. I want to save or create the record (if it’s id exists). Seems like this would be documented, but I’m not finding anything. I’m using a MOM to pass JSON between apps. Thanks.

Having issue updating a model with JSON. I simply “model.attributes=json”, which does update the attributes but not the id, or uuid in this case.
I want to save or create the record (if it’s id exists)
You are updating the records if an existing record is present or any other condition.
Create your custom method that checks if there are common ids in the json you are passing and the ids of the records present.For them, call obj.update_attributes!.

You’re saying fetch and update if it exists, or create new if it does not? That is what I’m doing. What I’m saying is when I update a new obj with json, the id does not get set. Is this not what you get?

You’re saying fetch and update if it exists, or create new if it does not? That is what I’m doing. What I’m saying is when I update a new obj with json, the id does not get set. Is this not what you get?

I am sorry, didn’t read it well.
Yes, you can’t do that. However, a workaround is:
Delete the previous obj, and create a new one.

You’re saying fetch and update if it exists, or create new if it does not? That is what I’m doing. What I’m saying is when I update a new obj with json, the id does not get set. Is this not what you get?

I am sorry, didn’t read it well.
Yes, you can’t do that. However, a workaround is:
Delete the previous obj, and create a new one.

Even that won’t work. Just tried it.
You can’t mass-assign protected attributes like id

To narrow down the issue I’m having, I can update an object that exists. Like so:

stu = Student.find(‘8edcfd63-801c-4ad3-b811-a6bdc440810e’)

This will update the obj, but leave the id unchanged

stu.update_attributes!(JSON.parse(json_string))

But this will not work:

Will not find this id

stu = Student.find(‘c6a4673e-90bd-4793-a62f-2e2fbdb857f6’)

stu = Student.new

ID will be nil after this update

stu.update_attributes!(JSON.parse(json_string))

a forced save false will create a new id, not the one assigned by json

I would think I’m just missing something simple, or how would a distributed system work in Rails/Ruby?

This whole method runs into a lot of issues. What about has_many objects, they won’t update with new id either. Is there not a method to have rails add or update based on id in a new object? Is this not how I should be working with distributed objects?

This whole method runs into a lot of issues. What about has_many objects,
they won't update with new id either. Is there not a method to have rails
add or update based on id in a new object? Is this not how I should be
working with distributed objects?

Have you tried using a before_create filter to force the id to the
appropriate value? I have not done this myself but I seem to remember
the technique being suggested.

Colin

Tinkering with what Rails consideres a “do-not-touch” value always gets you into trouble. Mass assignment for ids is out of the question, as far as I know so is setting the field individually through self.id= It’s with good reason too, the chance you’ll screw up referential integrity is too big.

There probably is some obscure way to set the id, but instead I would just slap myself on the head and wonder: why the hell did I ever come up with the idea of making that field the primary key instead of letting Rails handle it and store that external JSON ID in some field I can actually change :slight_smile:

BTW, what do you mean with a “distributed system”? If you’re using a distributed database, the database will take care of the ID assignment. My impression is that you have an external application that you access through a JSON API and you want to search the ID field from the other application in your Rails application. That would be so easy if you just let Rails handle it’s primary key thing (creating its own autoincrement ids), make a new field called “my_fancy_dandy_external_api_id”, index it and store the value of the external app there. You can then fiddle with it all you want, Rails won’t care.

Best regards

Peter De Berdt

Yes, I’m starting to learn this. When I say distributed, I mean I’m linking applications with rabbitmq and ruby amqp. I then pass a message that’s serialized in json. The issue is normal integer id’s between apps will clash, so I was going to use uuid as the id instead.I’m guessing you are saying make the uuid another field, and just update the object. So the two applications would maintain their own id’s, but match uuid’s? Is there not a method of doing this that already works. I know I’m not the first to pass objects around.

That’s exactly how we do it using Nanite. Nanite gives us a unique identifier (some hash like yours), we store it in the database with the record that will be affected, then search for that hash when the callback fires with the processed data. Nothing too complicated there. At the end of your callback function, you set the hash field to NULL so it no longer occupies space.

Something like this:

report = Report.new

report.uid = Nanite.request(’/report/generate’, report[:json_data], &NaniteCallbacks.report)

def NaniteCallbacks.report

lambda do |res|

uid = res[res.keys.first]

report.find_by_uid(uid).update_attributes!(res)

end

end

That’s the gist of it basically (it’s a bit more complicated in our application, but you get the point).

Best regards

Peter De Berdt

How do you handle say a one-many? Seems like the solution should be at the ActiveRecord::Base level . This is why I was changing the id system to uuid. That way rails could just update based on uuid.

Hmmm, I’m still not entirely sure what you are trying to do. Which of the following scenarios does your app belong to?

  • Certain tables should be kept in sync between two databases or even the database as a whole, but the databases need to be on different servers/locations: use database replication, some databases support it and you don’t have to worry about keeping them in sync.

  • Certain tables should just be accessible by both applications, but those tables can be on the same server: split out the common tables into a new application and use ActiveResource to access it. If one of the apps is not a Rails app, you’ll have to develop an ActiveResource compatible layer.

  • Only certain records need to be synced and only by manual interaction of the user: you can use what you were doing, but it will become quite complex as you just found out. You’ll need a common key between the databases (you call it uuid and I’m guess you generate them yourself), and you’ll have to store that on either or both sides.

You can of course try and go with your original idea and force an update of the primary key and all related keys (using raw sql executes probably, or maybe a private method somewhere in Rails). Rails might not be the ideal framework for that, since it’s very opinionated. My fear is also that you’ll find yourself with an inconsistent database sooner or later (probably even sooner than later). Am I the only one who feels changing IDs in one database to keep another remote one in sync doesn’t feel quite right?