ruby-on-railsruby-on-rails-3associationspolymorphic-associations

rails polymorphic Association doesn't save id


This is very weird and I don't get it.

I got a polymorphic new form for storing address, the create method doesn't actually create the association.

location/new

%= form_for [@mappable, @location] do |form| %>
<div class="inline-label-container">
  <%= form.label :street_address, "street address", :class => "inline" %>
  <%= form.text_field :street_address, :onkeyup => 'timed_input(this)', :value => current_user.location.address, :autocomplete => :off %>
  <%= form.label :location_description, "Found Location"%>
  <%= form.text_field :address, :value => @user.location.address, :disabled => :true%>
  <%= form.hidden_field :google_address, :value => @user.location.address %>
  <%= form.hidden_field :latitude, :value => @user.location.latitude %>
  <%= form.hidden_field :longitude, :value => @user.location.longitude %>
  <%= form.submit "Enter Location", :class => "btn btn-large btn-success" %>
  you can also drag and drop marker!
  <small>
  </small>
</div>
<%end%>

location_controller.rb

def new
    @user = current_user
    @mappable = load_mappable
    @location = @mappable.build_location
end


 def create

        @user = current_user
    @mappable = load_mappable
    @location = @mappable.build_location(params[:location], :mappable_id => @mappable.id, :mappable_type => @mappable.class.base_class.name)

    respond_to do |format|
    if @location.save     
      format.html { redirect_to :back, notice: "#{@mappable.id}, #{@mappable.class.base_class.name}"}
      format.json { render :json => :back, :status => :created, :location => @user }
      #redirect_to @commentable, notice: "Comment created."
    else
      format.html { redirect_to root_path }
      format.json { render :json => @location.errors, :status => :unprocessable_entity }
      #render :new
    end
    end

  def load_mappable
resource, id = request.path.split('/')[1,2]
@mappable = resource.singularize.classify.constantize.find(id)

end

In create method passing mappable_id and type were my last attempts to get them. It really odd because in the after create notice the correct mappable_id and mappable_type are displayed

2.1.1 :085 > Location.last
  Location Load (0.6ms)  SELECT "locations".* FROM "locations" ORDER BY "locations"."id" DESC LIMIT 1
 => #<Location id: 95, address: nil, longitude: -74.009457, latitude: 40.704513, postcode: nil, created_at: "2014-06-01 19:06:19", 
updated_at: "2014-06-01 19:06:20", actor_id: nil, country: nil, country_code: nil, postal_code: nil, city: nil, political: nil, loc
ality: nil, sublocality: nil, street_address: "108 Pearl Street", google_address: "108 Pearl Street, New York, NY 10004, USA", ward
_id: nil, **mappable_id: nil**, mappable_type: "Skill"> 

why is mappable_id missing ? please help

Location.rb

class Location < ActiveRecord::Base
  include ActiveModel::Validations

  belongs_to :mappable, :polymorphic => true

  searchkick

  has_one :map, :dependent => :destroy
  has_one :group, :through => :map, :source => :mappable, :source_type => 'Group'

  belongs_to :ward
  accepts_nested_attributes_for :ward
  has_one :localfeed
  attr_accessible :address, :latitude, :longitude, :postcode, :user_id, :actor_id, :group_id,
    :country,  
    :country_code,  
    :postal_code,  
    :city,  
    :political,  
    :locality,  
    :sublocality,  
    :street_address,
    :google_address,
    :location_attributes,
    :mappable_id,
    :mappable_type,
    :ward_id

  geocoded_by :to_s
  #before_save :geolocate
  validate :street_address, :address_validator => true

  after_validation :geocode
  #before_save :geolocate
  after_save :createward
  #after_save :ward_present #creates new ward and feed if not existant

  def address_changed?
  attrs = %w(street_address city postal_code)
  attrs.any?{|a| send "#{a}_changed?"}
  end

  def to_s
    "#{google_address} #{city} #{postal_code}" +" GB"
  end

  def gmaps4rails_address
    address
  end

  def ward_present
    if self.ward_id = nil?
      w = Ward.find_by_name(self.city)
      self.ward_id = w.id
    else
      self.destroy
    end
  end

    geocoded_by :to_s do |prof,results|
  if result = results.select{|res| res.country_code == "GB" }.first
    unless (result.latitude.nil? || result.longitude.nil?)
      prof.latitude = result.latitude
      prof.longitude = result.longitude
      prof.country = result.country
      prof.postal_code = result.postal_code
      prof.city = result.city
      #prof.political = result.political
     # prof.locality = result.locality
    #  prof.sublocality = result.sublocality
      prof.street_address = result.street_address
    end
    result.coordinates
  end
