External database association in Rails . Update data - mysql

I have the following problem:
I have two models , that are connected to an external database (Mysql)
The right name of the two tables in the external database are:
f_aziende and f_partecipanti (it's italian).
In my Rails app i created two models to connect to these tables and i called:
formation_db and reference_db.
Here my models code:
class ReferenceDb < ActiveRecord::Base
establish_connection "#{Rails.env}_db2"
self.table_name = "f_partecipanti"
self.primary_key = 'id'
belongs_to :formation_db
end
class FormationDb < ActiveRecord::Base
establish_connection "#{Rails.env}_db2"
self.table_name = "f_aziende"
self.primary_key = 'id'
has_many :reference_dbs , :foreign_key => "id_azienda"
end
The problem is that i'm not able to update all the rows associated to formation_db. Also , i'm not sure if the association of the two models is correct. I mean , in this case what is the convenction of the rails words?
In my others project i usually write:
#company.update_attributes(params[:company])
and i update all the things related to company in one line of code.
How can i create something similar to this in my case?

You're walking perilously close to the line of "multi tenancy", which is why you're running into difficulty I think.
Anyway, I found there's a way to connect to other DBs without having to redclare it in each model:
#app/vendor/db.rb
class Db < ActiveRecord::Base
establish_connection "#{Rails.env}_db2"
end
#app/models/reference_db.rb
class ReferenceDb < Db
self.table_name = "f_partecipanti"
belongs_to :formation_db
end
#app/models/formation_db.rb
class FormationDb < Db
self.table_name = "f_aziende"
has_many :reference_dbs , :foreign_key => "id_azienda"
end
This will at least set up your models succinctly.
--
These models will work like any other one in Rails. The only caveat is that you cannot join across different databases; IE you cannot have a has_many :through with the databases aforementioned.
#company = Company.find params[:id]
#company.update_all ....

Related

Rails query with has_many relation in a list of [types, ids]

I have models with variables (many model classes : polymorphic relation), and constraints between variables (variables are not necessarily in the same model).
I try to make a query to find all constraints associated to a list of models (with all vars associated to the models in list), and I really don't know how to do it.
My models looks like this.
class Model1 < ApplicationRecord
has_many :vars, as: :model
end
class Model2 < ApplicationRecord
has_many :vars, as: :model
end
class Var < ApplicationRecord
belongs_to :model, polymorphic: true
# model_type and model_id in vars table
has_many :cns_vars
has_many :constraints, through: :cns_vars
end
class_CnsVar < ApplicationRecord
belongs_to :var
belongs_to :constraint
end
class Constraint < ApplicationRecord
has_many :cns_vars
has_many :vars, through: :cns_vars
end
To find constraints related to one model I have this query :
Constraint.includes(:vars).where(active: true, vars: {model_id: model.id, model_type: model.class.to_s})
This query give me the constraints that have at least one var associated to my model.
I need constraints with all vars associated to a list of models.
Is there a way to make the same query, but with all vars associated to the model ?
Is there a way to make the same query, but with all vars associated to a list of models ?
Constraint.includes(:vars).where(active: true, vars: {*[var.model_type, var.model_id] in my models list*})
Is there a solution to do this with one query ?
Or do I have to do it another way ?
Thanks for your help.
(ruby : 2.6.0 / rails : 5.2.3)
EDIT :
To give better explanation, look at this function that returns what I need, but this make too much queries !
def constraints_for_models_list(models)
all_vars = models.flat_map(&:vars)
all_constraints = all_vars.flat_map(&:constraints)
all_constraints.uniq!
constraints = []
all_constraints.each do |constraint|
next unless constraint.vars.included_in?(all_vars)
constraints << constraint
end
return constraints
end
Constraint.includes(:vars).where(active: true).where.not(vars: { model: nil })
of course if I correctly get the point of what you're trying.
for what you asked in comment:
Constraint.includes(:vars).where(active: true).where('vars.model_type IN
?', ['Model1',Model2'])

Rails: active-records query for entry in range & included in

I am working on a shipping implementation for a checkout process.
My app has carts, cart_items, orders and order_items.
Weight and size of all items are in the database and I calculate total_weight in the order and cart models. I also have a shipping_service model with weightmin and weightmax for each shipping service + a postzone and land (country) model.
Now I would like to show on the shopping cart page only the shipping services which are conform to the weight of the cart or order.
I suppose my carts_controller should be something like:
class CartsController < ApplicationController
def show
#cart = Cart.find(params[:id])
#lands = Land.find(:all)
#shippingservices = Shippingservice.where('#cart.total_weight BETWEEN ? AND ?', :weightmin, :weightmax)
end
My cart model is:
class Cart < ActiveRecord::Base
attr_accessor :total_weight
has_many :cart_items
has_many :products, :through => :cart_items
has_many :lands
has_many :shipping_services, :through => :postzones
def total_weight
cart_items.inject(0) {|sum, n| n.weight * n.amount + sum}
end
end
My land model is
class Land < ActiveRecord::Base
has_many :shippingservices, :through => :postzones
has_many :postzones
has_many :carts
end
My shipping_service model is:
class Shippingservice < ActiveRecord::Base
has_many :lands, :through => :postzones
has_many :postzones
has_many :carts
has_many :orders
end
My postzone model is:
class Postzone < ActiveRecord::Base
belongs_to :shippingservice
belongs_to :land
end
The postzone table has foreign keys for lands and shipping_services.
Latter I would like to implement two selector fields: one for ship_to_countries and one for shipping_services, with the second selector being populate only with entries related to the entry selected in the first selector.
I had already this working inside the carts_controller:
#shippingservices = Shippingservice.includes(:lands, :postzones).where('postzones.land_id = ?', Land.first.id)
Which load only shipping services for a specific country into the second selector. But I do not know how to combine the two where clauses relative to weight and postzone into one query.
Any help is very much appreciated!
Thank you in advance.
The method total_weight is a ruby method which is defined in the model Cart
Then you cannot call this method within an SQL statement.
You need to calculate the total weight in the SQL statement.
You should try something like
#shippingservices = Shippingservice.joins(carts: :cart_items).where(
'(cart_items.weight * cart_items.amount) BETWEEN ? AND ?', :weightmin, :weightmax
)
I didn't try but I think it should work :)

