带有Neo4j的Carrierwave没有将图像关联保存到DB



Rails 4应用程序和neo4j.rb gem使用carrierwave-neo4j将图像附加到Report对象。

2.0.0-p353 :001 > r = Report.find_by(name: 'my new report')
 => #<Report avatar: #<AvatarUploader:0x0000000643d950 @model=#<Report avatar: #<AvatarUploader:0x0000000643d950 ...>, created_at: Thu, 19 Nov 2015 14:25:58 +0000, created_by: nil, description: nil, embedJSON: nil, gridsize: nil, name: "my new report", tableau_link: nil, thumbnail_uri: nil, timestamp: nil, type: nil, updated_at: Thu, 19 Nov 2015 15:01:18 +0000, updated_by: nil>, @mounted_as=:avatar>, created_at: Thu, 19 Nov 2015 14:25:58 +0000, created_by: nil, description: nil, embedJSON: nil, gridsize: nil, name: "my new report", tableau_link: nil, thumbnail_uri: nil, timestamp: nil, type: nil, updated_at: Thu, 19 Nov 2015 15:01:18 +0000, updated_by: nil>
2.0.0-p353 :002 > File.open('app/assets/images/nd-gray.png') { |f| r.avatar = f }
 => #<File:app/assets/images/nd-gray.png (closed)>
2.0.0-p353 :003 > r.avatar.url
 => "/vagrant/fenrir/tmp/uploads/1447945541-17455-0224/nd-gray.png"
2.0.0-p353 :004 > r.save
 => true
2.0.0-p353 :005 > r.avatar.url
 => "/uploads/development/Report/nd-gray.png"

目前一切都很顺利。但是,当我尝试重新加载Report对象时,关联会消失,就像从未发生过一样。

2.0.0-p353 :006 > r = Report.find_by(name: 'my new report')
 => #<Report avatar: #<AvatarUploader:0x00000004205108 @model=#<Report avatar: #<AvatarUploader:0x00000004205108 ...>, created_at: Thu, 19 Nov 2015 14:25:58 +0000, created_by: nil, description: nil, embedJSON: nil, gridsize: nil, name: "my new report", tableau_link: nil, thumbnail_uri: nil, timestamp: nil, type: nil, updated_at: Thu, 19 Nov 2015 15:05:53 +0000, updated_by: nil>, @mounted_as=:avatar>, created_at: Thu, 19 Nov 2015 14:25:58 +0000, created_by: nil, description: nil, embedJSON: nil, gridsize: nil, name: "my new report", tableau_link: nil, thumbnail_uri: nil, timestamp: nil, type: nil, updated_at: Thu, 19 Nov 2015 15:05:53 +0000, updated_by: nil>
2.0.0-p353 :007 > r.avatar.url
 => nil
2.0.0-p353 :007 > r.avatar.path
 => nil

切换到:aws-upload而不是:file可以很好地进行上传,但缺少相同的关联。

这是我的carrierwave类对象。

#app/uploaders/avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base
  # Choose what kind of storage to use for this uploader:
  storage :file
  # Override the directory where uploaded files will be stored.
  def store_dir
    "uploads/#{Rails.env}/#{model.class}/"
  end
  # Add a white list of extensions which are allowed to be uploaded.
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

和载波初始化器

#config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.storage    = :file
  config.aws_bucket = ENV['S3_BUCKET_NAME']
  config.aws_acl  = 'private'
  config.aws_credentials = {
    access_key_id:     ENV['S3_KEY'],
    secret_access_key: ENV['S3_SECRET'],
    region:            ENV['S3_REGION'] # Required
  }
  # The maximum period for authenticated_urls is only 10 minutes.
  # config.aws_authenticated_url_expiration = 60 * 60 * 24 * 7
  # # Set custom options such as cache control to leverage browser caching
  # config.aws_attributes = {
  #   expires: 7.days.from_now.httpdate,
  #   cache_control: 'max-age=60480'
  # }
  config.cache_dir = "#{Rails.root}/tmp/uploads/" # To let CarrierWave work on heroku
  # config.fog_directory    = ENV['S3_BUCKET_NAME']
  #config.s3_access_policy = :public_read   # Generate http:// urls. Defaults to :authenticated_read (https://)
  #config.fog_host         = "#{ENV['S3_ASSET_URL']}/#{ENV['S3_BUCKET_NAME']}"
