2

I am using Django 1.4.3 and TastyPie 0.9.11.

I have the following two django models:

class Event(models.Model):
    organizer = models.ForeignKey(User, related_name='Organizador')
    store = models.ForeignKey(Store, related_name='Tienda')
    name = models.CharField('Titulo', max_length=50)
    event_time = models.DateTimeField('Fecha y Hora del Evento')
    creation_date = models.DateTimeField('Fecha de Creación', auto_now_add=True)
    requires_confirmation = models.BooleanField('Require Confirmación')

    class Meta:
        verbose_name = "Encuentro"
        verbose_name_plural = "Encuentros"


class EventInvitees(models.Model):
    event = models.ForeignKey(Event, related_name='invitees')
    invitee = models.ForeignKey(User, verbose_name='Invitado')
    confirmed = models.BooleanField('Confirmado')
    confirmation_date = models.DateTimeField('Fecha de Confirmación', null=True, auto_now=True)

    class Meta:
        verbose_name = "Invitados"
        verbose_name_plural = "Invitados"

Then I have the following API Resources:

class EventInviteesResource(ModelResource):
    user = fields.ForeignKey(UserResource, 'invitee', full=True)
    event = fields.ForeignKey('bbservices.api.EventResource', 'event')

    class Meta:
        queryset = EventInvitees.objects.all()
        authentication = ApiKeyAuthentication()
        authorization = Authorization()


class EventResource(ModelResource):
    invitees = fields.ToManyField('bbservices.api.EventInviteesResource', 'invitees', full=True)
    store = fields.ForeignKey(StoreResource, 'store', full=True)

    class Meta:
        #default_format = 'application/json'

        queryset = Event.objects.all()

        fields = ['organizer_id', 'store', 'name', 'event_time', 'requires_confirmation']
        include_resource_uri = False
        #list_allowed_methods = ['get', 'post']
        authentication = ApiKeyAuthentication()
        authorization = Authorization()
        filtering = {
            #'user': ALL_WITH_RELATIONS
            'event_time': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'],
        }

    def dehydrate_event_time(self, bundle):
        return bundle.data['event_time']

    def obj_create(self, bundle, request=None, **kwargs):
        return super(EventResource, self).obj_create(bundle, request, organizer_id=request.user.pk, store_id=bundle.data['store_id'])

As you can see, I have set up a "ToManyField" relation ship in order to have Event Invitees to show up in GET lists of the Events resource. This works Correctly. Please note that there is also an FK relationship to a "store" and this also works.

The error comes when POSTing the following to "EventInviteesResource" to create and EventInvitee:

POST http://X.X.X.X:8000/api/v1/eventinvitees/?username=user&api_key=XXXXXXX

{
  "event" : {"pk" : 30},
  "invitee" : 2,
  "confirmed" : true
}

The error returned is:

"The 'invitees' field has no data and doesn't allow a null value."

Note that "invitees" does NOT exist in the "EventInviteesResource" but instead in the "parent" resource "EventResource". So I dont understand how this can even be an error. If I comment out the this line:

invitees = fields.ToManyField('bbservices.api.EventInviteesResource', 'invitees', full=True)

the resource "EventResource" the "invitees" error goes away and the error becomes:

{"error_message": "", "traceback": "Traceback (most recent call last):\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 192, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 397, in dispatch_list\n return self.dispatch('list', request, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 427, in dispatch\n response = method(request, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1165, in post_list\n updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1774, in obj_create\n bundle = self.full_hydrate(bundle)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 698, in full_hydrate\n value = field_object.hydrate(bundle)\n\n File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 636, in hydrate\n value = super(ToOneField, self).hydrate(bundle)\n\n File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 154, in hydrate\n elif self.attribute and getattr(bundle.obj, self.attribute, None):\n\n File \"/Library/Python/2.7/site-packages/django/db/models/fields/related.py\", line 343, in __get__\n raise self.field.rel.to.DoesNotExist\n\nDoesNotExist\n"}

If I try to POST this:

{
    "store_id" : 1, 
    "name" : "With Invitees", 
    "event_time" : "2013-02-06T18:30-3:00",
    "requires_confirmation" : true,
    "invitees" : [
                    {
                      "invitee": {"pk" : 1}
                    }
                ]
}

to the resource EventResource, with the 'invitees' relationship intact, the error is:

