1

嘿嘿,

我正在使用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]

我希望能够在编译时找出myA指向给定表(这里Surprise)的给定外键字段(这里)的名称,即使模型的名称与实际表不同或者是只读的模型(例如 A)。这样我就可以在编译时编写 SQL 查询,从而获得许多多对一的关系。

更重要的是,我希望这种外键关系的获取基于tableName模型,而不是模型本身。因此,如果我要定义一个 proc getRelatedFieldName(startType: typedesc[A], otherType: typedesc[B]),它需要为两个getRelatedFieldName(A, B)AND给出相同的结果getRelatedFieldName(A, Surprise)

我怎样才能做到这一点?

4

1 回答 1

2

感谢 nim discord 服务器上非常有帮助的人的一些提示,我能够编写解决方案。

答案是:Nim 的泛型、Nim 的getCustomPragmaVal宏和 Norm 的table模板。

下面的代码采用 2 种模型类型。它虚拟实例化了,sourceType因为这是可能对您的targetType. 然后,它遍历 of 的字段sourceType并检查它们是否直接是 Model 类型、是否使用fk又名 foreignKey pragma 进行注释,或者是一种Option[Model]类型。

如果该字段具有Model类型,则问题已解决,因为您只需调用即可Model.table()完成。如果该字段具有fk编译指示,您可以简单地调用getCustomPragmaVal以获取该字段作为外键的模型。有了它,你就有了类型,并且可以调用table()它。最后,你可能有一个Option[Model]类型。在这种情况下,您需要使用genericParams函数提取泛型参数(参见此处)。这样你就可以再次访问类型并调用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")


于 2022-01-07T23:30:20.127 回答