Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@
router,
transaction,
)
from django.db.models import NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value
from django.db.models import (
NOT_PROVIDED,
Expression,
ExpressionWrapper,
IntegerField,
Max,
Value,
)
from django.db.models.constants import LOOKUP_SEP
from django.db.models.deletion import CASCADE, DO_NOTHING, Collector, DatabaseOnDelete
from django.db.models.expressions import DatabaseDefault
Expand Down Expand Up @@ -1295,7 +1302,9 @@ def _prepare_related_fields_for_save(self, operation_name, fields=None):
"%s() prohibited to prevent data loss due to unsaved "
"related object '%s'." % (operation_name, field.name)
)
elif getattr(self, field.attname) in field.empty_values:
if (
field_val := getattr(self, field.attname)
) in field.empty_values or isinstance(field_val, Expression):
# Set related object if it has been saved after an
# assignment.
setattr(self, field.name, obj)
Expand Down
22 changes: 21 additions & 1 deletion tests/field_defaults/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from decimal import Decimal

from django.db import models
from django.db.models.functions import Coalesce, ExtractYear, Now, Pi
from django.db.models.functions import UUID4, Coalesce, ExtractYear, Now, Pi
from django.db.models.lookups import GreaterThan


Expand Down Expand Up @@ -78,3 +78,23 @@ class DBDefaultsOneToOnePK(models.Model):
db_default="kr",
on_delete=models.CASCADE,
)


class DBDefaultsFunctionPK(models.Model):
uuid = models.UUIDField(primary_key=True, db_default=UUID4())

class Meta:
required_db_features = {
"supports_uuid4_function_in_default",
"supports_expression_defaults",
}


class DBDefaultsFunctionFK(models.Model):
parent = models.ForeignKey(DBDefaultsFunctionPK, models.CASCADE)

class Meta:
required_db_features = {
"supports_uuid4_function_in_default",
"supports_expression_defaults",
}
16 changes: 15 additions & 1 deletion tests/field_defaults/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
OrderByList,
RawSQL,
)
from django.db.models.functions import Collate
from django.db.models.functions import UUID4, Collate
from django.db.models.lookups import GreaterThan
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature

Expand All @@ -23,6 +23,8 @@
DBDefaults,
DBDefaultsFK,
DBDefaultsFunction,
DBDefaultsFunctionFK,
DBDefaultsFunctionPK,
DBDefaultsOneToOnePK,
DBDefaultsPK,
)
Expand Down Expand Up @@ -155,6 +157,18 @@ def test_primary_key_assigned_db_default_expression(self):
# DatabaseDefault is used, but this behavior varies across databases.
self.assertNotEqual(obj.language_code_id, "tr")

@skipUnlessDBFeature(
"can_return_columns_from_insert",
"supports_expression_defaults",
"supports_uuid4_function_in_default",
)
def test_foreign_key_to_parent_with_expression_pk(self):
parent = DBDefaultsFunctionPK(pk=UUID4())
obj = DBDefaultsFunctionFK(parent=parent)
parent.save()
obj.save()
self.assertEqual(obj.parent_id, parent.pk)

@skipUnlessDBFeature("supports_expression_defaults")
def test_case_when_db_default_returning(self):
m = DBDefaultsFunction.objects.create()
Expand Down
Loading