end

  def geolocate 
    res = GoogleGeocoder.geocode(to_s)

    if res.success

    self.latitude = res.latitude
    self.lonitude = res.longitude
    else
      errors[:base] << "geocoding failed, check address"
      return false
  end
  end

  def createward
    unless self.city.nil?
    w = Ward.find_by_name(self.city)
    if w.nil?
    c = self.create_ward!({:name => self.city, :city => self.city}) # should be [name => self.sublocality]
    self.ward_id = c.id
    else
    self.ward_id = w.id
    end
    end
  end


  def self.groups
     self.where(:user_id => nil)
  end
  def self.users
    self.where(:group_id => nil)
  end
  def self.users_city(city)
    self.where(:mappable_type => "User", :city => city).map(&:mappable_id).uniq
    #returns array  of user_ids!
  end

  def search_and_fill_latlng(address=nil)
    okresponse = false
    geocoder = "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address="

    if address == nil
      address = self.address
    end

    if address != nil && address != ""
      url = URI.escape(geocoder+address)
      resp = RestClient.get(url)
      result = JSON.parse(resp.body)

      if result["status"] == "OK"
        self.latitude = result["results"][0]["geometry"]["location"]["lat"]
        self.longitude = result["results"][0]["geometry"]["location"]["lng"]
        okresponse = true
      end
    end
    okresponse
  end


end

Skill.rb

class Skill < ActiveRecord::Base

  attr_accessible :avatar_attributes, :photo_attributes, :name, :description,:skill_type_id, :properties, :teachers_title, :necessary_resources, :level, :required_experience,
  :price, :start_date, :min_students, :max_students, :activity_duration, :location_id, :user_id, :location_attributes, :skill_type_attributes


  belongs_to :user
  belongs_to :skill_type
  has_one :ward, :as => :warded
  accepts_nested_attributes_for :skill_type

  has_one :location, :as => :mappable
  accepts_nested_attributes_for :location

included both model classes the location and skill.

Console

Started POST "/skills/10/locations" for 127.0.0.1 at 2014-06-02 13:11:32 +0100
Processing by LocationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"hgJxwarAwYS4RP3GidzBnd3MRShCnvDltb0r6ULblBo=", "location"=>{"street_address"=>"1
01 Pearl Street cardiff", "google_address"=>"101 Pearl Street, Cardiff CF24 1NE, UK", "latitude"=>"51.4866055", "longitude"=>"-3.15
24131999999554"}, "commit"=>"Enter Location", "skill_id"=>"10"}
  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1
  Location Load (0.6ms)  SELECT "locations".* FROM "locations" WHERE "locations"."mappable_id" = 6 AND "locations"."mappable_type" 
= 'User' LIMIT 1
  Avatar Load (0.5ms)  SELECT "avatars".* FROM "avatars" WHERE "avatars"."avatarable_id" = 6 AND "avatars"."avatarable_type" = 'Use
r' LIMIT 1
  Profile Load (0.5ms)  SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = 6 LIMIT 1
  Skill Load (0.4ms)  SELECT "skills".* FROM "skills" WHERE "skills"."id" = $1 LIMIT 1  [["id", "10"]]
  CACHE (0.0ms)  SELECT "skills".* FROM "skills" WHERE "skills"."id" = $1 LIMIT 1  [["id", "10"]]
  Location Load (0.6ms)  SELECT "locations".* FROM "locations" WHERE "locations"."mappable_id" = 10 AND "locations"."mappable_type"
 = 'Skill' LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.8ms)  INSERT INTO "locations" ("actor_id", "address", "city", "country", "country_code", "created_at", "google_address", "
latitude", "locality", "longitude", "mappable_id", "mappable_type", "political", "postal_code", "postcode", "street_address", "subl
ocality", "updated_at", "ward_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) RE
TURNING "id"  [["actor_id", nil], ["address", nil], ["city", "Cardiff"], ["country", "United Kingdom"], ["country_code", nil], ["cr
eated_at", Mon, 02 Jun 2014 12:11:33 UTC +00:00], ["google_address", "101 Pearl Street, Cardiff CF24 1NE, UK"], ["latitude", 51.486
6055], ["locality", nil], ["longitude", -3.1524132], ["mappable_id", 10], ["mappable_type", "Skill"], ["political", nil], ["postal_
code", "CF24 1NE"], ["postcode", nil], ["street_address", "101 Pearl Street"], ["sublocality", nil], ["updated_at", Mon, 02 Jun 201
4 12:11:33 UTC +00:00], ["ward_id", nil]]
  Ward Load (0.5ms)  SELECT "wards".* FROM "wards" WHERE "wards"."name" = 'Cardiff' LIMIT 1
   (0.8ms)  COMMIT

Solution

  • I don't exactly know why, but the problem was with the Location controller #new method.

    I changed the @location = @mappable.build_location to @location = Location.new and it started to work.