Opened 3 months ago

Last modified 3 months ago

#35659 assigned Cleanup/optimization

Document db_default behaviour before instances are saved

Reported by: Markus Andresen Owned by: Jerome Cagado
Component: Documentation Version: 5.0
Severity: Normal Keywords: db_default
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

There seems to be an unexpected behavior when using the db_default attribute on a BooleanField. The bool() evaluation when the instance is not saved to the database does not reflect the specified db_default value.

Steps to Reproduce:

from django.db import models

class MyModel(models.Model):
    my_field = models.BooleanField(db_default=False)

my_obj = MyModel()
bool(my_obj.my_field)  # True, which is unexpected.

my_obj.save()
bool(my_obj.my_field)  #  False, as expected.

I know this can be worked around by adding a default attribute in addition to db_default, but in my opinion its not clear enough from the documentation that you have to.

Change History (5)

comment:1 by David Sanders, 3 months ago

Component: Database layer (models, ORM)Documentation
Summary: Unexpected Behavior(footgun) with db_default on a BooleanFieldDocument db_default behaviour before instances are saved
Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization

Thanks for the report, though I disagree with the analysis of it being unexpected 😊

There seems to be an unexpected behavior when using the db_default attribute on a BooleanField. The bool() evaluation when the instance is not saved to the database does not reflect the specified db_default value.

It's not safe to randomly cast things without at least checking what the underlying type of the variable is. Eg bool("false") == True. In this case my_field is an instance of DatabaseDefault.

The documentation of DatabaseDefault is missing. The docs could probably be improved to make mention of it and that field values will be of this type before saving to the database.

Accepting the ticket as a docs improvement 👍

comment:2 by Jerome Cagado, 3 months ago

Hello, I would like to take a shot on this issue as a new contributor. Could you please assign it to me? Thanks!

comment:3 by Jerome Cagado, 3 months ago

Has patch: set
Owner: set to Jerome Cagado
Status: newassigned

Hi team,

I’ve submitted a pull request to address this issue. You can review the changes here: https://github.com/django/django/pull/18454

Please let me know if there are any further updates needed.

Thanks!

comment:4 by Markus Andresen, 3 months ago

Hey, thanks for accepting the ticket!

It's not safe to randomly cast things without at least checking what the underlying type of the variable is. Eg bool("false") == True. In this case my_field is an instance of DatabaseDefault.

I'm aware, the reason this behaviour is problematic (if not properly documented) is because my_field is assumed to be a bool since that is the case when a BooleanField has a default attribute, would people assume that db_default behaves differently? They might have code that looks something like this:

my_model = MyModel
if MyModel.my_field:
    do_something

# later in code
my_model.save()

Docs currently looks like this

If both db_default and Field.default are set, default will take precedence when creating instances in Python code. db_default will still be set at the database level and will be used when inserting rows outside of the ORM or when adding a new field in a migration.

default taking precedence in Python code is somewhat misleading IMO, adding a proper "disclaimer" here might be good?

comment:5 by Sarah Boyce, 3 months ago

Patch needs improvement: set
Note: See TracTickets for help on using tickets.
Back to Top