Extra attribute in many to many join table - ruby-on-rails

I have a many to many association between User and Todo through a join model called UserTodo
Of the many users a todo has, there's one owner. So, I created a column in the user_todos join table called is_owner.
Question is, how do I populate this attribute while creating a Todo?
Currently, I'm creating the todo, then separately updating this attribute in TodoController#create action.
#todo = current_user.todos.create(todo_params)
#todo.user_todos.first.update_attribute(:is_owner, true)
This seems wrong. Is there a single call I can make to populate this attribute while creating the todo?
Second, is there a way to query if a user is an owner of a todo, this way?
current_user.todos.first.is_owner?

I would make a user_todo.rb file with a UserTodo class and do stuff like:
ut=UserTodo.new
ut.todo = Todo.create(todo_params)
ut.user = current_user
ut.is_owner = true
ut.save
current_user.todos_as_usertodos.first.is_owner?
You can make on user.rb
def todos_as_usertodos
UserTodo.where(user_id: id).to_a
end
See where I'm going with this? You want to return and use UserTodo objects vs. Todo objects because they have more info in them. The info you need. That extra is_owner boolean. When you goto just a plain todo object you lose that info.

That seems a bad way since you use .first to get your instance. I'll do something like
UserTodo.create(user: current_user, is_owner: true, todo: Todo.create(todo_params))
Second
I'm not sure if that is possible

You can use this one-liner to create the join model with extra attributes:
current_user.user_todos.create(todo: Todo.create(todo_params), is_owner: true)
Since is_owner is an attribute of the join model, you have to access it through that model too:
current_user.user_todos.first.is_owner?

Related

Updating if exist or create if not rails

So im using an api to get info on weather, its executes everyday, what im trying to do is to get updated if already exist, and create a new one if it doesn't in table.
I do want to update all attributs when udpdating.
i did try
model = Model.where(column_name: value).first_or_initialize(locked: false)
but i get an error saying :
unknown attribute locked for Model
raise UnknownAttributeError.new(self ,k.to_s)
If you need anything, ask and i will comment or edit. Im newb to ruby and rails
Firstly, the model.Model part should be just Model, as Model is your class.
locked is supposed to be a column/attribute of the Model class, although it seems is not the case judging from your error. Therefore, I'm gonna use other_column_name as an example.
Explanation of what this is doing:
Model.where(column_name: value).first_or_initialize(other_column_name: some_value)
Model.where(column_name: value): gets models that satisfy the condition column_name == value
first_or_initialize: if a model such that column_name == value was found, that one is returned. Otherwise, it initializes a model with column_name = value.
By passing other_column_name: some_value, if the model was not found and needs to be initialized, it sets other_column_name to some_value but: 1) it does not update it if it was initially found and 2) it does not save the record.
The equivalent of first_or_initialize that saves the new record would be first_or_create but this would still not update the record if it already existed.
So, you should do something like this:
m = Model.where(column_name: value).first_or_initialize
m.other_column_name = some_value
m.save
This way, you first get a model where column_name is value or initialize a new one with this value if it didn't already exist. Then, you set the attribute other_column_name to some_value and save the model.
A one-liner alternative would be
Model.where(column_name: value).first_or_create.update(other_column_name: some_value)
However, note that if it needs to be created, this one will perform 2 queries (the insert and the update).
About the error part. It says the attribute locked does not exist on the Model record. Are these classes you created? Are you using some pre-existing project? You could try posting Model.attribute_names and maybe your schema.rb
Firstly refer to the docs here
A table by the name of weather with the following attributes location: string temperature:integer wind:string needing to be updated or initialized based on the location would work like this
#weather_record = Weather.find_or_initialize_by(location: location_value)
#weather.temperature = -60
#weather.wind = strong
#weather.save
Next, never, ever use a reserved name for a model so do not have Model as the name of your table
Lastly in your example
model.Model.where(column_name: value).first_or_initialize(locked: false)
you are saying
a_record.ClassName.where which is just wrong, If you are using a class method then start with the class name e.g. Weather.where if you are using instance methods then use the instance name e.g. an_instance_of_weather.some_field
Try this mate:
column_name_value = (Way that you get the info from data)
model = Model.find_or_initialize_by column_name: column_name_value
Let me know if worked!

Group all records by custom method in Rails

I'd like to retrieve all records for a particular model and "index" each one by their name attribute. The end result should be a Hash, where the keys are the name of the record, and the value is the record. I can do this easy enough with a method like
def self.all_by_name
hash = {}
all.each { |model| hash[model.name] = model }
hash
end
Is there a way to do this with an active record query?
all.group(:name) is what you need!
Some documentation: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-group
No, active record will either return an array or an active record relation. You would have to massage it after into the data type of your choice. ActiveRecor dcan return anything that SQL can normally return. However, you could do use the group method which is essentially the group by in SQL.
I'm not sure in your case what your method should do because it seems like you are going to overwrite the value of your key if there are 2 objects with the same class. Perhaps if you better defined what you need in an example I could help you better.