RAILS ActiveRecord::Relation how to get many-to-many though: table name

I am in my quest of overriding activerecord method, but I need to be access the table name many-to-many relationship :through . How do i do it?
For example I have model Student and Subject, to connect the two I use has_many Subjects, through: :registers.
I have a statement Student.find(1).subjects, how do i get the table name register from the activerecord statement?
Thanks
Student.rb
Class Student < ActiveRecord::Base
has_many :registers
has_many :subjects, :through => :registers
end
subject.rb
class Subject < ActiveRecord::Base
has_many :registers
has_many :students, :through => :registers
end
register.rb
Class Register
belongs_to :student
belongs_to :subjet
end
Make sure your associations added like this. And You can get your all subject lists by Student.find(1).subjects
And You can also check entries in Register table by using Register.all
You can access all the register records like below.
Register.all
From your statement "Student.find(1).subjects", if you wish to get the registers along with the subjects details, you can do as:
Student.find(1).subjects.select("subjects.name, registers.name")

Rails 4 - how to generate statistics through "joins" and "includes" commands in Activerecord?

I kind of stuck at trying to generate statistics for my application. The relevant part of the application has the following structure:
class CarRegistration < ActiveRecord::Base
belongs_to :ride
belongs_to :car
...
end
class Car < ActiveRecord::Base
has_many :car_registration
...
end
class Ride < ActiveRecord::Base
belongs_to :passenger
belongs_to :driver
has_many :car_registration
...
end
class Driver < ActiveRecord::Base
has_many :cars
...
end
class Passenger < ActiveRecord::Base
has_many :cars
...
end
I am trying to get a list of rides, top drivers and and top passengers. I originally tried something like this:
#rides_finished = Ride.joins(:car_registration)
.select('rides.id')
.where("(car_registrations.ride_id = rides.id)
AND rides.status = 3
AND rides.driver_currency = ?
AND rides.passenger_currency = ?", currency, currency)
.distinct # against displaying one shipment multiple times
And then I tried:
#top_pasengers = #rides_finished.joins(:passenger)
.select('passengers.id, passengers.name, count(rides.passenger_id) AS count_all')
.where('rides.passenger_id IS NOT NULL')
.group('passengers.id')
.order('count_all DESC')
.limit(10)
But when I run these queries, I get
Mysql2::Error: Unknown column 'count_all' in 'order clause': ...
Any help how to get the needed numbers?
Thank you very much
Your question is a little confusing because your query uses Ride but there is no Ride in the model definitions listed. I've focussed purely on the example queries you listed.
I think it would be easier to start with a single query chain for 'top passengers':
Passenger
.select('passengers.*')
.select('count(1) as ride_count')
.joins(:rides)
.where(rides: { status: 3,
driver_currency: currency,
passenger_currency: currency })
.group('passengers.id')
.order('ride_count desc')
.limit(10)
That will get you an ActiveRecord::Relation of Passenger models that also respond to a ride_count call, e.g. you could use it like:
results.each do |p|
puts "#{p.name}: #{p.ride_count}'
end
If all that works, you should be able to adjust the query to get the top drivers.
To get the list of finished rides, I suggest a separate, simple query:
Ride.where(status: 3,
driver_currency: currency,
passenger_currency: currency)
Let me know if any of that produces an error.

Activerecord - how to made this?

Everyday, I need to run the a script and send all of my users an 'exam' or set of questions. I have modelled as class 'Exam' which subclasses ActiveRecord::Base. Now, how do I send user's instances of Exam?
What I was thinking was create a new class called 'ExamInstance' which would have a reference to 'Exam' and the user.
I am new to SQL and ActiveRecord so if someone can help me better model this so I can avoid problems later on or just give me some insight, that would be great.
Thanks
I'll suggest just use has_many :through create a model UserExam for many to many relation between exam and user
class User < ActiveRecord::Base
  has_many :users_exams
  has_many :exams, :through => :users_exams
end
 
class UserExam < ActiveRecord::Base
  belongs_to :users
  belongs_to :exams
end
 
class Exam < ActiveRecord::Base
  has_many :users_exams
  has_many :users, :through => :users_exams
end
For more information on has_many :through
Add 'ExamUser' model to keep track of exam and the corresponding users references. The model skeleton will look something like:
class ExamUser < ActiveRecord::Base
belongs_to :exam
belongs_to :user
end
You could then loop through the records of this table to send your questions.
You can design your new model like this..
class ExamUser < ActiveRecord::Base
belongs_to :user # user has many exams
belongs_to :exam # exam has many users
end