2
0
Эх сурвалжийг харах

Fixed #8669 -- Use a consistent version of create() across the board for
model/field instance creation. Based on a patch from Richard Davies.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@8884 bcc190cf-cafb-0310-a4f2-bffc1f526a37

Malcolm Tredinnick 16 жил өмнө
parent
commit
85ebb91846

+ 1 - 3
django/contrib/contenttypes/generic.py

@@ -271,9 +271,7 @@ def create_generic_related_manager(superclass):
         def create(self, **kwargs):
             kwargs[self.content_type_field_name] = self.content_type
             kwargs[self.object_id_field_name] = self.pk_val
-            obj = self.model(**kwargs)
-            obj.save()
-            return obj
+            return super(GenericRelatedObjectManager, self).create(**kwargs)
         create.alters_data = True
 
     return GenericRelatedObjectManager

+ 3 - 5
django/db/models/fields/related.py

@@ -306,9 +306,8 @@ class ForeignRelatedObjectsDescriptor(object):
             add.alters_data = True
 
             def create(self, **kwargs):
-                new_obj = self.model(**kwargs)
-                self.add(new_obj)
-                return new_obj
+                kwargs.update({rel_field.name: instance})
+                return super(RelatedManager, self).create(**kwargs)
             create.alters_data = True
 
             def get_or_create(self, **kwargs):
@@ -410,8 +409,7 @@ def create_many_related_manager(superclass, through=False):
             # from the method lookup table, as we do with add and remove.
             if through is not None:
                 raise AttributeError, "Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s's Manager instead." % through
-            new_obj = self.model(**kwargs)
-            new_obj.save()
+            new_obj = super(ManyRelatedManager, self).create(**kwargs)
             self.add(new_obj)
             return new_obj
         create.alters_data = True

+ 57 - 20
tests/modeltests/custom_pk/models.py

@@ -6,11 +6,11 @@ By default, Django adds an ``"id"`` field to each model. But you can override
 this behavior by explicitly adding ``primary_key=True`` to a field.
 """
 
-from django.db import models
+from django.conf import settings
+from django.db import models, transaction, IntegrityError
 
 class Employee(models.Model):
-    employee_code = models.CharField(max_length=10, primary_key=True,
-            db_column = 'code')
+    employee_code = models.IntegerField(primary_key=True, db_column = 'code')
     first_name = models.CharField(max_length=20)
     last_name = models.CharField(max_length=20)
     class Meta:
@@ -29,50 +29,50 @@ class Business(models.Model):
         return self.name
 
 __test__ = {'API_TESTS':"""
->>> dan = Employee(employee_code='ABC123', first_name='Dan', last_name='Jones')
+>>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones')
 >>> dan.save()
 >>> Employee.objects.all()
 [<Employee: Dan Jones>]
 
->>> fran = Employee(employee_code='XYZ456', first_name='Fran', last_name='Bones')
+>>> fran = Employee(employee_code=456, first_name='Fran', last_name='Bones')
 >>> fran.save()
 >>> Employee.objects.all()
 [<Employee: Fran Bones>, <Employee: Dan Jones>]
 
->>> Employee.objects.get(pk='ABC123')
+>>> Employee.objects.get(pk=123)
 <Employee: Dan Jones>
->>> Employee.objects.get(pk='XYZ456')
+>>> Employee.objects.get(pk=456)
 <Employee: Fran Bones>
->>> Employee.objects.get(pk='foo')
+>>> Employee.objects.get(pk=42)
 Traceback (most recent call last):
     ...
 DoesNotExist: Employee matching query does not exist.
 
 # Use the name of the primary key, rather than pk.
->>> Employee.objects.get(employee_code__exact='ABC123')
+>>> Employee.objects.get(employee_code__exact=123)
 <Employee: Dan Jones>
 
 # pk can be used as a substitute for the primary key.
->>> Employee.objects.filter(pk__in=['ABC123','XYZ456'])
+>>> Employee.objects.filter(pk__in=[123, 456])
 [<Employee: Fran Bones>, <Employee: Dan Jones>]
 
 # The primary key can be accessed via the pk property on the model.
->>> e = Employee.objects.get(pk='ABC123')
+>>> e = Employee.objects.get(pk=123)
 >>> e.pk