Check that record doesn't exist/retrieve existing record active record

This question has two parts:
Consider an active record relation that has a Student - name:string, favorite_class:references and FavoriteClass name:string, abbrev:string.
First question: When creating a student record and adding a favorite class (name and abbrev) I want to check if that name/abbrev combo exists and if it does load that one for this association, else create a new one.
Second: When I try to update (Put) the student I want to be able to pass the abbrev of the favorite class and look up the record by that portion (assuming abbrevs are unique) and use that, else fail.
I am not seeing the rails way of doing this type of operation.
For the first question, the trick is to use find_or_create method:
#in the place where you create a new Student
#this method return first entity from db, if it doesn't found, will create one
fav_class = FavoriteClass.find_or_create(name: some_name, abbrev: some_abbrev)
Student.create(name: some_student_name, favorite_class: fav_class)
something similar you can do for your second question. Please, give me more details about it.
Update 1
If you want to update student's favourite class, you can do it in this way:
#I assume that you use latest vertion of rails of not, use .first instead of .take
new_class = FavoriteClass.where(abbrev: some_abbrev).take
student.update_attributes(favorite_class: new_class) if new_class

find_by_sql which model should I use?

There are three tables:
users
schedules
schedules_users
The user-model and the schedules-model each have the has_and_belongs_to_many-relationship.
Now I simply want to do this:
user_id_binded = Schedule/User/Object/#I dont know!#.find_by_sql ["SELECT schedules_users.user_id FROM schedules_users WHERE schedules_users.schedule_id = ?", schedule.id]
#user_schedules_binded = User.find(user_id_binded)
BUT the return-value of the first find_by_sql must be a model, as I understood the Rails.Api correctly.
It's neither a user-model-return-value or a schedule-model-return-value.
In the schedules_users-table are all relationships between users and schedules.
So I want to get all users which are binded to a specific schedule.
First I thought this should be the right way to solve it, but at that moment I didn't know that the return-value must be a model.
How could I solve this problem?
It appears you have a schedule ID and want the users in the end - that can be done easier by join statement like #user_schedules_binded = User.joins(:schedules).where(schedules: { id: schedule_id })
Or, if you have the schedule object, schedule.users will do the same, both going through schedules_users table.

How to update table row from values determined within controller

ruby_on_rails rails 4 assignment non-screen data to insert record
Rather than using screen values (e.g. simple_form_for #user_evaluation_result) to populate the columns to insert a row I need to calculate some of the values in controller.
For example if I have these statements in the controller
….
# which if I had simple_form_for user_evaluation_result would get populated by the screen
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
….
# standard stuff I use for screen derived updates
def user_evaluation_result_params
params.require(:user_evaluation_result).
permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars )
end
How do I assign values to :user_assumption_id etc so that insert works. I have tried all sorts of statements. Alternatively do I use another format instead of calling "def user_evaluation_result_params".
Thanks in advance - Pierre
I'm hoping I've interpreted the question properly...
First, to make sure we're on the same page... The code inside of your user_evaluation_result_params method is using Strong Parameters to create an ActionController::Parameters object for the purpose of protecting your model from unpermitted mass-assignments. So, in general, when you're creating or updating an ActiveRecord object from a form in a view template, you want to use Strong Parameters so that users can't manipulate your form to set attributes that you're not expecting.
That said, if you want to set attributes on an object you don't have to use mass assignment. Here is an example of using one-at-a-time assignment (the opposite of mass-assignment):
obj = MyObject.new
obj.attr_one = "One"
obj.attr_two = "Two"
obj.save
There is nothing wrong with this approach other than that it's kind of a lot of work for the general case. So mass-assignment just saves us from having to do this all the time. But it sounds like this one-at-a-time assignment is what you're wanting in this case. So try something like this:
def create
#user_evaluation_result = UserEvaluationResult.new
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = "some value"
#user_evaluation_result.save
end
Note, instead of setting #user_evaluation_result.user_assumption_id directly, as you asked about, it is preferred to set the actual object association as I did above. Try to keep associations outside of mass-assignment and use object relationships to build up your object graphs.
Or, if you have some attributes coming from a form you can mix and match the two approaches:
def create
#user_evaluation_result = UserEvaluationResult.new(user_evaluation_result_params)
# assuming you have a UserAssumption object instance in `my_user_assumption`
#user_evaluation_result.user_assumption = my_user_assumption
#user_evaluation_result.some_other_attr = params[:user_evaluation_result][:some_other_attr]
#user_evaluation_result.save
end
private
def user_evaluation_result_params
params.require(:user_evaluation_result)
.permit(:evaluation_assumption_id,
:company_listing_id,
:target_share_price_dollars)
end

Resources