1

I'm trying to write a query that when an artist id is searched, that artist's info and all of their pieces are the result, basically a join query between Artist and ArtistPiece

#models.py
class Artist(models.Model):
    name = models.CharField(max_length=100)
    artist = models.CharField(max_length=150)
    created_by = models.ForeignKey(User)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class ArtistPiece(models.Model):
    artist = models.ForeignKey(Artist, on_delete = models.CASCADE)
    piece_name = models.CharField(max_length=50)
    details = models.CharField(max_length=100)
#views.py
from .models import Artist, ArtistPiece

class EditArtistViewSet(viewsets.ViewSet):

    def edit(self,request,artist_id):
        artist = Artist.objects.select_related().get(pk=artist_id)
        data = model_to_dict(artist)
        return render(
            request, 'ArtistInfo/edit.html', {
                'editing': 'true',
                'edit_data': json.dumps(data),
            }) 

    def submit_edit(self,request):
        return render(request, 'ArtistInfo/edit.html')

The above works, but it doesn't select any of the data from the ArtistPiece model, despite the primary key. Then after reading this post about using select_related I realized that rather than looking up the artist, I'd have to look up ArtistPiece by the foreign_key, and use select_related to find the info about the artist.

I then tried this

def edit(self, request, artist_id):
    artist = ArtistPiece.objects.get()
    data = model_to_dict(artist)

Which resulted in the following error get() returned more than one ArtistPiece -- it returned 5!. Searching, I found this post which states that I need to use filter rather than get. I then tried this

def edit(self, request, artist_id):
    artist = ArtistPiece.objects.select_related('artist').filter(artist=artist_id)
    data = model_to_dict(artist)

Which gave the following error 'QuerySet' object has no attribute '_meta'. I tried searching for this error and found this which basically suggests to use get, which seems to go against everything else I found.

To summarize, I just want to select an artist and their pieces but I can't seem to figure out how.

Edit - I tried a few things and made some progress, but it's still not working correctly

Just to be clear I'm trying to write a query that will look up an artist by their id, and the result will be the artist's info and the artist's pieces. I can query and retrieve the Artist model based on an artist_id or the ArtistPiece model, but not one query that will return both Artist and all of the ArtistPiece entries associated to that artist.

Adding related_name='pieces'to my model as @bignose has suggested, I then tried artist = Artist.objects.select_related('pieces').filter(pk=artist_id) but that still gave the no attribute.... error above. I then changed data = model_to_dict(artist) to data = [model_to_dict(a) for a in artist] and it worked, however the data was only the artist and nothing from the ArtistPiece model.

Instead of querying the Artist model I instead tried to query the ArtistPiece model as such: artist = ArtistPiece.objects.select_related('artist').filter(artist=artist_id), but then only the artist's pieces were returned and no info from the Artist model.

Community
  • 1
  • 1
user1852176
  • 455
  • 1
  • 9
  • 29
  • I've modified the name of `ArtistPiece` to be singular; the name should reflect the fact that each instance represents *one* of those. – bignose Nov 16 '16 at 00:31

3 Answers3

0

model_to_dict accepts single model object, but your are passing into that the object of QuerySet type (which is quite similar to list). To get the list of dicts all the objects, you need to explicitly call model_to_dict on each model object like:

data = [model_to_dict(a) for a in artist]

Better way is to use QuerySet.values() as:

data = artist.values()

As per the Queryset.Values() document:

Returns a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.

Each of those dictionaries represents an object, with the keys corresponding to the attribute names of model objects.

Also take a look at Serializer class. I think it will be useful for you.

Community
  • 1
  • 1
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • That partially works. I can query either the `Artist` model or the `ArtistPiece` model, but I can't seem to get a "combined" query using the foreign key. I edited my post above to show the changes. Thanks – user1852176 Nov 16 '16 at 01:34
0

The ForeignKey field type automatically also sets an attribute on the foreign model; in your case, Artist.artistpieces_set.

favourite_artist = Artist.objects.get(pk="foo")  # However you get an Artist instance.
favourite_artist.artistpieces_set  # Is a QuerySet of all the ArtistPiece instances that reference this instance.

# Because it's a QuerySet, you can iterate it, filter it, etc.
for piece in favourite_artist.artistpieces_set.all():
    put_in_exhibition(piece)

Thus, when you get an Artist instance, it automatically has an attribute that is a QueryManager for the ArtistPiece instances referencing that Artist.

This is good because it means the foreign key relationship is defined in one place, but it is also accessible from the other end.

See the documentation for “Following relationships “backwards”” for how to use these features.

bignose
  • 30,281
  • 14
  • 77
  • 110
  • When I try this `favourite_artist = Artist.objects.get(pk="foo")` the artist is returned, but `favourite_artist.pieces` throws an error stating `'QuerySet' object has no attribute 'pieces'`. I definitely have pieces as the related_name in my model. – user1852176 Nov 16 '16 at 03:31
  • That message tells you that you have a QuerySet instance, not an Artist instance. Double check how you're getting the object. – bignose Nov 16 '16 at 03:59
  • I made a couple changes and made some progress, but it's still not working. I removed `related_name='pieces'` from the model, also made `favourite_artist.pieces` singular, so it's now `favourite_artist.piece`, which I noticed is the way the [Django documentation](https://docs.djangoproject.com/en/1.10/ref/models/relations/) has it shown. However I'm now getting the error `'RelatedManager' object is not iterable` – user1852176 Nov 16 '16 at 04:49
  • I appreciate your help, but I think I might be misinterpreting how these searches work. I come from a CakePHP/Laravel background where a query for this would return both the Artist and the ArtistPiece in the same object. Ex: `{Artist : { 'name' : 'some_name'....}, 'ArtistPiece' : { 'piece_name' : 'name', ...}}`. Is that true for Django, or in this situation would it be standard practice to query `Artist` and set a variable, and then set a separate variable after querying `ArtistPiece`? – user1852176 Nov 16 '16 at 05:15
0

ArtistPiece.objects.filter(artist=artist_id), this will return a queryset.
If you want a list you can do ArtistPiece.objects.filter(artist=artist_id).values_list('id', flat=True)
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#values-list

drigger
  • 39
  • 1
  • 7
  • Let me know if this is not what you wanted – drigger Nov 16 '16 at 01:31
  • the first query works, but it only returns the `ArtistPiece` entries for that artist_id, not both `Artist` and `ArtistPiece`. The second one returns the following error `'int' object has no attribute '_meta'` – user1852176 Nov 16 '16 at 02:11