I need to store data about specific Objects which include information about Location(Country, City, District) of an object. I'm trying to figure out the best way to store described above location data in the MySQL database and how it should look like in the rails active model.
My dummy idea is to create 3 separate tables like below:
Location_countries
id
name
Location_cities
id
country_id
name
Location_districts
id
city_id
name
Create a separate table for an object and call it the item_location and include Ids of object its country... so on
Next is to create 3 Rails models (Country, City, Districts).. , but I think it's a very dirty way and it can be combined into the single Model like Item_location
Any Ideas?
Thank you in advance!
I don't entirely understand what you're looking to do, but either way, here's how I'd tackle it:
#app/models/object.rb
Class Object < ActiveRecord::Base
has_one :location
end
#app/models/location.rb
Class Location < ActiveRecord::Base
belongs_to :object
belongs_to :city
belongs_to :district
delegate :name, to: :city, prefix: true
delegate :name, to: :district, prefix: true
end
#app/models/city.rb
Class City < ActiveRecord::Base
has_many :locations
belongs_to :country
end
#app/models/district.rb
Class District < ActiveRecord::Base
has_many :locations
end
#app/models/country.rb
Class Country < ActiveRecord::Base
has_many :cities
end
This will give you the ability to call:
#object = Object.find params[:id]
#object.location.city_name
#object.location.district_name
Related
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 :)
I am new to Rails so I am going to try and explain this the best I can.
I have three models: artist, fest, and festival_artist
artist contains only an ID and an artist_name
fest contains only an ID and a festival_name
festival_artist contains an ID, a artist_id, and a festival_id
I created Fest using a scaffold so that is where my controller and show.html.erb is.
Below are my models:
class Artist < ApplicationRecord
belongs_to :festival_artist
end
class Fest < ApplicationRecord
belongs_to :festival_artist
end
class FestivalArtist < ApplicationRecord
has_many :artists
has_many :fests
end
In my fests_controller.rb I have:
def show
#festival_artists = FestivalArtist.where(festival_id: #fest.id)
end
I tried to add:
def show
#festival_artists = FestivalArtist.where(festival_id: #fest.id)
#artists = Artist.where(id: #festival_artists.artist_id)
end
However, that throws an undefined method artist_id for # error.
The goal is to display the Artist's name in the Fest's show.html.erb page for the festival that that artist belongs to.
In SQL it would be:
SELECT A.artist_name
FROM festival_artists AS FA
INNER JOIN artists AS A
ON FA.artist_id = A.id
Any suggestions? Even telling me what to Google would help out because I'm not sure my terminology is correct.
Let me know if you need anymore information.
Guess your models structure is not 100% correct. Try to check out http://guides.rubyonrails.org/association_basics.html for details.
There are to ways to handle your associations in Rails:
HABTM (has and belongs to many), as noticed in #grizzthedj answer.
has_many :through association
In this case your code will look like
class Artist < ApplicationRecord
has_many :festival_artists
has_many :fests, through: :festival_artists
end
class Fest < ApplicationRecord
has_many :festival_artists
has_many :artists, through: :festival_artists
end
class FestivalArtist < ApplicationRecord
belongs_to :artists
belongs_to :fests
end
So you can access artists in the controller
def show
#festival_artists = #fest.artists
end
I'm not sure that you need the FestivalArtist model. If you use "has_and_belongs_to_many" in Artist and Fest models, this will implement the many-to-many relation that you are looking for.
# fest.rb
class Fest < ActiveRecord::Base
has_and_belongs_to_many :artists
end
# artist.rb
class Artist < ActiveRecord::Base
has_and_belongs_to_many :fests
end
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")
I'm trying to create a form using form_for which will add Employees. For an employee i want to assign multiple specializations such as c#, asp, etc as the values dynamically. I'm using the following code in the form
<%= f.select :specilization, Specialization.all.collect{|p| [p.name, p.id]}, {}, :multiple => true %>
I've also made HABTM between employees and specialization like
Employee.rb
class Employee < ActiveRecord::Base
has_and_belongs_to_many :specializations
end
Specialization.rb
class Specialization < ActiveRecord::Base
has_and_belongs_to_many :employees
end
with these done, im not able to save the selected values in db(MySQl). Would appreciate if anyone could solve my problem or guide me on how to get this right?
Thanks in advance.
I usually solve this by using has_many :through and then in my form the select is a field_for on the join model. Like this:
class Employee < ActiveRecord::Base
has_many :employees_specializations
has_many :specializations, through: :employees_specializations
# we will be creating these join models on the employee form
accepts_nested_attributes_for :employees_specializations
end
class Specialization < ActiveRecord::Base
has_many :employees_specializations
has_many :employees, through: :employees_specializations
end
class EmployeesSpecialization < ActiveRecord::Base
belongs_to :employee
belongs_to :specializations
end
Now it's important to note that with this simplified approach I'm assuming Specializations already exist in the database and we're just selecting them and joining them to the employee we're creating/editing:
# in your controller make sure to build at least 1 new instance, the fields_for needs this
#employee.employees_specializations.build
# remember to add to your strong parameters the new join attributes
params.require(:employee).permit(
# ... other attributes ...
employees_specializations_attributes: [:id, :specialization_id]
)
You need to declare :id, :specialization_id as sub fields since employees_specializations_attributes will be a nested hash with those keys inside.
# now in your form use fields_for
<%= f.fields_for :employees_specializations do |ef| %>
<%= ef.select :specialization_id, Specialization.all.map{|p| [p.name, p.id]}, {}, multiple: true %>
<% end %>
That f.fields_for :employees_specializations will create form fields named employee[employees_specializations_attributes][][specialization_id]. Which basically says we are creating a nested association employees_specializations and setting that nested association's specialization_id (remember employees_specialization belongs_to :specialization) to the selected specialization. Note the [] in the middle of the field name, this means its an array of nested employees_specializations.
Post that and barring any validation errors you should be able to create/edit an employee and set it's specializations by selecting from a list of existing specializations and creating a join model between them.
Further reading:
http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormHelper/fields_for
http://apidock.com/rails/v4.0.2/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
http://laptite.github.io/blog/2014/02/26/deep-nesting-with-has-many-through-and-a-join-model/
Rails nested form with has_many :through, how to edit attributes of join model?
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