Rails 5 - belongs_to throws validation errors when data is valid - ruby-on-rails

Using Rails 5.1.3 with Mongoid 6.2.1, I am getting a validation error when data is valid. Here are the 2 classes I have noticed an issue with so far:
character.rb
belongs_to :user, inverse_of: :characters
belongs_to :bdo_class, class_name: 'BDOClass'
user.rb
has_many :characters, inverse_of: :user
I do user.characters.new(...) with BDO class specified, I save it and it saves.
If I then do a character search and save, eg;
ch = Character.where(:name => 'MyCharacter').first
ch.save!
I get a validation error: User can't be blank, Bdo class can't be blank
If I check ch.user_id myself I can see that the field is correct and valid, it references the ObjectId that it should.
I looked up possible fixes for this issue in Rails 5, all recommended I set optional: true on the classes. However I would like this field to be required. It also doesn't fix the issue because ch.user still returns nil.
Result of Character.where(:name => 'Drahcir').first
#<Character _id: 5a099cbabc80b75e845a997a, name: "Drahcir", name_case: "drahcir", level: "58", ap: 0, aap: 0, dp: 0, is_primary: true, user_id: BSON::ObjectId('5a099b48bc80b75e03a4ac3e'), bdo_class_id: BSON::ObjectId('5a098e86bc80b75998d141c9')>
Result of User.where(:username => 'Nishikin').first
#<User _id: BSON::ObjectId('5a099d6dbc80b75ee6e6d2d7'), guild_master: nil, member: nil, officer: nil, user_case: "nishikin", username: "Nishikin">
Sergio pointed out in the comments that user_id does not match the _id of the parent user. Which leads me to the question of why user.characters.new(...) linked the wrong ObjectId, same goes for the reference to BDOClass.
Any help is greatly appreciated :)

Related

Rails 6: Can I create an associated record to delegate to when a new record is created but not saved?

I have a CustomerProfile model that is associated with Mobile that handles mobile number verification, sms notifications, etc…
When creating the customer profile, I would like to set the user's mobile number as if that column existed on the profile table itself. To this end, I've used delegate. However, the associated mobile record is not created until after the new profile is saved.
class CustomerProfile < ApplicationRecord
belongs_to :user, optional: true
has_one :mobile, autosave: true, dependent: :destroy
delegate :number, to: :mobile, prefix: :mobile
delegate :number=, to: :mobile, prefix: :mobile
after_create -> { create_mobile }
...
end
So, I can't create a new customer profile and set a mobile number. I have to save the record before I set the number.
[1] pry(main)> profile = CustomerProfile.new
TRANSACTION (0.1ms) BEGIN
=> #<CustomerProfile:0x00007fb371201300 id: nil, first_name: nil, last_name: nil, user_id: nil, created_at: nil, updated_at: nil>
[2] pry(main)> profile.mobile_number = "123"
Module::DelegationError: CustomerProfile#mobile_number= delegated to mobile.number=, but mobile is nil: #<CustomerProfile id: nil, first_name: nil, last_name: nil, user_id: nil, created_at: nil, updated_at: nil>
From what I understand from the docs, there isn't a callback for this.
I don't think it is possible because the mobile record needs an id to point to and customer profile doesn't get one until it is saved. Is that right?
Is there a way to create an association when the model is first created without the save step?
You can create associated objects in Rails before save.
For your has_one association it would look something like this
customer_profile = CustomerProfile.new
customer_profile.build_mobile
Now you can use customer_profile object in your form to make input field for taking mobile number or just use customer_profile.save to save customer_profile and mobile objects that have has_one relationship.

RSpec has_many through #<ActiveRecord::Associations::CollectionProxy> on after_save

Heres my relationship model
class Address < ApplicationRecord
has_many :address_aliases, :inverse_of => :address, :foreign_key => :address_id
end
In spec I am building an address with address_aliases. In my after_save of address I have used address.address_aliases.pluck somewhere, and it does not give correct value.
address_aliases = FactoryGirl.build_list(:address_alias, 1, :alias_for_city => "TEST1")
address = FactoryGirl.build(:some_address, :company_id => "test_company", :address_aliases => address_aliases)
byebug
expect ...
address.save!
This is what I get on byebug. address.address_aliases has one element, but when I pluck it returns blank array.
(byebug) address.address_aliases
#<ActiveRecord::Associations::CollectionProxy [#<AddressAlias id: nil, alias_for_city: "TEST1", created_at: nil, updated_at: nil, address_id: nil>]>
(byebug) address.address_aliases.pluck(:alias_for_city)
[]
The problem is that the aliases are not yet persisted and pluck does a database query (see when you inspect address_aliases, the record does not have an ID yet, it's on memory, it's not on the database yet).
Replace that pluck with map(&:alias_for_city) so it doesn't do a database query and uses the already loaded collection.

find a user by model associations rails

