From 819e09b848e4f1cc45165a99ffbef1307b215a08 Mon Sep 17 00:00:00 2001 From: Daniel Pyrathon Date: Mon, 10 Mar 2014 15:17:57 +0000 Subject: [PATCH] Fixed #22210 -- Saving model instances to non-related fields. Previously, saving a model instance to a non-related field (in particular a FloatField) would silently convert the model to an Integer (the pk) and save it. This is undesirable behaviour, and likely to cause confusion so the validatio has been hardened. Thanks to @PirosB3 for the patch and @jarshwah for the review. --- django/db/models/sql/compiler.py | 10 +++++++++- docs/releases/1.7.txt | 4 ++++ tests/model_fields/models.py | 3 +++ tests/model_fields/tests.py | 12 +++++++++++- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 11c503cd5d..5636f344df 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -4,6 +4,7 @@ from django.conf import settings from django.core.exceptions import FieldError from django.db.backends.utils import truncate_name from django.db.models.constants import LOOKUP_SEP +from django.db.models.expressions import ExpressionNode from django.db.models.query_utils import select_related_descend, QueryWrapper from django.db.models.sql.constants import (CURSOR, SINGLE, MULTI, NO_RESULTS, ORDER_DIR, GET_ITERATOR_CHUNK_SIZE, SelectInfo) @@ -951,7 +952,14 @@ class SQLUpdateCompiler(SQLCompiler): values, update_params = [], [] for field, model, val in self.query.values: if hasattr(val, 'prepare_database_save'): - val = val.prepare_database_save(field) + if field.rel or isinstance(val, ExpressionNode): + val = val.prepare_database_save(field) + else: + raise TypeError("Database is trying to update a relational field " + "of type %s with a value of type %s. Make sure " + "you are setting the correct relations" % + (field.__class__.__name__, + val.__class__.__name__)) else: val = field.get_db_prep_save(val, connection=self.connection) diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 52007c98aa..2dd3931060 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -674,6 +674,10 @@ Models the new :attr:`ManyToManyField.through_fields ` argument. +* Assigning a model instance to a non-relation field will now throw an error. + Previously this used to work if the field accepted integers as input as it + took the primary key. + Signals ^^^^^^^ diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index e110578ce9..b16287a66c 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -48,6 +48,9 @@ class Whiz(models.Model): class BigD(models.Model): d = models.DecimalField(max_digits=38, decimal_places=30) +class FloatModel(models.Model): + size = models.FloatField() + class BigS(models.Model): s = models.SlugField(max_length=255) diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 194219a114..a39ad81f64 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -23,7 +23,7 @@ from django.utils.functional import lazy from .models import ( Foo, Bar, Whiz, BigD, BigS, BigInt, Post, NullBooleanModel, BooleanModel, PrimaryKeyCharModel, DataModel, Document, RenamedField, - VerboseNameField, FksToBooleans, FkToChar) + VerboseNameField, FksToBooleans, FkToChar, FloatModel) class BasicFieldTests(test.TestCase): @@ -78,6 +78,16 @@ class BasicFieldTests(test.TestCase): self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk') + def test_float_validates_object(self): + instance = FloatModel(size=2.5) + instance.save() + self.assertTrue(instance.id) + + obj = FloatModel.objects.get(pk=1) + obj.size = obj + with self.assertRaises(TypeError): + obj.save() + def test_choices_form_class(self): """Can supply a custom choices form class. Regression for #20999.""" choices = [('a', 'a')]