tags:

views:

832

answers:

4

This is a pretty simple Django question, but I can't find the answer in the Django docs, despite lots of hunting!

How do I check whether an object already exists, and only add it if it does not already exist?

Here's the code - I don't want to add the follow_role twice in the database if it already exists. How do I check first? Use get() maybe - but then will Django complain if get() doesn't return anything?

current_user = request.user
follow_role = UserToUserRole(from_user=current_user, to_user=user, role='follow')
follow_role.save()

Thanks!

A: 

You can use get() to retrieve the object and then check if it's None to see if it not already exists.

existing_follow_role = UserToUserRole.objects.filter(role='follow').get()
if existing_follow_role is None:
  ## add new role
tomlog
Calling `.get()` on an empty queryset raises an error, so this doesn't solve the problem
Ben James
Use a try/except block to catch the error.
singingwolfboy
No, get_or_create looks like a much cleaner way to handle this. Didn't know it existed.
tomlog
I have written that try/except DoesNotExist block so many times that I created my own 'get\_or\_none(queryset)' function to wrap it. It makes the code more succint.
Joe Holloway
No, sorry, that gives me an error when the object doesn't exist: DoesNotExist at xxxx UserToUserRole matching query does not exist.I have had to adapt your suggested code slightly - I need to check the from_user and to_user fields as well as the role. Here's what I had. current_user = request.user existing_follow_role = UserToUserRole.objects.filter(from_user=current_user, to_user=user, role='follow').get() if existing_follow_role is None: follow_role = UserToUserRole(from_user=current_user, to_user=user, role='follow') follow_role.save()
AP257
@AP257: yes, I forgot to add those fields in my example.
tomlog
+6  A: 

There's a helper function for this idiom called 'get_or_create' on your model manager:

role, created = UserToUserRole.objects.get_or_create(
    from_user=current_user, to_user=user, role='follow')

It returns a tuple of (model, bool) where 'model' is the object you're interested in and 'bool' tells you whether it had to be created or not.

Joe Holloway
Brilliant. Solves the problem. Thank you! Is there a converse function to check whether an object exists, then DELETE it if so, and do nothing if it doesn't?
AP257
I don't know of a helper function off the top of my head, but I think you can get close by doing UserToUserRole.objects.filter(...).delete(). This wouldn't give you a way to inspect whether the object actually existed, though. You'd have to check the count() on your query set before the delete. Of course, you could take a peek at the get\_or\_create source and create your own helper function easy enough.
Joe Holloway
OK. Thanks again for the very clear answer!
AP257
note: `exists()` is supposed to be a little faster than `count()` :)
Jiaaro
exists() must be fresh off the presses, I was unaware that exists() exists.
Joe Holloway
@Joe: technically, ``exists()`` isn't even off the presses now. It was committed a month ago and will be in Django 1.2 (http://code.djangoproject.com/changeset/11646)
piquadrat
+1  A: 

If you're using a recent version of Django, you can use the unique_together option to the UserToUserRole model, and then use the get_or_create() method Joe Holloway showed. This will guarantee that you can't get a duplicate.

Harper Shelby
A: 

If you want the check done every time before save you could use the pre save signal to check, http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.pre%5Fsave

Alternatively in the model you could specify unique=True for the property that determines whether an item is the same item, this will cause an Exception (django.db.IntegrityError) to be thrown if you try to save the same thing again.

If you have more than one field together that makes something unique you'll need to use unique_together in the Meta inner class http://docs.djangoproject.com/en/dev/ref/models/options/#unique-together

StephenPaulger