-u'ABC123'
+123
 
 # Or we can use the real attribute name for the primary key:
 >>> e.employee_code
-u'ABC123'
+123
 
 # Fran got married and changed her last name.
->>> fran = Employee.objects.get(pk='XYZ456')
+>>> fran = Employee.objects.get(pk=456)
 >>> fran.last_name = 'Jones'
 >>> fran.save()
 >>> Employee.objects.filter(last_name__exact='Jones')
 [<Employee: Dan Jones>, <Employee: Fran Jones>]
->>> emps = Employee.objects.in_bulk(['ABC123', 'XYZ456'])
->>> emps['ABC123']
+>>> emps = Employee.objects.in_bulk([123, 456])
+>>> emps[123]
 <Employee: Dan Jones>
 
 >>> b = Business(name='Sears')
@@ -96,15 +96,52 @@ u'ABC123'
 >>> Employee.objects.filter(business__pk='Sears')
 [<Employee: Dan Jones>, <Employee: Fran Jones>]
 
->>> Business.objects.filter(employees__employee_code__exact='ABC123')
+>>> Business.objects.filter(employees__employee_code__exact=123)
 [<Business: Sears>]
->>> Business.objects.filter(employees__pk='ABC123')
+>>> Business.objects.filter(employees__pk=123)
 [<Business: Sears>]
 >>> Business.objects.filter(employees__first_name__startswith='Fran')
 [<Business: Sears>]
 
 # Primary key may be unicode string
->>> emp = Employee(employee_code='jaźń')
->>> emp.save()
+>>> bus = Business(name=u'jaźń')
+>>> bus.save()
+
+# The primary key must also obviously be unique, so trying to create a new
+# object with the same primary key will fail.
+>>> try:
+...    sid = transaction.savepoint()
+...    Employee.objects.create(employee_code=123, first_name='Fred', last_name='Jones')
+...    transaction.savepoint_commit(sid)
+... except Exception, e:
+...    if isinstance(e, IntegrityError):
+...        transaction.savepoint_rollback(sid)
+...        print "Pass"
+...    else:
+...        print "Fail with %s" % type(e)
+Pass
 
 """}
+
+# SQLite lets objects be saved with an empty primary key, even though an
+# integer is expected. So we can't check for an error being raised in that case
+# for SQLite. Remove it from the suite for this next bit.
+if settings.DATABASE_ENGINE != 'sqlite3':
+    __test__["API_TESTS"] += """
+# The primary key must be specified, so an error is raised if you try to create
+# an object without it.
+>>> try:
+...     sid = transaction.savepoint()
+...     Employee.objects.create(first_name='Tom', last_name='Smith')
+...     print 'hello'
+...     transaction.savepoint_commit(sid)
+...     print 'hello2'
+... except Exception, e:
+...     if isinstance(e, IntegrityError):
+...         transaction.savepoint_rollback(sid)
+...         print "Pass"
+...     else:
+...         print "Fail with %s" % type(e)
+Pass
+
+"""

+ 19 - 0
tests/modeltests/get_or_create/models.py

@@ -16,6 +16,10 @@ class Person(models.Model):
     def __unicode__(self):
         return u'%s %s' % (self.first_name, self.last_name)
 
+class ManualPrimaryKeyTest(models.Model):
+    id = models.IntegerField(primary_key=True)
+    data = models.CharField(max_length=100)
+
 __test__ = {'API_TESTS':"""
 # Acting as a divine being, create an Person.
 >>> from datetime import date
@@ -61,4 +65,19 @@ False
 ...     else:
 ...         print "Fail with %s" % type(e)
 Pass
+
+# If you specify an existing primary key, but different other fields, then you
+# will get an error and data will not be updated.
+>>> m = ManualPrimaryKeyTest(id=1, data='Original')
+>>> m.save()
+>>> try:
+...    m, created = ManualPrimaryKeyTest.objects.get_or_create(id=1, data='Different')
+... except Exception, e:
+...    if isinstance(e, IntegrityError):
+...        print "Pass"
+...    else:
+...        print "Fail with %s" % type(e)
+Pass
+>>> ManualPrimaryKeyTest.objects.get(id=1).data == 'Original'
+True
 """}