Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed a regression with get_or_create and virtual fields.

refs #20429

Thanks Simon Charette for the report and review.
  • Loading branch information...
commit 3f416f637918cc162877be95a59d50825b203089 1 parent c7364a1
Tim Graham authored August 20, 2013 andrewgodwin committed August 21, 2013
20  django/db/models/query.py
@@ -411,7 +411,7 @@ def get_or_create(self, defaults=None, **kwargs):
411 411
         Returns a tuple of (object, created), where created is a boolean
412 412
         specifying whether an object was created.
413 413
         """
414  
-        lookup, params, _ = self._extract_model_params(defaults, **kwargs)
  414
+        lookup, params = self._extract_model_params(defaults, **kwargs)
415 415
         self._for_write = True
416 416
         try:
417 417
             return self.get(**lookup), False
@@ -425,7 +425,8 @@ def update_or_create(self, defaults=None, **kwargs):
425 425
         Returns a tuple (object, created), where created is a boolean
426 426
         specifying whether an object was created.
427 427
         """
428  
-        lookup, params, filtered_defaults = self._extract_model_params(defaults, **kwargs)
  428
+        defaults = defaults or {}
  429
+        lookup, params = self._extract_model_params(defaults, **kwargs)
429 430
         self._for_write = True
430 431
         try:
431 432
             obj = self.get(**lookup)
@@ -433,12 +434,12 @@ def update_or_create(self, defaults=None, **kwargs):
433 434
             obj, created = self._create_object_from_params(lookup, params)
434 435
             if created:
435 436
                 return obj, created
436  
-        for k, v in six.iteritems(filtered_defaults):
  437
+        for k, v in six.iteritems(defaults):
437 438
             setattr(obj, k, v)
438 439
 
439 440
         sid = transaction.savepoint(using=self.db)
440 441
         try:
441  
-            obj.save(update_fields=filtered_defaults.keys(), using=self.db)
  442
+            obj.save(using=self.db)
442 443
             transaction.savepoint_commit(sid, using=self.db)
443 444
             return obj, False
444 445
         except DatabaseError:
@@ -469,22 +470,17 @@ def _create_object_from_params(self, lookup, params):
469 470
     def _extract_model_params(self, defaults, **kwargs):
470 471
         """
471 472
         Prepares `lookup` (kwargs that are valid model attributes), `params`
472  
-        (for creating a model instance) and `filtered_defaults` (defaults
473  
-        that are valid model attributes) based on given kwargs; for use by
  473
+        (for creating a model instance) based on given kwargs; for use by
474 474
         get_or_create and update_or_create.
475 475
         """
476 476
         defaults = defaults or {}
477  
-        filtered_defaults = {}
478 477
         lookup = kwargs.copy()
479 478
         for f in self.model._meta.fields:
480  
-            # Filter out fields that don't belongs to the model.
481 479
             if f.attname in lookup:
482 480
                 lookup[f.name] = lookup.pop(f.attname)
483  
-            if f.attname in defaults:
484  
-                filtered_defaults[f.name] = defaults.pop(f.attname)
485 481
         params = dict((k, v) for k, v in kwargs.items() if LOOKUP_SEP not in k)
486  
-        params.update(filtered_defaults)
487  
-        return lookup, params, filtered_defaults
  482
+        params.update(defaults)
  483
+        return lookup, params
488 484
 
489 485
     def _earliest_or_latest(self, field_name=None, direction="-"):
490 486
         """
23  tests/generic_relations/tests.py
@@ -263,6 +263,29 @@ def test_generic_inline_formsets_initial(self):
263 263
         formset = GenericFormSet(initial=initial_data)
264 264
         self.assertEqual(formset.forms[0].initial, initial_data[0])
265 265
 
  266
+    def test_get_or_create(self):
  267
+        # get_or_create should work with virtual fields (content_object)
  268
+        quartz = Mineral.objects.create(name="Quartz", hardness=7)
  269
+        tag, created = TaggedItem.objects.get_or_create(tag="shiny",
  270
+            defaults={'content_object': quartz})
  271
+        self.assertTrue(created)
  272
+        self.assertEqual(tag.tag, "shiny")
  273
+        self.assertEqual(tag.content_object.id, quartz.id)
  274
+
  275
+    def test_update_or_create_defaults(self):
  276
+        # update_or_create should work with virtual fields (content_object)
  277
+        quartz = Mineral.objects.create(name="Quartz", hardness=7)
  278
+        diamond = Mineral.objects.create(name="Diamond", hardness=7)
  279
+        tag, created = TaggedItem.objects.update_or_create(tag="shiny",
  280
+            defaults={'content_object': quartz})
  281
+        self.assertTrue(created)
  282
+        self.assertEqual(tag.content_object.id, quartz.id)
  283
+
  284
+        tag, created = TaggedItem.objects.update_or_create(tag="shiny",
  285
+            defaults={'content_object': diamond})
  286
+        self.assertFalse(created)
  287
+        self.assertEqual(tag.content_object.id, diamond.id)
  288
+
266 289
 
267 290
 class CustomWidget(forms.TextInput):
268 291
     pass

0 notes on commit 3f416f6

Please sign in to comment.
Something went wrong with that request. Please try again.