doniyor
doniyor

Reputation: 37876

django model object filter

I have tables called 'has_location' and 'locations'. 'has_location' has user_has and location_id and its own id which is given by django itself.

'locations' have more columns.

Now I want to get all locations of some certain user. What I did is..(user.id is known):

users_locations_id = has_location.objects.filter(user_has__exact=user.id)
locations = Location.objects.filter(id__in=users_locations_id)
print len(locations)

but I am getting 0 by this print. I have data in db. but I have the feeling that __in does not accept the models id, does it ?

thanks

Upvotes: 7

Views: 61342

Answers (3)

Gareth Rees
Gareth Rees

Reputation: 65854

Using __in for this kind of query is a common anti-pattern in Django: it's tempting because of its simplicity, but it scales poorly in most databases. See slides 66ff in this presentation by Christophe Pettus.

You have a many-to-many relationship between users and locations, represented by the has_location table. You would normally describe this to Django using a ManyToManyField with a through table, something like this:

class Location(models.Model):
    # ...

class User(models.Model):
    locations = models.ManyToManyField(Location, through = 'LocationUser')
    # ...

class LocationUser(models.Model):
    location = models.ForeignKey(Location)
    user = models.ForeignKey(User)
    class Meta:
         db_table = 'has_location'

Then you can fetch the locations for a user like this:

user.locations.all()

You can query the locations in your filter operations:

User.objects.filter(locations__name = 'Barcelona')

And you can request that users' related locations be fetched efficiently using the prefetch_related() method on a query set.

Upvotes: 11

okm
okm

Reputation: 23871

What do your models look like?

For your doubt, __in does accept filtered ids.

For your current code, the solution:

locations = Location.objects.filter(id__in=has_location.objects.filter(user=user).values('location_id'))
# if you just want the length of the locations, evaluate locations.count()
locations.count()
# if you want to iterate locations to access items afterwards
len(locations)

Upvotes: 1

Pavel Anossov
Pavel Anossov

Reputation: 62908

You are using has_location's own id to filter locations. You have to use location_ids to filter locations:

user_haslocations = has_location.objects.filter(user_has=user)
locations = Location.objects.filter(id__in=user_haslocations.values('location_id'))

You can also filter the locations directly through the reverse relation:

location = Location.objects.filter(has_location__user_has=user.id)

Upvotes: 7

Related Questions