end

最后,报告模型本身就是

#app/models/report.rb
class Report
  include Neo4j::ActiveNode
  searchkick word_start: [:name], autocomplete: [:name]
  validates_presence_of :name
  validates_uniqueness_of :name, case_sensitive: false
  property :avatar, type: String
  mount_uploader :avatar, AvatarUploader
  def search_data
    {
      name: name,
      description: description
    }
  end
  property              :name
  property              :description
  property              :tableau_link
  property              :type
  property              :thumbnail_uri
  property              :gridsize
  property              :timestamp
  property              :embedJSON
  property              :created_at
  property              :updated_at
  property              :created_by
  property              :updated_by
  has_many :in,         :terms
  has_one  :in,         :office
  def selectable_terms
    @selectable_terms = []
    self.terms.each do |t|
      @selectable_terms << { id: t.id, text: t.name }
    end
    @selectable_terms.to_json
  end
  def update_terms(param_terms)
    param_terms ||= []
    term_instances = []
    param_terms.each do |t|
      term_instances << Term.find_by(name: t)
    end
    term_instances
  end
  def aggro_extro
    embedJSON.present? ? JSON.parse(embedJSON) : Hash.new
  end
end

我能想到的一个原因是,我们使用:name字段作为唯一标识符。也许carrierwave正在寻找UUID?

另一种可能性是carrierwave正在缓存关联。

我刚试过,它似乎起作用了。不过,我猜您使用的是neo4j/neo4j-core gems的旧版本,因为当我尝试使用您的模型时,它指出您没有指定type选项。从版本5.0.0起,这是必需的。你能更新到5.2.0系列看看是否能解决这个问题吗?还要注意,6.0.0很快就会发布(候选版本已经发布)。

我要提到的另一件事是,您可能应该在模型中为name属性指定一个索引。这在按名称查询时应该会有所帮助。由于您正在执行validates_uniqueness_of,您甚至可能想要指定一个约束。约束不会以不区分大小写的方式确保值是唯一的,但它会在数据库级别为您执行一些唯一性约束。您仍然需要validates_uniqueness_ofcase_sensitive: false。请参阅以下文档:

http://neo4jrb.readthedocs.org/en/5.2.x/ActiveNode.html#indexeshttp://neo4jrb.readthedocs.org/en/5.2.x/ActiveNode.html#constraints

还要注意,约束会自动创建索引,因此不需要同时指定这两个索引(事实上,在6.0.0中,我们不再允许您这样做)。

我最终放弃了Carrierwave,转而使用Paperclip。我们在回形针中使用的宝石如下所示,供其他在上传图像时遇到困难的人使用。回形针的连接器宝石是neo4j.rb团队的回形针叉子

gem 'rails', '4.0.2'
gem 'neo4j', '~> 4.1.1'
gem 'neo4jrb-paperclip', github: 'subvertallchris/neo4jrb-paperclip', require: 'neo4jrb_paperclip'
gem 'aws-sdk-v1'

新的报表模型-

class Report
  include Neo4j::ActiveNode
  include Neo4jrb::Paperclip
  has_neo4jrb_attached_file :avatar,
                            storage: :s3,
                            s3_permissions: :private,
                            default_url: '/assets/images/reports-icon-white.svg',
                            s3_credentials:
                              Proc.new { |a| a.instance.s3_credentials }
  validates_attachment_content_type :avatar, 
                                    content_type: ['image/jpg',
                                                   'image/jpeg',
                                                   'image/png',
                                                   'image/gif']
  # Rather than rename our variables in our secrets file, just rename them here
  def s3_credentials
    {
      bucket: ENV['S3_BUCKET_NAME'],
      access_key_id: ENV['S3_KEY'],
      secret_access_key: ENV['S3_SECRET']
    }
  end

最新更新