Django 使用Tastypie创建相关资源
我想让tastypie创建一个UserProfileResource,作为我发布到UserResource的结果 models.py:Django 使用Tastypie创建相关资源,django,tastypie,Django,Tastypie,我想让tastypie创建一个UserProfileResource,作为我发布到UserResource的结果 models.py: class UserProfile(models.Model): home_address = models.TextField() user = models.ForeignKey(User, unique=True) 资源.py class UserProfileResource(ModelResource): home_addre
class UserProfile(models.Model):
home_address = models.TextField()
user = models.ForeignKey(User, unique=True)
资源.py
class UserProfileResource(ModelResource):
home_address = fields.CharField(attribute='home_address')
class Meta:
queryset = UserProfile.objects.all()
resource_name = 'profile'
excludes = ['id']
include_resource_uri = False
class UserResource(ModelResource):
profile = fields.ToOneField(UserProfileResource, 'profile', full=True)
class Meta:
queryset = User.objects.all()
resource_name = 'user'
allowed_methods = ['get', 'post', 'delete', 'put']
fields = ['username']
filtering = {
'username': ALL,
}
curl命令:
curl -v -H "Content-Type: application/json" -X POST --data '{"username":"me", "password":"blahblah", "profile":{"home_address":"somewhere"}}' http://127.0.0.1:8000/api/user/
但我得到了:
Django Version: 1.4
Exception Type: IntegrityError
Exception Value:
null value in column "user_id" violates not-null constraint
这似乎是一个鸡和蛋的场景。我需要用户id来创建UserProfileResource,我需要配置文件来创建UserResource。显然,我在做一些非常愚蠢的事情
外面有人能发光吗?
非常感谢
约翰诺克
我按照巴勃罗的建议修改了我的代码
class UserProfileResource(StssRessource):
home_address = fields.CharField(attribute='home_address')
user = fields.ToOneField('resources.UserResource', attribute='user', related_name='profile')
class Meta:
queryset = UserProfile.objects.all()
resource_name = 'profile'
class UserResource(ModelResource):
profile = fields.ToOneField('resources.UserProfileResource', attribute='profile', related_name = 'user', full=True)
class Meta:
queryset = User.objects.all()
resource_name = 'user'
但我得到:
Django Version: 1.4
Exception Type: DoesNotExist
这与尝试访问ORM中的用户资源有关,但在创建相关的\u对象UserProfileResource时,该资源不存在。这是正确的。用户ORM只有在创建相关的\u对象之后才会创建
其他人看到了吗???在我最终设法保存相关资源的两天后,问题是您必须指定关系的双方及其相关名称,在您的情况下,可能是这样的:
class UserProfileResource(ModelResource):
home_address = fields.CharField(attribute='home_address')
user = fields.ToOneField('path.to.api.UserResource', attribute='user', related_name='profile')
#in my case it was a toManyField, I don't know if toOneField works here, you can try toManyField.
class UserResource(ModelResource):
profile = fields.ToOneField(UserProfileResource, 'profile', related_name='user', full=True)
EDIT#2:终于找到了解决问题的方法,但不幸的是,它需要一些子类化和重写。下面是我如何让它工作的:
首先,创建一个新的field子类-我将其称为my RelatedToOneField:
from tastypie.bundle import Bundle
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from tastypie.exceptions import ApiFieldError, NotFound
class RelatedToOneField(fields.RelatedField):
"""
Provides access to related data via foreign key.
This subclass requires Django's ORM layer to work properly.
"""
help_text = 'A single related resource. Can be either a URI or set of nested resource data.'
def __init__(self, to, attribute, related_name=None, default=fields.NOT_PROVIDED,
null=False, blank=False, readonly=False, full=False,
unique=False, help_text=None):
super(RelatedToOneField, self).__init__(
to, attribute, related_name=related_name, default=default,
null=null, blank=blank, readonly=readonly, full=full,
unique=unique, help_text=help_text
)
self.fk_resource = None
def dehydrate(self, bundle):
try:
foreign_obj = getattr(bundle.obj, self.attribute)
except ObjectDoesNotExist:
foreign_obj = None
if not foreign_obj:
if not self.null:
raise ApiFieldError("The model '%r' has an empty attribute '%s' and doesn't allow a null value." % (bundle.obj, self.attribute))
return None
self.fk_resource = self.get_related_resource(foreign_obj)
fk_bundle = Bundle(obj=foreign_obj, request=bundle.request)
return self.dehydrate_related(fk_bundle, self.fk_resource)
def hydrate(self, bundle):
value = super(RelatedToOneField, self).hydrate(bundle)
if value is None:
return value
# START OF MODIFIED CONTENT
kwargs = {
'request': bundle.request,
}
if self.related_name:
kwargs['related_obj'] = bundle.obj
kwargs['related_name'] = self.related_name
return self.build_related_resource(value, **kwargs)
#return self.build_related_resource(value, request=bundle.request)
#END OF MODIFIED CONTENT
然后覆盖“top”模型中的obj_create&save_相关函数,或者在本例中是UserResource。以下是相关的覆盖:
def obj_create(self, bundle, request=None, **kwargs):
"""
A ORM-specific implementation of ``obj_create``.
"""
bundle.obj = self._meta.object_class()
for key, value in kwargs.items():
setattr(bundle.obj, key, value)
bundle = self.full_hydrate(bundle)
# Save the main object.
# THIS HAS BEEN MOVED ABOVE self.save_related().
bundle.obj.save()
# Save FKs just in case.
self.save_related(bundle)
# Now pick up the M2M bits.
m2m_bundle = self.hydrate_m2m(bundle)
self.save_m2m(m2m_bundle)
return bundle
def save_related(self, bundle):
"""
Handles the saving of related non-M2M data.
Calling assigning ``child.parent = parent`` & then calling
``Child.save`` isn't good enough to make sure the ``parent``
is saved.
To get around this, we go through all our related fields &
call ``save`` on them if they have related, non-M2M data.
M2M data is handled by the ``ModelResource.save_m2m`` method.
"""
for field_name, field_object in self.fields.items():
if not getattr(field_object, 'is_related', False):
continue
if getattr(field_object, 'is_m2m', False):
continue
if not field_object.attribute:
continue
# Get the object.
# THIS HAS BEEN MOVED ABOVE the field_object.blank CHECK
try:
related_obj = getattr(bundle.obj, field_object.attribute)
except ObjectDoesNotExist:
related_obj = None
# THE 'not related_obj' CHECK HAS BEEN ADDED
if field_object.blank and not related_obj: # ADDED
continue
# Because sometimes it's ``None`` & that's OK.
if related_obj:
# THIS HAS BEEN ADDED
setattr(related_obj, field_object.related_name, bundle.obj) # ADDED
related_obj.save()
setattr(bundle.obj, field_object.attribute, related_obj)
将这些添加到API后,一切都应该正常工作(至少在0.9.11上)。修复的主要部分是没有为ToOneField正确添加相关的对象。我的RelatedToOneField子类实现了对字段代码的检查
编辑:我又错了,ToOneField的0.9.12版仍然不起作用。我的问题是已经有一个UserProfileResource,它的数据与我试图在数据库中发布的数据相同。它只是抓住那一行并修改它,而不是创建新的内容
在这方面花费了太多时间之后,ToOneField的bug似乎在0.9.12版中得到了修复(有关讨论,请参见Pablo接受答案中的注释) 如果django-tastypie>=0.9.12,则以下操作应有效:
class UserResource(ModelResource):
profile = fields.ToOneField('path.to.api.UserProfileResource', 'profile', related_name='user', full=True)
class UserProfileResource(ModelResource):
home_address = fields.CharField(attribute='home_address')
user = fields.ToOneField(UserResource, attribute='user', related_name='profile')
如果django tastypie我也有同样的问题。谢谢Pablo,我也有两天了!非常感谢。实际上我可能说得太快了。当我运行一个帖子时,它看起来确实起作用了。但当我再次尝试时,我得到了以下结果:“用户”字段没有数据,不允许空值。为了澄清,我现在可以使用它,但我必须让用户填写一个“ToManyField”(正如Pablo在评论中所建议的)。我还必须在profile字段中添加“null=True”。否则GET操作将无法工作。不管怎样,现在一切都好起来了。谢谢你的帮助,巴勃罗!你是怎么想到这个的?为什么会有什么不同?即使对方资源的模型没有对应关系,您是否在其上定义RelatedField?我没有对应关系,你的建议似乎对我没有任何影响。我觉得根本的问题是别的。
class UserResource(ModelResource):
profile = fields.ToOneField('path.to.api.UserProfileResource', 'profile', related_name='user', full=True)
class UserProfileResource(ModelResource):
home_address = fields.CharField(attribute='home_address')
user = fields.ToManyField(UserResource, attribute='user', related_name='profile')