RestKit 0.9.3 connectОтношение: withObjectForPrimaryKeyAttribute:

Я пытаюсь использовать пример RKCatalog из нового RestKit 0.9.3. В RKRelationshipMappingExample создатели забыли добавить код соединения между Задачей и Пользователем, но даже после добавления он работает некорректно.

{"project": {
    "id": 123,
    "name": "Produce RestKit Sample Code",
    "description": "We need more sample code!",
    "user": {
        "id": 1,
        "name": "Blake Watters",
        "email": "[email protected]"
    },
    "tasks": [
        {"id": 1, "name": "Identify samples to write", "assigned_user_id": 1},
        {"id": 2, "name": "Write the code", "assigned_user_id": 1},
        {"id": 3, "name": "Push to Github", "assigned_user_id": 1},
        {"id": 4, "name": "Update the mailing list", "assigned_user_id": 1}
    ]
}}

[taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"assignedUserID"];

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:gRKCatalogBaseURL];
        objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"RKRelationshipMappingExample.sqlite"];

        RKManagedObjectMapping* taskMapping = [RKManagedObjectMapping mappingForClass:[Task class]];
        [taskMapping mapKeyPath:@"id" toAttribute:@"taskID"];
        [taskMapping setPrimaryKeyAttribute:@"taskID"];
        [taskMapping mapKeyPath:@"name" toAttribute:@"name"];
        [taskMapping mapKeyPath:@"assigned_user_id" toAttribute:@"assignedUserID"];
        [taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"assignedUserID"];
        [objectManager.mappingProvider setMapping:taskMapping forKeyPath:@"task"];

        RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];
        [userMapping mapAttributes:@"name", @"email", nil];
        [userMapping mapKeyPath:@"id" toAttribute:@"userID"];
        [userMapping setPrimaryKeyAttribute:@"userID"];
        [userMapping mapRelationship:@"tasks" withMapping:taskMapping];
        [objectManager.mappingProvider setMapping:userMapping forKeyPath:@"user"];

        // NOTE - Project is not backed by Core Data
        RKObjectMapping* projectMapping = [RKObjectMapping mappingForClass:[Project class]];
        [projectMapping mapKeyPath:@"id" toAttribute:@"projectID"];
        [projectMapping mapAttributes:@"name", @"description", nil];
        [projectMapping mapRelationship:@"user" withMapping:userMapping];
        [projectMapping mapRelationship:@"tasks" withMapping:taskMapping];
        [objectManager.mappingProvider setMapping:projectMapping forKeyPath:@"project"];
    }

    return self;
}

Я все делаю хорошо? - не связывает задачу с пользователем по ID пользователя.


person Vlad Brylinskiy    schedule 07.09.2011    source источник


Ответы (3)


ОБНОВЛЕНО: мне следовало более внимательно прочитать связанный пост Блейка - - это быстрее направило бы меня на ветку разработки.

ИТОГ: проблема сопоставления «один-ко-многим» (обсуждаемая здесь в контексте обратной гидратации tasks объекта User) нарушена в 0.9.3. Извлечение ветки разработки от 8 марта 2012 г. подтверждает, что проблема там устранена - Блейк обратился к ней в выпуске № 284. Вам действительно нужно добавить логику connectRelationship:withObjectForPrimaryKeyAttribute:, которой нет в примере.


@Evan, спасибо за совет по RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace) - он помог профилировать то, что на самом деле происходит.

Я все еще нахожусь в стадии «поиска своего пути» RestKit, так что YMMV. Однако, согласно этому сообщению Блейка, на самом деле это выглядит как будто вам нужно и то, и другое - это единственный способ заставить пример работать. Однако ссылка на первичный ключ связанного объекта упоминается с помощью его локального атрибута [например, ivar задачи]:

        RKManagedObjectMapping* taskMapping = [RKManagedObjectMapping mappingForClass:[Task class]];
        [taskMapping mapKeyPath:@"id" toAttribute:@"taskID"];
        [taskMapping mapKeyPath:@"name" toAttribute:@"name"];
        [taskMapping mapKeyPath:@"assigned_user_id" toAttribute:@"assignedUserID"];
        taskMapping.primaryKeyAttribute = @"taskID";

        RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];
        [userMapping mapAttributes:@"name", @"email", nil];
        [userMapping mapKeyPath:@"id" toAttribute:@"userID"];
        [userMapping mapRelationship:@"tasks" withMapping:taskMapping];
        userMapping.primaryKeyAttribute = @"userID";
        [objectManager.mappingProvider setMapping:userMapping forKeyPath:@"user"];

        [taskMapping mapRelationship:@"assignedUser" withMapping:userMapping];
        [taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"assignedUserID"];
        [objectManager.mappingProvider setMapping:taskMapping forKeyPath:@"task"]; 

Это было сделано против попытки хозяина около 3 дней назад.

person jstevenco    schedule 21.02.2012
comment
Этот ответ правильно добавляет пользователей к задачам, но НЕ задачи для пользователей. Итерация по проекту теперь правильно отображает имя пользователя для задачи. Однако перебрать задачи для данного пользователя невозможно. - person Alex Stone; 06.03.2012

