clowne-rb/clowne

Is it possible to ignore an attribute?

Closed this issue · 3 comments

I know about nullify, but I would like to ignore an attribute all together. This is needed when cloning an object that contains a full text search field (TS_VECTOR) like we have when using Postgresql full text search.

palkan commented

How will ignore differ from nullify? Can you please provide an example?

Nullify will save a null value into the new record. Postgres full text search fields are updated (set) automatically by Postgres and do not accept null update. This generates an error.

Example:

This checklists table as a full text search fields searchable (last one). The content of this field is autogenerated by Postgres when creating or updating a record.

                                                                                                                                                 Table "public.checklists"
        Column         |            Type             | Collation | Nullable |                                                                                                                        Default
-----------------------+-----------------------------+-----------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 id                    | bigint                      |           | not null | nextval('checklists_id_seq'::regclass)
 type                  | character varying(55)       |           | not null |
 title                 | character varying(256)      |           | not null |
 description           | character varying           |           |          |
 is_completed          | boolean                     |           | not null | false
...
...
 completion_percentage | integer                     |           | not null | 0
 searchable            | tsvector                    |           |          | generated always as (setweight(to_tsvector('english'::regconfig, COALESCE(title, ''::character varying)::text), 'A'::"char") || setweight(to_tsvector('english'::regconfig, COALESCE(description, ''::character varying)::text), 'B'::"char")) stored

If we update to nil we get an error. First let's load the record with the console:

$ rails console
Loading development environment (Rails 6.1.3.2)
[1] pry(main)> checklist = Checklist.first
  Checklist Load (2.5ms)  SELECT "checklists".* FROM "checklists" ORDER BY "checklists"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> #<ChecklistTemplate:0x000000010f6a7b28>

Then we set the field to nil to simulate clowne nullify system and we save we have an error:

[2] pry(main)> c.searchable=nil
=> nil
[3] pry(main)> c.save
  TRANSACTION (0.6ms)  BEGIN
  Project Load (1.3ms)  SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ChecklistTemplate Update (2.6ms)  UPDATE "checklists" SET "updated_at" = $1, "searchable" = $2 WHERE "checklists"."id" = $3  [["updated_at", "2023-05-20 16:54:04.457417"], ["searchable", nil], ["id", 1]]
  TRANSACTION (0.5ms)  ROLLBACK
ActiveRecord::StatementInvalid: PG::GeneratedAlways: ERROR:  column "searchable" can only be updated to DEFAULT
DETAIL:  Column "searchable" is a generated column.

from /Users/supercobra/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/rack-mini-profiler-3.1.0/lib/patches/db/pg.rb:69:in `exec_params'
Caused by PG::GeneratedAlways: ERROR:  column "searchable" can only be updated to DEFAULT
DETAIL:  Column "searchable" is a generated column.

Hence the need to be able to ignore an attribute since nullify does not work in this case.

palkan commented

Thanks for the details; they're very helpful.

I think, in this case, defining an after_clone callback with the restore_attributes(%w[searchable]) should do the trick (restore_attributes docs).