update_all works but update_attributes doesn't - ruby-on-rails

The two codes below are for updating the database.
The first version, which uses the 'update_attributes' property is not working
#existing_exp = Emiexperiment.find(:first, :conditions => [ "EMI_COM_ID = ? and EMI_SUB_FK = ?", "EMI_999", "7789"])
#existing_exp.update_attributes(
:EMI_STATUS => "present",
:EMI_ADD_STATUS => "weak"
)
However, the code below, which uses the 'update_All' attribute seems to work great.
Emiexperiment.update_all "EMI_STATUS = 'present', EMI_ADD_STATUS = 'moderate'", ["EMI_COM_ID = ? and EMI_SUB_FK = ?", "EMI_999", "7789"]
Here is the class code for Emiexpression:
class Emiexperiment < ActiveRecord::Base
set_table_name "EMI_EXPERIMENT"
set_primary_key "EMI_OID"
attr_accessible :EMI_STATUS, :EMI_ADD_STATUS, :EMI_COM_ID, :EMI_SUB_FK
belongs_to :sub, :foreign_key => "EMI_SUB_FK"
end
I am confused as to why this is so.
I am not using any validation in my 'Emiexperiment' model.
Any hint on this is most appreciated. Many many thanks for your help :)

You may want attr_accessor for those fields. attr_accessor is a ruby method that makes a getter and a setter.
attr_accessible is a Rails method that allows you to pass in values to a mass assignment:, e.g. update_attributes(attrs).

Related

Covering belongs_to condition to Rails 5.1

I am upgrading a Rails 3 app to rails 5.1. In Rails-3 model I have a condition like
has_one :current_contract, :class_name => 'Contract',
:conditions => Proc.new { "contract_vw.country = '#{country_id}'"
if country_id }
I guess previous developer follow this hack
https://makandracards.com/makandra/983-dynamic-conditions-for-belongs_to-has_many-and-has_one-associations
I am not sure how to convert it to Rails 5.1
Any help appreciated.
The Rails 4+ way is to write the scope like so:
has_one :account, -> (country_id) { where('contract_vw.country = ?', country_id) }, class_name: 'Contract'
You've written the if country_id into the association though, which seems real weird to me. Although where('contract_vw.country = ?', country_id) if country_id might work, I'd probably extract that into a method like:
def country?
country_id.present?
end
And then, wherever you need it:
#model.account if #model.country?
If I understand your use-case correctly you're not bound to using has_one and in this instance I think it should not be used, use a regular method instead.
def current_contract
contracts.where("contract_vw.country = ?", country_id).first if country_id.present?
# or ...
contracts.find_by(country: country_id)
end

Uniqueness error in has_many nested attributes

I have a class student with has_many tests. The test class has a student_id, marks, name. Here the test name should be unique. The test is a nested attribute for student. So the parameters are this way:
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {:name => "bgc", :marks => "470"}}}
I have a problem with update. If I update_attributes with the tests_attributes, it throws a validation error saying the name for test is not unique. I am actually addressing the same record here. How do I overcome this?
Without seeing your models (& validations), it's going to be quite difficult to diagnose your error directly.
--
Nested Attributes
We've done something like this, and found that your nested data is passed to the child model as if it were receiving a new object (without being nested). This means if you've got validates uniqueness for that model, it should be okay:
#app/models/test.rb
Class Test < ActiveRecord::Base
belongs_to :student
validates :name, uniqueness: true
end
Reason I write this is because there's a method called inverse_of, which basically allows you to access the parent model data in your child model
--
Update
I think the problem will likely lie with your use of update_attributes. Problem being you're trying to update both the student and the test attributes at one time.
I'm not sure exactly why this would be a problem, but I'd test this:
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.test.update(name: params[:test_name], marks: params[:marks])
end
end
I think if you can explain your methodology a little more, it will be much more helpful. I.E are you trying to update student or test? If you're updating student & adding a new test, how are you updating the studet?
Thanks for the reply guys. I ended up finding the answer myself. I did have a uniqueness validation for name.
I had a situation where initially I wouldn't know the student but have only his details. So I would have to create this hash and pass it to update. The trick to not trying to create a new record for the same name in test is to pass the actual record's ID along with it. This solved the problem
Nested Attributes
I think the problem with nested_attributes. For update need to pass nested_attributes with ID.
Ex.
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {id: 1, :name => "bgc", :marks => "470"}}}
I have tried below-given example it is worked for me:
Update
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.update_attributes(student_params)
end
private
def student_params
params.require(:student).permit(:first_name, :email,
tests_attributes: [:id, :name, :marks])
end
end

Issue with collection `build` method record instantiation in Rails 4.1.1

I'm having a problem with the Rails collection.build(attrs) method, specifically with how the framework instantiates a new record. For example, here is a much simplified view of my models:
class Document < ActiveRecord::Base
belongs_to :account
has_many :descriptions, before_add: :id_test
validates :account, presence: true
def id_test
puts self.account_id
end
end
When I do something like:
current_account.documents.build(:descriptions => [desc])
then id_test prints nothing. That is, the account_id is not set in the before_add callback (and yes, I've tried the after_add callback as well; account_id is not set in that case either).
If I do:
d = current_account.documents.build
d.assign_attributes(:descriptions => [desc])
Then everything works as expected. However, I would prefer a better alternative, since this would be a pain to implement in the controllers...
Is there a way to get Rails to add the foreign_key first, or is there some better way to set this up? I haven't gone back to check for sure, but this seems different than the way Rails 3 evaluated collection.build statements.
EDIT
Looking through the Rails code a bit, I see I can do:
current_account.documents.build do |record|
record.assign_attributes(:descriptions => [desc])
end
and everything works as expected. Although a bit more verbose, I guess this is technically more accurate anyway.
I would suggest using
Document.build(:account_id => current_account.id, :descriptions => [desc])

Rails passing params

I feel like I'm missing a fundamentally easier way to do this; either way, I don't appear to have the syntax for array figured out. Trying to stuff things into the params array. Any help is appreciated.
#user = User.find(params[:user][:id])
array_of_match_information = Array.new
array_of_match_information[mentee] = #user.id
array_of_match_information[mentor] = self.id
array_of_match_information[status] = "Pending"
#match = Match.new(params[:array_of_match_information])
Thanks.
array_of_match_information = Hash.new
array_of_match_information[:mentee] = #user.id
array_of_match_information[:mentor] = self.id
array_of_match_information[:status] = "Pending"
EDIT
Hash is a key/value storage, like you intend to do.
mentee is a key that will be associated to a value #user_id
Array don't organize data (unless you consider the position in the Array is known and meaningful)
EDIT2:
And correct this:
#match = Match.new(array_of_match_information)
EDIT3:
I encourage you to have a look at http://railsforzombies.org, it seems you need a good tutorial.
Actually, building an app when you're learning could be hazardous because when you don't know basic architecture, you end up overcoding unmaintainable code.
For instance, your line:
array_of_match_information[:mentor] = self.id
seems really weird.
It seems, you're trying to implement a basic social network functionality. If I'm right, you should use associations. It would look something like this (I don't know the specifics of your mentor-mentee relation, so I suppose it's a many-to-many relationship):
class User < ActiveRecord::Base
has_many :matches
has_many :mentors, :through => :match
has_many :mentees, :through => :match
end
class Match < ActiveRecord::Base
belong_to :mentor, :class_name => 'User'
belong_to :mentee, :class_name => 'User'
end
Then, in your controller you could do this:
class matches_controller < ApplicationController
def create
# current_user is a Devise helper method
# which simply returns the current_user through sessions.
# You can do it yourself.
Match.create({ :mentee => #user, :mentor => current_user })
# "pending" status could be set up as a default value in your DB migration
end
end
But as I said, that's just a sample of code. I can't guarantee that it will work or suit your applications.
And you totally should check out this book
I'm not 100% sure what you're trying to do but at the very least, you should be using symbols when you set the 3 values:
array_of_match_information[:mentee] = #user.id
array_of_match_information[:mentor] = self.id
array_of_match_information[:status] = "Pending"
edit:
You should actually be doing this:
match_information = {}
match_information[:mentee] = #user.id
match_information[:mentor] = self.id
match_information[:status] = "Pending"
Without seeing your model, it's hard for me to know, but I suspect it actually wants the hash, not an array.

Custom getters in Ruby on Rails

I have a MailingList model that has_may :people
For most of my application, I only want to get people that are active
So #mailing_list.people should only return people that are active
In my model, I can't do
def people
self.people.find_all{ |p| !p.activated_at.nil? }
end
because that keeps calling itself. What is the ruby/rails way to automatically filter the people. Another possible issue is that I think self.people returns an array of active record objects where self.people.find_all... will return an array. This will cause some of my code to break. It's easy fixes but is there a way to return active record objects? It would be nice to have the option.
Thanks!
This is a perfect example for a named scope:
class Person < ActiveRecord::Base
named_scope :active, :conditions => 'activated_at is not null'
end
Then just call it:
# equivalent to Person.find(:all, :conditions => 'activated_at is not null')
#active_people = Person.active
You can also filter at the association level.
has_many :people, :conditions => {:activated => true}
You can used the standard find method or a dynamic finder. Your find might read as follows:
people.find(:all, :conditions => "activated_at = nil")
OR
people.find_all(:conditions => "activated_at = nil")
A dynamic version of this might read as:
people.find_by_activated_at(nil)

Resources