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")