Django - Signals multiple values for keyword argument 'sender' - django

def pre_save(self, model_instance, add):
value = super(MediaUploadField, self).pre_save(model_instance, add)
if value and add:
post_save.connect(self.someCallback, sender=model_instance.__class__, dispatch_uid='media_attachment_signal')
return value
def someCallback(sender, **kwargs):
print "callback"
print sender
return
Is throwing the following error:
someCallback() got multiple values for keyword argument 'sender'
I honestly can't work out what I'm doing wrong, I followed the documentation precisely. I tried replacing model_instance.class with the actual class import but it throws the same error.
Does any have any idea whats wrong with my code?

It seems that someCallback is a model method. The first argument to model methods is always the instance itself - which is usually referenced as self. But you've called the first argument sender - so Python is trying to receive sender both as the first positional argument, and as one of the keyword arguments.
The best way to solve this is to define someCallback as a staticmethod, as these don't take the instance or class:
#staticmethod
def someCallback(sender, **kwargs):
Also note that connecting your post_save handler in a pre_save method is a very strange thing to do. Don't forget that connecting a signal is a permanent thing - it's not something that's done on a per-call basis.

Related

Do I need to append dispatch_uid to signal in this case?

On Django Documentation, I read this : https://docs.djangoproject.com/en/2.0/topics/signals/#preventing-duplicate-signals
In some circumstances, the code connecting receivers to signals may
run multiple times. This can cause your receiver function to be
registered more than once, and thus called multiple times for a single
signal event.
Then, If I use signal as,
class CustomauthConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals
And
#receiver(post_save, sender=TestModel)
def update_log(sender, instance, **kwargs):
TestModelLog.objects.create(description=instance.description, datetime=instance.updated)
Question:
Is it right that I don't need dispatch_uid?
Or if I have to use dispatch_uid, Would you give me a sample for using dispatch_uid?
My purpose is to prevent duplicates
Since you are using the signal to create a new Log (although your function is named update_log) you are probably better off using update_or_create() method:
#receiver(post_save, sender=TestModel)
def update_log(sender, instance, **kwargs):
TestModelLog.objects.update_or_create(
description=instance.description, datetime=instance.updated
)

TypeError: connect() got an unexpected keyword argument 'signal' - Django signals

I'm trying to create a simple signal which prints something after a new object of the Staff model is saved in the Django-admin. The MVC python files live in AppName. Here is the code in each file:
models.py
from django.db import models
from django.db.models import signals
from django.dispatch import Signal
from django.contrib.auth.models import User
from AppName.signals import printfunction
from django.db.models.signals import post_save
class Staff(User):
class Meta:
proxy = True
app_label = 'auth'
verbose_name_plural = 'Users - Staff'
Signal.connect(printfunction, signal=signals.post_save, sender=Staff)
signals.py
def printfunction(sender, instance, signal, *args, **kwargs):
print ("signal alpha!")
However it is raising the following exception:
TypeError: connect() got an unexpected keyword argument 'signal'
I followed the 1.8 django documentation on signals. Why is this error occurring and how to fix it?
Signal.connect(receiver[, sender=None, weak=True, dispatch_uid=None])
This is a very common notation for documentation. It is not literal code that can be used as-is. The arguments in between [ and ] are optional, if you leave them out they will use the default values. connect is a method on the class Signal. Unless otherwise specified, you can assume it is an instance method. Instead of literally calling Signal.connect(), you should call signal_instance.connect(), where signal_instance is of course an instance of the Signal class.
In this case, signals.post_save is an instance of Signal, and it's the instance to which you want to connect your function. The receiver argument is required, and in this case it is your function printfunction. sender, weak and dispatch_uid are all optional. In your example you're only passing in Staff as the sender, and you leave the other arguments as their default values. So, your final function call should look like this:
signals.post_save.connect(printfunction, sender=Staff)
Here's its the right way to do it:
#receiver(post_save, sender=Staff)
def printfunction(sender, instance, signal, *args, **kwargs):
print ("signal alpha!")

Django - Celery: #transaction and #task don't stack

I want to run a Django - Celery task with manual transaction management, but it seems that the annotations do not stack.
e.g.
def ping():
print 'ping'
pong.delay('arg')
#task(ignore_result=True)
#transaction.commit_manually()
def pong(arg):
print 'pong: %s' % arg
transaction.rollback()
results in
TypeError: pong() got an unexpected keyword argument 'task_name'
while the reverse annotation order results in
---> 22 pong.delay('arg')
AttributeError: 'function' object has no attribute 'delay'
It makes sense, but I'm having trouble finding a nice workaround. The Django docs don't mention alternatives to the annotation, and I don't want to make a class for each celery Task when I don't need one.
Any ideas?
Previously Celery had some magic where a set of default keyword arguments
were passed to the task if it accepted them.
Since version 2.2 you can disable this behaviour, but the easiest is to
import the task decorator from celery.task instead of celery.decorators:
from celery.task import task
#task
#transaction.commit_manually
def t():
pass
The decorators module is deprecated and will be completely removed in 3.0,
and the same for the "magic keyword arguments"
Note:
For custom Task classes you should set the accept_magic_kwargs attribute to False:
class MyTask(Task):
accept_magic_kwargs = False
Note2: Make sure your custom decorators preserves the name of the function using functools.wraps, otherwise the task will end up with the wrong name.
The task decorator generates a class x(Task) from your function with the run method as your target. Suggest you define the class and decorate the method.
Untested e.g.:
class pong(Task):
ignore_result = True
#transaction.commit_manually()
def run(self,arg,**kwargs):
print 'pong: %s' % arg
transaction.rollback()

