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?
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.
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()
Related
Let's say I've got FooManager() that I set as the default manager for a model Foo, i.e. by having it be the first manager that Django encounters in the model definition. Now, admin, dumpdata, and Django generally uses this manager by default, as desired.
However, I don't want related field managers to use FooManager(), i.e. if Foo has a foreign key to Bar, then the related manager bar_instance.foo_set should use the "plain" Foo manager (to use the term in Django docs), not FooManager.
This doesn't seem possible, because the way to have FooManager NOT applied to related fields, is by setting its use_for_related_fields attribute to False. But all that does is get Django to use the the default manager (as opposed to the "plain" manager) for related fields.
And what's the default manager in this case? FooManager! Conundrum.
Any way to make this work, i.e. use FooManager as default manager but not for related fields?
NOTE: I'm using Django 1.6. I don't see anything in the 1.7 docs to suggest default/related manager behavior has changed. Also note I do NOT want to have to explicitly set the related manager per the new 1.7 functionality here; I want the reverse related manager to use the plain manager by default, as I'm refactoring an existing project and would prefer not to change every reverse related manager reference.
In this section the docs say the behavior is exactly what you want it to be.
The implementation also seems to do exactly what you want. 1.6 too.
Update:
If you don't want to change every usage, then create subclass of ForeignKey, which uses subclass of ForeignRelatedObjectsDescriptor as a related_accessor_class with this method overwritten.
Pseudo code:
class PlainManagerForeignRelatedObjectDescriptor(ForeignRelatedObjectDescriptor):
#cached_property
def related_manager_cls(self):
return create_foreign_related_manager(
models.Manager,
self.rel,
)
class PlainManagerForeignKey(ForeignKEy):
related_accessor_class = PlainManagerForeignRelatedObjectDescriptor
In Django, I would like the ability to mark certain model fields as required at the model (or at least database) level, to make sure that I am specifying them explicitly (i.e. not relying on defaults) when creating objects.
Currently, Django lets you designate a model field as required at the forms level (by specifying blank=False in the model). However, it doesn't seem like there is a similar option to get this behavior at the model or database level.
It does seem like there are various hacks to achieve something similar though. For example, you can set the default to something that violates a database constraint. For example, you can do things like:
models.CharField(_('characters'), max_length=4, default=None)
or
models.CharField(_('characters'), max_length=4, default="abcdef")
The former example works when saving to the database since None violates the default not-null constraint of null=False (raising an IntegrityError). The latter works because a DataError is raised when saving. But I don't know if this is guaranteed to work across all databases, etc.
Am I missing something, or is there a better way?
If django models called full_clean() automatically on save(), your check would run at the model level without a form. I've been playing with making this the default behavior in my django projects by creating an auto-clean model subclass which does full_clean() on save(), then deriving my models off that.
If you want to learn why it isn't already like this: Why doesn't django's model.save() call full_clean()?
Not really sure what the difference is. Seems like all the Manager does is have a bunch of functions related to the Model. But these functions could also be placed in the Model too....
Django documentation describes the Manager as follows,
A Manager is the interface through which database query operations are
provided to Django models.
So is there anything else fundamentally different about the Manager than this simple abstraction?
Or a better question: what methods should be defined in the Model vs. the Manager? Is there an ACTUAL difference or just stylistic one?
In Django, a models' manager is the object through which models perform database queries. Each Django model has at least one manager, which is objects, and you can create your own to change the default behavior.
So, your statement
But these functions could also be placed in the Model too
Well, not really because the model is still depending on the default manager to retrieve the queryset.
Let me try to explain in terms of an example. Lets say your application requires a model object to show only objects with a status of published. Now, MyModel.objects.all() retrieves everything, and you would have to specify the filter MyModel.objects.filter(published=True) every single time.
Now, you can override this default behavior.
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
return MyModel.objects.filter(published=True)
What we just did was override the default behaviour of the default manager.
Now, lets say you want everything, You can do something like
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
return MyModel.objects.filter(published=True)
def all_objects(self, request):
return MyModel.objects.all()
and while accessing all objects, just do
MyModel.objects.all_objects()
It is also possible to have multiple managers to a single model
In short, managers give a lot of flexibility in terms of accessing querysets to the model.
I have a Django model which has a foreign key that is optional. I set it up this way so that table would contain default options of different service_types but one could override this option by specifying a controller foreign key.
Now in my views I have a bunch of code that looks like this:
try:
modeService = RegressionServices.objects.get(controller=controller, service_type=SERVICE_TYPE_CONTROLLER_MODE)
except RegressionServices.DoesNotExist:
modeService = RegressionServices.objects.get(service_type=SERVICE_TYPE_CONTROLLER_MODE)
I would like to reduce this down to one call and I think that a Django model Manager should be the way to do so. Essentially it would check for the controller in **kwargs and if present it would basically do the code above. I am looking in the docs and only finding that the get_query_set method is usually being overridden. Is it okay to override the get() method? Will I even have access to the RegressionServices model in a model Manager get() method?
Can anyone think of a way to do this other than a model manager?
Just do a logical OR, using Q objects.
I want to make the following modification to the Django framework.
I want it to create a "deleted" field for each model I create.
I want it to be checked as deleted when I delete it from the admin page instead of being physically deleted.
I do not want these records checked as deleted to be listed.
I'm new to Django, I'm seeing if I can do what I want to do with it easily. I need this change because it's the way we currently work.
So far these are the changes I have made, I would like to understand how the whole Django framewok works inside but I'm so far from that, is there any documentation online which explains clearly how the inside framework parts/files/modules/classes work together, the specific role of each one, etc
In the base.py file, in the modelbase class, below this code,
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj)
I added,
from django.db import models
new_class.add_to_class('deleted', models.BooleanField())
When it creates a model it adds the "deleted" field to it.
In the base.py file, in the save method, I changed what it was there for
self.deleted = True
self.save()
So, now it check as deleted a record instead of physically delete it.
Now what I want is those records not to be listed.
I don't understand why you're modifying the framework code instead of putting your deleted field in a model base class that all of your models extend from.
Nevertheless, a nice way to filter those records out would be to add a custom manager to the model (or your base model class, if you choose to create one). To this manager, override the get_query_set method as described here. In your overridden method, add a exclude(deleted=True) filter.
Take a look at the Django-logicaldelete app, You just inherit your models from their provided Model class and you get Logical delete for all of them.
It comes with an adminModel as well so you can manage logically deleted models there too.
Override the delete() method in your model class, set the deleted attribute there
Create a custom manager which will filter by deleted attribute and set it as the default one (objects = MyDeletedManager)