Opened 3 years ago

Last modified 3 years ago

#23931 new Bug

db_manager() method doesn't increment creation_counter

Reported by: Rhett Garber Owned by: nobody
Component: Database layer (models, ORM) Version: 1.7
Severity: Normal Keywords:
Cc: Loic Bistuer Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Creating a manager with the db_manager() method can cause unexpected behavior. Consider the following:

class MyModel(models.Model):
    objects = models.Manager()
    ro_objects = objects.db_manager('ro')

There are certain situations where ro_objects becomes the 'default' manager of the object. Both of these managers have the same 'creation_counter'.

Patch is here:
https://github.com/rhettg/django/compare/db_manager_creation_counter?expand=1

Change History (7)

comment:1 Changed 3 years ago by Tim Graham

Triage Stage: UnreviewedAccepted

comment:2 Changed 3 years ago by Markus Holtermann

I looked through the documentation and none of the examples show this as a feature. I'd rather inherit from Manager and pass a db attribute along:

class ROManager(models.Manager):
    def __init__(self, db):
        super(ROManager, self).__init__(self)
        self._db = db

class MyModel(models.Model):
    objects = models.Manager()
    ro_objects = ROManager('ro')

Given that, maybe the BaseManager implementation could accept db and hints directly.

comment:3 Changed 3 years ago by Rhett Garber

I think it does make sense to allow creation of a manager with db or hints directly rather than relying on the copy behavior.

However,

  1. That's obviously a bit more involved since BaseManager doesn't currently take any arguments and sub-classing is a really popular pattern. It could conceivable cause problems for people.
  2. That doesn't mean this patch isn't also correct. I don't think it's appropriate to have any 'copy' patterns that don't increment the creation_counter.

So maybe this patch is appropriate for backporting (since not incrementing the counter is a bug), while adding arguments to init would be a valid for a release?

comment:4 Changed 3 years ago by Loic Bistuer

I'm sitting on the fence here.

db_manager() was designed to generate short-lived managers, not to be assigned as class managers. That said, other short-lived managers (and short-lived fields in custom lookups/expressions for that matter) increase the creation counters (e.g. Category().article_set) so db_manager() probably should too, if only for consistency.

However supporting non-empty db or hints attributes for long-lived managers is something that would require a fair amount of investigation and some hard design decisions:

  • Are these supposed to be default values to be used when nothing else is provided at query time? In that case what about hints, should they be merged? Current implementation of RelatedManager.get_queryset() completely overrides hints.
  • Are these supposed to be definitive values? Current implementation of RelatedManager.create|get_or_create|update_or_create() asks the router directly for db and doesn't account that one may be set already on the manager.

If we recognize that long-lived managers can have a db or hints state (as it would be the case if we made them __init__() arguments) then we must define the behavior. Right now setting these may work for some cases, but the behavior is largely undefined as far as Django is concerned.

comment:5 Changed 3 years ago by Loic Bistuer

Cc: Loic Bistuer added

comment:6 Changed 3 years ago by Tim Graham

Patch needs improvement: set

comment:7 Changed 3 years ago by Rhett Garber

Sorry I'm not quite following what is happening with this ticket.

Is the conclusion that my change is incorrect because incrementing the creation counter is not the desired behavior?

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