I have a model with overridden __init__
method like this:
class MyModel(models.Model):
...
def __init__(self, *args, **kwargs):
if not kwargs.get('skip', False):
do_something()
super().__init__(*args, **kwargs)
How can I pass skip
argument to __init__
, when I iter the queryset:
data = [obj for obj in MyModel.objects.all()]
I would like to implement this via method in custom manager, for use this something like this: queryset.with_skip()
I see that you don't remove the the argument skip
from kwargs
before passing it to super().__init__
. That means that "skip" is a name of field, otherwise you got exception TypeError("'skip' is an invalid keyword argument for this function")
.
If you really need do_something()
when the object is created before using so indispensably that nobody should forget to avoid all unsupported ways (??) then custom managers etc. are not enough.
Your problem is that models.Model.__init__(...)
supports both *args
and **kwargs
arguments so perfectly that they should be interchangeable. You broke it and if the "skip" is passed by the complete tuple of positional arguments, you ignore it. That is if the object is created from the database. Read the docs Customizing model loading:
... If all of the model’s fields are present, then values are guaranteed to be in the order
__init__()
expects them. That is, the instance can be created bycls(*values)
...
.| @classmethod
| def from_db(cls, db, field_names, values):
| ...
| instance = cls(*values)
| ...
An easy way to fix it is to call do_something()
after super().__init__
and read self.skip
instead of implement parsing both kwargs and args.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.skip:
do_something()
A problem could be the signal "post_init" that is sent at the end of super().__init__
if you need it.
The last possibility is to support *args
(hacky, but still uses documented names someway):
def __init__(self, *args, **kwargs):
if kwargs:
skip = kwargs.get('skip', False)
else:
# check "val is True" in order to skip if val is DEFERRED
skip = any(field.name == 'skip' and val is True
for val, field in zip(args, self._meta.concrete_fields)
)
if not skip:
do_something()
super().__init__(*args, **kwargs)
EDIT: Maybe you you don't need what you wanted and a Proxy model that can do something extra sometimes over a basic model on the same data in the same database table is the right solution. ("Skip" doesn't look like a name describing the object data, but like a name describing a mode of object creation. It is easier to test and maintain a subclass than a mysterious switch inside.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With