Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
  • Loading branch information...
commit 85ebb91846fd5bb1a766d1bed905d6f60667274c 1 parent 0e5faf2
@malcolmt malcolmt authored
View
4 django/contrib/contenttypes/generic.py
@@ -271,9 +271,7 @@ def clear(self):
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
View
8 django/db/models/fields/related.py
@@ -306,9 +306,8 @@ def add(self, *objs):
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(self, **kwargs):
# 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
View
77 tests/modeltests/custom_pk/models.py
@@ -6,11 +6,11 @@
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 @@ def __unicode__(self):
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 @@ def __unicode__(self):
>>> 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
+
+"""
View
19 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 @@ def __unicode__(self):
... 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
"""}
Please sign in to comment.
Something went wrong with that request. Please try again.