Skip to content

Commit

Permalink
Docs for get_or_create()
Browse files Browse the repository at this point in the history
  • Loading branch information
coleifer committed Apr 22, 2015
1 parent 928c7f0 commit 44ccf2b
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
32 changes: 32 additions & 0 deletions docs/peewee/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,38 @@ Models
found. If more than one row is found, the first row returned by the
database cursor will be used.

.. py:classmethod:: get_or_create([defaults=None[, **kwargs]])
:param dict defaults: A dictionary of values to set on newly-created model instances.
:param kwargs: Django-style filters specifying which model to get, and what values to apply to new instances.
:returns: A 2-tuple containing the model instance and a boolean indicating whether the instance was created.

This function attempts to retrieve a model instance based on the provided filters. If no matching model can be found, a new model is created using the parameters specified by the filters and any values in the ``defaults`` dictionary.

Example **without** ``get_or_create``:

.. code-block:: python
# Without `get_or_create`, we might write:
try:
person = Person.get(
(Person.first_name == 'John') &
(Person.last_name == 'Lennon'))
except Person.DoesNotExist:
person = Person.create(
first_name='John',
last_name='Lennon',
birthday=datetime.date(1940, 10, 9))
Equivalent code using ``get_or_create``:

.. code-block:: python
person, created = Person.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': datetime.date(1940, 10, 9)})
.. py:classmethod:: alias()
:rtype: :py:class:`ModelAlias` instance
Expand Down
26 changes: 25 additions & 1 deletion docs/peewee/querying.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ For more information, see the documentation on:
Get or create
-------------

While peewee has a :py:meth:`~Model.get_or_create` method, this should really not be used outside of tests as it is vulnerable to a race condition. The proper way to perform a *get or create* with peewee is to rely on the database to enforce a constraint.
While peewee has a :py:meth:`~Model.get_or_create` method, I do not advise you use it. The proper way to perform a *get or create* with peewee is to *create then get*, relying on database constraints to avoid duplicate records.

Let's say we wish to implement registering a new user account using the :ref:`example User model <blog-models>`. The *User* model has a *unique* constraint on the username field, so we will rely on the database's integrity guarantees to ensure we don't end up with duplicate usernames:

Expand All @@ -284,6 +284,30 @@ Let's say we wish to implement registering a new user account using the :ref:`ex
# making it safe to call .get().
return User.get(User.username == username)
The above example first attempts at creation, then falls back to retrieval, relying on the database to enforce a unique constraint.

If you prefer to attempt to retrieve the record first, you can use :py:meth:`~Model.get_or_create`. This method is implemented along the same lines as the Django function of the same name. You can use the Django-style keyword argument filters to specify your ``WHERE`` conditions. The function returns a 2-tuple containing the instance and a boolean value indicating if the object was created.

Here is how you might implement user account creation using :py:meth:`~Model.get_or_create`:

.. code-block:: python
user, created = User.get_or_create(username=username)
Suppose we have a different model ``Person`` and would like to get or create a person object. The only conditions we care about when retrieving the ``Person`` are their first and last names, **but** if we end up needing to create a new record, we will also specify their date-of-birth and favorite color:

.. code-block:: python
person, created = Person.get_or_create(
first_name=first_name,
last_name=last_name,
defaults={'dob': dob, 'favorite_color': 'green'})
Any keyword argument passed to :py:meth:`~Model.get_or_create` will be used in the ``get()`` portion of the logic, except for the ``defaults`` dictionary, which will be used to populate values on newly-created instances.

For more details check out the documentation for :py:meth:`Model.get_or_create`.


Selecting multiple records
--------------------------

Expand Down

0 comments on commit 44ccf2b

Please sign in to comment.