【问题标题】:Can Rails Active Storage map to a pre-existing Images table in the database?Rails Active Storage 可以映射到数据库中预先存在的图像表吗?
【发布时间】:2021-10-23 21:01:17
【问题描述】:

我正在开发一个旧版 Rails 应用程序,最近升级到 Rails 5.2。它已经有一个自己滚动的上传图片实现,并且数据库中已经有数百万张图片。 Image型号belongs_to其他型号;他们每个人要么has_many :images要么has_one :image

这是 Image 表的架构:

  create_table "images", id: :integer, unsigned: true, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "content_type", limit: 100
    t.integer "user_id"
    t.date "when"
    t.text "notes"
    t.string "copyright_holder", limit: 100
    t.integer "license_id", default: 1, null: false
    t.integer "num_views", default: 0, null: false
    t.datetime "last_view"
    t.integer "width"
    t.integer "height"
    t.float "vote_cache"
    t.boolean "ok_for_export", default: true, null: false
    t.string "original_name", limit: 120, default: ""
    t.boolean "transferred", default: false, null: false
    t.boolean "gps_stripped", default: false, null: false
  end

部分实现是ImageImage::Url 的子类,它根据请求的大小以及 ImageMagick 已经生成的各种大小获取图像的 URL(在 AWS 上)。

class Image
  class Url
    SUBDIRECTORIES = {
      full_size: "orig",
      huge: "1280",
      large: "960",
      medium: "640",
      small: "320",
      thumbnail: "thumb"
    }.freeze

    SUBDIRECTORY_TO_SIZE = {
      "orig" => :full_size,
      "1280" => :huge,
      "960" => :large,
      "640" => :medium,
      "320" => :small,
      "thumb" => :thumbnail
    }.freeze

    attr_accessor :size, :id, :transferred, :extension

    def initialize(args)
      size = args[:size]
      size = SUBDIRECTORY_TO_SIZE[size] unless size.is_a?(Symbol)
      size = :full_size if size == :original
      self.size        = size
      self.id          = args[:id]
      self.transferred = args[:transferred]
      self.extension   = args[:extension]
    end

    def url
      for source in source_order
        return source_url(source) if source_exists?(source)
      end
      source_url(fallback_source)
    end

    def source_exists?(source)
      spec = format_spec(source, :test)
      case spec
      when :transferred_flag
        transferred
      when /^file:/
        local_file_exists?(spec)
      when /^http:/
        remote_file_exists?(spec)
      when /^https:/
        remote_file_exists?(spec)
      else
        raise("Invalid image source test spec for "\
              "#{source.inspect}: #{spec.inspect}")
      end
    end

    def local_file_exists?(spec)
      File.exist?(file_name(spec)[7..])
    end

    def remote_file_exists?(spec)
      url = URI.parse(file_name(spec))
      result = Net::HTTP.new(url.host, url.port).request_head(url.path)
      result.code == 200
    end

    def source_url(source)
      file_name(format_spec(source, :read))
    end

    def file_name(path)
      "#{path}/#{subdirectory}/#{id}.#{extension}"
    end

    def subdirectory
      SUBDIRECTORIES[size] || raise("Invalid size: #{size.inspect}")
    end

    def source_order
      OurApp.image_precedence[size] || OurApp.image_precedence[:default]
    end

    def fallback_source
      OurApp.image_fallback_source
    end

    def format_spec(source, mode)
      spec = specs(source)[mode]
      spec.is_a?(String) ? format(spec, root: OurApp.root) : spec
    end

    def specs(source)
      OurApp.image_sources[source] ||
        raise("Missing image source: #{source.inspect}")
    end
  end
end

所以我正在考虑是否可以以某种方式迁移我们现有的数据表以使用 Active Storage(此 Rails 应用程序上未安装)。

编辑 上面的课程提出的一个问题,我没有写,是我们建立的图像 URL 结构是否与 Active Storage 兼容。即使将实际的源 url 信息迁移到 blob,这个“url 生成器”也会工作吗?可能我不了解 AWS 存储的工作原理,也许没有真正的“源”网址。

我发现的所有教程和解释器都讨论了在新的 Rails 应用程序上安装 Active Storage,或使用它向现有模型添加附件。那不是我的情况——我已经有一个与其他十几个模型相关的图像模型,并且他们已经知道他们的“附件”,即图像关系是什么。

我的问题是,Active Storage 能否以某种方式利用现有的 Image 表及其关系——或者更恰当地将 Active Storage 理解为这种自行滚动设置的替代方案,即无法与之集成。