Отображение пользователя -> задачи настраивается в userMapping со строкой [userMapping mapRelationship:@"tasks" withMapping:taskMapping];. Я полагаю, вы говорите, что инверсия не создавалась?

Если это проблема, я думаю, вы обнаружите, что эта фиксация устраняет проблему, хотя Сам еще не пробовал. (Вы можете вытащить дерево в этот момент или, лучше, потянуть текущую главную ветвь).

Кроме того, у вас есть проблема со строкой [taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"assignedUserID"];. Она ищет объект с атрибутом первичного ключа "assignUserID", но, глядя на другие сопоставления, такого объекта не существует. Обратите внимание, что в userMapping первичный ключ определяется [userMapping setPrimaryKeyAttribute:@"userID"];

Вы, наверное, хотите [taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"userID"]; или даже лучше [taskMapping mapRelationship:@"assignedUser" withMapping:userMapping];

(И с фиксацией, которую я связал, я даже не уверен, нужно ли вообще что-то добавлять, чтобы получить обратное - вы можете попробовать оба и посмотреть.)

Если проблемы по-прежнему возникают, вы можете включить журналы для ObjectMapper с помощью RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);

person Evan Cordell    schedule 07.09.2011

Я возился с этим кодом почти полтора часа, и наконец заставил его работать. Шаги, которые я предпринял: 1) Измените порядок создания отношений, как показано ниже. 2) Снимите флажок «необязательный» для отношения RKRelationshipMappingExample.xcdatamodel> task.assignedUser. Я заметил, что оба варианта были «необязательными» и минимум 1, максимум 1. Я удалил приложение после этого изменения и переустановил его. Теперь пользователи добавлены в задачи и задачи пользователям!

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:gRKCatalogBaseURL];
        objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"RKRelationshipMappingExample.sqlite"];


        RKManagedObjectMapping* taskMapping = [RKManagedObjectMapping mappingForClass:[Task class]];
        [taskMapping mapKeyPath:@"id" toAttribute:@"taskID"];
        [taskMapping mapKeyPath:@"name" toAttribute:@"name"];
        [taskMapping mapKeyPath:@"assigned_user_id" toAttribute:@"assignedUserID"];
        taskMapping.primaryKeyAttribute = @"taskID";

        RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];
        [userMapping mapAttributes:@"name", @"email", nil];
        [userMapping mapKeyPath:@"id" toAttribute:@"userID"];

        userMapping.primaryKeyAttribute = @"userID";
        [objectManager.mappingProvider setMapping:userMapping forKeyPath:@"user"];
        [objectManager.mappingProvider setMapping:taskMapping forKeyPath:@"task"];

        [userMapping mapRelationship:@"tasks" withMapping:taskMapping];        
        [taskMapping mapRelationship:@"assignedUser" withMapping:userMapping];

        [taskMapping connectRelationship:@"assignedUser" withObjectForPrimaryKeyAttribute:@"assignedUserID"];


        // NOTE - Project is not backed by Core Data
        RKObjectMapping* projectMapping = [RKObjectMapping mappingForClass:[Project class]];
        [projectMapping mapKeyPath:@"id" toAttribute:@"projectID"];
        [projectMapping mapAttributes:@"name", @"description", nil];
        [projectMapping mapRelationship:@"user" withMapping:userMapping];
        [projectMapping mapRelationship:@"tasks" withMapping:taskMapping];
        [objectManager.mappingProvider setMapping:projectMapping forKeyPath:@"project"];



    }

    return self;
}

//to see NSLog messages, change the LOGGER_DEBUG to 2 (in LoggerClient.m) like this:
#define LOGGER_DEBUG 2
#ifdef NSLog
    #undef NSLog
#endif



//test method to make sure all relationships are created.
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
    _objects = [objects retain];

    for(Project* managedObject in objects)
    {   NSLog(@" project user: %@", [managedObject.user description]);
        NSLog(@"###### user tasks count: %i",[managedObject.user.tasks  count]);
        for(Task* task in managedObject.user.tasks)
        {
            NSLog(@"+++++managedObject.user.tasks: %@", [task description]);
        }

        for(Task* task in managedObject.tasks)
        {
            NSLog(@"*****task: %@", [task description]);
        }

    }
    [self.tableView reloadData];
}
person Alex Stone    schedule 05.03.2012
comment
Хорошая уловка атрибута Core Data - увидев ваш комментарий к моему сообщению, я посмотрел на ту же проблему, полагая, что это должно быть что-то с отношениями между объектами. Попытка изучить и RestKit, и Core Data одновременно - непростая задача! Переставлять код не нужно, кстати. - person jstevenco; 06.03.2012
comment
Хммм - к сожалению, это выглядит более многообещающим, чем есть на самом деле. Первая загрузка на чистой установке правильно гидратирует граф объектов, но последующие вызовы показывают те же ошибки связи для user.tasks NSSet. Я немного поигрался с этим с включением и выключением необязательного флага для Task сущности assignedUser, и я не могу добиться повторяемого правильного поведения. - person jstevenco; 06.03.2012
comment
Подтверждено - порядок операторов и необязательный флаг не имеют ничего общего с этой проблемой - см. Мой ответ для обновления - person jstevenco; 09.03.2012