Rails creates many objects for has_one relationship - ruby-on-rails

I've got two models in my Rails 5 app - User and Login with below association:
class User < ApplicationRecord
belongs_to :login, optional: true
end
class Login < ApplicationRecord
has_one :user
end
I thought such an association would prevent the Login from having more than one User but it turns out that in a case when the Login.last already has associated user object:
2.4.5 :108 > Login.last.user
=> #<User id: 1, type: "Registrant", login_id: 43, first_name: "test", last_name: "test", created_at: "2022-02-11 11:22:25", updated_at: "2022-02-11 11:22:25">
You can still create a new user with the same Login:
User.create(first_name: 'test2', last_name: 'test2', login_id: 43
=> #<User id: 2, login_id: 43, first_name: "test2", last_name: "test2", created_at: "2022-02-11 12:03:36", updated_at: "2022-02-11 12:03:36">
But when you are trying to fetch last login user you will get the first created user:
Login.last.user
=> #<User id: 1, type: "Registrant", login_id: 43, first_name: "test", last_name: "test", created_at: "2022-02-11 11:22:25", updated_at: "2022-02-11 11:22:25">
How to prevent creating a new user with a login that has already been used once?

You can use validation to say that a login just can be use by one user.
This will look like this in model User:
validates :login_id, uniqueness: true

Related

Load related model in rails 7

I'm new in ruby rails and face some difficult to achieve something.
Now I have a product model:
class Product < ApplicationRecord
belongs_to :category
And category model:
class Category < ApplicationRecord
has_many :products
When I call the product like so: #products.inspect i wan to see the category belongs to the product. My output now looks like so:
<ActiveRecord::Relation [#<Product id: 1, name: "Adidas", description: "Adidas is a Dutch multinational corporation that i...", price: 100, created_at: "2022-08-23 10:02:11.110159000 +0000", updated_at: "2022-08-23 10:02:11.114384000 +0000", category_id: 1>, #<Product id: 2, name: "asd", description: "12easd", price: 12, created_at: "2022-08-24 09:35:38.839809000 +0000", updated_at: "2022-08-24 09:35:38.847999000 +0000", category_id: 1>]>
Getting the product.category working fine, but, I need also the name of the category attached to this output. I need this because the product is sent to a JavaScript function and I need to call the category name from there.
What I already try is the follow:
def index
#products = Product.includes(:category).all
end
Thanks in advance.
I found it!
#product.to_json(include: :category)
And now I get the related category model

rails check_box_tag to include existing values of from array of records

The following tag in a nested form
<%= check_box_tag "friend_ids[]", ff.id, #contentrestrictions.friends.include?(ff.id) %>
is handling the following array of records
>> #contentrestrictions
[
#<Contentrestriction id: 29, usercontent_id: nil, friend_id: nil, created_at: "2019-04-28 10:55:32", updated_at: "2019-04-28 10:55:32">,
#<Contentrestriction id: 30, usercontent_id: nil, friend_id: 2, created_at: "2019-04-28 10:55:32", updated_at: "2019-04-28 10:55:32">,
#<Contentrestriction id: 31, usercontent_id: nil, friend_id: 4, created_at: "2019-04-28 10:55:32", updated_at: "2019-04-28 10:55:32">]
Even though
class Contentrestriction < ApplicationRecord
belongs_to :friend, optional: true
#contentrestrictions. followed by any of friend_id, friend_ids both appended with or without [] all lead to NoMethodError: undefined method for Array.
how can this include function get a proper array to work with?
the issue is in this line:
#contentrestrictions.friends.include?(ff.id)
you are comparing a friend object with a friend id, you could use pluck to get the friend ids, or you could just compare the objects:
#contentrestrictions.friends.include?(ff)
this will make a query for the .friends and you could remove this query by pre-loading the friends association, e.g. eager_load(:friends) or includes(:friends)

Rails - how to automatically uniq joins method?

This question is based on this: Rails, why joins returns array with non-uniq values?
Let say I get non uniq array by .joins() method:
City.joins(:locations)
# => [#<City id: 5, name: "moscow", created_at: "2010-07-02 15:09:16", updated_at: "2010-07-02 15:09:16">, #<City id: 5, name: "moscow", created_at: "2010-07-02 15:09:16", updated_at: "2010-07-02 15:09:16">, #<City id: 5, name: "moscow", created_at: "2010-07-02 15:09:16", updated_at: "2010-07-02 15:09:16">, #<City id: 5, name: "moscow", created_at: "2010-07-02 15:09:16", updated_at: "2010-07-02 15:09:16">]
I can make records uniq by using
City.joins(:locations).group('cities.id') # or simpler
City.joins(:locations).uniq
# => [#<City id: 5, name: "moscow", created_at: "2010-07-02 15:09:16", updated_at: "2010-07-02 15:09:16">]
How can I make .joins() method returns uniq records by default?
You could try overriding the .joins method for the models you need, but I would suggest just writing a scope, e.g.
scope :unique_locations, -> { joins(:locations).uniq }
Then just call City.unique_locations. It's cleaner and more readable that way.
Generally overwriting methods should be done only when you're sure you won't need it 'the old way', and it makes sense. Plus, when you say City.joins(:locations) the reader expects default behaviour, and returning something else will cause chaos and confusion.
You can define has_many macro, with the stubby lambda as an argument:
has_many :locations, -> { joins(:locations).uniq }
Also you can define own AR relation method, it stil use a simple has_many macro.
has_many :locations do
def only_uniq
joins(:locations).uniq
end
end
Now use it:
c = City.find(123)
c.locations.only_uniq
It does the same thing as scope or lambda in has_many.

Deleting records from HABTM association

I'm trying to do something fairly simple. I have two models, User and Group. For simplicity's sake, let's say they look like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
and
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
Now, for some reason, I have a user that has the same group twice. In the Rails Console:
user = User.find(1000)
=> #<User id: 1000, first_name: "John", last_name: "Doe", active: true, created_at:
"2013-01-02 16:52:36", updated_at: "2013-06-17 16:21:09">
groups = user.groups
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">, #<Group id: 1, name: "student", is_active: true,
created_at: "2012-12-24 15:08:59", updated_at: "2012-12-24 15:08:59">]
user.groups = groups.uniq
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">]
user.save
=> true
And there is some SQL output that I've silenced. I would think that everything should be all set, but it's not. The groups aren't updated, and that user still has both. I could go into the join table and manually remove the duplicates, but that seems cludgy and gross and unnecessary. What am I doing wrong here?
I'm running Rails 3.2.11 and Ruby 1.9.3p392
Additional note: I've tried this many different ways, including using user.update_attributes, and using group_ids instead of the groups themselves, to no avail.
The reason this doesn't work is because ActiveRecord isn't handling the invalid state of duplicates in the habtm association (or any CollectionAssociation for that matter). Any ids not included in the newly assigned array are deleted - but there aren't any in this case. The relevant code:
# From lib/active_record/associations/collection_association.rb
def replace_records(new_target, original_target)
delete(target - new_target)
unless concat(new_target - target)
#target = original_target
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
"new records could not be saved."
end
target
end
The 'targets' being passed around are Arrays of assigned records. Note the call to delete(target - new_target) is equivalent in your case to delete(user.groups - user.groups.uniq) which results in an empty Array passed (since comparison is based on the id attribute of each record).
Instead, you'll need to clear out the association and then reassign the single group again:
group = user.groups.first
user.groups.clear
user.groups << group
This might be a way to cleanup those duplicates (it handles any number of groups of duplicate associations):
user = User.find(1000)
user.groups << user.groups.group_by(&:id).values.find_all {|v| v.size > 1}.each {|duplicates| duplicates.uniq_by! {|obj| obj.id}}.flatten.each {|duplicate| user.groups.delete(duplicate)}

Rails, Mongoid: use custom key and custom key format

I have model Account:
class Account
include Mongoid::Document
include Mongoid::Timestamps
...
end
I want to use specific ids with specific format. I want id to be 16-digits instead of 4ceede9b5e6f991aef000007, something like that: 1111222233334444.
What is the best practice to do it?
If the id is a simple number, try:
class Account
include Mongoid::Document
include Mongoid::Timestamps
identity :type => Integer
end
account = Account.new :id => 1111222233334444
#=> #<Account _id: 1111222233334444, created_at: nil, updated_at: nil>
account.save
#=> true
account
#=> #<Account _id: 1111222233334444, created_at: 2010-11-26 00:48:27 UTC, updated_at: 2010-11-26 00:48:27 UTC>
Account.count
#=> 1
Account.first
#=> #<Account _id: 1111222233334444, created_at: 2010-11-26 00:48:27 UTC, updated_at: 2010-11-26 00:48:27 UTC>
If you want to use letters in the id too, you can do identity :type => String instead.

Resources