arustgi
arustgi

Reputation: 848

Django: get the first object from a filter query or create

In Django, queryset provides a method called get_or_create that either returns an objects or creates an object.

However, like the get method, get_or_create can throw an exception if the query returns multiple objects.

Is there an method to do this elegantly:

objects = Model.manager.filter(params)
if len(objects) == 0:
   obj = Model.objects.create(params)
else:
   obj = objects[0]

Upvotes: 40

Views: 98402

Answers (7)

demux
demux

Reputation: 4654

I just came across this question and wanted to contribute because no one has suggested actually catching the IndexError.

try:
    obj = Model.objects.filter(params)[0]
except IndexError:
    obj = Model.objects.create(params)

Upvotes: 7

Sagar Adhikari
Sagar Adhikari

Reputation: 1382

Do it in one line:

obj = Model.objects.filter(params).first() or Model.objects.create(params)

Upvotes: 5

Afsa Ahmed
Afsa Ahmed

Reputation: 19

You can try this:

result = Model.objects.filter(field__lookup=value)[0]
if not result:
   result = Model.objects.create(...)
return result

Upvotes: 1

Duncan
Duncan

Reputation: 2560

Here's a manager method that will allow you to extend this function elegantly

class FilterOrCreateManager(models.Manager):
"""Adds filter_or_create method to objects
"""
def filter_or_create(self, **kwargs):
  try:
    created = False
    obj = self.filter(**kwargs).first()
    if obj is None:
      obj = self.create(**kwargs)
      created = True
    return (obj,created)
  except Exception as e:        
    print(e)

Then ensure you add the manager to whichever model(s) you want to use this on:

class MyObj(models.Model):
  objects = FilterOrCreateManager()

After that, you will be able to use it as you would get_or_create:

obj_instance, created = MyObj.filter_or_create(somearg='some value')

Upvotes: 11

Sean
Sean

Reputation: 15961

From django 1.6 there is a convenience method first() that returns the first result in a filter query, or None.

obj = Model.manager.filter(params).first()
if obj is None:
    obj = Model.objects.create(params)

Upvotes: 42

bento
bento

Reputation: 5026

This works for me:

In your view, call something like this:

obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))

In your Model manager, create a function like this:

class CategoryManager(models.Manager):

    def get_or_create_category(self, query):
        try:
            result = Category.objects.filter(name = query)[0]
        except:
            result = Category.objects.create(name = query)
        return result

The logic is simple. First, try to retrieve the first Category object who's name matches the query string (which is provided by the form). If the retrieval fails (because it doesn't exist), create a new Category with the string as its name. Return the result for use in your view.

Upvotes: 0

Timmy O'Mahony
Timmy O'Mahony

Reputation: 54000

get_or_create() is just a convenience function so there's nothing wrong with writing your own, like pavid has shown or

result = Model.objects.filter(field__lookup=value)[0]
if not result:
   result = Model.objects.create(...)
return result

EDIT As suggested, changed the [:1] slice (which returns a single-entry list) after the filter to [0] (which returns the actual object). The problem with this is it will raise an exception if there is not match to the query.

This will also raise a simliar exception:

Model.objects.filter(field_lookup=value).latest()

Looking at the question again, I'm not sure whether the original poster is looking to return multiple objects/rows, or just a way to get around raising an exception when retrieving a single object/row.

Here's another option?

results = Model.objects.filter(...)
if results.exists():
    return results
else:
    return Model.objects.create(...)

and another:

result = None
try:
    result = Model.objects.get(...)
except Model.DoesNotExist:
    result = Model.objects.create(...)

There's nothing wrong with raising & catching exceptions!

Upvotes: 56

Related Questions