Mongoid belongs_to specify foreign_key - foreign-keys

I'd like to set up a relationship between two Mongoid models with has_many and belongs_to and specify a foreign key, like so:
class Author
include Mongoid::Document
field :serial_num, :type => Integer
field :author_name, :type => String
has_many :books
end
class Book
include Mongoid::Document
field :serial_num, :type => Integer
field :book_name, :type => String
belongs_to :author, foreign_key: 'serial_num'
end
This doesn't work, however. My IRB output:
irb :001> b = Book.first
=> #<Book _id: 1, serial_num: "12345", book_name: 'something', author_id: nil>
irb :002> b.author
=> nil
Is it possible to specify 'serial_num' as a foreign key for this relationship, or am I stuck with author_id?
Many thanks.

You need to set both foreign_key and primary_key given your field definitions. The code you gave does respect primary_key but on author this value is looked up in _id, not serial_num. See the documentation for further details and examples.
belongs_to :author, foreign_key: 'serial_num', primary_key: 'serial_num'

I know this is an old post, but I'm guessing it has to do with the field type the foreign key is stored as. If it's not a BSON ObjectId, then it may not return anything.

Related

ActiveAdmin Autocomplete

I have a model called LetterResponse
belongs_to :user
LetterResponse have following field (just shortening my model)
:id
:title
:user_id
while admin creates new letter_response he will fill the title and when he types user name it should autofill
I have 100k users so can't do
f.input :response_user_id, :as => :select, :collection =>
User.all.collect {|user| [user.first_name, user.user_id] }, label: "Parent"
even choozen-rails gem does same pre loads data.
so tried with activeadmin-addons ajax-search by following
https://github.com/platanus/activeadmin_addons/blob/master/docs/select2_search.md
f.input :response_user_id, as: :search_select, url: admin_users_path,
fields: [:user_last_name], display_name: 'name',
minimum_input_length: 2
but I get an error:
undefined method response_user
How to proceed?
Shouldn't your input field be :user_id instead of :response_user_id, (becuase you mention your LetterResponse has a belongs_to :user relation (not belongs_to :response_user.
So, it should look like
f.input :user_id, as: :search_select, url: admin_users_path, fields: [:user_last_name], display_name: 'name', minimum_input_length: 2

Polymorphic Relationships in ActiveAdmin Forms in Rails 4

I have an Active Admin form where I need to be able to update/add a polymorphic relationship to an object. I can get the form to display it, but it won't update the table with the polymorphic relationship. The models are Category and TargetArea and they both have Tags. Here is the model setup:
#category.rb
class Category < ActiveRecord::Base
has_many :tags, as: :taggable
accepts_nested_attributes_for :tags
end
#tag.rb
class Tag < ActiveRecord::Base
belongs_to :category
belongs_to :target_area
belongs_to :taggable, polymorphic: true
accepts_nested_attributes_for :taggable
end
#Active Admin Form for Categories
permit_params :name, tags: []
form do |f|
f.actions
f.inputs 'Categories' do
f.input :name
f.inputs do
f.has_many :tags do |t|
t.input :name
end
end
end
f.actions
end
I want to be able to update and create new categories and add tags to the category in the form. I can't seem to find an example that does the same thing and this just doesn't seem to work.

Active Record join, SUM and group_by on votes given

I'm using rails 4.2 and ruby 2.2
I have the following three models
Entrant (Devise User)
Entrant.column_names
=> ["id", "email", "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at", "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "created_at", "updated_at", "confirmation_token", "confirmed_at", "confirmation_sent_at", "unconfirmed_email", "entrant_name"]
class Entrant < ActiveRecord::Base
has_many :entries
has_many :votes, :through => :entries
end
Entry
Entry.column_names
=> ["id", "entrant_id", "title_of_work", "price", "created_at", "updated_at", "media_file_name", "media_content_type", "media_file_size", "media_updated_at", "paid", "height", "width", "type_of_media"]
class Entry < ActiveRecord::Base
belongs_to :entrant
has_many :votes
has_many :entrants, :through => :votes
end
Vote
Vote.column_names
=> ["id", "entry_id", "judge_id", "score", "created_at", "updated_at"]
class Vote < ActiveRecord::Base
belongs_to :entry
belongs_to :judge
end
I need a query that will return the following table. I've tried starting with Entrant and joining Votes but nothing has worked.
The query needs to pull out all entries, and sum the votes.score field and order by votes.score but it also needs to group by entry so that the entries are not duplicated.
This is as close as I have got, but it doesnt work.
#entries = Entrant.joins(:entries).joins(:votes).select('entrants.*, entries.*, SUM(votes.score) as total_votes').distinct.group('entrants.id, entries.id, votes.score')
It should be something like:
Entry.joins(:entrant).joins(:votes)
.group('entries.id')
.order('SUM("votes.score")').
.pluck(:entrant_name, :title_of_work, 'SUM("votes.score")'),'COUNT("votes.score")')
I worked this out in the console with a similar set of relationships I have set up so it may need a bit of tweaking but it should get you pointed in the right direction. Let me know if you get stuck.

has_many relationship with two foreign keys

I have a relationship in which I'm trying to reference two different foreign keys. Excuse the lame example here, but I'd like to do something like the following:
class Account
has_many :orders,
-> { where('orders.account_id IS NULL OR orders.account_id = ?', self.id ) },
primary_key: :user_id, foreign_key: :user_id
end
However the dynamic value in the where clause is not correct, throwing a 'NoMethodError: undefined method `id' for ActiveRecord_Relation'.
Any ideas on how to reference the parent record within the where clause?
You don't have a record to call .id on. I guess that this is what you are looking for:
class Account
has_many :orders,
-> { where('orders.account_id IS NULL OR orders.account_id = accounts.id') },
primary_key: :user_id, foreign_key: :user_id
end
I believe I just came across the answer. By providing a property to the lambda, rails will provide the record.
class Account
has_many :orders,
->(record) { where('orders.account_id IS NULL OR orders.account_id = ?', record.id ) },
primary_key: :user_id, foreign_key: :user_id
end

find_or_create_by using mongoid and sidekiq leads to duplication

Rails 4.0.2, Mongoid git alpha, sidekiq 2.17.0
Using a worker to parse a CSV file and put the contents into mongo, but running into several issues... Most obvious is a product with the same name end up as duplicate documents while using find_or_create_by.
class Category
include Mongoid::Document
field :title, type: String
has_many :products
end
Class Product
include Mongoid::Document
field :name, type: String
belongs_to: :category
end
Class ProductWorker
include Sidekiq::Worker
def perform(category, name)
category = Category.find_or_create_by( title: category )
product = Product.find_or_create_by(name: name)
product.category = category
product.save
end
end
With a CSV file with only two products in the same category, I'll end up with two entries for category with the same name, each product in separate categories. When I remove sidekiq async and perform it on the model directly I get the correct result of 1 category with two relational products.
It makes sense that if the workers perform the find_and_create_by fast enough, both workers will find nil and thus create new objects. How can I prevent this?
Uniqueness validation is not enough, because they are defined at model level by Mongoid.
The only way to guarantee uniqueness is defining unique indexes:
class Category
include Mongoid::Document
field :title, type: String
has_many :products
index({ title: 1 }, background: true, unique: true)
validates_uniqueness_of :title
end
Looks like it's missing a uniqueness validation.
class Category
include Mongoid::Document
field :title, type: String
has_many :products
validates_uniqueness_of :title # Won't duplicate records
end
You can also use a different, more accurate querying:
Category.where(title: category).first_or_create!
This way you can even rescue if something else goes wrong.