Is there a way to change the default object manager for all Models? (which would include the object managers on third party apps)
The default manager is attached in the function ensure_default_manager in django.db.models.manager. It attaches by default a manager of class Manager. You could monkeypatch this function to attach a different (subclass of) Manager.
But you have to consider whether this is the most ideal solution to the problem you're trying to solve.
If you really need to do that modify the django code itself. Monkey patching is an option also, there are a lot of techniques for that out there.
Related
I am trying to serialize a class that has external dependency.
The way the class is created is that it receives a config in its init function, and creates an object that receives that config and assign it to self.
What I'm trying to accomplish, is that I want to serialize the class, and depending upon the context of creation, I want to be able to inject a different config.
class Foo:
def __init__(self, some_value, config):
self.some_value = some_value
self.some_service = SomeService(config)
What I'd want in this scenario, is to serialzie self.some_value, but not self.some_service (and neither config, as this is changed).
So, what is the proper pattern? I've had a look at the getstate/setstate dunder, which is perfect for serializing only part of the class, but not injecting config when unpickling. I would have expected Unpickler to work perfectly in this instance, but it doesn't seem like it (and seems to work only with files for some reason? The data is serialized in a redis DB, so no file). I'd rather not have a service locator either, but have the config injected than fetched.
Clarifications :
The issue is not how to use pickle or the pickler. The issue is more of a choice of pattern. I have external dependecies in the object (as denoted by self.some_service = SomeService(config)).
There are 2 ways to reconstruct that object at unpickling :
Use a custom Pickler/Unpickler to detect external dependency instance, serialize a hash of it, and when unpickling, give it whatever instance it requries at that moment
create the get/setstate dunder functions that detects the service, and does not serialize it.
Both have their pros and cons, but I'd like to know which one would be recommended. The unpickler can have the external dependecies when unpickling, and reassign it to the object when unpickling, but it seems like a 'heavy' solution. Using the get/setstate dunder requires to have the class the know how to fetch the external dependencies, and seems a bit 'magical' (the class fectching external services instead of them being given to the class).
The solution I ended up with is essentially a bit like a service locator : I unpickle the instance, then I call a custom install_dependencies(injector)
My IDE keeps suggesting I convert my instance methods to static methods. I guess because I haven't referenced any self within these methods.
An example is :
class NotificationViewSet(NSViewSet):
def pre_create_processing(self, request, obj):
log.debug(" creating messages ")
# Ensure data is consistent and belongs to the sending bot.
obj['user_id'] = request.auth.owner.id
obj['bot_id'] = request.auth.id
So my question would be: do I lose anything by just ignoring the IDE suggestions, or is there more to it?
This is a matter of workflow, intentions with your design, and also a somewhat subjective decision.
First of all, you are right, your IDE suggests converting the method to a static method because the method does not use the instance. It is most likely a good idea to follow this suggestion, but you might have a few reasons to ignore it.
Possible reasons to ignore it:
The code is soon to be changed to use the instance (on the other hand, the idea of soon is subjective, so be careful)
The code is legacy and not entirely understood/known
The interface is used in a polymorphic/duck typed way (e.g. you have a collection of objects with this method and you want to call them in a uniform way, but the implementation in this class happens to not need to use the instance - which is a bit of a code smell)
The interface is specified externally and cannot be changed (this is analog to the previous reason)
The AST of the code is read/manipulated either by itself or something that uses it and expects this method to be an instance method (this again is an external dependency on the interface)
I'm sure there can be more, but failing these types of reasons I would follow the suggestion. However, if the method does not belong to the class (e.g. factory method or something similar), I would refactor it to not be part of the class.
I think that you might be mixing up some terminology - the example is not a class method. Class methods receive the class as the first argument, they do not receive the instance. In this case you have a normal instance method that is not using its instance.
If the method does not belong in the class, you can move it out of the class and make it a standard function. Otherwise, if it should be bundled as part of the class, e.g. it's a factory function, then you should probably make it a static method as this (at a minimum) serves as useful documentation to users of your class that the method is coupled to the class, but not dependent on it's state.
Making the method static also has the advantage this it can be overridden in subclasses of the class. If the method was moved outside of the class as a regular function then subclassing is not possible.
I'm using a third party library (PySphere) for a project I'm working on. PySphere provides a simple API to interact with VMware. This is a general problem though, not specific to this library.
A simple use of the library would be to get a VM object, and then perform various operations on it:
vm_obj = vcenter.get_vm_by_name("My VM")
vm_obj.get_status()
vm_obj.power_on()
I'd like to add a few methods to the vm_obj class. These methods are highly specific to the OS in use on the VM and wouldn't be worthwhile to commit back to the library. Right now I've been doing it like so:
set_config_x(vm_obj, args)
This seems really unpythonic. I'd like to be able to add my methods to the vm_obj class, without modifying the class definition in the third party library directly.
While you can attach any callable to the class object (that is, vm_obj.__class__), that function would not be a method and would not have a self attribute. To make a real method, you can use the new module from the standard library:
vm_obj.set_config_x = new.instancemethod(callableFunction, vm_obj, vm_obj.__class__)
where callableFunction takes self (vm_obj) as its first argument.
After reading up on Django Managers, I'm still unsure how much benefit I will get by using it. It seems that the best use is to add custom queries (read-only) methods like XYZ.objects.findBy*(). But I can easily do that with static methods off of the Model classes themselves.
I prefer the latter always because:
code locality in terms of readability and easier maintenance
slightly less verbose as I don't need the objects property in my calls
Manager classes have weird rules regarding model inheritance, might as well stay clear of that.
Is there any good reason not to use static methods and instead use Manager classes?
Adding custom queries to managers is the Django convention. From the Django docs on custom managers:
Adding extra Manager methods is the preferred way to add "table-level" functionality to your models.
If it's your own private app, the convention word doesn't matter so much - indeed my company's internal codebase has a few classmethods that perhaps belong in a custom manager.
However, if you're writing an app that you're going to share with other Django users, then they'll expect to see findBy on a custom manager.
I don't think the inheritance issues you mention are too bad. If you read the custom managers and model inheritance docs, I don't think you'll get caught out. The verbosity of writing .objects is bearable, just as it is when we do queries using XYZ.objects.get() and XYZ.objects.all()
Here's a few advantages of using manager methods in my opinion:
Consistency of API. Your method findBy belongs with get, filter, aggregate and the rest. Want to know what lookups you can do on the XYZ.objects manager? It's simple when you can introspect with dir(XYZ.objects).
Static methods "clutter" the instance namespace. XYZ.findBy() is fine but if you define a static method, you can also do xyz.findBy(). Running the findBy lookup on a particular instance doesn't really make sense.
DRYness. Sometimes you can use the same manager on more than one model.
Having said all that, it's up to you. I'm not aware of a killer reason why you should not use a static method. You're an adult, it's your code, and if you don't want to write findBy as a manager method, the sky isn't going to fall in ;)
For further reading, I recommend the blog post Managers versus class methods by James Bennett, the Django release manager.
So, I've read most of the docs and I've been looking around on SO a bit, but I can't quite find the answer to my question. I'll start with the code.
# Manager
class ActiveManager(models.Manager):
def get_query_set(self):
return super(ActiveManager, self).get_query_set().filter(is_active=True)
# Model
class ModelA(models.Model):
# ...
is_active = models.BooleanField()
objects = ActiveManager()
all_objects = models.Manager()
So, while I was playing around I noticed that if I wrote it this way and used get_object_or_404(), then it would use the ActiveManager to first search for all active records and then return the one related to my query. However, if I switched the order of the managers:
class ModelA(models.Model):
# ...
all_objects = models.Manager()
objects = ActiveManager()
Then it uses the default manager, in this case all_objects, to do the query. I'm wondering what other functions does this change impact.
EDIT: I understand that the first manager found in the class becomes the default manager, but I'm wondering which specific functions use this default manager (like get_object_or_404)
Here's the relevant bit from the docs: "If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they're defined in the model) has a special status. Django interprets the first Manager defined in a class as the "default" Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it's a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_query_set() results in an inability to retrieve objects you'd like to work with".
If you look at the way get_object_or_404 is implemented, they use the _default_manager attribute of the model, which is how Django refers to the first manager encountered. (As far as I know, all Django internals work this way -- they never use Model.objects etc. because you shouldn't assume the default manager happens to be called objects).
It effects many things. The default name for the manager, objects, is just that, a default, but it's not required. If you didn't include objects in your model definition and just defined a manager as all_objects, ModelA.objects wouldn't exist. Django merely assigns a default manager to that if no other managers are present on the model and you have not defined objects on your own.
Anyways, because of this Django takes the first manager defined in a model and calls that the "default", and later uses the "default" manager anytime is needs to reference the model's manager (because, again, it can't simply use objects because objects might not be defined).
The rule of thumb is that the standard manager that Django should use (in a sense, the manager that should most normally be used), should be the first one defined, whether it be assigned to objects or something else entirely. Every other additional manager should come after that.