Nim - Norm - 如何在编译时获取相关字段名
Nim - Norm - How to get related fieldnames at compile time
嘿嘿,
我正在使用 norm,一种 nim 编程语言中的 orm。
我有 2 个不同的模型,例如:
import std/options
import norm
type
A {.tableName: "Surprise".} = ref object of Model
name: string
Surprise = ref object of Model
name: string
anotherFieldThatExistsOnTheSQLTableButNotOnA: int
B = ref object of Model
name: string
myA: Option[A]
我希望能够在编译时找出指向给定 table(此处 Surprise
)的给定外键字段(此处 myA
)的名称) 即使模型的名称与实际 table 不同或者是只读模型(例如 A)。这样我就可以在编译时编写 SQL 查询,从而获得许多多对一关系。
更重要的是,我希望这种外键关系的抓取基于模型的 tableName
,而不是模型本身。因此,如果我要定义一个过程 getRelatedFieldName(startType: typedesc[A], otherType: typedesc[B])
,它需要为 getRelatedFieldName(A, B)
和 getRelatedFieldName(A, Surprise)
.
提供相同的结果
我怎样才能做到这一点?
感谢 nim discord 服务器上非常有帮助的人们的一些提示,我能够编写解决方案。
答案是:Nim 的泛型,Nim 的 getCustomPragmaVal
宏和 Norm 的 table
模板。
下面的代码采用 2 种模型类型。它虚拟实例化了 sourceType
,因为这是可能对您的 targetType
具有外键字段的类型。然后它遍历 sourceType
的字段并检查它们是否直接是模型类型,是否用 fk
aka foreignKey pragma 注释,或者是 Option[Model]
类型。
如果该字段具有 Model
类型,则问题已解决,因为您只需调用 Model.table()
即可。如果该字段有一个 fk
pragma,您可以简单地调用 getCustomPragmaVal
来获取该字段是其外键的模型。有了它,你就有了类型,就可以调用 table()
了。最后,您可能有一个 Option[Model]
类型。在这种情况下,您需要使用 genericParams function (see ) 提取通用参数。这样您就可以再次访问该类型并对其调用 table()
。
proc getRelatedFieldName[M: Model, O:Model](targetType: typedesc[O], sourceType: typedesc[M]): Option[string] =
let source = sourceType()
for sourceFieldName, sourceFieldValue in source[].fieldPairs:
#Handles case where field is an int64 with fk pragma
when sourceFieldValue.hasCustomPragma(fk):
when O.table() == sourceFieldValue.getCustomPragmaVal(fk).table():
return some(sourceFieldName)
#Handles case where field is a Model type
when sourceFieldValue is Model:
when O.table() == sourceFieldValue.type().table():
return some(sourceFieldName)
#Handles case where field is a Option[Model] type
when sourceFieldValue is Option:
when sourceFieldValue.get() is Model:
when O.table() == genericParams(sourceFieldValue.type()).get(0).table():
return some(sourceFieldName)
return none(string)
例子
type
A = ref object of Model # <-- has implicit tableName "A"
name: string
AC {.tableName: "A".} = ref object of Model
myothername: string
name: string
B = ref object of Model # <-- has implicit tableName "B"
name: string
myA: Option[A]
D = ref object of Model
myothernameid: int
myDA: A
E = ref object of Model
myotherbool: bool
myEA {.fk: A.}: int64
echo A.getRelatedFieldName(B) # some("myA")
echo AC.getRelatedFieldName(B) # some("myA")
echo A.getRelatedFieldName(D) # some("myDA")
echo AC.getRelatedFieldName(D) # some("myDA")
echo A.getRelatedFieldName(E) # some("myEA")
echo AC.getRelatedFieldName(E) # some("myEA")
嘿嘿,
我正在使用 norm,一种 nim 编程语言中的 orm。 我有 2 个不同的模型,例如:
import std/options
import norm
type
A {.tableName: "Surprise".} = ref object of Model
name: string
Surprise = ref object of Model
name: string
anotherFieldThatExistsOnTheSQLTableButNotOnA: int
B = ref object of Model
name: string
myA: Option[A]
我希望能够在编译时找出指向给定 table(此处 Surprise
)的给定外键字段(此处 myA
)的名称) 即使模型的名称与实际 table 不同或者是只读模型(例如 A)。这样我就可以在编译时编写 SQL 查询,从而获得许多多对一关系。
更重要的是,我希望这种外键关系的抓取基于模型的 tableName
,而不是模型本身。因此,如果我要定义一个过程 getRelatedFieldName(startType: typedesc[A], otherType: typedesc[B])
,它需要为 getRelatedFieldName(A, B)
和 getRelatedFieldName(A, Surprise)
.
我怎样才能做到这一点?
感谢 nim discord 服务器上非常有帮助的人们的一些提示,我能够编写解决方案。
答案是:Nim 的泛型,Nim 的 getCustomPragmaVal
宏和 Norm 的 table
模板。
下面的代码采用 2 种模型类型。它虚拟实例化了 sourceType
,因为这是可能对您的 targetType
具有外键字段的类型。然后它遍历 sourceType
的字段并检查它们是否直接是模型类型,是否用 fk
aka foreignKey pragma 注释,或者是 Option[Model]
类型。
如果该字段具有 Model
类型,则问题已解决,因为您只需调用 Model.table()
即可。如果该字段有一个 fk
pragma,您可以简单地调用 getCustomPragmaVal
来获取该字段是其外键的模型。有了它,你就有了类型,就可以调用 table()
了。最后,您可能有一个 Option[Model]
类型。在这种情况下,您需要使用 genericParams function (see table()
。
proc getRelatedFieldName[M: Model, O:Model](targetType: typedesc[O], sourceType: typedesc[M]): Option[string] =
let source = sourceType()
for sourceFieldName, sourceFieldValue in source[].fieldPairs:
#Handles case where field is an int64 with fk pragma
when sourceFieldValue.hasCustomPragma(fk):
when O.table() == sourceFieldValue.getCustomPragmaVal(fk).table():
return some(sourceFieldName)
#Handles case where field is a Model type
when sourceFieldValue is Model:
when O.table() == sourceFieldValue.type().table():
return some(sourceFieldName)
#Handles case where field is a Option[Model] type
when sourceFieldValue is Option:
when sourceFieldValue.get() is Model:
when O.table() == genericParams(sourceFieldValue.type()).get(0).table():
return some(sourceFieldName)
return none(string)
例子
type
A = ref object of Model # <-- has implicit tableName "A"
name: string
AC {.tableName: "A".} = ref object of Model
myothername: string
name: string
B = ref object of Model # <-- has implicit tableName "B"
name: string
myA: Option[A]
D = ref object of Model
myothernameid: int
myDA: A
E = ref object of Model
myotherbool: bool
myEA {.fk: A.}: int64
echo A.getRelatedFieldName(B) # some("myA")
echo AC.getRelatedFieldName(B) # some("myA")
echo A.getRelatedFieldName(D) # some("myDA")
echo AC.getRelatedFieldName(D) # some("myDA")
echo A.getRelatedFieldName(E) # some("myEA")
echo AC.getRelatedFieldName(E) # some("myEA")