38

Models

class Beer(models.Model):
    pass

class Salas(models.Model):
    name =  models.CharField(max_length=20)
    beers =     models.ManyToManyField('Beer', blank=True)

View

beer = Beer.objects.get(user=user)
id_sala = request.GET['id_sala']
sala = get_object_or_404(Salas, id=id_sala)


if beer.salas_set.filter(nombre=sala):
# if beer.objects.filter(sitios__id=beer).exists():
    sala.beers.remove(beer)

else:
    sala.beers.add(beer)

I want to see if there is relationship of beer with sala, how to make this??

bfox
  • 274
  • 2
  • 13
Henry Ramos
  • 397
  • 1
  • 3
  • 7

3 Answers3

97

I advise you to use:

if beer.salas_set.filter(pk=sala.pk).exists():
    # do stuff

If you use sala in beer.salas_set.all() instead, it selects all records from the relation table and loops over them to find, whether the given object is there or not. However, beer.salas_set.filter(pk=sala.pk).exists() only selects zero or one row from the database and immediately gives the result (without looping).

culix
  • 10,188
  • 6
  • 36
  • 52
Jakub QB Dorňák
  • 1,231
  • 1
  • 9
  • 6
  • 18
    Three years later, I feel ashamed by my answer as I was just starting out with Django. I highly recommend yours instead. Thanks! – Patrick Bassut May 20 '17 at 21:44
  • @PatrickBassut: Why !? I think your (accepted) answer is just as good - `in` operator for QuerySet should be as efficient -- and much more pythonic !! (i.e. fewer parenthesis ; but I couldn't find the proper reference in the docs...) – sebhaase May 31 '17 at 09:14
  • 2
    @sebhaase well, because `in` will force django to evaluate the queryset to result in a boolean. That is, querysets are lazily-evaluated, if it's a 10000-object queryset, it'll be pretty slow. On the other hand, `.filter().exists()` will do an additional query in the database, but it'll be just one. – Patrick Bassut May 31 '17 at 16:42
  • and here is now the reference to the docs: https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.exists – sebhaase Jun 04 '17 at 14:10
25

With your solution there might be a problem since name of salas is not unique. So, if you filter by the name of the sala expecting to find one and only one sala, that might not happen.

I advice you to do:

if sala in beer.salas_set.all():
    #do stuff:
else:
    # do other stuff

that way, beer.salas_set.all() will return a QuerySet in which you can check if a specific object is there with the 'in' keyword.

UPDATE: as noted in the comments, you might not want to use .all() since it's expensive. Use .exists() instead. Thanks to @illagrenan

Patrick Bassut
  • 3,310
  • 5
  • 31
  • 54
  • Please try to flesh out your answer a bit more. – Xaver Kapeller May 02 '14 at 17:57
  • 12
    For future visitors: This topic is covered in the Django Docs https://docs.djangoproject.com/en/dev/ref/models/querysets/#exists. `in` operator is supported but `exists()` is according to the Docs faster. – illagrenan Nov 08 '16 at 17:39
  • 1
    `exists()` by itself checks if the queryset has any results in it, that's not the same as what Patrick is doing. If you know how to query for the particular object, you can do: `if beer.salas_set.filter(pk=sala.pk).exists():` and that would be faster unless you already loaded the `salas_set` with `prefetch_related('salas_set')` before (See Jakub's answer) – Apollo Data May 20 '17 at 20:10
4

Django 4.0 has added a new contains() method to QuerySets, which is a natural fit for this use-case.

if beer.salas_set.contains(sala):
    # do stuff
Alan Rowarth
  • 2,250
  • 2
  • 14
  • 10