How to implement partial update via rails api - ruby-on-rails

Let's say we have a resourceful Student model. I have a query regarding updating a student resource via PUT api.
If we send a put request to PUT /students/1 along with request body containing the few attributes that we want to update.
Let's the Student having many attributes like name,age,rollno,country,dob and we just want to update the country, so in the put request body we will pass something like {country: 'germany'} , some other request might pass only the dob.
How should we handle it in server side to only update the attributes passed in the body ?

The update method on your ActiveRecord objects takes an attributes hash. You can pass only one attribute, or all attributes of a model, and ActiveRecord will figure out what has changed and only update those columns and leave the rest alone.
student = Student.create(name: 'name', age: 'age', rollno: 'rollno', country: 'country', dob: 'dob')
params = { country: 'germany' } # stand-in for your actual params hash in the controller
student.update(params)
Only country will be updated, everything else will remain the same. On the next request when you update dob it works the same way.
params = { dob: '1/1/2000` }
student.update(params)

Related

How to edit a serialized array with known keys

I came across this question on how to edit serialized columns in Rails, where the keys are unknown.
In my case, I want to be able to edit an array of hashes where the keys are actually known, in the serialized attribute.
An example:
class Person
serialize :addresses
end
Where in Addresses would be an array of hashes:
{
line_1: "1 First Street",
line_2: "Apt 202",
city: "Tampa",
state: "FL",
zip: "12313"
}
And since I would know the index of this hash within the array with the each_with_index on a show view, I want to be able to edit the information in an edit or a new form view.

Rails 6 get the difference of the 2 timestamp and assigned to one new field in query

I am working on the rails 6 application with postgresql database, where I have to count to the time of the call.
I have 2 fields in my database one is answered_at(timestamp) and another is end_at(timestamp)
I use the following query, and in that query, I need the difference of (end_at - answered_at) and access in call_duration field
So I can access the call_duration field by attr_accessor but I do not know how to get the difference and set it into the query.
My original query is
#in_app_calls = InAppCall.where("caller_user_id =? OR receiver_user_id =?", chat_user&.first&.id, chat_user&.first&.id)
.group_by(&:call_type) unless chat_user.nil?
following is my database record
<InAppCall id: 1, caller_user_id: 10, receiver_user_id: 61, call_type: "audio", call_answered: true, answered_at: "2020-07-16 04:24:17", end_at: nil, created_at: "2020-07-08 09:58:36", updated_at: "2020-07-16 04:24:17", on_going_updated_at: nil>
I may get nil value in answered_at(timestamp) or end_at(timestamp) field, in that case, I will not consider that record.
Can anyone help me with the query?
An elegant way to do this is to use virtual attributes that ActiveRecord basically creates on the fly for you when you modify the select list. You can do it like so:
user_id = chat_user&.first&.id
#in_app_calls = InAppCall.
select("in_all_calls.*, EXTRACT(EPOCH FROM end_at - answered_at)::int AS call_duration").
where("caller_user_id = :user_id OR receiver_user_id = :user_id", user_id: user_id).
where.not(answered_at: nil, end_at: nil).
group_by(&:call_type) if user_id.present?
Note the additional field that I'm creating with AS call_duration => ActiveRecord simply turns this into an attr_reader on the model, so you can do #in_app_calls[0].call_duration and such things like you would with any regular column.
I also took the liberty of cleaning up your handling of the user_id – I hope it's correct this way, otherwise you should have no trouble reverting this bit back to your original.

searchkick requests together with active record requests rails

I have a trouble. For example I have a model Article, and my search_data method below
def search_data
title: title,
body: body
end
And I need to receive some records from my controller according to some attributes, for example:
def index
#articles = Article.where(user_id: user_id).search(query)
end
But this approach received all data whos according to query in search method ignoring where method, while I need to search by search method among data received by where method. How to resolve this?
Update
Desirely to use where before search method, not the inside it
It appears SearchKick doesn't allow you to chain a search onto an ActiveRecord Relation like that. It uses ElasticSearch to search instead of your database so if you did that you would be querying two databases for the information. However, it does provide a where option for the search method:
Article.search(query, where: {user_id: user_id})
If you absolutely have to make the ActiveRecord query first, you could get the IDs from the first query and provide them to the second.
ids = Article.where(user_id: user_id)
Article.search(query, where: {id: ids})
The key here tis to make sure that every attribute in your where query also exist in your search_data method. Example:
Message.search search_term, where: {or: [[{user_id: current_user.id}, {recipient_id: current_user.id}]]}
I am looking to find messages with the search_term where the current user is either sender or recipient.
Therefore in my search_data method (in model.rb) I should have:
def search_data
{ user_id: user_id,
recipient_id: recipient_id,
title: title,
description: description,
}
And this works well. this is using Searchkick gem.

concatenating objects in ruby

I have an object called teacher and another one called students. I am trying to return both of them in a controller as json response, and I want the response to be like
{'teacher': {'first_name': 'adam', 'last_name': 'smith'}, 'students': [{'id':'5', 'age' :15}, {'id':'8', 'age' :18}]}
or in case of one to one relationship(one teacher has one student):
{'teacher': {'first_name': 'adam', 'last_name': 'smith'}, 'students':{'id':'8', 'age' :18}}
The point is to return a concatenated json response where one object appears inside the other one
I have tried teacher.as_json.merge(students.as_json), but this does not embed the students objects inside the teacher. it just append the data
I have read in one post the teacher['students'] = students should work but I get always an error: can't write unknown attribute students
teacher.as_json.merge(students: (students.count > 1 ? students.as_json : students.first.as_json ))
Do exactly as you would in JSON. If you have teacher object and students object, render this object:
{ teacher: teacher, students: students }

How to make find_or_initialize case insensitive on a Postgres DB?

In a Rails 3.2 app I have a Post model that belongs to a Category. In the new post form is a text field for assigning a category or creating a new category.
It works via the following method on the Post model
def category_name=(name)
if name.present?
post_cat = category.find_or_initialize_by_name(name)
if post_cat.new_record?
post_cat.save(:validate => false)
self.category = post_cat
else
self.category = post_cat
end
end
end
This is more or less working, but is case sensitive. For example, the database now contains records for "Featured", "featured" and "FEATURED" categories.
How can I make the above find_or_initialize function behave as though it were case insensitive. I am using a Postgres database, and I have a suspicion that this is not quite as easy as "making find_or_initialize case insensitive".
I'm grateful for any advice as to best practice, what to consider, and useful references.
I refer to your comment above: Maybe you could add a second field, containing the value of the string which is displayed to the user, and find_or_initialize by the name column:
post_cat = category.find_or_initialize_by_name(name.downcase)
post_cat.display_name = name if post_cat.new_record?
So the second line ensures, that an existing record doesn't get overriden by another user, who tries to add categories like bRandnAmE. The only pitfall I see is, that the first user, who creates the category, has to spell it correct.

Resources