Code

Ticket #3182: 3182.2.diff

File 3182.2.diff, 12.3 KB (added by gwilson, 5 years ago)

added versionadded directive for update_or_create

Line 
1=== modified file 'django/db/models/base.py'
2--- django/db/models/base.py    2009-03-15 03:41:33 +0000
3+++ django/db/models/base.py    2009-03-15 16:32:50 +0000
4@@ -331,6 +331,18 @@
5 
6     save.alters_data = True
7 
8+    def update(self, **kwargs):
9+        """
10+        Set the object's fields to the new values passed in as keyword
11+        arguments and then save the object.  Fields not specified in the
12+        keyword arguments will not be altered.
13+        """
14+        for k, v in kwargs.iteritems():
15+            setattr(self, k, v)
16+        self.save()
17+
18+    update.alters_data = True
19+
20     def save_base(self, raw=False, cls=None, force_insert=False,
21             force_update=False):
22         """
23
24=== modified file 'django/db/models/manager.py'
25--- django/db/models/manager.py 2009-03-15 03:41:33 +0000
26+++ django/db/models/manager.py 2009-03-15 16:09:08 +0000
27@@ -119,6 +119,9 @@
28     def get_or_create(self, **kwargs):
29         return self.get_query_set().get_or_create(**kwargs)
30 
31+    def update_or_create(self, **kwargs):
32+        return self.get_query_set().update_or_create(**kwargs)
33+
34     def create(self, **kwargs):
35         return self.get_query_set().create(**kwargs)
36 
37
38=== modified file 'django/db/models/query.py'
39--- django/db/models/query.py   2009-03-11 07:06:50 +0000
40+++ django/db/models/query.py   2009-03-15 16:09:08 +0000
41@@ -379,6 +379,19 @@
42                 except self.model.DoesNotExist:
43                     raise e
44 
45+    def update_or_create(self, **kwargs):
46+        """
47+        Looks up an object with the given kwargs, creating one if necessary.
48+        If the object already exists, then its fields are updated with the
49+        values passed in the defaults dictionary.
50+        Returns a tuple of (object, created), where created is a boolean
51+        specifying whether an object was created.
52+        """
53+        obj, created = self.get_or_create(**kwargs)
54+        if not created:
55+            obj.update(**kwargs.pop('defaults', {}))
56+        return obj, created
57+
58     def latest(self, field_name=None):
59         """
60         Returns the latest object, according to the model's 'get_latest_by'
61
62=== modified file 'docs/ref/models/instances.txt'
63--- docs/ref/models/instances.txt       2008-10-06 08:28:18 +0000
64+++ docs/ref/models/instances.txt       2009-03-15 16:57:38 +0000
65@@ -5,7 +5,7 @@
66 ========================
67 
68 .. currentmodule:: django.db.models
69-   
70+
71 This document describes the details of the ``Model`` API. It builds on the
72 material presented in the :ref:`model <topics-db-models>` and :ref:`database
73 query <topics-db-queries>` guides, so you'll probably want to read and
74@@ -49,7 +49,7 @@
75 your object the first time you call ``save()``::
76 
77     >>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
78-    >>> b2.id     # Returns None, because b doesn't have an ID yet.
79+    >>> b2.id     # Returns None, because b2 doesn't have an ID yet.
80     >>> b2.save()
81     >>> b2.id     # Returns the ID of your new object.
82 
83@@ -94,8 +94,8 @@
84 Given the above ``'Cheddar Talk'`` blog example, this example would override the
85 previous record in the database::
86 
87-    b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
88-    b4.save()  # Overrides the previous blog with ID=3!
89+    >>> b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
90+    >>> b4.save()  # Overrides the previous blog with ID=3!
91 
92 See `How Django knows to UPDATE vs. INSERT`_, below, for the reason this
93 happens.
94@@ -187,6 +187,35 @@
95 errors that are difficult to track down. This feature is for advanced use
96 only.
97 
98+
99+.. _ref-models-update:
100+
101+Updating objects
102+================
103+
104+.. versionadded:: 1.1
105+
106+To update and save a model instance in one step, call ``update()``:
107+
108+.. method:: Model.update(**kwargs)
109+
110+A convenience method for updating and saving a model instance in one step,
111+where the keyword arguments are the names of the fields on your model you want
112+to update.  Like ``save()``, the ``update()`` method has no return value.
113+
114+Given the earlier ``'Not Cheddar'`` blog example, this example would update
115+and save the existing model instance::
116+
117+    >>> b4.name
118+    'Not Cheddar'
119+    >>> b4.update(name='All Cheddar', tagline='Nothing but cheddar cheese')
120+    >>> b4.name
121+    'All Cheddar'
122+
123+Since ``update()`` calls ``save()`` behind the scenes, Django will hit the
124+database every time ``update()`` is called.
125+
126+
127 .. _model-instance-methods:
128 
129 Other model instance methods
130
131=== modified file 'docs/ref/models/querysets.txt'
132--- docs/ref/models/querysets.txt       2009-02-17 10:17:29 +0000
133+++ docs/ref/models/querysets.txt       2009-03-15 17:10:37 +0000
134@@ -896,6 +896,54 @@
135 
136 .. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
137 
138+``update_or_create(**kwargs)``
139+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140+
141+.. versionadded:: 1.1
142+
143+A convenience method for looking up an object with the given kwargs, and then
144+either updating the values of the object if one is found or creating an
145+object if one was not found.
146+
147+This method calls ``get_or_create()`` behind the scenes, and similarly
148+returns a tuple of ``(object, created)``, where ``object`` is the updated or
149+created object and ``created`` is a boolean specifying whether a new object
150+was created.
151+
152+This is meant as a shortcut to the following type of code::
153+
154+    obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
155+                       defaults={'birthday': date(1940, 10, 9)})
156+    if not created:
157+        obj.update(birthday=date(1940, 10, 9))
158+
159+This pattern gets quite unwieldy as the number of fields in a model goes up.
160+The above example can be rewritten using ``update_or_create()`` like so::
161+
162+    obj, created = Person.objects.update_or_create(first_name='John', last_name='Lennon',
163+                       defaults={'birthday': date(1940, 10, 9)})
164+
165+Any keyword arguments passed to ``update_or_create()`` will be used in a
166+call to ``get_or_create()``. If ``get_or_create()`` creates an object, then
167+nothing needs to be done by ``update_or_create()`` and a tuple of the created
168+object and ``True`` is returned. If, on the other hand, ``get_or_create()``
169+does not create a new object, then ``update_or_create()`` will update the
170+object with the values passed in the ``defaults`` parameter and a tuple of
171+the updated object and ``True`` is returned.
172+
173+The ``defaults`` parameter should be a dict of attribute-value pairs that
174+you want to update. If ``defaults`` is empty or not specified, then
175+``update_or_create()`` will act exactly like ``get_or_create()`` since there
176+would be nothing to update.
177+
178+As with ``get_or_create()``, if you need to use ``update_or_create()`` in a
179+view, please make sure to use it only in ``POST`` requests unless you have a
180+good reason not to. ``GET`` requests shouldn't have any effect on data; use
181+``POST`` whenever a request to a page has a side effect on your data. For
182+more, see `Safe methods`_ in the HTTP spec.
183+
184+.. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
185+
186 ``count()``
187 ~~~~~~~~~~~
188 
189
190=== modified file 'tests/modeltests/update/models.py'
191--- tests/modeltests/update/models.py   2008-08-09 17:19:44 +0000
192+++ tests/modeltests/update/models.py   2009-03-15 16:28:04 +0000
193@@ -1,6 +1,6 @@
194 """
195-Tests for the update() queryset method that allows in-place, multi-object
196-updates.
197+Tests for the update() queryset and model methods that allow in-place updates
198+to multiple objects or a single object.
199 """
200 
201 from django.db import models
202@@ -22,13 +22,16 @@
203 
204 
205 __test__ = {'API_TESTS': """
206+# QuerySet method #############################################################
207+
208 >>> DataPoint(name="d0", value="apple").save()
209 >>> DataPoint(name="d2", value="banana").save()
210 >>> d3 = DataPoint.objects.create(name="d3", value="banana")
211 >>> RelatedPoint(name="r1", data=d3).save()
212 
213 Objects are updated by first filtering the candidates into a queryset and then
214-calling the update() method. It executes immediately and returns nothing.
215+calling the update() method. It executes immediately and returns the number of
216+objects affected.
217 
218 >>> DataPoint.objects.filter(value="apple").update(name="d1")
219 1
220@@ -74,5 +77,43 @@
221     ...
222 AssertionError: Cannot update a query once a slice has been taken.
223 
224+# Model method ################################################################
225+
226+# Get an existing DataPoint object.
227+>>> d = DataPoint.objects.get(name="d2")
228+>>> d.value, d.another_value
229+(u'thing', u'peaches')
230+
231+# Update multiple fields.
232+>>> d.update(value='oranges', another_value='apples')
233+
234+# Check that the object was updated.
235+>>> d.value, d.another_value
236+('oranges', 'apples')
237+
238+# Check that the object was saved.
239+>>> d = DataPoint.objects.get(name="d2", value="oranges", another_value="apples")
240+>>> d.value, d.another_value
241+(u'oranges', u'apples')
242+
243+# Get an existing RelatedPoint object.
244+>>> r = RelatedPoint.objects.get(name="r1")
245+>>> r.data
246+<DataPoint: d1>
247+
248+# Update the ForeignKey field.
249+>>> r.update(data=d)
250+>>> r.data
251+<DataPoint: d2>
252+
253+# Check that the object was saved.
254+>>> RelatedPoint.objects.get(name="r1").data
255+<DataPoint: d2>
256+
257+# You can also update an object that has not yet been saved.
258+>>> d = DataPoint(name="d4", value="planes", another_value="trains")
259+>>> d.update(another_value="automobiles")
260+>>> d.value, d.another_value
261+('planes', 'automobiles')
262 """
263 }
264
265=== added directory 'tests/modeltests/update_or_create'
266=== added file 'tests/modeltests/update_or_create/__init__.py'
267=== added file 'tests/modeltests/update_or_create/models.py'
268--- tests/modeltests/update_or_create/models.py 1970-01-01 00:00:00 +0000
269+++ tests/modeltests/update_or_create/models.py 2009-03-15 16:09:08 +0000
270@@ -0,0 +1,73 @@
271+"""
272+update_or_create() tries to look up an object with the given parameters.
273+If an object is found, it updates the object.  If an object isn't found, it
274+creates one with the given parameters.
275+"""
276+
277+from django.db import models
278+
279+class Person(models.Model):
280+    first_name = models.CharField(max_length=100)
281+    last_name = models.CharField(max_length=100)
282+    birthday = models.DateField()
283+
284+    def __str__(self):
285+        return '%s %s, Birthday: %s' % (self.first_name, self.last_name,
286+                                        self.birthday)
287+
288+    class Meta:
289+        ordering = ('last_name',)
290+
291+__test__ = {'API_TESTS': """
292+# Create a Person.
293+>>> from datetime import date
294+>>> p = Person.objects.create(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
295+
296+# Only one Person is in the database at this point.
297+>>> Person.objects.all()
298+[<Person: John Lennon, Birthday: 1940-10-09>]
299+
300+# update_or_create() a Person with the same name.
301+>>> p, created = Person.objects.update_or_create(first_name='John', last_name='Lennon', defaults={'birthday': date(1970, 10, 9)})
302+
303+# update_or_create() didn't have to create an object.
304+>>> created
305+False
306+
307+# There's still only one Person in the database, and their birthday was updated.
308+>>> Person.objects.all()
309+[<Person: John Lennon, Birthday: 1970-10-09>]
310+
311+# update_or_create() a Person with a different name.
312+>>> p, created = Person.objects.update_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)})
313+>>> created
314+True
315+
316+# Two People in the database now.
317+>>> Person.objects.all()
318+[<Person: George Harrison, Birthday: 1943-02-25>, <Person: John Lennon, Birthday: 1970-10-09>]
319+
320+# If we execute the exact same statement, it won't create a Person.
321+>>> p, created = Person.objects.update_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)})
322+>>> created
323+False
324+
325+# The two People in the database haven't changed.
326+>>> Person.objects.all()
327+[<Person: George Harrison, Birthday: 1943-02-25>, <Person: John Lennon, Birthday: 1970-10-09>]
328+
329+# update_or_create() can take an empty 'defaults' parameter, but in this
330+# situation behaves exactly like get_or_create().  This is useful if you are
331+# building the 'defaults' dictionary dynamically.
332+>>> p, created = Person.objects.update_or_create(first_name='George', last_name='Harrison', defaults={})
333+>>> created
334+False
335+
336+# A different name with an empty 'defaults'.
337+>>> p, created = Person.objects.update_or_create(first_name='John', last_name='Smith', birthday=date(1950, 2, 10), defaults={})
338+>>> created
339+True
340+
341+>>> Person.objects.all()
342+[<Person: George Harrison, Birthday: 1943-02-25>, <Person: John Lennon, Birthday: 1970-10-09>, <Person: John Smith, Birthday: 1950-02-10>]
343+"""}
344