Postgresql error when reverse geocoding in Ruby on Rails - ruby-on-rails

I am having an issue trying to do a bulk reverse geocode using the geocoder rails gem: https://github.com/alexreisner/geocoder
I have the following models:
class School < ActiveRecord::Base
reverse_geocoded_by :latitude, :longitude
has_one :address
end
and
class Address < ActiveRecord::Base
belongs_to :school
end
And the following migrations:
class CreateSchools < ActiveRecord::Migration
def change
create_table :schools do |t|
t.string :name
t.integer :address_id
t.timestamps null: false
end
end
end
and
class CreateAddresses < ActiveRecord::Migration
def change
create_table :addresses do |t|
t.string :line_1
t.string :line_2
t.string :line_3
t.string :city
t.string :region
t.string :country
t.string :code
t.timestamps null: false
end
end
end
and when I run the following line:
rake geocode:all CLASS=School REVERSE=true SLEEP=0.5
I get this error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column schools.address does not exist
LINE 1: SELECT "schools".* FROM "schools" WHERE (schools.address IS...
^
: SELECT "schools".* FROM "schools" WHERE (schools.address IS NULL) ORDER BY "schools"."id" ASC LIMIT 1000
I know the readme says this:
"For geocoding your model must provide a method that returns an
address. This can be a single attribute, but it can also be a method
that returns a string assembled from different attributes (eg: city,
state, and country)."
I took that to mean I needed either a method on the School model or the attribute on the school table and I opted for the latter but I'm not sure what I'm missing.
Thanks!

The problem is that the reverse-geocoding rake task starts by loading all the records with no address column yet. It uses this scope:
scope :not_reverse_geocoded, lambda {
where("#{table_name}.#{geocoder_options[:fetched_address]} IS NULL")
}
The problem is you don't have any column on schools you could use. Instead, you should move the reverse_geocoded_by declaration to the Address class. You will also need to either add an addresses.address column or do something like this:
reverse_geocoded_by :latitude, :longitude, fetched_address: :line_1
Also you don't seem to have columns for latitude and longitude. And of course those should be on Address too, not School. After all, if a school can have several addresses, which one is its lonlat?

Related

Rails inherits subclass

I have this three classes user, driver, company.
every company or driver belongs a user. The models look like
class Company < User
has_many :driver
end
class Driver < User
end
class User < ActiveRecord::Base
enum role: [:admin, :support, :B2B , :B2C]
end
and the database looks like
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.timestamps null: false
end
end
end
class CreateCompanies < ActiveRecord::Migration
def change
create_table :companies do |t|
t.string :comp_name
t.string :first_name_counterpart
t.string :last_name_counterpart
t.string :iban_nr
t.string :bic
t.string :email_counterpart
t.string :addresse
t.string :city
t.string :zip
t.references :user
t.timestamps null: false
end
end
end
class CreateDrivers < ActiveRecord::Migration
def change
create_table :drivers do |t|
t.string :first_name
t.string :last_name
t.date :birthday
t.integer :sex
t.integer :dpi
t.integer :score
t.references :user
t.timestamps null: false
end
end
end
Why can't I create a Driver-instance. For example, if I try d = Driver.new, I get a user-instance.d = Driver.new
=> #<Driver id: nil, email: nil, created_at: nil, updated_at: nil>
This is how Rails guesses the table name from the model classes. Quoting from the ActiveRecord docs for table_name:
Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used to guess the table name even when called on Reply.
You should be able to force the proper table name by the table_name= setter, e.g.:
class Driver < User
self.table_name = "drivers"
end
On the other hand, I am also not sure that your approach (with such inheritance) will not cause problems somewhere else.
If you have models with inheritance like you do:
class User < ActiveRecord::Base
enum role: [:admin, :support, :B2B , :B2C]
end
class Company < User
has_many :driver
end
class Driver < User
end
rails infers that you are after Single Table Inheritance (STI) and expects there is just a base table users with a column type which stores the records of User, Company and Driver with actual class name (ex: Company or Driver etc).
If you would rather want to have separate tables users, companies and drivers because each of those tables have different set of columns, and the only reason why you are put inheritance in place is to share some common functionality, then you should extract the common functionality into modules and mix them into those models (by just inheriting from ActiveRecord::Base.
rails, through active_support provides whats called concerns to extract the common functionality into modules and mix them intuitively.
You could probably get away with inheritance and still have these models point to separate tables with the declaration of self.table_name = "table_name". But it is not a good idea, as it goes around the rails conventions and may cause problems down the lane.
Refer to ActiveRecord::Inheritance and ActiveSupport::Concern for more info.

Querying the database passing multiple parameters rails

I have a user table and an activity table. The user has many activities. This is what i have in my users table:
class SorceryCore < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :surname
t.integer :previous_award
t.integer :chosen_award
t.string :email, :null => false
t.string :crypted_password
t.string :salt
t.timestamps
end
add_index :users, :email, unique: true
end
This is what I have in my activities table:
class CreateActivities < ActiveRecord::Migration
def change
create_table :activities do |t|
t.integer :activity_type
t.string :activity_name
t.string :approver_email
t.references :users, index: true, foreign_key: true
t.timestamps null: false
end
end
In my view I want to show the activity_name, where user_id = the current user's id, and where the the activity_type = 1. I'm not sure where to write this method or how to call it either. I have read through the following link but can't seem to get anything working. http://guides.rubyonrails.org/active_record_querying.html
I think I need to use something along the lines of this:
Activity.where("activity_type <= ?", 1).where("user_id <= ?", current_user.id)
But I'm not sure if this is supposed to go into a method in the controller or the model, and I'm not sure which controller or model it's supposed to go into
In the User model:
# user.rb
def activity_by_type(type = 1)
self.activities.where(activity_type: type).first
end
and then, you can call current_user.activity_by_type(<specify the type here>)
You can use the above method to get any of the activity type for the specified user, by specifying the type in the method call.
One advice I'll give though is to try and use the concept of enums to categorize your activity_type column in the activities table. An example on how, can be found here.
You simply have to query on the current_user.activities association:
#activities = current_user.activites.where(activity_type: "1")
You could also use a scope (which is what SunnyK recommended):
#app/models/user.rb
class User < ActiveRecord::Base
has_many :activities
scope :by_type, ->(type = 1) { activities.where(activity_type: type) }
end
--
If you only wanted to return a single record, you'd have to replace .where with .find_by:
#activity = current_user.activities.find_by activity_type: 1
--
Since you're using an enum, you may wish to use the built-in class method that you'd be able to call:
enum activity_type: [:sports, :photography]
#activity = current_user.activities.sports

Relation one to many with 2 foreigns keys of the same table Ruby on rails

I have a table called customers. These table has two addresses . One address of work and One direccion of house
Those 2 addresses belong to a table called addresses
I don't know how to relation those 2 tables
Migrations
class CreateCustomers < ActiveRecord::Migration
def change
create_table :customers do |t|
t.string :name
t.integer :address_id #Address of work
t.integer :address_id_1 #Address of home
t.timestamps
end
end
end
class CreateAdresses < ActiveRecord::Migration
def change
create_table :adresses do |t|
t.string :street
t.timestamps
end
end
end
I do not believe this is a good approach or database design. If you want to proceed this way and not get out of the rails convention just create two columns address_id and address_two_id
and in customer.rb
belongs_to :address, class_name: "Address"
belongs_to :address_two, class_name: "Address"
By default rails takes the name of the foreign key and stores it in a column called "name"+"_id"
The better way is two have a column customer_id in your Address model and create a relation in your customer class
customer.rb
has_many :addresses
And you can also validate that a customer has no more than two addresses by adding this validation to
address.rb
validate :validate_two_addresses
def validate_two_addresses
address_count = Address.where(customer_id: self.customer_id).count
errors.add(:base, "You cannot have more than 2 addresses.") unless address_count < 3
end

Custom foreign_key in model gives PG::Error column does not exist - Rails

I have a VideoCollection model that will contain many records from another model (called VideoWork), using the has_many relationship. The VideoCollection model inherits from the Collection model using single table inheritance, while the VideoWork model inherits from the Work model.
I'm having a problem when I try to call up the video_works that belong to a video_collection.
In my video_collection#show action, I use the following to try to display a collection's works:
def show
#video_collection = VideoCollection.find(params[:id])
#collections = #video_collection.children
#works = #video_collection.video_works
end
But when I try to use #works in the show view, I get the following:
PG::Error: ERROR: column works.video_collection_id does not exist
SELECT "works".* FROM "works" WHERE "works"."type" IN ('VideoWork') AND "works"."video_collection_id" = $1
##(Error occurs in the line that contains <% #works.each do |work| %>)
My model files:
#----app/models/video_collection.rb----
class VideoCollection < Collection
has_many :video_works
end
#----app/models/video_work.rb----
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
The "parent" models:
#----app/models/collection.rb - (VideoCollection inherits from this)
class Collection < ActiveRecord::Base
end
#----app/models/work.rb - (VideoWork inherits from this)
class Work < ActiveRecord::Base
end
The Schema file:
#----db/schema.rb----
create_table "works", force: true do |t|
t.string "header"
t.string "description"
t.string "type"
t.string "folder_id"
end
create_table "collections", force: true do |t|
t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
t.text "ancestry"
t.string "name"
t.string "tile_image_link"
end
My Question
I assume that since I have a folder_id column in the works table that I should be able to set up the belongs_to relationship properly, but it seems that Rails still wants me to have a video_collection_id column instead. I would prefer not use something specific like video_collection_id as a foreign key in the works table since I need to set up other relationships (e.g.: photo_collection has_many photo_works, etc).
What am I doing wrong here?
I don't really use has_many and belongs_to with different foreign keys than the standard, but according to the docs I would do this:
class VideoCollection < Collection
has_many :video_works, foreign_key: "folder_id"
end
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
Your Pg error says that the association is looking for 'video_collection_id' instead of 'folder_id'
Guides (chapter 4.3.2.5)

Geokit plus Rails 3.1.1, lat and lon issue.

I am using geokit-rails3 gem to find products in all colleges within range of particular college. A college has_many products and a product belong to college, there is another category model which has_many products and product belongs_to category. But when I try to find college from database on basis of addess using geokit it tell me lat coloumn is missing in my table.
Colleges migration is
create_table :colleges do |t|
t.string :name
t.text :address
t.string :city
t.string :state
t.integer :zipcode
t.timestamps
Controller
#products = College.within(5, :origin=>#current_product.college.address).product
Error:
Mysql::Error: Unknown column 'colleges.lat' in 'field list': SELECT `colleges`.*,
(ACOS(least(1,COS(0.3223824452162744)*COS(1.2891920858347756)*COS(RADIANS(colleges.lat))*COS(RADIANS(colleges.lng))+
COS(0.3223824452162744)*SIN(1.2891920858347756)*COS(RADIANS(colleges.lat))*SIN(RADIANS(colleges.lng))+
SIN(0.3223824452162744)*SIN(RADIANS(colleges.lat))))*3963.19)
AS distance FROM `colleges` WHERE ((colleges.lat>18.398868573573203 AND colleges.lat<18.543438426426793 AND colleges.lng>73.78905443427034 AND colleges.lng<73.94147656572967)) AND ((
(ACOS(least(1,COS(0.3223824452162744)*COS(1.2891920858347756)*COS(RADIANS(colleges.lat))*COS(RADIANS(colleges.lng))+
COS(0.3223824452162744)*SIN(1.2891920858347756)*COS(RADIANS(colleges.lat))*SIN(RADIANS(colleges.lng))+
SIN(0.3223824452162744)*SIN(RADIANS(colleges.lat))))*3963.19)
<= 5))
Any hint how to solve this issue?
Anything that acts_as_mappable with geokit needs a lat & lnt fields (these can be overridden using different names)
see the top of: http://geokit.rubyforge.org/readme.html
I'd recommend:
add_column :colleges, :lat, :float
add_column :colleges, :lng, :float
add_index :colleges, [:lat, :lng]
Also, to auto-update the address:
before_save :update_location
def update_location
#you probably you to use a full address, or join all the address parts instead of just self.address
loc=Geocoder.geocode(self.address)
if loc.success
self.lat = loc.lat
self.lng = loc.lng
end
end
You can also have different columns and map them to the acts_as_mappable this way:
acts_as_mappable :default_units => :miles,
:default_formula => :sphere,
:distance_field_name => :distance,
:lat_column_name => :lat,
:lng_column_name => :lng

Resources