I have a model Camera in which
belongs_to :user, :foreign_key => 'owner_id', :class_name => 'EvercamUser'
i have asscociation like this. when i do Camera.first
#<Camera id: 6, created_at: "2013-12-12 17:30:32", updated_at: "2015-11-19 10:19:33", exid: "dublin-rememberance-floor2", owner_id: 4, is_public: true
i can get owner id, is there any way to create such function that , along side getting owner id, i can get the data which linked with this id for example at id = 4
#<EvercamUser id: 4, created_at: "2013-12-12 16:43:46", updated_at: "2015-04-16 15:23:19", firstname: "Garrett", lastname: "Heaver", username: "garrettheaver"
this user is present, what if when i do Camera.first then instead of OnwerID, how can i get the owners Name?
Any help will be appreciated!
how can i get the owners Name
You'd call the associative object on the Camera object:
#camera = Camera.find x
#user = #camera.user
#user.name #-> outputs name of associated user object
... this will allow you to call the attributes of the child object on it: #camera.user.name or #camera.user.email, etc
Off topic, but I always include a reference to delegate for this type of issue; it avoids the law of demeter (where you're using more than one point to access data).
This would allow you to use:
#app/models/camera.rb
class Camera < ActiveRecord::Base
belongs_to :user, foreign_key: :owner_id, class_name: 'EvercamUser'
delegate :name, to: :user, prefix: true #-> #camera.user_name
end
#camera = Camera.find x
#camera.user_name #-> outputs the user's name on the camera object (not user object)
To give you some context, Rails uses ActiveRecord to invoke/create objects for you.
In line with the object orientated nature of Rails, ActiveRecord is known as an ORM (Object Relationship Mapper). This basically allows you to create an object through ActiveRecord, and if it is associated to another (as Rails does with its associations), it will append the associated object onto the parent.
Thus, when you're asking about calling owner_id, you're referring to the foreign_key of the association (the database column which joins the two tables together):
What you need is to reference the associated object, which I've detailed above.
What about using join here?
Camera.all.joins(:evercamusers)
Camera.where(:id => 1).joins(:users).first
Note: I'm a bit unsure if the correct parameter should be ":users" or ":evercamusers"
http://apidock.com/rails/ActiveRecord/QueryMethods/joins
You could also add methods to your class to do this.
class Camera < ActiveRecord::Base
belongs_to :user, :foreign_key => 'owner_id', :class_name => 'EvercamUser'
def firstname
self.user.firstname
end
end
When you try to output data from Camera like this:
#<Camera id: 6, created_at: "2013-12-12 17:30:32", updated_at: "2015-11-19 10:19:33", exid: "dublin-rememberance-floor2", owner_id: 4, is_public: true
It won't show. But if you call the method like this, it should work:
Camera.first.firstname # "Garrett"
Also, if JSON is acceptable you could override the as_json method.
def as_json(options={})
{ :firstname => self.user.firstname }
end
Then call it with
Camera.first.as_json
If you need to do it with all, simply loop it
Camera.all.each { |c| puts c.firstname }

NoMethodError: undefined method `l1' for #<model

I have 2 models, Passenger and Location
class Location < ActiveRecord::Base
has_many :passengers
end
and
class Passenger < ActiveRecord::Base
has_one :location
end
I insert some data in rails console
p1 is passenger record
p1
=> #<Passenger id: 2, name: "Saba", lastname: "las", phone: 1234, created_at: "2015-11-15 13:04:03", updated_at: "2015-11-15 13:04:03", id_location: 1>
and l1 is a location record
l1
=> #<Location id: 1, latitude: 1.4, longitude: 4.5, created_at: "2015-11-15 13:02:00", updated_at: "2015-11-15 13:02:00">
"id_location" is a foreign key for location in Passenger
I did p1.save and l1.save.
i expect p1.l1 gives me the complete data including l1 record like(Location id: 1, latitude: 1.4)
instead it gives me the following erorr:
NoMethodError: undefined method `l1' for #<Passenger:0x000000022b96c8>
Looks like your foreign_key is wrong; Rails default foreign_keys are association_id (not id_association, as you have it).
--
To fix it, you'll either need to set the foreign key explicitly (and call the correct associative object), or rename the id_location column in your table:
#app/models/passenger.rb
class Passenger < ActiveRecord::Base
belongs_to :location, foreign_key: :id_location
end
You should also note the belongs_to association call -
With ActiveRecord, if you have the foreign key in a model, that model needs to belong to another. Using has_one suggests you should put the foreign key in the associated model, which is incorrect in this case.
--
Finally, you also need to make sure you're calling the associated objects by the correct name.
If your Passenger belongs_to Location, you will need to call #passenger.location

When I try to "put" or "<%=" an ActiveRecord (rails) attribute, I get nil, but when I look at the object hash its actually there

So here is an example of a hash for a record of the 'properties' table, the attribute in question being 'owner'
Property.first #=>
#<Property id: 3684, ss_property_id: 1, owner_full_name: "Holliday Associates", owner: "HA",
owners_pctg: 100, tax_map_id: "0460001047", county: "Horry", description: "L.S. Alford", acreage:
131.0, prop_taxes_2009: 180.72, prop_taxes_2010: 173.99, prop_taxes_2011: 172.94, notes: nil,
created_at: "2013-04-03 01:16:23", updated_at: "2013-04-03 01:16:26">
When I do something like this, however
1.9.3p194 :011 > Property.first.owner
Property Load (0.3ms) SELECT "properties".* FROM "properties" LIMIT 1
=> nil
it is nil.
EDIT: here is my model (dramatically over-simplified...)
class Property < ActiveRecord::Base
belongs_to :owner
end
My model ended up having a conflict with the :owner namespace. It actually belongs_to :owner, a new model I haven't even started using yet. The :owner namespace apparently got overwritten by the ActiveRelation method to the Owner model

Resources