How to create/update record if some variables are not set through strong params? - ruby-on-rails

Some of the variables are required for a record but not included in the params. How to then create/update such a record? Would the code below be correct? I belief the new method works, but have doubts about the update method.
New record:
connect = Connect.new(update_params(connection))
connect.first_id = comp1.id
connect.second_id = comp2.id
connect.save
Update existing record:
#If record exists, do this:
connect.first_id = comp1.id
connect.second_id = comp2.id
if connect.save
unless connect.update_attributes(update_connection_params(connection))
return render json: #organization, message: connect.errors.full_messages, status: :bad_request
end
end

Just write validations for these fields, on validation error(s) the record will not be saved and save will return false.
Common pattern is to render the form again, with errors, asking the user to correct
Your code for new record is correct, just check return value of save like for existing:
connect.assign_attributes update_connection_params(connection)
connect.first_id = comp1.id
connect.second_id = comp2.id
if connect.save
redirect_to somewhere_path
else
return render json: #organization, message: connect.errors.full_messages, status: :bad_request
end

Related

How can I get my rails controller (CREATE) to accept/POST multiples JSON records?

I have a backend Rails JSON API. Right now, I am only able to create one record per POST based on the way I've coded my controller.
Let's say we have an application where Users can create To-Do Lists and in those lists they can create items.
3 models for our example, User, UserList, and UserListItems
User has_many UserLists
UserLists has_many UserListItems
Now right now, I'm updating UserListItems with a POST, but I can only add one item at a time. The JSON looks like...
{
"user_list_item":
{
"item_title": "Buy Milk"
}
}
And using Active Model Serializers, I am returning the record that it creates, and it looks as follows...
respond_with :api, :v1, #current_user, user_list, user_list_item, location: nil, serializer: Api::V1::UserListItemSerializer
{
"user_list_item": {
"id": 11,
"user_list_id": 2,
"item_title": "Buy Milk"
}
}
There is a serious flaw with this. If a User creates a 'Grocery To-Do List' and we POST 1 UserList record... that's fine. But then they might begin to fill the grocery list and add 3 things at once. "Buy Milk", "Buy Eggs", "Get Gas". We now need to POST to UserItemList 3 times, and that's far from ideal. What if they added 20 items. We should aim to add all 20 in 1 POST.
How can I do this?!?
1) Can someone show me the entire sample code for the CREATE in the controller to do this, as I learn best by seeing/doing syntax. I want to be able to pass in...
{
"user_list_item":
[
{
"item_title": "Buy Milk"
},
{
"item_title": "Buy Eggs"
},
{
"item_title": "Get Gas"
}
]
}
and have my controller parse, loop, and create them. 2) And I also want to be able to return the record(s) to the creator via Active Model Serializers. So for example, all 3 of these newly added records in one return (mostly I'm just interested in the first thing right now though).
EDIT: Adding my original controller#create code
if authenticate_user
user_list = #current_user.user_lists.find_by_id(params[:user_list_id])
user_list_item = user_list.user_list_items.new(user_list_item_params)
if (user_list_item.save!)
respond_with :api, :v1, #current_user, user_list, user_list_item, location: nil, serializer: Api::V1::UserListItemSerializer
else
render json: { error: "Could not create new User List Item."}, status: :unprocessable_entity
end
else
render json: { error: "User is not signed in." }, status: :unauthorized
end
private
def user_list_item_params
params.require(:user_list_item).permit(:user_list_id, :title, :due_date)
end
Now, with #Arvoreniad's suggestion below, I now have the following...
if authenticate_user
#user_list = #current_user.user_lists.find_by_id(params[:user_list_id])
#error = false
user_list_item_params[:user_list_item].each do |attribute|
item = #user_list.user_list_items.new
item.title = attribute.title
item.due_date = attribute.due_date
#error = true unless item.save
end
if (error)
????????????????
else
render json: { error: "Could not create new User List Item(s)."}, status: :unprocessable_entity
end
private
def user_list_item_params
params.require(:user_list_item).permit(:user_list_id, :title, :due_date)
end
My new questions are
1) Where all the ?'s are... where I was previously using ActiveModelSerializers to show the newly added record, how can I return something showing all the newly added records? Or this that not typical of an API? Is it just common to return something empty and just go off of whether it was successful or not?
2) Can-I/Should-I create both things in 1 POST (UserList and Multiple UserListItem's all in 1 POST)? If so, how?
3) How can I rollback all saves if one does not work. Let's say I'm trying to add 10 items, and 9 succeed but 1 fails, how do I roll them all back?
Your question is a little difficult to answer without your previous controller code, but this should give you a starting point based on the info you supplied. EDIT: Added the code for points 1 and 3.
If you want to create a UserList in the same action, just post the required data and create one from the parameters, rather than finding it in the database. I can help you with this if you'd like, but it shouldn't be too hard.
def create
#user_list = UserList.find(params[:id)
#error = false # Use this to check before rendering if there has been an error.
params[:user_list_item].each do |attributes|
item = UserListItem.new
item.item_title = attributes.item_title
item.user_list_id = #user_list.id
#error = true unless item.valid?
#items << item
end
if(#error)
render json: { error: "Could not create new User List Item(s)."}, status: :unprocessable_entity
else
#items.each do |item|
item.save
end
serialized = ActiveModel::ArraySerializer.new(#items, each_serializer: UserListItemSerializer)
render json: serialized
end
end

Updating boolean attr with ? before it is saved

I have a Message model that has a boolean from_posting? attribute that defaults to false.
In some cases, I want to set this attr to true before the Message is saved.
def create
#message = current_user.sent_messages.build(message_params)
if params[:reply] == 'true'
#message.from_posting? = true
end
if #message.save
render json: #message, status: 200, serializer: MessageSerializer
else
render json: #message.errors, status: 400
end
end
But #message.from_posting? = true throws an unexpected = error. I can do #message.title ='something', but why can't I use attr_accessor to set the boolean value when the attr ends in a ??
I thought about saving the record and then updating from_posting? if I need to, but that just seems like an extra db write. I tried using #posting.toggle(from_posting?) but that throws an error on from_posting? as well.
I highly suggest the refactor suggested in the comments. Renaming the from_posting? field to from_posting is the correct course of action.
If a refactor is not possible, you should be able to use:
send('from_posting?=', true)
or
write_attribute('from_posting?', true)
or
#message['from_posting?'] = true
The last option is an alias of the second.
David Verhasselt has an excellent writeup of different ways for setting ActiveRecord attributes here.

How to create temporary attribute & use that in json reponse in rails active record

I am new to ROR.
I am having a controller where i am getting the search results in available_users variable..
availble_users //some active record result with id,name & address
available_users.each do |userstatus|
userstatus.class_eval do
attr_accessor :is_friend
end
if current_user.invitations.find_by(:friend_id => userstatus.id) //invitation is another table
userstatus.is_friend = "true"
else
userstatus.is_friend = "false"
end
end
render json: available_users
but when i am getting the response on ajax request it is serving the same array without including is_friend column.
here is my json response.
id: 2
name: abc
address:
please can anyone figure me out why it is not appending this temporary attribute.
Thanks.
what you have will work if you pass the methods option to_json
render json: available_users.to_json(:methods => :is_friend)
Or you could do this
available_users.each do |userstatus|
if current_user.invitations.find_by(:friend_id => userstatus.id) //invitation is another table
userstatus["is_friend"] = "true"
else
userstatus["is_friend"] = "false"
end
end
render json: available_users
[]= is an alias for write_attribute

Rails Activerecord: Update where conditions... else create

I need to check multiple columns of a table to see if I find a match. If I find a match I need to "updateattributes" of the matching record with all of my form params... Else I need to add a new record with all of my form params.
if #somethingtoupdate = Model.where("column1 = ? and column2 = ?", params[:something][:column1], params[:something][:column2])
if #somethingtoupdate = Model.update_attributes(params[:something])
redirect_to somewhere_path, :notice => "The existing record was updated"
else
render "myformlocation"
end
else
#added = Model.new(params[:something])
if #added.save
redirect_to somewhere_path, :notice => "The new record was created"
else
render "myformlocation"
end
end
Update
#somethingtoupdate = Model.where("this_id = ? and that_id = ?", params[:something][:this_id], params[:something][:that_id])
if ! #somethingtoupdate.empty?
if #somethingtoupdate.update_attributes(params[:something])
redirect_to some_path, :notice => "The existing record was updated"
else
render "myformlocation"
end
else
#added = Model.new(params[:something])
if #added.save
redirect_to some_path, :notice => "The new record was created"
else
render "myformlocation"
end
end
This is where I stand now thanks to #micahbf.
But, I am still getting an error on my "update_attributes" when there is a matching record.
Seems like this should work.... What am I missing or doing wrong?
This is because where does not return nil if it doesn't find anything, it returns an empty array, which is still truthy, so the block gets executed.
You can use empty? to check whether to run the block or not.
Note also that if it finds a match, the match will still be returned inside of an array (even if there was only one match). So you will have to do something like call first on the result to take the first returned model and update it.
So, the top might look like:
#somethingtoupdate = Model.where("column1 = ? and column2 = ?", params[:something][:column1], params[:something][:column2])
if ! #somethingtoupdate.empty?
if #somethingtoupdate.first.update_attributes(params[:something])
redirect_to some_path, :notice => "The existing record was updated"
else
render "myformlocation"
end
else
// do stuff if the query found no matches
end
I think here is short method to find record and if found then update record and if record not found then create it.
#somethingtoupdate = Model.where("column1 = ? and column2 = ?", params[:something][:column1], params[:something][:column2]).first_or_initialize
#somethingtoupdate.update_attributes(params[:something])
First of all, Model.update_attributes(params[:something]) is not working (at least in Rails 3.2.12). It should be #somethingtoupdate.update_attributes(params[:something]).
Also, there is an existing method for this kind of purpose: first_or_create.
#somethingtoupdate = Model.where("column1 = ? and column2 = ?", params[:something][:column1], params[:something][:column2]).first_or_create

Not adding data to associated table

With the code below and entry is created in the venuetypes table with the correct *venue_id* and time stamps however, the type column remains as null
def new
#new1 = "gfdsgfd"
#venue = Venue.new
#venue.save
#venuetype = #venue.venuetypes.create(:type => "test")
#venuetype.save
respond_to do |format|
format.html # new.html.erb
format.json { render json: #venue }
end
end
Unless you've specified otherwise, rails expects a type column to be used for single table inheritance which is probably causing problems.
Also, venuetypes.create will only save the venue type if it is created successfully, as will the .save call afterwards. You have almost certainly got an error on the venue type which is causing it not to be saved. Try using .save! which will throw an error or by lookins at #venuetype.errors which will contain any error messages that have caused it not to be saved.

Resources