问题
I am trying to post these coordinates from the metadata of the image i upload with paperclip to another table called places."so coordinates go to places table." There are columns latitude and longitude in the places table. After submitting the post i run into this error. The highlighted portion of the error is self.latitude=parse_latlong(etc....).
post_id is a foreign key in the places table. This worked previously when i had latitude and longitude in the post table. but now i gave it its own table for better database structure.. i just need to know how to get my post controller to work with my places controller if that is the main problem??
Places Controller
class PlacesController < ApplicationController
before_action :set_post
def create
@place = @post.places.build(place_params)
if @place.save
flash[:success] = "coorinates saved"
redirect_to :back
else
flash[:alert] = "Check the form, something went wrong."
render root_path
end
end
private
def place_params
params.require(:place).permit(:continent, :country, :city, :address, :latitude, :longitude)
end
def set_post
@post = Post.find(params[:post_id])
end
end
post controller
class PostsController < ApplicationController
before_action :authenticate_user!, :except => [:show, :index, :new]
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :owned_post, only: [:edit, :update, :destroy]
def index
@post = Post.new
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def new
@post = current_user.posts.build
@posts = Post.all
end
def create
@post = current_user.posts.build(post_params)
if @post.save
flash[:success] = "Your post has been created!"
redirect_to root_path
else
flash[:alert] = "Your new post couldn't be created! Please check the form."
render :new
end
end
def edit
@post = Post.find(params[:id])
end
def update
if @post.update(post_params)
flash[:success] = "Post updated."
redirect_to root_path
else
flash.now[:alert] = "Update failed. Please check the form."
render :edit
end
end
def destroy
@post.destroy
flash[:success] = "Your Post has been removed."
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:image, :caption, :address)
end
def set_post
@post = Post.find(params[:id])
end
def owned_post
unless current_user == @post.user
flash[:alert] = "That post doesn't belong to you!"
redirect_to root_path
end
end
end
post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :place
has_many :comments, dependent: :destroy
has_one :place, dependent: :destroy
validates :user_id, presence: true
validates :image, presence: true
accepts_nested_attributes_for :place
has_attached_file :image, styles: { :medium => "640x" }
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
after_post_process :save_latlong
private
def save_latlong
exif_data = MiniExiftool.new(image.queued_for_write[:original].path)
self.latitude = parse_latlong(exif_data['gpslatitude'])
self.longitude = parse_latlong(exif_data['gpslongitude'])
end
def parse_latlong(latlong)
return unless latlong
match, degrees, minutes, seconds, rotation = /(\d+) deg (\d+)' (.*)" (\w)/.match(latlong).to_a
calculate_latlong(degrees, minutes, seconds, rotation)
end
def calculate_latlong(degrees, minutes, seconds, rotation)
calculated_latlong = degrees.to_f + minutes.to_f/60 + seconds.to_f/3600
['S', 'W'].include?(rotation) ? -calculated_latlong : calculated_latlong
end
end
All in All i would like to get that latitude and longitude variable updated into the database from the exif extraction.. the extraction isn't the problem but instead I believe how Im saving that information into the database is the true problem!!! thank you!!!!
回答1:
The issue seems to stem from the fact that you are updating attributes from an associated model. You could do so by calling
update_attributes(place_attributes: {latitude: , longitude: })
But I would recommend keeping this logic out of the posts model, this really is a concern for a form model where you transform the raw user inputs into a database consumable format. If you do not want to add an additional layer to your app at least move these methods to their own model. Everytime, you see a group of private method calling each other and passing state around, I think it's a good sign that they should for a class: So
class LotLangParser
def initialize(image)
@image = image
@exif_data = MiniExiftool.new(image.queued_for_write[:original].path)
end
def lat
parse_latlong(exif_data['gpslatitude'])
end
def lon
parse_latlong(exif_data['gpslongitude'])
end
private
def parse_latlong(latlong)
return unless latlong
match, degrees, minutes, seconds, rotation = /(\d+) deg (\d+)' (.*)" (\w)/.match(latlong).to_a
calculate_latlong(degrees, minutes, seconds, rotation)
end
def calculate_latlong(degrees, minutes, seconds, rotation)
calculated_latlong = degrees.to_f + minutes.to_f/60 + seconds.to_f/3600
['S', 'W'].include?(rotation) ? -calculated_latlong : calculated_latlong
end
end
As a note I would also encapsulate MiniExiftool and inject it as a dependency in the constructor. But let's not lose sight of our goal here.
Then in your controller you can call your parser to give you the place params
def place_params
long_lat_parser = LongLatParser(image)
{place_attributes: {longitude: long_lat_parser.lon, latitude: long_lat_parser.lat}}
end
and then simply merge them into the post params:
@post = current_user.posts.build(post_params.merge(place_params))
The benenfit of this approach is that you have introduced an object with a clear responsibility and returned to AR model as mere database wrapper. In general I try to encapsulate more complex interaction in some sort of service object, but in the simple case your controller can play the role of mediator orchestrating how different object in your system interact.
回答2:
Instead of:
self.latitude = parse_latlong(exif_data['gpslatitude'])
self.longitude = parse_latlong(exif_data['gpslongitude'])
Use:
update_attributes(
latitude: parse_latlong(exif_data['gpslatitude']),
longitude: parse_latlong(exif_data['gpslongitude'])
)
来源:https://stackoverflow.com/questions/37087454/nomethoderror-in-postscontrollercreate-undefined-method-latitude-saving-lat