EDIT # 2: Cuối cùng đã tìm ra cách để sửa chữa mọi thứ, nhưng tiếc là nó đòi hỏi một chút subclassing và ghi đè. Đây là cách tôi đã nhận nó làm việc:
Đầu tiên, tạo một lớp con lĩnh vực mới - Tôi gọi RelatedToOneField tôi:
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
Sau đó ghi đè lên obj_create & chức năng save_related trong mô hình "top" của bạn, hoặc trong trường hợp này, UserResource. Dưới đây là các ghi đè có liên quan:
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)
Sau khi bạn thêm chúng vào API, mọi thứ sẽ hoạt động (Ít nhất là trên 0.9.11). Phần chính của bản sửa lỗi này là của related_obj không được thêm vào đúng cho ToOneField. Lớp con RelatedToOneField của tôi thực hiện kiểm tra này vào mã hydrate của trường.
EDIT: Tôi đã sai lần nữa, ToOneField vẫn không hoạt động trong 0.9.12. Gotcha của tôi là đã có một UserProfileResource với cùng một dữ liệu mà tôi đã cố gắng đăng trong cơ sở dữ liệu.Nó chỉ nắm lấy hàng đó và sửa đổi nó thay vì tạo ra một cái gì đó mới.
Sau cũng dành quá nhiều thời gian về vấn đề này, có vẻ như rằng có một lỗi cho ToOneField rằng đã được cố định trong phiên bản 0.9.12 (xem chú thích trong câu trả lời chấp nhận của Pablo để thảo luận có liên quan).
Nếu django-tastypie> = 0.9.12, sau đây nên làm việc:
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')
nếu django-tastypie < 0.9.12, bạn sẽ cần phải làm như sau:
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')
Lưu ý: đã chuyển thứ tự của UserResource & UserProfileResource vì điều đó có ý nghĩa hơn đối với mô hình tinh thần của tôi.
Tôi gặp vấn đề tương tự ở đây. – Pablo