Skip to content

Commit 1e282be

Browse files
authored
Merge pull request #222 from activeadmin-plugins/docs/update-by-id-strategies
docs: split id-update into upsert vs delete-then-insert strategies
2 parents 3b01e3e + d6f350b commit 1e282be

1 file changed

Lines changed: 32 additions & 28 deletions

File tree

README.md

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -225,48 +225,52 @@ end
225225

226226
##### Update existing records by id
227227

228-
Delete colliding rows just before each batch insert:
228+
Two strategies, depending on your database and whether you need validations.
229+
230+
###### Native upsert (recommended where supported)
231+
232+
On databases that support upserts (MySQL, PostgreSQL 9.5+, SQLite 3.24+),
233+
`:on_duplicate_key_update` updates colliding rows and inserts new ones in a
234+
single statement — no extra delete. The option is passed straight to
235+
`activerecord-import`, so its shape depends on the adapter:
229236

230237
```ruby
231-
ActiveAdmin.register Post do
232-
active_admin_import before_batch_import: ->(importer) {
233-
Post.where(id: importer.values_at('id')).delete_all
234-
}
235-
end
236-
```
238+
# PostgreSQL / SQLite
239+
on_duplicate_key_update: { conflict_target: [:id], columns: %i[name last_name birthday] }
237240

238-
On databases that support upserts (MySQL, PostgreSQL 9.5+, SQLite 3.24+) you can
239-
update colliding rows and insert new ones in a single pass with
240-
`:on_duplicate_key_update` — no `delete_all` required:
241+
# MySQL (infers the key from the columns)
242+
on_duplicate_key_update: %i[name last_name birthday]
243+
```
241244

242245
```ruby
243246
ActiveAdmin.register Author do
244-
# PostgreSQL / SQLite
245247
active_admin_import validate: false,
246-
on_duplicate_key_update: {
247-
conflict_target: [:id],
248-
columns: %i[name last_name birthday]
249-
}
248+
on_duplicate_key_update: { conflict_target: [:id], columns: %i[name last_name birthday] }
250249
end
251250
```
252251

253252
Notes:
254253

255-
* The option shape is **adapter-specific**, since it is passed straight to
256-
`activerecord-import`:
257-
* PostgreSQL / SQLite need an explicit `:conflict_target` — the unique
258-
column(s) used to detect a collision (`[:id]` for the primary key).
259-
* MySQL infers the conflicting key, so pass just the column list and omit
260-
`:conflict_target` (passing it raises `Unknown column 'conflict_target'`):
261-
262-
```ruby
263-
on_duplicate_key_update: %i[name last_name birthday]
264-
```
265-
* Turn `validate` off for id-based upserts. `activerecord-import` runs
266-
uniqueness validations against the very rows the upsert is about to overwrite,
267-
so a model-level `validates_uniqueness_of` would otherwise reject the update.
254+
* Only the columns you list are updated; other columns on the existing row keep their values.
255+
* Use `validate: false``activerecord-import` runs uniqueness validations against the very rows the upsert is about to overwrite, so `validates_uniqueness_of` would otherwise reject the update.
268256
* Active Record callbacks are not fired for bulk imports.
269257

258+
###### Delete-then-insert (any database)
259+
260+
When you can't rely on upsert support — an older database, or you need your model
261+
validations to run — delete the colliding rows just before each batch insert.
262+
The old row is gone before the insert, so `validates_uniqueness_of` doesn't trip,
263+
at the cost of a second query and full-row **replacement** (columns absent from
264+
the CSV are reset, not preserved):
265+
266+
```ruby
267+
ActiveAdmin.register Post do
268+
active_admin_import before_batch_import: ->(importer) {
269+
Post.where(id: importer.values_at('id')).delete_all
270+
}
271+
end
272+
```
273+
270274
##### Tune batch size
271275

272276
```ruby

0 commit comments

Comments
 (0)