Использование TastyPie для обновления поля ForeignKey до нуля

Можно ли использовать TastyPie для обновления поля ForeignKey до None?

Связанный вопрос: tastypie не будет удалять ссылку на внешний ключ во время PUT

Что у меня есть:

class SomeObject(models.Model):
    some_field = models.ForeignKey(SomeOtherObject, null=True)

(и, конечно же, соответствующий класс ресурсов TastyPie, который отлично подходит для обновления других полей)

Что я хочу:

Чтобы обновить some_field до None с помощью TastyPie.

Что я пробовал (в JavaScript):

$.put('/my/api/model/someobject/42/', { some_field: null });
$.put('/my/api/model/someobject/42/', { some_field: '/my/api/model/someotherobject/null/' });
$.put('/my/api/model/someobject/42/', { some_field: '' });
$.put('/my/api/model/someobject/42/', { some_field: 0 });
$.put('/my/api/model/someobject/42/', { some_field: false });

И так далее. Все они приводят к 404 или 400. Некоторые приводят к 204, но база данных не обновляется.

Читая код в full_dehydrate(), кажется, что в настоящее время это невозможно сделать.

Я просмотрел последний код на github. , и я не уверен, что это возможно.


person djsmith    schedule 14.03.2012    source источник
comment
В качестве обходного пути я написал собственный метод obj_update() для своего класса ресурсов, в котором я проверяю if 'some_field' in bundle.data and bundle.data['some_field'] is None:, после чего я устанавливаю some_field объекта в None и save() его. В моем JavaScript я передаю { some_field: null }   -  person djsmith    schedule 14.03.2012
comment
Хотя мне очень нравится это решение, для меня оно не работает, когда запись уже сделана в моей базе данных. В этом случае он просто загружает существующие данные, а оператор if не равен True (хотя так и должно быть)... Кто-нибудь нашел решение для этого?   -  person gabn88    schedule 08.10.2014


Ответы (4)


Вот универсальное решение. Создайте базовый ресурс и расширьте все остальные ресурсы из этого. Это хак, но он работает.

class MyResource(ModelResource):
    def obj_update(self, bundle, request=None, **kwargs):
        for field_name in self.fields:
            field = self.fields[field_name]

            if type(field) is ToOneField and field.null and bundle.data[field_name] is None:
                setattr(bundle.obj, field_name, None)

        return super(MyResource, self).obj_update(bundle, **kwargs)
person Adam Thomas    schedule 26.03.2012
comment
Спасибо за рабочее решение, что-то вроде этого определенно должно быть частью самого Tastypie. - person Andrew Swihart; 02.05.2012
comment
Это почти то, что я придумал, но у него есть дополнительное преимущество, заключающееся в более широком применении, благодаря проверке файла type(field). Отличный ответ. - person djsmith; 03.05.2012
comment
Немного O / T здесь: PEP8 говорит, что сравнения с синглтонами, такими как None, всегда должны выполняться с операторами «есть» или «нет», а не с операторами равенства. И это не просто соглашение : ) Также == True является избыточным. - person minder; 01.08.2012
comment
Спасибо моторист, исправлено. Также я переместил save() из цикла, нужно сделать это только один раз. - person Adam Thomas; 01.08.2012
comment
Отличный ответ, но ваш код сохраняет объект дважды - один раз в супервызове и один раз в вызове save(). Вы можете избежать этого, удалив две последние строки и просто вернув супервызов. Я предложу редактирование, которое делает это. - person Erik; 17.04.2014
comment
Я использую NamespacedModelResource, это тоже сработает? - person gabn88; 08.10.2014

Просто добавьте метод гидрата для поля:

def hydrate_some_field(self, bundle):
    some_object = bundle.obj
    some_other_object = bundle.data['some_field']

    if some_other_object == '':
        some_object.some_field = None
        del bundle.data['some_field']

    return bundle

Удачи!

person Erik    schedule 14.02.2014
comment
Это кажется более подходящим решением для данной задачи. Спасибо. - person Vladimir Prudnikov; 20.03.2015
comment
Кстати, вы должны вернуть новое значение поля из метода гидрата для каждого поля. Как говорится в документации, возвращаемое значение помещается в атрибут bundle.obj для этого имени поля. - person Vladimir Prudnikov; 21.03.2015
comment
Я должен посмотреть на это поближе. Все мои API-интерфейсы возвращают пакет в методах гидратации для каждого поля. Хм.... - person Erik; 21.03.2015

Для меня ответ Адама Томаса не работал. Мне пришлось изменить условие if.

Таким образом, общий код становится:

def obj_update(self, bundle, request=None, **kwargs):
    super(MyResource, self).obj_update(bundle, **kwargs)
    field_to_update=[]
    for field_name in self.fields:
        field = self.fields[field_name]
        if field.null and (field_name in request.POST):
            if request.POST[field_name] is u'':
                setattr(bundle.obj, field_name, None)
                field_to_update.append(field_name)
    bundle.obj.save(update_fields=field_to_update)
    return bundle

(обратите внимание: я использовал поле update_field, чтобы сделать SQL-запрос намного приятнее :))

(кстати: это также работает для ресурсов модели с пространством имен :))

(Я не мог заставить его работать с обратным супервызовом, тогда он у меня не сохранялся)

person gabn88    schedule 09.10.2014

Попробуй это:

$.put('/my/api/model/someobject/42/', { some_field: 'None' }); //DOES NOT WORK

У меня была аналогичная проблема, когда я пытался отфильтровать некоторые объекты по нулевому внешнему ключу и смог получить их с помощью GET:

http://localhost:8000/api/v1/page/?format=json&next_page=None

Обновление:

Хотя мне удалось получить соответствующие объекты, передав «Нет», похоже, что это не работает для PUT. Я смог обновить поле внешнего ключа до None с помощью этого вызова:

$.ajax('/api/v1/page/1/',{
    contentType: 'application/json', 
    type: 'put', 
    data: JSON.stringify({"next_page": null})
});

Я предполагаю, что это решает проблему, передавая ноль как application/json. Вероятно, можно передать null как application/x-www-form-urlencoded, но я не знаю, как это сделать.

Кстати, application/json — это кодировка, которую по умолчанию ожидает deliciouspie, тогда как application/x-www-form-urlencoded — это кодировка, которую jquery и некоторые другие (все?) библиотеки используют для ajax. Возможно, с кодировкой json по умолчанию для deliciouspie проще работать.

person dokkaebi    schedule 15.03.2012
comment
Не работает для меня. Это дает мне 404, потому что TastyPie пытается найти ресурс, указанный "None", который не существует. Разве вы не получаете 404, когда пытаетесь это сделать? - person djsmith; 17.03.2012
comment
Обновлен кодом, который работал у меня. Похоже, передача javascript null помогла мне. Может ваша ошибка в другом? Можете ли вы обновить этот конкретный some_field до другого объекта, кроме None? - person dokkaebi; 17.03.2012
comment
Да, я могу обновить some_field до другого объекта, но null у меня не работает. Мне пришлось добавить собственный код в obj_update(), чтобы проверить, работает ли None. - person djsmith; 18.03.2012
comment
Итак, код, который я разместил, сработал для вас? Я определенно смог обновиться до None с помощью этого звонка. - person dokkaebi; 19.03.2012