Realm - невозможно создать объект с существующим значением первичного ключа
У меня есть объект Person with many dogs. Приложение имеет отдельную страницу, на которой отображаются только собаки и другая страница, на которой показаны персональные собаки.
Моя модель выглядит следующим образом
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)
}
}
При попытке обновить человека со своими собаками исключение из сферы действия
Невозможно создать объект с существующим значением первичного ключа
Ответы
Ответ 1
Проблема заключается в том, что даже если вы создаете совершенно новый объект Realm Dog
, вы фактически не сохраняете его в базе данных, и поэтому, когда вы вызываете append
, вы пытаетесь добавить вторая копия.
Когда вы вызываете realm.create(Dog, 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, 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)
}
}
Ответ 2
Нам больше не нужен метод TiM.
Используйте add(_:update:)
.
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
Ответ 3
Я предполагаю, что это строка adda(), а не create(). Если messageThreadToStore - это управляемый объект Realm, то append (contentsOf: messages) попытается добавить все объекты в сообщениях в Realm, а вызов realm.create() просто не работает.
Одним из возможных решений этой проблемы было бы изменить запись на:
try! realm.write {
realm.add(messages, update: true)
messageThreadToStore.messages.append(contentsOf: messages)
}