rails icon indicating copy to clipboard operation
rails copied to clipboard

Cannot create model with attached activestorage file with strict_loading set to true

Open javinto opened this issue 3 years ago • 3 comments

Steps to reproduce

Create a simple model with

has_one_attached :profile, strict_loading: true

Then create the model with a such a profile (file).

An ActiveRecord::StrictLoadingViolationError is raised while the model is being created.

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "rails", "~> 7.0.0"
  gem "sqlite3"
end

require "active_record/railtie"
require "active_storage/engine"
require "tmpdir"

class TestApp < Rails::Application
  config.root = __dir__
  config.hosts << "example.org"
  config.eager_load = false
  config.session_store :cookie_store, key: "cookie_store_key"
  secrets.secret_key_base = "secret_key_base"

  config.logger = Logger.new($stdout)
  Rails.logger  = config.logger

  config.active_storage.service = :local
  config.active_storage.service_configurations = {
    local: {
      root: Dir.tmpdir,
      service: "Disk"
    }
  }
end

ENV["DATABASE_URL"] = "sqlite3::memory:"

Rails.application.initialize!

require ActiveStorage::Engine.root.join("db/migrate/20170806125915_create_active_storage_tables.rb").to_s

ActiveRecord::Schema.define do
  CreateActiveStorageTables.new.change

  create_table :users, force: true
end

class User < ActiveRecord::Base
  has_one_attached :profile, strict_loading: true
end

require "minitest/autorun"

class BugTest < Minitest::Test
  def test_upload_and_download
    user = User.create!(
      profile: {
        content_type: "text/plain",
        filename: "dummy.txt",
        io: ::StringIO.new("dummy"),
      }
    )

    assert_equal "dummy", user.profile.download
  end
end

Expected behavior

The strict_loading validation should not occur on creating the record, only on reading it.

Actual behavior

An ActiveRecord::StrictLoadingViolationError is raised.

System configuration

Rails 7.0.2.3:

Ruby 3.1.2:

javinto avatar Apr 23 '22 20:04 javinto

Hi! I have been digging into this issue, and I found that it happens when we initialize a model class sending the file as an argument. e.g.

class Admin < User
  has_one_attached :avatar, strict_loading: true
end

user = Admin.new(name: "Jason", avatar: create_blob(filename: "funky.jpg"))
user.save!

# ActiveRecord::StrictLoadingViolationError

The rule is being applied even if it is initialization and not a query. I understand that strict loading prevents the user from accessing lazy-loaded associations of a query result. So it doesn't make sense to avoid lazy loading when creating or initializing a model. I think that a good solution would be to check in the ActiveRecord::Associations::Association#violates_strict_loading? if the owner was previously a new record previously_new_record? for ignoring the rule in that case. it can be something like this =>

# activerecord/lib/active_record/associations/association.rb
def violates_strict_loading?
  return if owner.previously_new_record?
  return reflection.strict_loading? if reflection.options.key?(:strict_loading)

  owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
end

It's just a thought. I tested this in my local machine, and all the ActiveStorage tests passed, and also the issue got fixed, but I'm not sure if this can break other parts of the code 🤔

neriojnavea avatar May 30 '22 16:05 neriojnavea

Hey @neriojnavea ! I'd also been digging into this a bit, but had shelved it to focus on some other things.

So it doesn't make sense to avoid lazy loading when creating or initializing a model

I think a problem here is that we not only want to avoid the strict loading error when initializing a model with an attachment, but also when using the #attach API. For example:

class Admin < User
  has_one_attached :avatar, strict_loading: true
end

user = Admin.new(name: "Jason")
user.avatar.attach(create_blob) # This currently raises ActiveRecord::StrictLoadingViolationError

In which case, checking whether the attachment owner is a new record doesn't help us.

I think we need a way to only check for strict loading violations when a model's attachment(s) are loaded. I haven't found the right spot for this yet though 🤔

adrianna-chang-shopify avatar Jun 01 '22 15:06 adrianna-chang-shopify

This issue has been automatically marked as stale because it has not been commented on for at least three months. The resources of the Rails team are limited, and so we are asking for your help. If you can still reproduce this error on the 7-0-stable branch or on main, please reply with all of the information you have about it in order to keep the issue open. Thank you for all your contributions.

rails-bot[bot] avatar Aug 30 '22 15:08 rails-bot[bot]

Would you mean?

user = Admin.create(name: “Jason”) user.avatar.attach(create_blob)

to clarify that there should not be any check on strict_loading when adding an attachment on an existing record (parent) ?

Op 1 jun. 2022, om 17:18 heeft Adrianna Chang @.***> het volgende geschreven:

Hey @neriojnavea https://github.com/neriojnavea ! I'd also been digging into this a bit, but had shelved it to focus on some other things.

So it doesn't make sense to avoid lazy loading when creating or initializing a model

I think a problem here is that we not only want to avoid the strict loading error when initializing a model with an attachment, but also when using the #attach https://edgeapi.rubyonrails.org/classes/ActiveStorage/Attached/One.html#method-i-attach API. For example:

class Admin < User has_one_attached :avatar, strict_loading: true end

user = Admin.new(name: "Jason") user.avatar.attach(create_blob) # This currently raises ActiveRecord::StrictLoadingViolationError In which case, checking whether the attachment owner is a new record doesn't help us.

I think we need a way to only check for strict loading violations when a model's attachment(s) are loaded. I haven't found the right spot for this yet though 🤔

— Reply to this email directly, view it on GitHub https://github.com/rails/rails/issues/44946#issuecomment-1143745202, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACY2N5VLUDLTLJO4VKNSE3VM55MXANCNFSM5UFGMLOA. You are receiving this because you authored the thread.

javinto avatar Oct 11 '22 07:10 javinto