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.

On Wed, Jun 15, 2011 at 7:22 PM, Justin S. [email protected]
wrote:

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?

On Wed, Jun 15, 2011 at 7:47 PM, Justin S. [email protected]
wrote:

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.

On Wed, Jun 15, 2011 at 8:03 PM, Jatin kumar
[email protected]wrote:

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?

On 15 Jun 2011, at 17:22, Colin L. wrote:

the technique being suggested.

Like so:
by json

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

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

On 15 June 2011 16:14, Justin S. [email protected] wrote:

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

On 15 Jun 2011, at 18:25, Justin S. wrote:

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.

On Wed, Jun 15, 2011 at 12:35 PM, 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.

On Wed, Jun 15, 2011 at 12:05 PM, Peter De Berdt

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?

On 15 Jun 2011, at 18:46, Justin S. wrote:

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.

Best regards

Peter De Berdt