GenieFramework / SearchLight.jl

ORM layer for Genie.jl, the highly productive Julia web framework
https://genieframework.com
MIT License
139 stars 16 forks source link

Can't update models with is_unique Validator #44

Open mcvmcv opened 2 years ago

mcvmcv commented 2 years ago

Describe the bug Consider this model:

@kwdef mutable struct Mymodel <: AbstractModel
    id::DbId = DbID()
    name::String = ""
    updatefield::String = ""

Validation.validator([m::Type{Mymodel}) = ModelValidator([
    ValdiationRule(:name, MymodelsValidator.is_unique),
])

Create and save one:

julia > m = Mymodel(name="foo", updatefield = "bar") |> save!

Then try to edit it:

julia > m.updatefield = "baz"
julia > save!(m)

The validator sees the already existing Mymodel with name "foo" in the database, and doesn't let you save the updated one.

Error stacktrace

julia> save!(m)
[ Info: 2022-01-14 13:39:54 SELECT "mymodels"."id" AS "mymodels_id", "mymodels"."name" AS "mymodels_name", "mymodels"."updatefield" AS "mymodels_updatefield" FROM "mymodel" WHERE "name" = 'foo' ORDER BY mymodels.id ASC
[ Info: 2022-01-14 13:39:54 SELECT "mymodel"."id" AS "mymodels_id", "mymodels"."name" AS "mymodels_name", "mymodels"."updatefield" AS "mymodels_updatefield" FROM "mymodels" WHERE "updatefield" = 'baz' ORDER BY mymodels.id ASC
ERROR: SearchLight.Exceptions.InvalidModelException{Mymodel}(Mymodel
| KEY          | VALUE |
|--------------|-------|
| updatefield::String  | baz   |
| id::DbId     | 16    |
| name::String | foo   |
, SearchLight.Validation.ValidationError[SearchLight.Validation.ValidationError(:name, :is_unique, "already exists")], "Model Mymodel is not valid: Name already exists")
Stacktrace:
 [1] _save!!(m::Mymodel; conflict_strategy::Symbol, skip_validation::Bool, skip_callbacks::Vector{Symbol})
   @ SearchLight ~/.julia/packages/SearchLight/B9d2o/src/SearchLight.jl:194
 [2] save!!(m::Mymodel; conflict_strategy::Symbol, skip_validation::Bool, skip_callbacks::Vector{Symbol})
   @ SearchLight ~/.julia/packages/SearchLight/B9d2o/src/SearchLight.jl:164
 [3] #save!#40
   @ ~/.julia/packages/SearchLight/B9d2o/src/SearchLight.jl:161 [inlined]
 [4] save!(m::Mymodel)
   @ SearchLight ~/.julia/packages/SearchLight/B9d2o/src/SearchLight.jl:161
 [5] top-level scope
   @ REPL[835]:1

If any error is thrown, please copy from the REPL and paste it here

To reproduce As above

Expected behavior SearchLight would recognise the object in the DB as the same as the one you are updating, and allow you to update it

Additional context Please include the output of julia> versioninfo() and pkg> st

julia> versioninfo()
Julia Version 1.6.2
Commit 1b93d53fc4 (2021-07-14 15:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i3-6100 CPU @ 3.70GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)
Environment:
  JULIA_REVISE = auto

and

(Myproject) pkg> st
     Project Myproject v0.1.0
      Status `~/projects/Myproject/Project.toml`
  [336ed68f] CSV v0.9.11
  [c43c736e] Genie v4.9.0
  [e115e502] GenieAuthentication v1.1.0
  [6d011eab] Inflector v1.0.1
  [e6f89c97] LoggingExtras v0.4.7
  [739be429] MbedTLS v1.0.3
  [0aa819cd] SQLite v1.3.0
  [340e8cb6] SearchLight v2.0.1
  [21a827c4] SearchLightSQLite v2.0.0
  [4acbeb90] Stipple v0.19.3
  [30ddb3f0] StippleCharts v0.16.0
  [a3c5d34a] StippleUI v0.14.3
  [ade2ca70] Dates
  [56ddb016] Logging
essenciary commented 2 years ago

@mcvmcv Thanks for reporting this. I'm pretty sure that's how it's supposed to work. Let's see...

1/ the model validator is in the user/app space and it's designed to be modified by the user as needed. 2/ the app outputs a snippet/template as the starting point - which seems to be old and incompatible with the DbId logic - and we should update. 3/ in one of my recent apps I implemented this. Can you try it? If good, we can update the template so we use it:

function is_unique(field::Symbol, m::T, args::Vararg{Any})::ValidationResult where {T<:AbstractModel}
  samename = findone(typeof(m); NamedTuple(field => getfield(m, field))... )
  samename === nothing || samename.id == m.id ||
    return ValidationResult(invalid, :is_unique, "already exists")

  ValidationResult(valid)
end
mcvmcv commented 2 years ago

Ah, so I should have named the topic "Can't update models with default is_unique Validator"?

Your new version works like a charm, many thanks!