{"error_message": "int() argument must be a string or a number, not 'dict'", "traceback": "Traceback (most recent call last):\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 192, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 397, in dispatch_list\n return self.dispatch('list', request, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 427, in dispatch\n response = method(request, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1165, in post_list\n updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))\n\n File \"/Users/jleidigh/Documents/Starbucks - In Office/trunk/backend/bbservices/api.py\", line 234, in obj_create\n return super(EventResource, self).obj_create(bundle, request, organizer_id=request.user.pk, store_id=bundle.data['store_id'])\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1783, in obj_create\n m2m_bundle = self.hydrate_m2m(bundle)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 743, in hydrate_m2m\n bundle.data[field_name] = field_object.hydrate_m2m(bundle)\n\n File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 742, in hydrate_m2m\n m2m_hydrated.append(self.build_related_resource(value, **kwargs))\n\n File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 593, in build_related_resource\n return self.resource_from_data(self.fk_resource, value, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 548, in resource_from_data\n return fk_resource.obj_update(fk_bundle, **data)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1814, in obj_update\n bundle.obj = self.obj_get(request, **lookup_kwargs)\n\n File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1752, in obj_get\n base_object_list = self.get_object_list(request).filter(**kwargs)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/query.py\", line 624, in filter\n return self._filter_or_exclude(False, *args, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/query.py\", line 642, in _filter_or_exclude\n clone.query.add_q(Q(*args, **kwargs))\n\n File \"/Library/Python/2.7/site-packages/django/db/models/sql/query.py\", line 1250, in add_q\n can_reuse=used_aliases, force_having=force_having)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/sql/query.py\", line 1185, in add_filter\n connector)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/sql/where.py\", line 69, in add\n value = obj.prepare(lookup_type, value)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/sql/where.py\", line 320, in prepare\n return self.field.get_prep_lookup(lookup_type, value)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/fields/related.py\", line 137, in get_prep_lookup\n return self._pk_trace(value, 'get_prep_lookup', lookup_type)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/fields/related.py\", line 210, in _pk_trace\n v = getattr(field, prep_func)(lookup_type, v, **kwargs)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/fields/__init__.py\", line 310, in get_prep_lookup\n return self.get_prep_value(value)\n\n File \"/Library/Python/2.7/site-packages/django/db/models/fields/__init__.py\", line 537, in get_prep_value\n return int(value)\n\nTypeError: int() argument must be a string or a number, not 'dict'\n"}

I believe this error is documented here:

https://github.com/toastdriven/django-tastypie/issues/307

If I comment the "invitees" line and POST to EventResource the error disappears but of course the invitees are not created.

So........any one have any ideas? Is this just another error associated with issue 307 (link above) or am I doing something wrong??

Thanks very much in advance!!!!

4

2 回答 2

2

好的,我找到了自己的答案。在 EventInviteesResource

user = fields.ForeignKey(UserResource, 'invitee', full=True)

需要以下内容才能镜像我的 Django 模型:

invitee = fields.ForeignKey(UserResource, 'invitee', full=True)

虽然这看起来是合乎逻辑的,但我必须说我得到的“被邀请者”(注意“s”)错误不是但是哦,好吧......

奖励答案,在 EventResource 中,更新:

invitees = fields.ToManyField('bbservices.api.EventInviteesResource', 'invitees', full=True)

至:

invitees = fields.ToManyField('bbservices.api.EventInviteesResource', 'invitees', related_name='event', full=True)

现在,您可以与受邀者一起发布到 EventResource 并自动创建那些。帖子数据如下所示:

{
    "store" : "/api/v1/store/2/", 
    "name" : "Yes again?", 
    "event_time" : "2013-02-06T18:30-3:00",
    "requires_confirmation" : true,
    "invitees" : [
                    {
                      "invitee" : "/api/v1/user/1/",
                      "confirmed" : true
                    }
                ]
}

所以现在我唯一的疑问是……谁能告诉我为什么我不能对 FK 使用 PK 语法:

{ "store" : {"pk" : 2}, ...

这会导致错误指出 Store 对象的字段不能为空,就好像它试图创建一个新的商店对象一样。如果我使用下面的 URI 路径,它可以正常工作:

{ "store" : "/api/v1/store/2/", ...

但我宁愿不必传回完整的 URI,而且我应该这样做。这就是为什么我在 obj_create 中使用 store_id 技巧但它非常笨拙......

有任何想法吗?

于 2013-01-18T06:08:17.730 回答
0

当您提供 {"store":"/api/v1/store/2/"} 时,您只需指定 EventResource 的 store 值。但是当您提供 {"store": {"pk":2}} 时,您不仅要指定 store 的值,还要编辑 store(保存相关对象)。那是美味的“风格”。

我们可以做的是构建一组自动从/到资源 uri 转换的 Javascript 函数。使用现有的 Javascript MVC 框架,如 AgularJS 或 Backbone 作为前端,使用 Django Tastypie 作为后端非常强大。

于 2013-12-21T06:10:33.113 回答