我使用的是Django 1.4.3和TastyPie 0.9.11。
我有以下两个django模型: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"
然后我有以下API资源:
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'])
您可以看到,我已经设置了一个"ToManyField"关系,以便让Event Invitees显示在Events资源的GET列表中。这工作正确。请注意,还有一个FK关系到"store",这也是有效的。
当将以下内容发送到"EventInviteesResource"以创建和EventInvitee时出现错误:
文章http://X.X.X.X: 8000/api/v1/eventinvitees ?用户名= user& api_key = XXXXXXX
{
"event" : {"pk" : 30},
"invitee" : 2,
"confirmed" : true
}
返回的错误是:
" 'invitees'字段没有数据,不允许为空值。"
注意,"invitees"不存在于"EventInviteesResource"中,而是存在于"父"资源"EventResource"中。所以我不明白这怎么可能是一个错误。如果我注释掉这一行:
invitees = fields.ToManyField('bbservices.api.EventInviteesResource', 'invitees', full=True)
资源"EventResource", "invitees"错误消失,错误变成:
{"error_message": "", "traceback": "Traceback (most recent call last):nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 192, in wrappern response = callback(request, *args, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 397, in dispatch_listn return self.dispatch('list', request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 427, in dispatchn response = method(request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1165, in post_listn updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1774, in obj_createn bundle = self.full_hydrate(bundle)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 698, in full_hydraten value = field_object.hydrate(bundle)nn File "/Library/Python/2.7/site-packages/tastypie/fields.py", line 636, in hydraten value = super(ToOneField, self).hydrate(bundle)nn File "/Library/Python/2.7/site-packages/tastypie/fields.py", line 154, in hydraten elif self.attribute and getattr(bundle.obj, self.attribute, None):nn File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 343, in __get__n raise self.field.rel.to.DoesNotExistnnDoesNotExistn"}
如果我试图发布这个:
{
"store_id" : 1,
"name" : "With Invitees",
"event_time" : "2013-02-06T18:30-3:00",
"requires_confirmation" : true,
"invitees" : [
{
"invitee": {"pk" : 1}
}
]
}
到资源EventResource,在'invitees'关系不变的情况下,错误是:
{"error_message": "int() argument must be a string or a number, not 'dict'", "traceback": "Traceback (most recent call last):nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 192, in wrappern response = callback(request, *args, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 397, in dispatch_listn return self.dispatch('list', request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 427, in dispatchn response = method(request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1165, in post_listn updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))nn File "/Users/jleidigh/Documents/Starbucks - In Office/trunk/backend/bbservices/api.py", line 234, in obj_createn return super(EventResource, self).obj_create(bundle, request, organizer_id=request.user.pk, store_id=bundle.data['store_id'])nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1783, in obj_createn m2m_bundle = self.hydrate_m2m(bundle)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 743, in hydrate_m2mn bundle.data[field_name] = field_object.hydrate_m2m(bundle)nn File "/Library/Python/2.7/site-packages/tastypie/fields.py", line 742, in hydrate_m2mn m2m_hydrated.append(self.build_related_resource(value, **kwargs))nn File "/Library/Python/2.7/site-packages/tastypie/fields.py", line 593, in build_related_resourcen return self.resource_from_data(self.fk_resource, value, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/fields.py", line 548, in resource_from_datan return fk_resource.obj_update(fk_bundle, **data)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1814, in obj_updaten bundle.obj = self.obj_get(request, **lookup_kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1752, in obj_getn base_object_list = self.get_object_list(request).filter(**kwargs)nn File "/Library/Python/2.7/site-packages/django/db/models/query.py", line 624, in filtern return self._filter_or_exclude(False, *args, **kwargs)nn File "/Library/Python/2.7/site-packages/django/db/models/query.py", line 642, in _filter_or_excluden clone.query.add_q(Q(*args, **kwargs))nn File "/Library/Python/2.7/site-packages/django/db/models/sql/query.py", line 1250, in add_qn can_reuse=used_aliases, force_having=force_having)nn File "/Library/Python/2.7/site-packages/django/db/models/sql/query.py", line 1185, in add_filtern connector)nn File "/Library/Python/2.7/site-packages/django/db/models/sql/where.py", line 69, in addn value = obj.prepare(lookup_type, value)nn File "/Library/Python/2.7/site-packages/django/db/models/sql/where.py", line 320, in preparen return self.field.get_prep_lookup(lookup_type, value)nn File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 137, in get_prep_lookupn return self._pk_trace(value, 'get_prep_lookup', lookup_type)nn File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 210, in _pk_tracen v = getattr(field, prep_func)(lookup_type, v, **kwargs)nn File "/Library/Python/2.7/site-packages/django/db/models/fields/__init__.py", line 310, in get_prep_lookupn return self.get_prep_value(value)nn File "/Library/Python/2.7/site-packages/django/db/models/fields/__init__.py", line 537, in get_prep_valuen return int(value)nnTypeError: int() argument must be a string or a number, not 'dict'n"}
我相信这个错误记录在这里:
https://github.com/toastdriven/django-tastypie/issues/307如果我注释"受邀者"行并POST到EventResource,错误就会消失,但是受邀者当然不会被创建。
所以……有人有什么想法吗?这只是与问题307(上面的链接)相关的另一个错误,还是我做错了什么?
非常感谢提前!!!!
好了,我找到了我自己的答案。在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与邀请,并有那些自动创建。post数据看起来像这样:
{
"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
}
]
}
所以现在我唯一的疑问是……谁能告诉我为什么我不能使用PK语法的fk:
{ "store" : {"pk" : 2}, ...
这会导致错误,指出Store对象的字段不能为空,就好像它试图创建一个新的Store对象一样。如果我使用下面的URI路径,它可以正常工作:
{ "store" : "/api/v1/store/2/", ...
但是我不希望必须传回完整的URI,而且我应该这样做。这就是为什么我在obj_create中使用store_id技巧,但它非常笨拙…
任何想法?
当你提供{"store":"/api/v1/store/2/"}时,你只指定EventResource的store值。但是当您提供{"store": {"pk":2}}时,您不仅可以指定store的值,还可以编辑store(保存相关对象)。这才是品味十足的"风格"。
我们能做的是构建一组Javascript函数来自动从/转换为资源uri。使用现有的Javascript MVC框架,如AgularJS或Backbone作为前端,Django tastype作为后端是非常强大的。