I'm trying to find a way of connecting two active record objects, not a full merge but somehow having them associated.
For example if I had two models, City and Restaurant, each city can have many restaurants. In this example if there are two City records, "Napoli" and "Naples" that represent the same city, I would like to connect them in the db so regardless of whether the user clicked on restaurants in "Napoli" or "Naples" they would be taken to the same page.
I apologize if I've explained this poorly, I can't fully articulate what I'm after without using an example.
I'm using Rails 3.2, ruby 1.9.2 and postgres
Thanks.
class City < ActiveRecord::Base
has_many :city_name, :dependent => :destroy
end
class CityName < ActiveRecord::Base
belongs_to :city
end
When you search for a city in your controller, you can check all the names and if any matches you render the same restaurant.
You can add a lookup field that two cities that are actually the same have in common (kind of ad-hoc solution, but, I think it works).
So, Napoli and Naples would have maybe a "City id" of 46, whereas "Mumbai" and "Bombay" would have a city id of 32.
Related
I'm fairly new to rails and I'm trying to make a database app for a school as practice. So here's the ERD that I constructed:
What I want to have is a list of students under a school. I know how to retrieve the teachers under a school and the students under the teacher it's basically just school.teachers and teacher.students but I don't know how to get what school -> students.
You can setup a has many through relation:
class Teacher < ApplicationRecord
belongs_to :school
has_many :students
end
class School < ApplicationRecord
has_many :teachers
has_many :students, through: :teachers
end
And then just do
school = School.find(some_id)
students = school.students
Some notes:
Foreign keys in Rails are usually of the format something_id, so instead of TeacherId you would use teacher_id. Primary keys are usually just called id. Of course you can call them whatever you want, things just require less configuration if you go with the defaults.
Unless this is just to play around with Rails I'd probably change the relation between teacher and students to a many to many. In this case you'd need to distinct the students:
has_many :students, -> { distinct }, through: :teachers
UPDATE:
Many To Many
Yes, you are right. To have a many to many relation you will need a "join table". This is not mandated by Rails though, but the way relational DBs work. Perhaps it's best if you start a new question if you need help with goign that route.
through
:through is an option you can pass to has_many. It is described here
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association (altough they use a "has and belongs to many" association in the example)
In your example I think of it like: "Hey school, please give me all students that you can reach via the teachers"
And ActiveRecord will create a query similiar to this:
school = School.first
school.students.to_sql
=> "SELECT \"students\".* FROM \"students\" INNER JOIN \"teachers\" ON \"students\".\"teacher_id\" = \"teachers\".\"id\" WHERE \"teachers\".\"school_id\" = 1"
(where 1 is the ID of the school you called the students method on).
If you do the query as suggested by #TTD then it will most likely result in something like this:
school = School.first
Student.where(teacher: Teacher.where(school: s)).to_sql
=> "SELECT \"students\".* FROM \"students\" WHERE \"students\".\"teacher_id\" IN (SELECT \"teachers\".\"id\" FROM \"teachers\" WHERE \"teachers\".\"school_id\" = 1)"
Which is working as well, but uses a nested query to obtain the teacher ids.
There is yet another way that I see used from time to time:
school = School.first
teacher_ids = school.teachers.pluck(:id) # only select the teacher ids
students = Student.where(teacher_id: teacher_ids)
I'd not recommend to do it this way though. It will fire two queries to get the students and transfer back and forth more data:
one to get all the teacher ids
one to get all the students belonging to to those teacher ids
Student.where(teacher: Teacher.where(school: school))
i'm new to rails and your help and advise would be much appreciated as i am finding this challenging
Aim: i want the creator of the event to be able to select more than one user as
hosts for a created event (just like how facebook allows the creator of
a page to be be able to select users as admins of a created page). Is the below how my model and schema should be displayed?
i was aiming to build something like this image. Event1 i can select Ian & Jesse as hosts, Event2 i can also select Ian again as a host and select Emma
This is how i imagine it so far to be built (your guidance would be much appreciated):
models
user.rb
has_many events
event.rb
belongs_to user
host.rb
belongs_to user
has_many events
schema
users
name
email
events
title
address
user_id
hosts
user_id
event_id
Started writing this as a comment but realised it was getting too wordy.
your model is broken ... an event has many users .. it doesn't belong_to a single user.
What you have is a many to many relationship between users and events which needs resolving through a join table (aka associative/junction table). You have gone some way to resolving this with the hosts table though this goes against the rails convention.
What you want is something like:
models
user.rb
has_and_belongs_to_many :events
event.rb
has_and_belongs_to_many :users
and create a join table that references the two models
users table
name
email
events table
title
address
events_hosts table
user_id
event_id
The rails convention is for the join table to be named by joining the two names of the tables it is joining lexically ordered - i.e. events before hosts, concatenated together to give events_hosts.
Alternatively, you can also create a join model if you prefer:
EventHost
belongs_to :user
belongs_to :event
and modify the has_and_belongs_to_many to has_many :event_hosts in the other two models - the database schema will remain the same.
Let's say I have a single web page form user interface with 2 sets of checkboxes. With set 1 checkboxes, I can check off what Trainers I would like ("Jason", "Alexandra, etc.) With set 2 checkboxes, I can check off what animals I would like to see ("Tigers", "Bears", etc.) Once I submit the form with these options, I get back a list of zoos that match the criteria (let's assume all the trainers work at all the zoos and all the animals are at all the zoos for discussion's sake)
We'll be running our database query by "name" (e.g., search using trainer names and animal names, NOT database ids)
Let's say we are using a Postgres database that has hundreds of thousands of rows (if not millions).
Is it more efficient to search using an "ILIKE" query or is it better to do a standard join query (e.g., Zoo.includes(:animals, :trainers).where("animals.name = ? and trainers.name = ?", animal_names, trainer_names)?
Is there a better way than what I just showed in #1 above?
model setup
class Zoo < ActiveRecord::Base
has_many :animals, through: zoo_animals
has_many :trainers, through: zoo_trainers
has_many :zoo_trainers
has_many :zoo_animals
end
class Animal < ActiveRecord::Base
has_many :zoos, through :zoo_animals
has_many :zoo_animals
end
class Trainer < ActiveRecord::Base
has_many :zoos, through :zoo_trainers
has_many :zoo_trainers
end
class ZooAnimal < ActiveRecord::Base
belongs_to :animal
belongs_to :zoo
end
class ZooTrainer < ActiveRecord::Base
belongs_to :zoo
belongs_to :trainer
end
EDIT: let's suppose I don't have access to the database ID's.
LIKE '%Jason%' is much less efficient than querying for the exact string 'Jason' (or querying for an ID), because while exact comparisons and some uses of LIKE can use an index on the column being queried, LIKE with a pattern beginning with a wildcard can't use an index.
However, performance doesn't sound like the most important consideration here. LIKE %Jason% will still probably be fast enough on a reasonably sized database under reasonable load. If the application really needs to search for things by substring (which implies that a search might have multiple results), that requirement can't be met by simple equality.
There are an endless number of higher-powered solutions to searching text, including Postgres built-in full-text search and external solutions like Elasticsearch. Without specific requirements for scaling I'd go with LIKE until it started to slow down and only then invest in something more complicated.
Ruby: 1.9.2
Rails: 3.0beta3
I need some help with associations in Rails 3.
I have the following models (see excerpts below):
School, State, SchoolLocale
The schools table has the following fields:
id, name, state_id, school_locale_id
The states table has the following fields:
id, abbr, name
The school_locales table has the following fields:
id, code, name
Unfortunately, my data-source didn't have IDs for school_locales. Thus, the data stored in the 'school_locale_id' field in the schools table actually maps to the 'code' field in the school_locales table.
school.rb:
class School < ActiveRecord::Base
belongs_to :state
belongs_to :school_locale
end
state.rb:
class State < ActiveRecord::Base
has_many :schools
end
school_locale.rb:
class SchoolLocale < ActiveRecord::Base
has_many :schools
end
I would like a query for a given school, let's say School.find(1), that would output the school name, the state name and the school-locale name. I assume that I need to add an index to the 'code' field in the school_locales table and somehow specify it as a foreign key, but I'm not certain. Any help would be appreciated.
This doesn't exactly answer your question, but I think it is a useful bit of information. Regarding your use of a states table, let me refer to Surrogate Vs. Natural/Business Keys.
#Ted says here:
Remember there is nothing special about a primary key, except that it is labelled as such. It is nothing more than a NOT NULL UNIQUE constraint, and a table can have more than one.
If you use a surrogate key, you still want a business key to ensure uniqueness according to the business rules.
There's no point in having a state_id foreign key that links to a states table. Each state already has a unique id; its 2-letter abbreviation. This unique id is just as good as a numeric one. And because this data doesn't change often, there's no harm in having it statically defined within your application somewhere.
I'm not really sure about it, but you could try using this:
class SchoolLocale < ActiveRecord::Base
has_many :schools, :primary_key => :code
end
Let me know if it works :]
I'm relatively new to rails and I'm working on a task which requires a similar functionality to the Cities tab here: http://haystack.com/cities . I've searched all the books and can't seem to understand the logic that makes all of this possible. The only thing I can think of is reverse engineer e-commerce code to do what I want. Does anyone have any bright ideas?
You're going to at least need to get a listing of city/state data, either from somewhere like geonames.org or zipcodeworld.com. You'll then have to map cities to states, states to countries, and then figure out a clean way to display that, much like the haystack.com site. I would guess top cities is either weighted by how many users have requested each city or weighted simply by supposed popular cities.
Mostly it will just involve how you relate each of those data types (City, State, Country) together. Actually displaying, apart from how to lay all that out, is simple then. Basically have a Cities, States and Countries table in your DB, then create models something like:
class Country
has_many :states
end
class State
belongs_to :country
has_many :cities
end
class City
belongs_to :state
end
EDIT:
To associate it with users: Assuming that a user can only belong to one city (although multiple would not be that difficult either), it would look something like:
class User
belongs_to :city
end
class City
belongs_to :state
has_many :users
end
You should be able to do things like:
usr = User.find(a_user_id)
usr.city #New York City
usr.city.state #New York
usr.city.state.country #United States
Perhaps something like this:
http://www.geonames.org/export/
is what you're looking for?