wmarchewka
wmarchewka

Reputation: 393

django queryset returns "classname object" instead of DB values

I have a problem with django queryset.

models:

class Rooms(models.Model,):
room_state = models.CharField(max_length=255, choices=[('emptyReady', 'empty'), ('emptyWaiting4Clean', 'clean ready'), ('busy', 'busy')])
room_type = models.IntegerField()
room_number = models.IntegerField()

and I ty to check working of queryset by typing in python shell:

for d in Rooms.objects.filter(room_state="emptyReady", id='1'):    print(d)

In my DB there is table Rooms_rooms, and inside there is column room_state, and if we make query on DB :

SELECT room_state FROM Rooms_rooms where id = 1

return value is emptyReady

But when I using queryset (like example above) I alwas get: Rooms object

What I did wrong? I try to search answer in all WEB for a 3 hours, and my patience is now over so I count on You all ;)

Upvotes: 3

Views: 7416

Answers (3)

J-a-n-u-s
J-a-n-u-s

Reputation: 1587

Update to and refinement of @karthikr 's answer...

If you're trying to take a queryset and pass it as context to a template to then iterate through in a loop such as:

(in views.py)

# The manytomanyfield named subscribers in the Feed object points to the userprofile object which has a onetoonefield relationship with the user object
# [...]
queryset = Feed.objects.filter(subscribers=request.user.userprofile)
        context = {
                    "result_list"    : queryset,
        }
        return render(request, "subscriptions/subscriptions.html", context)

(in subscriptions.html)

# [...]
    {% for instance in result_list %}
        <p> {{ instance.pk }} |---> <a href=''>{{ instance.url }}</a> </p>
    {% endfor %}
# [...]

You will get the error:

'Feed' object has no attribute '__name__'

When changing the template to show result_list without the for-loop such as:

# [...]
{{ result_list }}
# [...]

I saw a the same output as the OP (this is what the renderer outputted):

<QuerySet [<Feed 'https://news.ycombinator.com/rss'>

In my case it was Feed object instead of Rooms object.

When originally trying to loop through result_list, the error arose when the template renderer thought the string Feed was a column of the queryset or the instance of an object in and of itself, and then tried to determine what to call it in a dictionary-like data structure.

I have no idea why Feed.objects.filter() in my case or Rooms.objects.filter() in the OP's case, decides to include the class name without quotes thus confusing the template renderer.

The solution for me was to change my query to:

queryset = Feed.objects.filter(subscribers=request.user.userprofile).values_list('pk','url', named=True)

Using named=True is important to be able to address the column values individually within the template (e.g. {{ instance.pk }}). With the above query and the for-loop in subscriptions.html template as shown above, the rendering goes through without a hitch:

1 |---> https://news.ycombinator.com/rss

Upvotes: 0

Salva
Salva

Reputation: 6847

If I understand, you are trying to simulate the following query with Django:

SELECT room_state FROM Rooms_rooms where id = 1

Given your models, with Django, this can be achieved by issuing:

Rooms.object.filter(room_state='emptyReady', id=1).values('room_state')

The values() function is available in Django since version 1.2 (if not earlier) and allows you to select only those fields you will need to query for. This is considerably faster than obtaining the complete model as it is only giving you a dictionary with the selected fields.

Note in your code you was sending the id as a string, not a number. Furthermore, functions like filter() or values() never return the collection but a Django object that you need to iterate over in order to get the values. So, finally you need to do something like:

Rooms.object.filter(room_state='emptyReady', id=1).values('room_state')[0]['room_state']

This is done intentionally to delay the effective work until it is truly needed.

Upvotes: 0

karthikr
karthikr

Reputation: 99630

Here:

Rooms.objects.filter(room_state="emptyReady", id='1')

returns a queryset of Room objects.

Now, if you want a specific value:

Rooms.objects.filter(room_state="emptyReady", id='1').values_list('room_state', flat=True)

Read more on values_list here

Alternatively:

for d in Rooms.objects.filter(room_state="emptyReady", id='1'):    print(d.room_state)

Another way is to specify a __unicode__ attribute on class Room

class Rooms(models.Model,):
    room_state = models.CharField(max_length=255, choices=[('emptyReady', 'empty'), ('emptyWaiting4Clean', 'clean ready'), ('busy', 'busy')])
    room_type = models.IntegerField()
    room_number = models.IntegerField()

    def __unicode__(self):
        return u'%s' % self.room_state

This should prin room_state value if print d is called

Upvotes: 7

Related Questions