django multiwidget subclass not calling decompress()

I am trying to implement a MultiValueField for IP Adress/Domain Name entries. It works as expected for entering data.
My Problem is that if I want to display the form bound to specific data, the IP Address/Domain Name field stays empty. All other fields are filled with the desired data. If I use a normal CharField, I get the data that I would expect. But it does not work with my custom field.
I have tracked it down to the fact that my custom MultiWidget does not call its decompress method.
Here is my Field:
class accessIPField(forms.MultiValueField):
"""
custom Field for access IP
"""
def __init__(self, *args, **kwargs):
self.fields=(
forms.IPAddressField(label='IP Adress'),
forms.CharField(max_length=50,label='Domain Name')
)
self.widget=accessIPWidget()
super(accessIPField,self).__init__(self.fields,self.widget, *args, **kwargs)
def compress(self,data_list):
if data_list:
return " ".join(data_list)
And here is my widget:
class accessIPWidget(forms.MultiWidget):
"""
Widget to display IP Adress / Domain name pairs
"""
def __init__(self,*args,**kwargs):
self.widgets=(forms.TextInput(),forms.TextInput())
super(accessIPWidget,self).__init__(self.widgets,*args,**kwargs)
def decompress(self,value):
print 'decompress called'
if value:
return value.rsplit()
return [None,None]
def format_output(self, rendered_widgets):
return u'\n'.join(rendered_widgets)
The whole thing is called (in a larger context) as
self.fields['access_IPs'] = accessIPField()
Now as you can see, I put a print statement in my compress method, and I never get to see that statement. Also, if I rename compress to something like foobar, I would expect (according to the django code for MultiWidget) to get the NotImplementedError, which is not the case. Any suggestions?
I am using python 2.6.5, django 1.1 on ubuntu server 10.04.
It turns out that the problem was with the value_from_datadict() method as implemented by MultiWidget. First of all, it allready returned a list, so that is why decompress() was not called in the first place. Secondly, it allways returen a [None,None] list, so that is why the bound form stayed empty.
I needed to implement my own (within my accessIPWidget class):
def value_from_datadict(self, data, files, name):
try:
return data.get(name,None).rsplit()
except AttributeError:
return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
Now the last line is what the original method did. In order to get the data into the bound form, I needed to add data.get(name,None).rsplit().
As far as I understand, the original value_from_datadict method only works for unbound fields. Because it changes the name of the original field to name + '_%s', which is what you get when pressing the submit button. In order to fill in a bound method, the datadict needs to be queried for 'name' only.
Hm, not shure if there is a way around this, but it seems to me that this behaviour should at least be documented somewhere.
Maybe I misunderstood something?

Django-contact-forms: how to subclass and supply 'initial' parameter?

I have a Django form that is subclassed from the django-contact-form application. I want to supply an initial parameter to the form (which will vary depending on context).
This code returns the contact form fine, but obviously doesn't supply an initial parameter, so I need to extend it:
def contact_form(request):
scraper_form = scraperContactForm
return contact_views.contact_form(request=request, form_class=scraper_form)
This attempt to supply an initial parameter fails:
def contact_form(request):
scraper_form = scraperContactForm(initial={'title' : 'hello world'})
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
Keyword argument 'request' must be supplied
Hence, I have tried to supply a request argument, but weirdly, that fails by saying the form object is not callable:
def contact_form(request):
scraper_form = scraperContactForm(request=request, initial={'title' : 'hello world'})
# supply initial subject_dropdown field, if there's anything in the subject_type
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
'scraperContactForm' object is not callable
And no matter how I try to supply the request parameter, I keep on getting 'scraperContactForm' object is not callable.
FYI, this is the code for my subclassed form:
class scraperContactForm(ContactForm):
subject_dropdown = django.forms.ChoiceField(label="Subject type", choices=(('suggestion', 'Suggestion for improvement'), ('bug', 'Report a bug'), ('other', 'Other')))
title = django.forms.CharField(widget=django.forms.TextInput(), label=u'Subject')
recipient_list = [settings.FEEDBACK_EMAIL]
Please can anyone suggest what's going wrong?
You don't supply the full traceback in any of your examples. If you had, I suspect we would see that it's not your code that's giving the 'scraperContactForm' object is not callable error, but the subsequent call to the main contact_form view.
This is because that view is clearly expecting a form class, as indicated by the keyword parameters. However, you're already instantiating the form, by calling it in your first line, so you're actually passing a form instance, which isn't callable.
I would suggest that if you want to provide an initial value for a field, you do so in your subclassed form's definition:
class scraperContactForm(ContactForm):
title = django.forms.CharField(initial='hello world')