PostsController 中的 NoMethodError#create undefined method `latitude=' "saving lat and long to connected tables!!"

NoMethodError in PostsController#create undefined method `latitude=' "saving lat and long to connected tables!!"

我正在尝试 post 我用回形针上传的图像元数据中的这些坐标到另一个 table 地方。"so coordinates go to places table." 这些地方有纬度和经度列table。提交后 post 我 运行 进入这个错误。错误的突出显示部分是 self.latitude=parse_latlong(等....)。

post_id 是 table 处的外键。当我在 post table 中有纬度和经度时,这以前有效。但现在我给了它自己的 table 以获得更好的数据库结构。如果这是主要问题,我只需要知道如何让我的 post 控制器与我的 places 控制器一起工作??

地点控制器

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 控制器

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 型号

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

总而言之,我想从 exif 提取中将纬度和经度变量更新到数据库中。提取不是问题,但我相信我将该信息保存到数据库中的方式是真实的问题!!!谢谢!!!

而不是:

self.latitude = parse_latlong(exif_data['gpslatitude'])
self.longitude = parse_latlong(exif_data['gpslongitude'])

使用:

update_attributes(
  latitude: parse_latlong(exif_data['gpslatitude']),
  longitude: parse_latlong(exif_data['gpslongitude'])
)

问题似乎源于您正在更新关联模型的属性。您可以通过调用

update_attributes(place_attributes: {latitude: , longitude: })

但我建议将此逻辑排除在 posts 模型之外,这对于将原始用户输入转换为数据库可使用格式的表单模型来说确实是一个问题。如果您不想向您的应用程序添加额外的层,至少将这些方法移动到它们自己的模型中。 每次,您都会看到一组私有方法互相调用并传递状态,我认为这是一个好兆头,它们应该 class: 所以

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

请注意,我还将封装 MiniExiftool 并将其作为依赖项注入构造函数中。但我们不要忘记我们的目标。

然后在你的控制器中你可以调用你的解析器给你位置参数

def place_params
  long_lat_parser = LongLatParser(image)
  {place_attributes: {longitude:  long_lat_parser.lon,  latitude: long_lat_parser.lat}}
end

然后简单地将它们合并到 post 参数中:

@post = current_user.posts.build(post_params.merge(place_params))

这种方法的好处是您引入了一个具有明确责任的对象,并作为单纯的数据库包装器返回到 AR 模型。一般来说,我尝试将更复杂的交互封装在某种服务对象中,但在简单的情况下,您的控制器可以扮演中介者的角色,协调系统中不同对象的交互方式。