Save model id as foreign key through has_one / belongs_to - mysql

I'll briefly explain my situation: I have a model called "tax" which belongs_to a "user" and of which a "user" has_one.
In my users table I have a column called "tax_id" which I want to store the id of the tax model when a user creates one.
Currently, in my tax model the create function looks something like this:
class Tax < ActiveRecord:Base
belongs_to :user
tax = Tax.new(income: income, taxes: taxes, rrsp: rrsp)
tax.save
and then in the taxes_controller file, the create function looks like this:
def create
#tax = Tax.new(secure_params)
#tax.user = User.find(current_user.id)
if #tax.save
redirect_to show_tax_path(current_user.tax)
else
render :new
end
end
(secure_params) being the strong parameters for the field inputs set in a private definition.
Now, someone mentioned that I may have better luck if I use build but unfortunately I couldn't get it to work at all, something to do with how I'm using current_user (devise). Currently, my setup works just fine, other than saving the tax model id in the user model column "tax_id" like I said.
I'm wondering if I perhaps need to add a foreign ID key to either the belongs_to or has_one statement, even though I was under the impression that the "link" should be made automatically as long as the column was named "[model]_id"

try using
user.build_tax
I think this might help you out.
The build syntax for has_many association:
user.taxes.build
The build syntax for has_one association:
user.build_tax # this will work
user.tax.build # this will throw error

Related

Elixir Ecto: Using a Pivot Attribute in a Many To Many Relationship

I am currently having troubles figuring out how to use pivot attributes in an Ecto many-to-many relationship.
I found the following question but unfortunately nobody had an answer: https://stackoverflow.com/questions/37158184/elixir-ecto-pivot-many-to-many-table-attributes
Basically I need the same setup as mentioned in the question. Two models and I need to store data to the pivot entry.
Has anybody a solution for this problem?
Thanks in advance!
I am doing something like the following in one of my applications
def Foo do
schema "foos" do
field :name, :string
has_many :bars_foos
end
end
def Bar do
schema "bars" do
field :other, :integer
has_many :bars_foos
end
end
def BarFoo do
schema "bars_foos" do
field :size, :integer
belongs_to :bars
belongs_to :foos
end
end
This uses has_many and belongs_to instead of many_to_many, but it accomplishes something very similar. You can also still use the many_to_many with through if you need a direct link to the other dataset.

Combining data from two tables in rails

I have two models, one belongs to the other. They look like this:
class LittleClass < ActiveRecord::Base
has_many :little_class_sessions
end
and
class LittleClassSession < ActiveRecord::Base
belongs_to :little_class
end
LittleClassSession has a column called little_class_id. I want to get all LittleClassSession but also have the associated LittleClass returned to me in the same hash.
Is there some way to do this that's built into Rails? Or is there a clean way to do this?
And is this something that I build into the LittleClass or LittleClassSession model with scope?
When you query ActiveRecord you will get an array of ActiveRecord:Relation. It is a specific entity which starts your query. You can of course join dependent tables (as in your example with one-to-many relation). But you will still need to go over those dependent relations to build whatever object you need.
Here is a sketch of what I mean (assume we search for all little class sessions with specific little class id):
class_sessions = LittleClassSession.includes(:little_class).where(:little_classes => {:id => 1})
class_sessions.each do |relation|
test_hash = relation.attributes.merge!({:little_class => relation.little_class.attributes});
puts test_hash
end
test_hash will include all the attributes of the little class session as well as attributes of the little class under corresponding key.

Sorting associated objects based on the association's creation date

Right now, I'm working on a simple app. It requires to get the associated objects ordered by the date that they we're added to the object. For that, I want to order them based on the pivot-table's id.
My app looks a bit like this:
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
However, when a user wants to buy a product, I would add a new relation into the pivot table courses_users. When I then run #product.users, I will get them back in the order the users where created, not added as the relation.
I've tried creating a query scope, but it didn't work. I also tried to create a order on the has_and_belongs_to_many, as such:
has_and_belongs_to_many :users, order: 'course_users.id ASC'
But none of that seemed to work, no ORDER statement could be found in the logs.
Add the created_at field to your table.
rails g migration AddTimestampsToCourseUsers created_at:datetime
then you can
#product.users.order "course_users.created_at ASC"

Preventing duplicates via a custom foreign key in has_many :through

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 (i.e: 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
in your 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.

Ruby on Rails/Activerecord mySQL modeling

This is a pretty simple question really, but let's say I'm creating a model for Person. Person obviously has first name, last name, age, etc. But the person also has contact info consisting of things like address line 1, address line 2, city, state, zip, country, phone 1, phone 2, etc...
Does it make more sense to create a model for Person and then list that contact information as tables in the model or to also create, say, a ContactInfo (or Address, etc) model, then associate the Person to ContactInfo through an association (Person has_one ContactInfo/Person has_one Address/Address belongs_to Person, etc)?
Which of these is a better approach and what are the benefits/drawbacks to each method?
edit: in re to j..
So with this approach, would I have to then create an Addressable model?
script/generate model Addressable
class Addressable < ActiveRecord::Base
#stuff here?
end
or is this unnecessary?
Also, would i need to add this line to the create_users.rb:
t.references :addressable, :polymorphic => true
I feel like I'm missing something, but I'm not sure what. I appreciate the help a ton, btw! Thanks!
I'd create separated tables/models for address, phone and stuff like this and would make them polymorphic. Like this:
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
class Person < ActiveRecord::Base
has_one :address, :as => :addressable
end
I believe this is the best way because later you may need to add, for example, a Company model and it'll be easy to make it addressable.
Edit
Using the address as example, you'd need an Address model, not Addressable.
And you'll have to add
t.references :addressable, :polymorphic => true
or
t.belongs_to :addressable, :polymorphic => true
to your create_addresses migration, so you'll have the addressable_id and addressable_type in the addresses table.
Let me know if you have any other doubts :]
the answer above makes sense, but think about how many fields you need and how many records you have to manage. creating a table for each additional field may be too much effort.
another approach could be something more flexible: create a table (say, person_details) with 3 fields: person_id:integer, field_name:string, field_data:string, then the model:
class PersonDetail < ActiveRecord::Base
belongs_to :person
end
this way you can add whatever additional field you need: phone1..phoneN, address1..addressN, and so on.
another similar approach is to pre-determine fields names, to avoid different labels during inserts:
class PersonDetail < ActiveRecord::Base
belongs_to :person
FIELD_NAMES => { 'Address' => 1, 'Phone' => 2)
end
in this case you'll declare the field_name as integer (because it stores only the value of the hash, not a string).