I'm creating an instance of a model (Container), and it seems like the pre_save function is not triggered.
This is the class in the 'signals':
class ContainerCreatedMixin(object):
#staticmethod
#receiver(pre_save, sender=Container)
def container_pre_save(sender, instance, **kwargs):
# create container folder if not created yet
if instance.folder_created_at is None:
is_folder_created = ContainerCreatedMixin().create_folder(instance)
if is_folder_created:
instance.folder_created_at = now()
def create_virtual_folder(self, container):
try:
......
Using the receiver decorator on a class method doesn't really make sense.
Put your decorated method out of class and it should be registered if the file is imported. Also, there is no need of creating Mixings for the following.
I'm using django, but this is rather a generic python question.
I have defined a class that I intend to use to extend the ModelForm and Form classes, from django.forms.
The code looks like this:
class FormMixin(object):
def __init__(self, *args, **kwargs):
""" every method ocurrence must call super """
super(FormMixin, self).__init__(*args, **kwargs)
self.new_attr = 'This is an attribute'
class ModelFormAdapter(forms.ModelForm):
""" I use this class so __init__ signatures match """
def __init__(self, *args, **kwargs):
""" every method ocurrence must call super """
super(ModelFormAdapter, self).__init__(*args, **kwargs)
class BaseModelForm(ModelFormAdapter, FormMixin):
def __init__(self, *args, **kwargs):
""" BaseModelForm never gets the attribute new_attr """
super(BaseModelForm, self).__init__(*args, **kwargs)
I have even debugged this and the FormMixin init method is never called. What am I doing wrong? What I want to achieve is to add some attributes to the form and preprocess field labels and css classes
That's because one of ModelFormAdapter's ancestors (BaseForm), doesn't call super, and the chain breaks. Put FormMixin first in the parent list.
I'm trying to find the best way to create a class decorator that does the following:
Injects a few functions into the decorated class
Forces a call to one of these functions AFTER the decorated class' __init__ is called
Currently, I'm just saving off a reference to the 'original' __init__ method and replacing it with my __init__ that calls the original and my additional function. It looks similar to this:
orig_init = cls.__init__
def new_init(self, *args, **kwargs):
"""
'Extend' wrapped class' __init__ so we can attach to all signals
automatically
"""
orig_init(self, *args, **kwargs)
self._debugSignals()
cls.__init__ = new_init
Is there a better way to 'augment' the original __init__ or inject my call somewhere else? All I really need is for my self._debugSignals() to be called sometime after the object is created. I also want it happen automatically, which is why I thought after __init__ was a good place.
Extra misc. decorator notes
It might be worth mentioning some background on this decorator. You can find the full code here. The point of the decorator is to automatically attach to any PyQt signals and print when they are emitted. The decorator works fine when I decorate my own subclasses of QtCore.QObject, however I've been recently trying to automatically decorate all QObject children.
I'd like to have a 'debug' mode in the application where I can automatically print ALL signals just to make sure things are doing what I expect. I'm sure this will result in TONS of debug, but I'd still like to see what's happening.
The problem is my current version of the decorator is causing a segfault when replacing QtCore.QObject.__init__. I've tried to debug this, but the code is all SIP generated, which I don't have much experience with.
So, I was wondering if there was a safer, more pythonic way to inject a function call AFTER the __init__ and hopefully avoid the segfault.
Based on this post and this answer, an alternative way to do this is through a custom metaclass. This would work as follows (tested in Python 2.7):
# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
def __call__(cls, *args, **kwargs):
"""Called when you call MyNewClass() """
obj = type.__call__(cls, *args, **kwargs)
obj.new_init()
return obj
# then create a new class with the __metaclass__ set as our custom metaclass
class MyNewClass(object):
__metaclass__ = NewInitCaller
def __init__(self):
print "Init class"
def new_init(self):
print "New init!!"
# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!
The basic idea is that:
when you call MyNewClass() it searches for the metaclass, finds that you have defined NewInitCaller
The metaclass __call__ function is called.
This function creates the MyNewClass instance using type,
The instance runs its own __init__ (printing "Init class").
The meta class then calls the new_init function of the instance.
Here is the solution for Python 3.x, based on this post's accepted answer. Also see PEP 3115 for reference, I think the rationale is an interesting read.
Changes in the example above are shown with comments; the only real change is the way the metaclass is defined, all other are trivial 2to3 modifications.
# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
def __call__(cls, *args, **kwargs):
"""Called when you call MyNewClass() """
obj = type.__call__(cls, *args, **kwargs)
obj.new_init()
return obj
# then create a new class with the metaclass passed as an argument
class MyNewClass(object, metaclass=NewInitCaller): # added argument
# __metaclass__ = NewInitCaller this line is removed; would not have effect
def __init__(self):
print("Init class") # function, not command
def new_init(self):
print("New init!!") # function, not command
# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!
Here's a generalized form of jake77's example which implements __post_init__ on a non-dataclass. This enables a subclass's configure() to be automatically invoked in correct sequence after the base & subclass __init__s have completed.
# define a new metaclass which overrides the "__call__" function
class PostInitCaller(type):
def __call__(cls, *args, **kwargs):
"""Called when you call BaseClass() """
print(f"{__class__.__name__}.__call__({args}, {kwargs})")
obj = type.__call__(cls, *args, **kwargs)
obj.__post_init__(*args, **kwargs)
return obj
# then create a new class with the metaclass passed as an argument
class BaseClass(object, metaclass=PostInitCaller):
def __init__(self, *args, **kwargs):
print(f"{__class__.__name__}.__init__({args}, {kwargs})")
super().__init__()
def __post_init__(self, *args, **kwargs):
print(f"{__class__.__name__}.__post_init__({args}, {kwargs})")
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
print(f"{__class__.__name__}.configure({args}, {kwargs})")
class SubClass(BaseClass):
def __init__(self, *args, **kwargs):
print(f"{__class__.__name__}.__init__({args}, {kwargs})")
super().__init__(*args, **kwargs)
def configure(self, *args, **kwargs):
print(f"{__class__.__name__}.configure({args}, {kwargs})")
super().configure(*args, **kwargs)
# when you create an instance
a = SubClass('a', b='b')
running gives:
PostInitCaller.__call__(('a',), {'b': 'b'})
SubClass.__init__(('a',), {'b': 'b'})
BaseClass.__init__(('a',), {'b': 'b'})
BaseClass.__post_init__(('a',), {'b': 'b'})
SubClass.configure(('a',), {'b': 'b'})
BaseClass.configure(('a',), {'b': 'b'})
I know that the metaclass approach is the Pro way, but I've a more readable and easy proposal using #staticmethod:
class Invites(TimestampModel, db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
invitee_email = db.Column(db.String(128), nullable=False)
def __init__(self, invitee_email):
invitee_email = invitee_email
#staticmethod
def create_invitation(invitee_email):
"""
Create an invitation
saves it and fetches it because the id
is being generated in the DB
"""
invitation = Invites(invitee_email)
db.session.save(invitation)
db.session.commit()
return Invites.query.filter(
PartnerInvites.invitee_email == invitee_email
).one_or_none()
So I could use it this way:
invitation = Invites.create_invitation("jim#mail.com")
print(invitation.id, invitation.invitee_email)
>>>> 1 jim#mail.com
Can someone please explain me the following code
TickGenerator inherit from object and methods of Observer, why do we need both observer.init?
class TickGenerator(Observer):
def __init__(self):
Observer.__init__(self)
self.price = 1000
I guess you came from a language where the parent class constructor is automatically called.
In Python, if you override the __init__ method, the parent class constructor will not be called unless you call it explicitly.
Until Python 3, it used to be called as:
def __init__(self, *args, **kwargs):
super(TickGenerator, self).__init__(*args, **kwargs)
The new [super()][1] syntax (PEP-3135) is just:
def __init__(self, *args, **kwargs):
super().method(*args, **kwargs)
Because programmer needs Observer class __init__ to be done in addition to
what is being done in the current class's (TickGenerator) __init__.
This Stackoverflow answer will help you understand more.
If you don't call Observer.init as below:
class TickGenerator(Observer):
def __init__(self):
self.price = 1000
It means you override the TickGenerator.init method and Observer.init will not be called automaticlly.
In django, I have a form being called from the view, which is passed an extra object that popped in the form init. I want to use this object data (person) in the clean def's outside of init. How can I fix the scope of this passed information? Thanks!
class RegForm(forms.Form):
first = forms.CharField(min_length=5)
def __init__(self, *args, **kwargs):
person = kwargs.pop("person")
super(CompleteRegistrationForm, self).__init__(*args, **kwargs)
def clean_first(self):
if not self.cleaned_data['first'] == person.first:
raise forms.ValidationError(_("This information does not match records."))
else:
return self.cleaned_data['first']
person should be an instance variable:
def __init__(self, *args, **kwargs):
self.person = kwargs.pop("person")
super(CompleteRegistrationForm, self).__init__(*args, **kwargs)
Then, in other methods, refer to it as self.person (not just person).
You should assign it to self:
self.person = kwargs.pop("person")
This is fairly basic Python - you would probably benefit from doing a tutorial.