【问题标题】:Subclass currently has no primary key relationship子类目前没有主键关系
【发布时间】:2020-09-28 18:54:53
【问题描述】:

我的 JSON 看起来像这样,它返回 Posts 的列表:

[
    {
        "id" : 1,
        "message": "Hello"
        "urls" : {
            "png" : "https://example.com/image.png",
            "jpg" : "https://example.com/image.jpg",
            "gif" : "https://example.com/image.gif"
        }
    }
]

如您所见,我需要创建两个类。一个用于父对象 (Post),一个用于对象 "urls" (PostUrls)。

我是这样做的:

class Post: Object, Decodable {
    @objc dynamic var id = 0
    @objc dynamic var message: String? = nil
    @objc dynamic var urls: PostUrls? = nil
    
    override static func primaryKey() -> String? {
        return "id"
    }
    
    private enum PostCodingKeys: String, CodingKey {
        case id
        case message
        case urls
    }
    
    convenience init(id: Int, message: String, urls: PostUrls) {
        self.init()
        self.id = id
        self.message = message
        self.urls = urls
    }
    
    convenience required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: PostCodingKeys.self)
        
        let id = try container.decode(Int.self, forKey: .id)
        let message = try container.decode(String.self, forKey: .message)
        let urls = try container.decode(PostUrls.self, forKey: .urls)
        
        self.init(id: id, message: message, urls: urls)
    }
    
    required init() {
        super.init()
    }
}

@objcMembers class PostUrls: Object, Decodable {
    dynamic var png: String? = nil
    dynamic var jpg: String? = nil
    dynamic var gif: String? = nil
    
    private enum PostUrlsCodingKeys: String, CodingKey {
        case png
        case jpg
        case gif
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: PostUrlsCodingKeys.self)
        
        png = try container.decodeIfPresent(String.self, forKey: .png)
        jpg = try container.decodeIfPresent(String.self, forKey: .jpg)
        gif = try container.decodeIfPresent(String.self, forKey: .gif)
        
        super.init()
    }
    
    required init() {
        super.init()
    }
    
}

但是,问题是我在PostPostUrls 之间没有关系,因为没有连接两者的主键。此外,这也意味着我目前无法控制 PostUrls 表中的重复项。

所以我的问题是:如何在两个表之间创建关系,并防止PostUrls 表中的重复?

【问题讨论】:

  • 我抛出了一个答案来处理你关于人际关系的问题的第一部分。但是,防止重复是一个完全不同的问题。您将如何确定某物是否是重复的 - 重复类型? .jpg、.gif 等或重复的 url 或两者兼而有之……或其他?

标签: ios swift realm


【解决方案1】:

在这种情况下,您确实拥有这些对象之间的关系。对象Post 包含对象PostUrls。 Realm 不需要主键来建立这种关系,因为它在幕后创建了主键。所以它使用它,即使你无法访问它。

要手动设置primaryKey,您必须覆盖一个名为primaryKey()的函数

@objcMembers class DBFilterModel: Object {
    
    // MARK: Properties
    
    dynamic var id: Int = 0
    
    override public static func primaryKey() -> String? {
        return "id"
    }
}

通过这种方式,您告诉领域您希望将此属性用作唯一键。 为了防止重复它们,有两种方法。首先 - 尝试在您的数据库中找到一个已经存在 id 的对象,如果它存在 - 不要创建它。

第二 - 通过向 Realm 的保存方法添加冲突处理程序。您可以将具有相同 ID 的对象设置为仅被修改,但不被复制。或者您可以说您想在尝试插入重复对象时抛出错误。

realm.add(objects, update: update ? .modified : .error)

【讨论】:

  • 这个答案对于这个问题并不真正正确,但您的对象之间存在概念关系是正确的。利用列表是一种解决方案。不过,我认为主键与答案无关;虽然它会阻止两个对象具有相同的主键(它们是唯一的),但它不会阻止对象内的重复数据,这是 OP 所要求的(我认为)。所以,你可以有一个主键 id 为 1 的对象,一个指向 www.somejpg.com 的 jpg 对象,然后一个主键 id 为 2 的对象指向完全相同的东西。
  • @Jay 没有人阻止使用 URL 作为主键,因此您将拥有唯一的 URL。或者在添加它们之前手动检查。
  • 或者你可以在 Realm 中使用复合键,就像在常规数据库中一样
  • 明白。一个 url 是一个具有挑战性的主键,就好像它改变(发生)它会使该对象和其余数据无效。也许可以扩展和澄清答案以解决以下问题:a)在 Post 对象及其类型和 URLS 之间创建关系,以及 b)防止重复数据?你能提供一些代码吗?它目前使用整数主键,不会创建关系或防止重复。
【解决方案2】:

问题中有两个问题

  1. 如何在两个“表”之间建立关系
  2. 防止重复

让我谈谈 1)

从一个保存图像类型(.jpg 等)的类开始,然后是 url

class ImageUrlClass: Object {
    @objc dynamic var imageType = ""
    @objc dynamic var imageUrl = ""
}

然后是处理解码的主类

class Post: Object, Decodable {
    @objc dynamic var id: Int = 0
    @objc dynamic var message: String = ""
    let urlList = List<ImageUrlClass>()
   
    ...edited for brevity

    convenience init(id: Int, message: String, urls: [String: String]) {
        self.init()
        self.id = id
        self.message = message
        
        //create a ImageUrlClass from each dictionary entry
        for url in urls {
            let key = url.key
            let value = url.value
            let aUrl = ImageUrlClass(value: ["imageType": key, "imageUrl": value])
            self.urlList.append(aUrl)
        }
    }

    convenience required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: PostCodingKeys.self)

        let id = try container.decode(Int.self, forKey: .id)
        let message = try container.decode(String.self, forKey: .message)
        let urls = try container.decode([String: String].self, forKey: .urls)

        self.init(id: id, message: message, urls: urls)
    }
}

上面将创建一个带有 List 属性的 Post 对象,该属性包含图像类型和 url(List 的行为与数组非常相似)

您可以通过将LinkingObjects 属性添加到 ImageUrlClass 来进一步实现这一点,当对象添加到 List 时,它会自动创建与 Post 对象的反向关系。不确定您是否需要它,但它可用。

您可以这样做以打印出帖子属性

let decoder = JSONDecoder()
    
let post = try! decoder.decode(Post.self, from: encodedData)
        
print(post.id)
print(post.message)
for url in post.urlList {
    let a = url.imageType
    let b = url.imageUrl
    print(a,b)
}

这会导致这样的输出

1
Hello
jpg https://example.com/image.jpg
png https://example.com/image.png
gif https://example.com/image.gif

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-03
    • 2021-09-28
    • 2015-09-06
    • 1970-01-01
    • 2011-02-23
    • 1970-01-01
    • 2011-02-28
    • 2013-07-08
    相关资源
    最近更新 更多