There is an SO question 关于“Rails Active Storage without model”似乎暗示 Active Storage 和模型之间可以发生映射。我不明白 Active Storage 和现有 Image 模型之间的关系。据我了解,Image 模型将 has_one_attachedhas_many_attached (因为用户或产品将具有附件)是没有意义的 - 它已经是附件本身的模型。还是我弄错了?

【问题讨论】:

    标签: ruby-on-rails rails-activestorage


    【解决方案1】:

    ActiveStorage 的核心实际上是三个表(和模型),它们在某种程度上对应于您的图像表:

    class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
      def change
        # Use Active Record's configured type for primary and foreign keys
        primary_key_type, foreign_key_type = primary_and_foreign_key_types
    
        create_table :active_storage_blobs, id: primary_key_type do |t|
          t.string   :key,          null: false
          t.string   :filename,     null: false
          t.string   :content_type
          t.text     :metadata
          t.string   :service_name, null: false
          t.bigint   :byte_size,    null: false
          t.string   :checksum,     null: false
    
          if connection.supports_datetime_with_precision?
            t.datetime :created_at, precision: 6, null: false
          else
            t.datetime :created_at, null: false
          end
    
          t.index [ :key ], unique: true
        end
    
        create_table :active_storage_attachments, id: primary_key_type do |t|
          t.string     :name,     null: false
          t.references :record,   null: false, polymorphic: true, index: false, type: foreign_key_type
          t.references :blob,     null: false, type: foreign_key_type
    
          if connection.supports_datetime_with_precision?
            t.datetime :created_at, precision: 6, null: false
          else
            t.datetime :created_at, null: false
          end
    
          t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
          t.foreign_key :active_storage_blobs, column: :blob_id
        end
    
        create_table :active_storage_variant_records, id: primary_key_type do |t|
          t.belongs_to :blob, null: false, index: false, type: foreign_key_type
          t.string :variation_digest, null: false
    
          t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
          t.foreign_key :active_storage_blobs, column: :blob_id
        end
      end
    
      private
        def primary_and_foreign_key_types
          config = Rails.configuration.generators
          setting = config.options[config.orm][:primary_key_type]
          primary_key_type = setting || :primary_key
          foreign_key_type = setting || :bigint
          [primary_key_type, foreign_key_type]
        end
    end
    

    从迁移中可以看出,它使用active_storage_blobs 来存储有关所存储文件的实际信息。一个 blob 也可以有多个变体。

    active_storage_attachments 通过多态关联将 blob 与资源(附加附件的模型)连接起来。这使您可以将has_one_attached/has_many_attached 添加到应用程序中的任何模型,而无需添加任何其他数据库列或表。

    所以我正在考虑 Active Storage(未安装在此 Rails 应用程序上)是否可以与此现有设置一起使用。

    让我们这么说吧 - 您不应该期望您可以将旧数据插入并播放到 ActiveStorage 中。它是一款非常自以为是的软件,主要围绕能够以最少的配置插入任意数量的模型的目标而设计。

    ActiveStorage 可能与您现有的设置协同工作(将其替换为新记录),但用 AS 替换旧代码很可能需要进行大量数据迁移,您还需要对 AS 的工作原理有很好的了解.

    我不明白 Active Storage 和现有图像模型之间的关系。

    那是因为真的没有。 ActiveSupport::AttachmentActiveSupport::Blob 为 Rails 应用程序中的所有具有附件的模型提供相同的作用。它的设计并未考虑到旧版支持。

    【讨论】:

    • 我很欣赏这个非常彻底的答案......并且觉得我们应该坚持我们所拥有的。除非必要,否则我们不会优先考虑大量数据迁移。
    • @nimmolo 这听起来是个不错的主意,除非现有代码中存在特定缺陷使其无法成立。 AS 的主要价值确实在于它使设置文件上传变得非常容易,但它的模块化肯定是有代价的,而不是定制的解决方案。
    • 感谢@max。我们的定制/黑客解决方案在一段时间内运行良好。但是,保持/使代码可由他人维护也是一个优先事项,尽可能保持现代和“按书本”。如果您喜欢看游戏,我将在原始问题中附加一些代码:我们的 Images 表的架构,以及我们的 Image::Url 类的相关方法,以防万一它成为一个陷阱迁移。例如,我想将数据迁移到 Blob 表可能需要调用这些 Image::Url 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多