Search code examples
pythondjangotastypie

Foreign Key Resource from dynamic field


I've got an API endpoint called TrackMinResource, which returns the minimal data for a music track, including the track's main artist returned as an ArtistMinResource. Here are the definitions for both:

class TrackMinResource(ModelResource):
    artist = fields.ForeignKey(ArtistMinResource, 'artist', full=True)

    class Meta:
        queryset = Track.objects.all()
        resource_name = 'track-min'
        fields = ['id', 'artist', 'track_name', 'label', 'release_year', 'release_name']
        include_resource_uri = False
        cache = SimpleCache(public=True)

    def dehydrate(self, bundle):
        bundle.data['full_artist_name'] = bundle.obj.full_artist_name()
        if bundle.obj.image_url != settings.NO_TRACK_IMAGE:
            bundle.data['image_url'] = bundle.obj.image_url

class ArtistMinResource(ModelResource):
    class Meta:
        queryset = Artist.objects.all()
        resource_name = 'artist-min'
        fields = ['id', 'artist_name']
        cache = SimpleCache(public=True)

    def get_resource_uri(self, bundle_or_obj):
        return '/api/v1/artist/' + str(bundle_or_obj.obj.id) + '/'

The problem is, the artist field on Track (previously a ForeignKey) is now a model method called main_artist (I've changed the structure of the database somewhat, but I'd like the API to return the same data as it did before). Because of this, I get this error:

{"error": "The model '<Track: TrackName>' has an empty attribute 'artist' and doesn't allow a null value."}

If I take out full=True from the 'artist' field of TrackMinResource and add null=True instead, I get null values for the artist field in the returned data. If I then assign the artist in dehydrate like this:

bundle.data['artist'] = bundle.obj.main_artist()

...I just get the artist name in the returned JSON, rather than a dict representing an ArtistMinResource (along with the associated resource_uris, which I need).

Any idea how to get these ArtistMinResources into my TrackMinResource? I can access an ArtistMinResource that comes out fine using the URL endpoint and asking for it by ID. Is there a function for getting that result from within the dehydrate function for TrackMinResource?


Solution

  • You can use your ArtistMinResource in TrackMinResource's dehydrate like this (assuming that main_artist() returns the object that your ArtistMinResource represents):

    artist_resource = ArtistMinResource()
    artist_bundle = artist_resource.build_bundle(obj=bundle.obj.main_artist(), request=request)
    artist_bundle = artist_resource.full_dehydrate(artist_bundle)
    artist_json = artist_resource.serialize(request=request, data=artist_bundle, format='application/json')
    

    artist_json should now contain your full artist representation. Also, I'm pretty sure you don't have to pass the format if you pass the request and it has a content-type header populated.