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?
Related
I'm an experienced programmer who is relatively new to ruby/rails and databases. I have created a large website for signing up for courses. I thought I was being clever creating categories of course signups using scope and has_many. Logically, it encapsulates the information well, but my pages are now loading super-slow, and trying to eager load is confusing me.
I have models for Course, Person, CourseRole (student, teacher, etc.), and CourseSignup which includes one of each (Course, Person, CourseRole). It all works smoothly. Recently, I set up scopes in CourseRole to define the categories of signups (I had been hardcoding the role name, and wanted to get away from that). I then set up has_many relationships in Course for each of the categories. So, Course inherently has_many course_signups, and has my categories
has_many :student_signups, -> { CourseSignup.student }, class_name: 'CourseSignup', foreign_key: :course_id
has_many :teacher_signups, -> { CourseSignup.teacher }, class_name: 'CourseSignup', foreign_key: :course_id
etc. (I have 6 categories). I have a page that lists all courses and all of the signups for each course. Like:
Dodge Ball:
info about course
Students:
names of students
Teachers:
names of teachers
etc.
This page loads incredibly slowly. I was trying to add includes statements to the query (based on recommendations from the Bullet gem), but it actually makes it slower. This leads me to think I'm making this more complicated than I should, but I don't know enough to have a clue how to fix it. I imagine I should restructure my models. But I like the abstraction of the scopes/has_many
The page is generated by looping through rendering a partial which shows one course.
#courses = #cuco_session.assigned_courses.includes(:period).order('periods.start_time')
works but is very slow.
#courses = #cuco_session.assigned_courses
.includes(course_signups: [:person, :course_role])
.includes([:courses_rooms, :rooms])
.includes([:helper_signups, :student_signups, :volunteer_signups, :waiting_list_signups, :person_in_room_signups])
.includes(:period).order('periods.start_time')
Also works but is even slower.
We need more information about what is actually happening in the view to really answer why your page is slow.
Also a snippet of your log showing the actual SQL would be necessary.
You mention being new to using databases online with Rails. It's hard to give advice without more of a look into the structure of some of these models, but have you considered replacing CourseSignup with just a join table between Course and Person? It seems like that's what you're getting at here but it's hard to tell.
*has_many :student_signups, -> { CourseSignup.student }, class_name: 'CourseSignup', foreign_key: :course_id
has_many :teacher_signups, -> { CourseSignup.teacher }, class_name: 'CourseSignup', foreign_key: :course_id*
above code can be refactored using Single table inheritance
# app/models/person.rb
class Person < ApplicationRecord
#your code
end
# app/models/teacher.rb
class Teacher < Person
#your code
end
# app/models/student.rb
class Student < Person
#your code
end
For Course Role you can have a separate master table of Role and put references of Person table and Role table in Course Role table
I hope above solution put some light on your question.
I'm considering this an add-on question of sorts to the thread below:
Using join tables in ruby on rails
So we have 'Student' and 'Course' scaffolds joined by a many-to-many association, but in addition there is also a 'Semester' scaffold and what I wish to do is, for each student that is added to a course, for the application to search for previous iterations of the same course through past semesters, to that it's known how many times a student has taken that class before. I'm kind of mixed up at the moment as to how to implement this, so I was hoping someone could help me pin down the logic and code I should be operating by.
Some underlying assumptions I have so far:
'Course' and 'Semester' should, like 'Student' and 'Course', be joined
by a many-to-many association (many courses are taught per semester,
and a course is taught for more than one semester).
There should be an action (let's say get_student) within the course
controller to locate the student via student_id. This would be the main area I'm scratching my head as to what to do. What would this method look like in code?
Within the student-course join table I should have an attribute
'attempts' which increments each time get_student finds this
student_id combined with the course_id that calls the method.This
would be the mechanism that actually tells how many times the course
had been attempted by the student.
I initially wondered if there should be a 'semester' controller
action to activate get_student across all semesters, but now I'm
thinking that get_student should work fine without that.
Appreciate any help I can get on this. Thanks.
This is not a good answer, just a comment.
I would comment, but hear will be more clear. I ll update for the other points. This is just an ongoing feedback/discussion, not an answer.
class Semester < ApplicationRecord
has_many :courses
end
class Course < ApplicationRecord
has_many :students
end
And
semester.courses[0].students => outputs the students array for that
This could be the method to calculate the number of student that did that course:
def studentForCourse
#input_params.course_id => course id you are selecting
semester = Semester.find(input_params)
semester.courses.each do |course|
if course.id = input_params.course_id
nstudents = course.students.size
end
end
This is probably a really simple question, but I've been searching the web for probably around an hour and I can't really find an answer to my problem. It should be clear by what follows that I am very new to Rails, so my terminology and explanation might be a bit confusing.
Let's say that I were making a social media app on Rails, where one of the models is User. I want to make a many-to-many relationship called "friends", which links two users together. Let's say in this situation I also wanted to make a many-to-many between two users called "enemies".
This is all completely hypothetical, but the idea is the same one that I want to use for something I'm working on.
Because a user can have many friends and enemies, but also be many friends and enemies, I would use:
class User < ActiveRecord::Base
has_and_belongs_to_many :users #this should be the friends association
has_and_belongs_to_many :users #this should be the enemies association
end
Now I'm guessing I can't just do that, because I would have to have two tables both named users_users. So, then I switch to:
class User < ActiveRecord::Base
has_and_belongs_to_many(:users, join_table: 'friends',
foreign_key: 'user_id', associate_foreign_key: 'friend_id')
end
With a similar statement for the enemies table. Now, my problem is that I want to have a form that the user can use when they sign up, where they can input their information (this is the User object details), and also list their friends and enemies.
Because the user won't have the database id key for their friends or enemies, they'll have to input the users' names. This is fine, though because the name is also a unique key, guaranteed by the validation.
However, if the user types in the name of a friend, I can't join the two if the friend happens to not exist. So, I use a custom validation class that looks something like this:
class FriendValidator < ActiveModel::Validator
def validate(object)
#lookup user and throw error if not found.
end
end
which will access the variable (object.friends) and (object.enemies)
With something similar for enemies. So therefore, above my has_and_belongs_to_many statements, I have lines that say:
attr_accessor :friends, :enemies #these are attrs because they don't exist within the model's db
validates_with FriendValidator
When I create the form with erb, I have the standard form_for block
<%= form_for(#user) do |f| %>
It seems to me that I can't just stick
<%= f.text_area :friends %>
because friends isn't actually something that will get passed to the User object, but rather a separate table. (Can I, though? Because the attr_accessor is declared in the user's model class?)
So now, we have my main problem. I have two many-to-many tables with a model to its own model class, and I don't know how to ensure that the validation class will take the two attributes, lookup and throw necessary errors, and then add a row to the join tables using the id of the user, rather than the string inputted. What form fields should I use to pass the input to the right place? Where do I change the controller methods so that the input gets sent to the join table rather than the user object?
This definitely seems like a pretty specific situation, so I can't really find an answer in the Rails documentation, which I've been learning from.
My initial impression of this problem has to do with your associations. To me, a user has_many enemies and has_many friends.
friends belong_to user
enemies belong_to user
Not sure if a many to many relationship makes sense in this case. Maybe that's why you are having such a hard time finding an answer online. Just my two cents.
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.
In Rails, I want to override the behavior of an association. For example, by default, if Person has_many :hats, calling some_person.hats would do a simple join using person.id and hat.person_id.
I want to modify that query to include some other criteria. For example, maybe a person's collection of hats should be just the hats that are appropriate to their country.
It seems that I could do something like this:
class Person < ActiveRecord::Base
has_many :hats, :through => :country do
# John lives in Canada, so he gets a baseball cap and a hockey helmet
self.country.hats
end
end
Can I control what an association returns like this? If not, would a scope be the best solution?
I know this is a silly example, but explaining the domain logic that I need this for would be way too boring for everyone here. :)
Scopes are probably your best option because they're chainable and reusable outside your association. Otherwise, you could use association extensions. Check out this thread for more info. Association Extensions