带有 Neo4j 的 Carrierwave 不将图像关联保存到数据库

Carrierwave with Neo4j not saving image assocation to DB

Rails 4 应用程序与 neo4j 和 neo4j.rb gem using carrierwave-neo4j 将图像附加到报告对象。

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 可以很好地上传,但同样缺少关联。

这是我的载波 class 对象。

#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 宝石,因为当我尝试使用你的模型时它指出你没有指定 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#indexes http://neo4jrb.readthedocs.org/en/5.2.x/ActiveNode.html#constraints

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

我最终放弃了 Carrierwave 并选择 Paperclip. The gems we're using with paperclip are as follows for anyone else having trouble with getting image uploads to play nice with neo4j.rb. The connector gem for paperclip is the neo4j.rb team's paperclip-fork

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