I'm trying to implement a two-way has_many :through
association between a User model and a Location model using a UserLocations join table. This will enable setting user locations with built in ActiveRecord methods, ie. @user.locations = [<Location 1>, <Location 2>, ...]
. My goal is to not associate locations to users individually, but rather for users to add and remove them, one or more at a time, via another field: :zip_code
. This means that when a user adds a zip code, ActiveRecord should insert a single record into UserLocations (something like INSERT INTO user_locations (user_id, zip_code) VALUES (1, 12345)
). Then, when @user.locations
is called, ActiveRecord should join by :zip_code
and get the matching location(s). My current implementation works, except that one INSERT
into UserLocations is generated for each location associated with a zip code.
class User < ActiveRecord::Base
has_many :user_locations
has_many :locations, through: :user_locations
end
class UserLocation < ActiveRecord::Base
belongs_to :user
belongs_to :location, primary_key: :zip_code, foreign_key: :zip_code
end
class Location < ActiveRecord::Base
has_many :user_locations, primary_key: :zip_code, foreign_key: :zip_code
has_many :users, through: :user_locations
end
Things I've tried:
validates_uniqueness_of :zip_code, scope: :user_id
- just throws a validation error and prevents all record creation has_many unique: true
- doesn't prevent duplicate DB queries add_index unique: true
for (user_id, zip_code)
- would at least prevent duplicate entries from being created, but I'm trying to prevent unnecessary queries entirely Using questions like this one for guidance hasn't gotten me any closer. Is what I'm trying to do possible without using my own methods to get/set user locations?
First of all, I'm not very experienced in rails yet, but I'll still try to help :)
What I would do is not using zipcodes as a key. When a user inputs zip codes you look up the code in the Location:
@zip_code = Location.where(zipcode: user_input).first
@zip_code.user_locations.create!(user_id #some other stuff you want)
This way you store the id of the location into the user location and no duplicates are made. You can then generate user locations by joining the UserLocation and Location.
But as I said, there may be a better way of doing this as I'm beginner.
Stop me if I'm wrong :)
You have zipcodes in your locations
table (ie: 111, 222, 333) When a user selects a zipcode of '111' for him self, his record is associated with the existing locations
record; but when a user selects a zipcode of '444' a new locations
record is created and link to that user. Next use that selects '444' will be linked to this same record.
If my assumption if correct, you should have:
validates_uniqueness_of :zip_code
(without scope) in your Location
model User
model while creating/updating you could use Location.find_or_create_by(params[:zipcode])
This is pseudo-code (don't copy-paste it), I don't exactly know how your code is writen, but my point is for you to have a look at find_or_create
, I believe it could be your solution
It looks like you have the association setup correctly.
When you have a has_many
association in rails and want to do something like this:
@user.locations = [<Location 1>, <Location 2>, ...]
Rails will create individual INSERT
statements for each location in the array, although it will do a bulk DELETE
for you. If you want it to do bulk INSERT
statements, you'll need to roll your own code or look into the activerecord-import gem to do this.
As for the duplicates, if you are only doing the above code, there shouldn't be duplicate record errors unless there are duplicates in that location array, in which case you should call uniq
on it first.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.