TastyPie:如何 POST 到中间 ManyToMany 'through' 资源
TastyPie: How to POST to an intermediate ManyToMany 'through' Resource
我有 2 个模型(RiderProfile
和 Ride
),它们具有通过中间模型 (RideMembership
) 管理的多对多关系。
我希望能够 POST 我的中间模型资源的新关系条目,但我收到一条错误消息,告诉我我正在为我的资源提供无效的 URL。
注意有类似的another question,但是没有提供POST数据
这是我的模型:
class RiderProfile(models.Model):
user = models.OneToOneField(User)
age = models.IntegerField(max_length=2, null=True, blank=True)
rides = models.ManyToManyField('riderapp.Ride', through="RideMembership", null=True, blank=True)
def __unicode__(self):
return self.user.get_username()
class Ride(models.Model):
name = models.CharField(max_length=64)
riders = models.ManyToManyField(RiderProfile, through="RideMembership", null=True, blank=True)
def __unicode__(self):
return self.name
class RideMembership(models.Model):
rider = models.ForeignKey(RiderProfile)
ride = models.ForeignKey(Ride)
date_joined = models.DateField(default=datetime.now)
invite_reason = models.CharField(max_length=64)
def __unicode__(self):
return self.rider.user.get_username() + ' to ' + self.ride.name()
这是我的资源:
class UserResource(ModelResource):
...
class RiderProfileResource(ModelResource):
class Meta:
queryset = RiderProfile.objects.all()
resource_name = 'riders'
user = fields.ForeignKey(UserResource, 'user', full=True)
rides = fields.ToManyField('riderapp.api.RideForRiderProfileResource', 'rides', full=True)
class RideResource(ModelResource):
class Meta:
queryset = Ride.objects.all()
resource_name = 'rides'
authorization = Authorization()
always_return_data = True
riders = fields.ToManyField('riderapp.api.RiderProfileForRideResource', 'riders', full=True, blank=True, readonly=True)
class RideMembershipResource(ModelResource):
class Meta:
queryset = RideMembership.objects.all()
resource_name = 'rider_memberships'
authorization = Authorization()
always_return_data = True
rider = fields.ForeignKey(RideForRiderProfileResource, 'rider')
ride = fields.ForeignKey(RideResource, 'ride')
"""special ride resource for inclusion in a RiderProfile. Omits the `riders` relational field to avoid infinite recursion"""
class RideForRiderProfileResource(ModelResource):
class Meta:
queryset = Ride.objects.all()
resource_name = 'rides_for_riders'
allowed_methods = [];
""" special rider profile resource for inclusion in a Ride. Omits the `rides` relational field to avoid infinite recursion """
class RiderProfileForRideResource(ModelResource):
class Meta:
queryset = RiderProfile.objects.all()
resource_name = 'riders_for_ride'
allowed_methods = [];
user = fields.ForeignKey(UserResource, 'user', full=True)
以下是我尝试通过 POST 创建新的 RideMemebership 关系的方式:
URL: http://[...]/api/v1/rider_memberships/
数据:
{
"rider": "/api/v1/riders/1/",
"ride": "/api/v1/rides/12/",
"invite_reason": "because it's my ride!"
}
回复:
{
"error_message": "An incorrect URL was provided '/api/v1/riders/1/' for the 'RideForRiderProfileResource' resource.",
"traceback": "Traceback (most recent call last):
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 201, in wrapper
response = callback(request, *args, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 432, in dispatch_list
return self.dispatch('list', request, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 464, in dispatch
response = method(request, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1340, in post_list
updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 2103, in obj_create
bundle = self.full_hydrate(bundle)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 896, in full_hydrate
value = field_object.hydrate(bundle)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 746, in hydrate
return self.build_related_resource(value, request=bundle.request)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 659, in build_related_resource
return self.resource_from_uri(self.fk_resource, value, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 578, in resource_from_uri
obj = fk_resource.get_via_uri(uri, request=request)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 810, in get_via_uri
raise NotFound(\"An incorrect URL was provided '%s' for the '%s' resource.\" % (uri, self.__class__.__name__))
NotFound: An incorrect URL was provided '/api/v1/riders/1/' for the 'RideForRiderProfileResource' resource.
"
}
我的 POST 数据中的两个资源 URI 都指向有效的资源,所以我不确定是什么导致了这个失败。如果重要的话,我正在 Chrome 中使用 Postman 扩展进行测试。我可以 POST 新游乐设施,但不能参加 RideMembership。
谢谢大家。
更新
正如 ozgur 指出的(参见已接受的答案),我在 RideMembershipResource
中引用了错误的资源。我的 Ride
和 RiderProfile
模型有多个 ModelResources
,因此我可以在多对多关系的两侧包含每个模型的有限版本。我更新了 RideMembershipResource
以指向正确的 ForgeignKey
资源,如下所示:
class RideMembershipResource(ModelResource):
class Meta:
queryset = RideMembership.objects.all()
resource_name = 'rider_memberships'
authorization = Authorization()
always_return_data = True
rider = fields.ForeignKey('riderapp.api.RiderProfileForRideResource', 'rider')
ride = fields.ForeignKey('riderapp.api.RideForRiderProfileResource', 'ride')
并将我的 post 数据更改为:
{
"rider": "/api/v1/riders_for_ride/1/",
"ride": "/api/v1/rides_for_riders/12/",
"invite_reason": "because it's my ride!"
}
现在我可以 POST 关系了。干杯!
资源 RideMembershipResource
正在加入两个单独的 Ride
实例。
我认为您应该将 RideMembershipResource
资源中的 rider
字段更改为以下内容;
rider = fields.ForeignKey(RiderProfileForRideResource, 'rider')
我对 tastypie 了解不多,但您可能需要将 POST 数据中的 resource url
更改为:
{
"rider": "/api/v1/riders_for_ride/1/",
"ride": "/api/v1/rides/12/",
"invite_reason": "because it's my ride!"
}
我有 2 个模型(RiderProfile
和 Ride
),它们具有通过中间模型 (RideMembership
) 管理的多对多关系。
我希望能够 POST 我的中间模型资源的新关系条目,但我收到一条错误消息,告诉我我正在为我的资源提供无效的 URL。
注意有类似的another question,但是没有提供POST数据
这是我的模型:
class RiderProfile(models.Model):
user = models.OneToOneField(User)
age = models.IntegerField(max_length=2, null=True, blank=True)
rides = models.ManyToManyField('riderapp.Ride', through="RideMembership", null=True, blank=True)
def __unicode__(self):
return self.user.get_username()
class Ride(models.Model):
name = models.CharField(max_length=64)
riders = models.ManyToManyField(RiderProfile, through="RideMembership", null=True, blank=True)
def __unicode__(self):
return self.name
class RideMembership(models.Model):
rider = models.ForeignKey(RiderProfile)
ride = models.ForeignKey(Ride)
date_joined = models.DateField(default=datetime.now)
invite_reason = models.CharField(max_length=64)
def __unicode__(self):
return self.rider.user.get_username() + ' to ' + self.ride.name()
这是我的资源:
class UserResource(ModelResource):
...
class RiderProfileResource(ModelResource):
class Meta:
queryset = RiderProfile.objects.all()
resource_name = 'riders'
user = fields.ForeignKey(UserResource, 'user', full=True)
rides = fields.ToManyField('riderapp.api.RideForRiderProfileResource', 'rides', full=True)
class RideResource(ModelResource):
class Meta:
queryset = Ride.objects.all()
resource_name = 'rides'
authorization = Authorization()
always_return_data = True
riders = fields.ToManyField('riderapp.api.RiderProfileForRideResource', 'riders', full=True, blank=True, readonly=True)
class RideMembershipResource(ModelResource):
class Meta:
queryset = RideMembership.objects.all()
resource_name = 'rider_memberships'
authorization = Authorization()
always_return_data = True
rider = fields.ForeignKey(RideForRiderProfileResource, 'rider')
ride = fields.ForeignKey(RideResource, 'ride')
"""special ride resource for inclusion in a RiderProfile. Omits the `riders` relational field to avoid infinite recursion"""
class RideForRiderProfileResource(ModelResource):
class Meta:
queryset = Ride.objects.all()
resource_name = 'rides_for_riders'
allowed_methods = [];
""" special rider profile resource for inclusion in a Ride. Omits the `rides` relational field to avoid infinite recursion """
class RiderProfileForRideResource(ModelResource):
class Meta:
queryset = RiderProfile.objects.all()
resource_name = 'riders_for_ride'
allowed_methods = [];
user = fields.ForeignKey(UserResource, 'user', full=True)
以下是我尝试通过 POST 创建新的 RideMemebership 关系的方式:
URL: http://[...]/api/v1/rider_memberships/
数据:
{
"rider": "/api/v1/riders/1/",
"ride": "/api/v1/rides/12/",
"invite_reason": "because it's my ride!"
}
回复:
{
"error_message": "An incorrect URL was provided '/api/v1/riders/1/' for the 'RideForRiderProfileResource' resource.",
"traceback": "Traceback (most recent call last):
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 201, in wrapper
response = callback(request, *args, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 432, in dispatch_list
return self.dispatch('list', request, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 464, in dispatch
response = method(request, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 1340, in post_list
updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 2103, in obj_create
bundle = self.full_hydrate(bundle)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 896, in full_hydrate
value = field_object.hydrate(bundle)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 746, in hydrate
return self.build_related_resource(value, request=bundle.request)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 659, in build_related_resource
return self.resource_from_uri(self.fk_resource, value, **kwargs)
File \"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 578, in resource_from_uri
obj = fk_resource.get_via_uri(uri, request=request)
File \"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 810, in get_via_uri
raise NotFound(\"An incorrect URL was provided '%s' for the '%s' resource.\" % (uri, self.__class__.__name__))
NotFound: An incorrect URL was provided '/api/v1/riders/1/' for the 'RideForRiderProfileResource' resource.
"
}
我的 POST 数据中的两个资源 URI 都指向有效的资源,所以我不确定是什么导致了这个失败。如果重要的话,我正在 Chrome 中使用 Postman 扩展进行测试。我可以 POST 新游乐设施,但不能参加 RideMembership。
谢谢大家。
更新
正如 ozgur 指出的(参见已接受的答案),我在 RideMembershipResource
中引用了错误的资源。我的 Ride
和 RiderProfile
模型有多个 ModelResources
,因此我可以在多对多关系的两侧包含每个模型的有限版本。我更新了 RideMembershipResource
以指向正确的 ForgeignKey
资源,如下所示:
class RideMembershipResource(ModelResource):
class Meta:
queryset = RideMembership.objects.all()
resource_name = 'rider_memberships'
authorization = Authorization()
always_return_data = True
rider = fields.ForeignKey('riderapp.api.RiderProfileForRideResource', 'rider')
ride = fields.ForeignKey('riderapp.api.RideForRiderProfileResource', 'ride')
并将我的 post 数据更改为:
{
"rider": "/api/v1/riders_for_ride/1/",
"ride": "/api/v1/rides_for_riders/12/",
"invite_reason": "because it's my ride!"
}
现在我可以 POST 关系了。干杯!
资源 RideMembershipResource
正在加入两个单独的 Ride
实例。
我认为您应该将 RideMembershipResource
资源中的 rider
字段更改为以下内容;
rider = fields.ForeignKey(RiderProfileForRideResource, 'rider')
我对 tastypie 了解不多,但您可能需要将 POST 数据中的 resource url
更改为:
{
"rider": "/api/v1/riders_for_ride/1/",
"ride": "/api/v1/rides/12/",
"invite_reason": "because it's my ride!"
}