Realm - невозможно создать объект с существующим значением первичного ключа

У меня есть объект Человек со многими собаками. В приложении есть отдельная страница, где показаны только собаки, и другая страница, где показаны собаки человека.

Моя модель выглядит следующим образом

class Person: Object {
    dynamic var id = 0
    let dogs= List<Dog>()

    override static func primaryKey() -> String? {
        return "id"
    }
}

class Dog: Object {
    dynamic var id = 0
    dynamic var name = ""

    override static func primaryKey() -> String? {
        return "id"
    }
}

У меня есть люди, хранящиеся в Царстве. У человека есть страница с подробностями, на которой мы приводим и показываем его собак. Если собака уже существует, я обновляю последнюю информацию об этой собаке и добавляю ее в список собак, иначе создаю новую собаку, сохраняю и добавляю в список людей. Это работает в coredata.

// Fetch and parse dogs
if let person = realm.objects(Person.self).filter("id =\(personID)").first {
    for (_, dict): (String, JSON) in response {
        // Create dog using the dict info,my custom init method
        if let dog = Dog(dict: dict) {
            try! realm.write {
                // save it to realm
                realm.create(Dog, value:dog, update: true)
                // append dog to person
                person.dogs.append(dog)
            }
        }
    }
    try! realm.write {
        // save person
        realm.create(Person.self, value: person, update: true)
    }
}

При попытке обновить человека с его собаками realm выдает исключение Невозможно создать объект с существующим значением первичного ключа.


person Deekshith Bellare    schedule 14.11.2016    source источник
comment
Откуда взялось значение recipe в person.dogs.append(recipe)? Не совсем понятно, что вы здесь делаете, извините!   -  person TiM    schedule 14.11.2016
comment
Извините, это должен быть person.dogs.append (собака). Я просто создаю / обновляю собаку и добавляю ее к собакам человека @TiM   -  person Deekshith Bellare    schedule 14.11.2016
comment
Ага, ладно. Думаю, теперь я понимаю, что здесь происходит. Добавлю ответ. :)   -  person TiM    schedule 14.11.2016


Ответы (2)


Проблема здесь в том, что даже если вы создаете совершенно новый объект Realm Dog, вы на самом деле не сохраняете его в базе данных, и поэтому, когда вы вызываете append, вы пытаетесь добавить вторую копию.

Когда вы вызываете realm.create(Dog.self, value:dog, update: true), если объект с этим идентификатором уже существует в базе данных, вы просто обновляете этот существующий объект значениями из созданного вами экземпляра dog, но этот экземпляр dog по-прежнему является независимой копией; это не объект Dog в базе данных. Вы можете подтвердить это, проверив, совпадает ли dog.realm с nil или нет.

Поэтому, когда вы вызываете person.dogs.append(dog), поскольку dog еще нет в базе данных, Realm пытается создать полностью новую запись в базе данных, но терпит неудачу, потому что уже есть собака с этим идентификатором.

Если вы хотите добавить этот dog объект к person, потребуется запросить Realm, чтобы получить правильный dog объект, который ссылается на запись в базе данных. К счастью, это действительно просто с объектами Realm, поддерживаемыми первичными ключами, поскольку вы можете использовать метод Realm.object(ofType:forPrimaryKey:):

if let person = realm.object(ofType: Person.self, forPrimaryKey: "id") {
    for (_, dict): (String, JSON) in response {
        //Create dog using the dict info,my custom init method
        if let dog = Dog(dict: dict)
        {
            try! realm.write {
                //save it to realm
                realm.create(Dog.self, value: dog, update: true)
                //get the dog reference from the database
                let realmDog = realm.object(ofType: Dog.self, forPrimaryKey: "id")
                //append dog to person
                person.dogs.append(realmDog)
            }
        }
    }
    try! realm.write {
        //save person
        realm.create(person .self, value: collection, update: true)
    }
}
person TiM    schedule 14.11.2016
comment
В моем случае у меня есть 6 событий для сохранения, каждое из которых имеет уникальный тип. Я сохраняю все эти 6 событий в таблице заданий. Но когда я пытаюсь создать еще один объект задания с другим идентификатором задания, когда я создаю первое событие для второго задания. Я получаю ошибку. не может создать объект с таким же первичным ключом. - person siva krishna; 07.12.2017
comment
При необходимости вам может потребоваться запросить базу данных, чтобы узнать, существует ли уже объект с таким же первичным ключом. Эта ошибка возникает только тогда, когда вы пытаетесь добавить второй объект с тем же первичным ключом, поэтому вам просто нужно убедиться, что ваша логика этого не делает. :) - person TiM; 09.12.2017

Нам больше не нужен метод ТиМ.

Используйте 1_.

try realm.write {
    realm.add(objects, update: Realm.UpdatePolicy.modified)
    // OR
    realm.add(object, update: .modified)
}

Realm.UpdatePolicy перечисляет:

error (default)
modified //Overwrite only properties in the existing object which are different from the new values.
all //Overwrite all properties in the existing object with the new values, even if they have not changed

NB: работает на Realm Swift 3.16.1

person Lal Krishna    schedule 11.06.2019