EDIT # 2: I finally figured out how to fix the situation, but unfortunately, this requires a few subclasses and overrides. This is how I got the job:
First create a new subclass of the class - I named my Associated ToOneField:
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 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
Then override the obj_create and save_related functions in your "top" model, or in this case UserResource. Here are the relevant overrides:
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)
After you add them to your API, everything should work (at least 0.9.11). The main part of the fix - related_obj was not added properly for ToOneField. The RelatedToOneField subclass implements this check in field hydrated code.
EDIT: I made a mistake again, ToOneField still doesn't work in 0.9.12. I realized that there is already a UserProfileResource with the same data that I was trying to publish to the database. He simply grabbed this line and modified it instead of creating something new.
After you spent too much time on this, it seems that there was a bug for ToOneField that was fixed in version 0.9.12 (see comments in Pablo for an answer to the corresponding discussion).
If django-tastypie> = 0.9.12, then the following should work:
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')
If django-tastypie <0.9.12, you need to do the following:
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')
Note: the order of UserResource and UserProfileResource switched, as this made more sense for my mental model.