Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to disable attribute discovery in ActiveRecord #27903

Closed
wants to merge 1 commit into from
Closed

Allow to disable attribute discovery in ActiveRecord #27903

wants to merge 1 commit into from

Conversation

kirs
Copy link
Member

@kirs kirs commented Feb 4, 2017

This works as an opposite to ignore_columns. Sometimes we want to explicitly declare used columns with Attributes API in the model and use only them, ignoring the rest of columns:

class Cat < ActiveRecord::Base
  self.attribute_auto_discovery = false

  attribute :name, :string
  attribute :engines_count, :integer
  attribute :created_at, :datetime
  attribute :updated_at, :datetime
end

@wvanbergen @rafaelfranca @matthewd

@rails-bot
Copy link

r? @sgrif

(@rails-bot has picked a reviewer for you, use r? to override)

#
# Despite on the fact that `projects` table has more columns that `name` and `members_count`,
# ActiveRecord will only use explicitly declared attributes
def attribute_auto_discovery
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't be this moved to included block and defined via mattr_accessor or class_attribute with default value as another options?

@@ -448,6 +471,11 @@ def load_schema

def load_schema!
@columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)

unless attribute_auto_discovery
@columns_hash.reject! { |k| !attributes_to_define_after_schema_loads.key?(k) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this filtering necessary? Why can't we just skip the schema load entirely? 😕

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This should definitely just be return unless attribute_auto_discovery

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filtering in necessary because otherwise @columns_hash will include all table columns. It's loaded in the line above.

We already do this filter for ignored columns (.except(*ignored_columns))

@sgrif
Copy link
Contributor

sgrif commented Feb 4, 2017

I'd prefer something like define_attributes_from_schema for the option name.

@kaspth
Copy link
Contributor

kaspth commented Feb 5, 2017

Generally if the default is true for a new configuration is true, we invert the name: self.skip_defining_attributes_from_schema = true.

See #26976 (comment) for more.

@wvanbergen
Copy link
Contributor

  • id is automatically defined?
  • Should you define foo_id, or should belongs_to :foo take care of that?

@sgrif
Copy link
Contributor

sgrif commented Feb 7, 2017

id is automatically defined?

id should continue to refer to the primary key. If self.primary_key is set to something other than id, it will need to be defined.

Should you define foo_id, or should belongs_to :foo take care of that?

foo_id should need to be manually defined. The only reason id is treated specially is because the method id exists without the column

@sgrif
Copy link
Contributor

sgrif commented Feb 7, 2017

Note: I want to be conservative to start. We can always change it to have foo_id defined automatically later without breaking backwards compat.

@kirs
Copy link
Member Author

kirs commented Feb 14, 2017

Thanks everyone for the feedback!
I've updated the PR and it's ready for another round of 👀

@@ -149,6 +176,9 @@ module ModelSchema
class_attribute :ignored_columns, instance_accessor: false
self.ignored_columns = [].freeze

class_attribute :skip_defining_attributes_from_schema, instance_accessor: false
self.skip_defining_attributes_from_schema = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we drop the skip_ and have it default to true?


if skip_defining_attributes_from_schema
if ignored_columns.any?
raise ArgumentError, "can't use ignored_columns and skip_defining_attributes_from_schema at the same time"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's handle this in the writer method for define_attributes_from_schema and ignored_columns. We want to error early, and we shouldn't assume this is the only place the two can interact.

raise ArgumentError, "can't use ignored_columns and skip_defining_attributes_from_schema at the same time"
end

if primary_key && !attributes_to_define_after_schema_loads.key?(primary_key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like it should be unnecessary.

Sometimes you want to explicitly declare attributes in the model
with Attributes API and ignore the rest of table columns.
@kirs
Copy link
Member Author

kirs commented Feb 15, 2017

I've addressed all concerns 👌

Let's handle this in the writer method for define_attributes_from_schema and ignored_columns. We want to error early, and we shouldn't assume this is the only place the two can interact.

For that, I had to move away from class_attribute and define custom accessors.

@kirs
Copy link
Member Author

kirs commented Feb 20, 2017

🙏 👀 when you have a minute.

@columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
@columns_hash = connection.schema_cache.columns_hash(table_name)

unless define_attributes_from_schema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the wrong place to handle things. We should not be modifying columns_hash based on this setting (other than perhaps not setting it at all). It seems like we should be skipping the loop down below instead.

@kirs
Copy link
Member Author

kirs commented Jul 27, 2017

@sgrif I'm ready to invest time into finishing this feature and addressing the feedback, but is it something that you'd like to see in ActiveRecord?

@robotdana
Copy link
Contributor

robotdana commented Feb 15, 2019

is this something that is being worked on? I would like to use a feature like this. I'm happy to take finishing this PR if @kirs would like me to

@rafaelfranca
Copy link
Member

@robotdana yes! I was planning to have someone in my team to work on it but we are happy to also review a PR if it is open.

@robotdana
Copy link
Contributor

#35309 I have a PR ready, rebasing and finishing this. Unfortunately @sgrif it looked like not modifying the columns_hash was more complex than not, and if we changed it we should also change the existing implementation of ignored_columns (which does modify the columns_hash)

@kirs
Copy link
Member Author

kirs commented Dec 11, 2019

Closing in favour of #35309

@kirs kirs closed this Dec 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants