Reputation: 402
I'm using Django 1.7. I've got a default custom manager that filters on an "active" boolean field. According to the docs, it needs to be the default manager to work with related fields (ie. accessing User.story_set only shows active Story objects). I'm keeping the standard manager for admin and shell access, but I am unable to save changes to objects, I'm speculating because save() methods pass through the default manager at some point.
class Story(models.Model):
active = models.BooleanField(default=True)
....
objects = ActiveStoryManager()
full_set = models.Manager()
class ActiveStoryManager(models.Manager):
def get_query_set(self):
return super(ActiveStoryManager, self).get_query_set().filter(active=True)
use_for_related_fields = True
This works well for all public-facing use. However, in admin and shell I am unable to affect inactive objects, including turning them back active.
story = Story.full_set.get(id=#)
will fetch a story with active=False
, but after setting active=True
I am unable to save, getting a
django.db.utils.IntegrityError: duplicate key value violates unique constraint "stories_story_pkey"
DETAIL: Key (id)=(#) already exists.
Calling save.(force_update=True)
returns django.db.utils.DatabaseError: Forced update did not affect any rows.
So while save() is a model method, it seems to depend on the default manager at some point in the saving process.
A workaround is using the Queryset API, e.g. Story.full_set.filter(id=#).update(active=True)
, but that's only usable in the shell, and requires manually typing each change, still can't save inactive instances in the admin.
Any help on this?
Upvotes: 5
Views: 2237
Reputation: 402
It cannot be done! As inancsevinc pointed out, save() calls on the default manager. The Django docs mention that get_query_set should not be modified on default managers, and I have sadly found out why. Hopefully in the future relatedManagers can be specified/controlled, but for now this method will not work for me. Confirmed in Django IRC chat.
Instead, I'm throwing together a ordinary Manager method, as well as model methods for some models, to get equivalent functionality. Also requires changing all the related_set
calls in the template to include the new methods, so it's a pain, but no other way.
Upvotes: 5
Reputation: 2708
To make admin page work with a different manager, you can implement get_queryset method on your ModelAdmin class.
class StoryAdmin(ModelAdmin):
def get_queryset(self, request):
return self.model.full_set.get_queryset()
Upvotes: 1