Opened 3 years ago

Last modified 3 weeks ago

#23816 assigned New feature

Ability to defer model field by default

Reported by: Robert Coup Owned by: Denis.Tarykin
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: akiskesoglou@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Today's small #IWantAPony is to be able to mark model fields as deferred by default. No need to chase down every queryset and add .defer(), or use a custom manager setup & remember to set use_for_related_fields.

class MyModel(models.Model):
   enormous_field = models.TextField(defer=True)
   other_field = models.IntegerField()

Which is (simplistically) equivalent to the following:

class MyManager(models.Manager):
  use_for_related_fields = True

  def get_queryset(self, *args, **kwargs):
    return super(MyManager, self).get_queryset(*args, **kwargs).defer("enormous_field")

class MyModel(models.Model):
   enormous_field = models.TextField()
   other_field = models.IntegerField()

   objects = MyManager()

Obviously, you can still do MyModel.objects.defer(None) to do the full query.

Change History (8)

comment:1 Changed 3 years ago by Akis Kesoglou

I find this really useful, as most of the times you (myself actually, assuming most people too) really don't make a manager just for this optimisation. So defining deferred fields at model level makes sense for me. Though I would prefer to specify the fields in the model's Meta options, as a list of field names, similar to unique_together option.

comment:2 Changed 3 years ago by Akis Kesoglou

Cc: akiskesoglou@… added

comment:3 Changed 3 years ago by Tim Graham

Triage Stage: UnreviewedAccepted

I am a little hesitant to accept since there is already a way to accomplish this, but tentatively accepting so we can see what a patch looks like and someone else really likes the idea.

Last edited 3 weeks ago by Tim Graham (previous) (diff)

comment:4 Changed 3 years ago by Carl Meyer

I'm also hesitant about adding something that's already doable via a custom manager; I'm probably -0.

If we do it, it should definitely be a Meta option, not a field arg, IMO. It applies to fields, but it's really a characteristic of querying on the model class, not inherently of the Field.

comment:5 Changed 3 years ago by Akis Kesoglou

I'm inclined to have a go at this. Here's how I envision it:

class Post(models.Model):
    body_text = models.TextField()
    body_html = models.TextField(blank=True)

    class Meta:
        initially_deferred_fields = ('body_text', 'body_html')

Some thoughts:

  • Not sure about the name of the meta attribute, it requires almost as much effort to get the spelling right as creating a custom manager, but I'd like to avoid naming it defer or deferred_fields. I'd like input on this, but it's not pressing.
  • The patch author should be aware of the model meta refactor, perhaps beginning work after it lands?

comment:6 Changed 22 months ago by Will Stott

I can see a use for this. Currently using a manager which looks at auto_defer (on the manager due to 5793).

class AutoDeferManagerMixin(object):
    use_for_related_fields = True
    auto_defer = tuple()

    def get_queryset(self, *args, **kwargs):
        return super(AutoDeferManagerMixin, self).get_queryset(*args, **kwargs).defer(*self.auto_defer)

comment:7 Changed 2 months ago by Denis.Tarykin

Owner: changed from nobody to Denis.Tarykin
Status: newassigned

comment:8 Changed 6 weeks ago by Denis.Tarykin

Has patch: set
Note: See TracTickets for help on using tickets.
Back to Top