Sorting associated objects based on the association's creation date - mysql

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"

Related

Ruby on rails data fetching issue

Hello i'm new to rails
I have a table named 'messages' which has columns current_user__id , to_user_id and created time
I am trying to build a chat application where different users can chat individually and those message will be stored at messages table with their respective ids.
Now in order to print the messages on screen.
I'm facing issues
I need a query such that both the current_user__id and to_user_id conversations and to_user_id and current_user__id conversation will be listed by the latest created time.
i will assume that you have two ActiveModel's: User and Message. Make sure that you have classes like:
class User < ApplicationRecord
has_many :messages
end
class Message < ApplicationRecord
belongs_to :current_user, class_name: 'User', foreign_key: 'current_user_id'
belongs_to :to_user, class_name: 'User', foreign_key: 'to_user_id'
end
A small trivia when you add t.timestamps to your migrations it creates created_at and updated_at fields for you.
Now I will just hard code the raw sql query for you:
def get_messages(current_user_id, to_user_id)
#messages = Message.where(' current_user_id=? OR receiver_user_id=? OR current_user_id=? OR receiver_user_id=? ',
current_user_id, current_user_id, to_user_id, to_user_id).order('created_at DESC')
end
You can play with order('created_at DESC') in order if you just want in ascending order you can replace DESC with ASC or order(:created_at)
You may put any other query conditions also like not showing deleted messages etc. You can learn more from official Ruby on Rails document for Active Record Query Interface.

Using form to update has_many through join table

I am short of implementation ideas for my rails project. at this point it seems impossible to implement in rails.
I am cloning a sort of accounting software into a web app using rails framework for small manufacturing companies to keep track of their products stock in their different distribution branches.
I have 3 different models: "Product", "Branch" & "Accumulator"
class Branch < ActiveRecord::Base
has_many :accumulators
has_many :products, :through => :accumulators
end
class Product < ActiveRecord::Base
has_many :accumulators
has_many :branches, :through => :accumulators
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
end
end
class Accumulator < ActiveRecord::Base
belongs_to :product
belongs_to :branch
end
I am new to rails and after reading up on many to many associations I came across using collections to add products to a branch "#branch.products << Product.all"
Is there a possible way to use a form "number_field_tag" in the branch show view to add multiple of specific products into the join table?
eg
I want to add 10 of (Product) called "garden eggs" to a (Branch) called "lagos branch" to the (Accumulator) join table using a form in Branches show view?
Congratulations for choosing :has_many, through: you will not regret it.
Is the relationship between a product and a branch static?
Or does it change a lot?
In your Accumulator model, add an integer field called amount (count can have conflicts). Then you either create a form for your Accumulators or you add a nested form with for example Cocoon.
This way you can add Accumulators to your Branch with a certain Product and a certain amount.
Off topic:
Here is an article about why has_many through has some advantages:
http://blog.flatironschool.com/why-you-dont-need-has-and-belongs-to-many/
If you have problems with forms I can really recommend SimpleForm and for nice Javascript-assisted fields I recommend Select2.
If the table accumulators needs to save only two things: product_id, and branch_id, you can easily use has_and_belongs_to_many associations.
class Branch < ActiveRecord::Base
has_and_belongs_to_many :products, join_table: 'accumulators'
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :branches, join_table: 'accumulators'
end
And now, there is no need for the third model.
As far as it goes, how to add a relationship, it's pretty easy in this case:
branch = Branch.last
branch.products << Product.create # you don't need to touch the middle table.
Instead of using number_field_tag to ask for plain ids, you can use something fancy like jQuery Chosen Plugin. This plugin will allow you to use tag like input, and will send the ids to the server separated by ,'s.

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.

Save model id as foreign key through has_one / belongs_to

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

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.