I would appreciate some help. Does anyone know why this signal is only being triggered only when the "xp value" is different from the one that already exist ? so eg. if a UserLeaderboardTracking objects already exist for that user with the value 10 the it won't create it otherwise it will .
def create_user_leaderboard_tracking(sender, instance, *args, **kwargs):
if instance.xp_collected == 0:
pass
else:
UserLeaderboardTracking.objects.get_or_create(user=instance.user,
xp_value=instance.xp_collected)
I was using <get_or_create> and instead I should have use
def create_user_leaderboard_tracking(sender, instance, *args, **kwargs):
if instance.xp_collected == 0:
pass
else:
UserLeaderboardTracking.objects.create(user=instance.user,
xp_value=instance.xp_collected) ```
Related
I have 2 models: Transaction and Wallet, a wallet has multiple transactions.
I need to update the amount field in Wallet model whenever a new transaction is created. The way I see is overwrite save method in the model. Currently, I wrote like this
def save(self, *args, **kwargs):
if self.kind == "Income":
self.wallet.amount += self.amount
elif self.kind == "Outcome":
self.wallet.amount -= self.amount
self.wallet.save()
super(Transaction, self).save(*args, **kwargs)
It creates a new transaction correctly but not update the wallet model. How I can fix that?
You should not be doing this because that information can be accessed via aggregate functions. Check out on django documentation.
But, if for some specific reason you you need to do it, you need to do it after the transaction is saved, with F expression:
from django.db.models import F
def save(self, *args, **kwargs):
super(Transaction, self).save(*args, **kwargs)
if self.kind == "Income":
self.wallet.amount = F('amount') + self.amount
elif self.kind == "Outcome":
self.wallet.amount = F('amount') - self.amount
self.wallet.save()
Did you check to see that your conditional statements are getting triggered?
This should actually work without you having to call self.wallet.save() if
you have already passed a valid Wallet object to the Transaction.
def save(self, *args, **kwargs):
if self.kind == "Income":
self.wallet.amount += self.amount
elif self.kind == "Outcome":
self.wallet.amount -= self.amount
super(Transaction, self).save(*args, **kwargs)
See: https://docs.djangoproject.com/en/3.1/topics/db/queries/#saving-changes-to-objects
For information on how to save Object references.
So I think two things could be going wrong. Either your conditional statements are not getting triggered, or you are not passing a valid Wallet Object reference to the Transaction.
Either way...I think this is a good use case for Django's signals. First, I would remove the save method you have, and in the Transaction model I would add
def update_transaction_wallet(sender, instance, **kwargs):
if instance.kind == "Income":
instance.wallet.amount += instance.amount
elif instance.kind == "Outcome":
instance.wallet.amount -= instance.amount
signals.post_save.connect(update_transaction_wallet, sender=Transaction)
You may have to tweak this a bit in order for it to work in your specific case. You didn't provide a lot of information about your models and situation.
But basically, this bit of code tells Django that whenever a Transaction Objects gets saved to also run the update_transaction_wallet() method. See: https://docs.djangoproject.com/en/3.0/topics/signals/
You have to instantiate Wallet and then make the proper addition or subtraction on it.
I would do it inside a Transaction method,
which you will call on save or any other place.
and if saves fail, rowback the wallet amount too ;)
Generally, in order to send an email when an object is created, I would override the save method:
def save(self, *args, **kwargs):
send_email(context)
return super().save(*args, **kwargs)
However, I now need the context to contain an attribute of the object that cannot be known until the object is saved, namely the url of a File object associated with the model object.
I am aware that this can be done with post_save signal, but the docs give the impression that this is best used when disparate models need access to such information. I get the impression that it's not good practice to use it in a single-model setup like this.
I've tried this:
foo = super().save(*args, **kwargs)
send_email(foo.document.url)
return foo
But foo seems to be None.
The save method doesn't return anything. But the item is self, you can use that after calling super.
super().save(*args, **kwargs)
send_email(self.document.url)
Daniel's answer is correct, but if you only want to send the email when the object is created, not if it's updated, you should also check if the instance has a pk assigned, for example:
def save(self, *args, **kwargs):
created = self.pk is None
super().save(*args, **kwargs)
if created:
send_email(context)
I am trying to create a custom form field in Django.
class CustomTypedMultipleChoiceField(MultipleChoiceField):
def __init__(self, *args, **kwargs):
self.coerce = kwargs.pop('coerce', lambda val: val)
self.empty_value = kwargs.pop('empty_value', [])
super(CustomTypedMultipleChoiceField, self).__init__(*args, **kwargs)
def to_python(self, value):
"""
Validates that the values are in self.choices and can be coerced to the
right type.
"""
value = super(CustomTypedMultipleChoiceField, self).to_python(value)
if value == self.empty_value or value in self.empty_values:
return self.empty_value
new_value = []
for choice in value:
try:
new_value.append(self.coerce(choice))
except (ValueError, TypeError, ValidationError):
raise ValidationError(self.error_messages['invalid_choice'] % {'value': choice})
return new_value
def validate(self, value):
if value != self.empty_value:
super(CustomTypedMultipleChoiceField, self).validate(value)
elif self.required:
raise ValidationError(self.error_messages['required'])
I am getting the error CustomTypedMultipleChoiceField has no attribute empty_values. This is the exact same code that Django in built TypedMultipleChoiceField is built with. So I dont understand why I am getting this error.
I also thought of sub-classing the TypedMultipleChoiceField, but I wanted its error to be different in to_python method and didn't want to return the value thing, so opted for this method.
Please help me.
I don't know if it's a typo or you intended that way but actually empty_values (in plural) is not defined in your code anywhere. I also take a look at the source code of the super class MultipleChoiceField and is not defined there either.
What I could find in the super super class of your class (ChoiceField) was a reference to validator.EMPTY_VALUES and of course, it is in capital letters.
The line more alike yours in the source code was this one:
if value == self.empty_value or value in validators.EMPTY_VALUES:
Take a look deep in your code and see if that was what you intended to do.
Hope this helps!
I have a model that sends signal:
class WMTransaction(models.Model):
def save(self, *args, **kwargs):
if self.status == 'completed':
self.completed = datetime.datetime.now()
try:
old = WMTransaction.objects.get(pk=self.pk)
if old.status == 'processing':
print 'sending signal'
payment_done.send(self)
except:
pass
super(WMTransaction, self).save(*args, **kwargs)
Also I have receivers in 2 modules:
#receiver(payment_done, dispatch_uid="make_this_signal_unique", weak=False)
def subscribe(sender, **kwargs):
print 'subscribing'
# processing
And:
#receiver(payment_done, dispatch_uid="this_signal_is_also_unique", weak=False)
def buy(sender, **kwargs):
print 'buying'
# processing
The problem is that subscribe function is called, and buy - isn't... Both modules are in installed apps, other functions from these modules work correctly. What's the problem with signals?
Has module_B been installed and the definition of buy actually gets executed? Check payment_done.receivers before the payment_done.send line.
I need to change the widget used in the admin, based on the value of the db_field. Here's where I'm trying to step in:
def formfield_for_dbfield(self,db_field,**kwargs):
field = super(MyAdmin, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == "my_custom_name":
# how can I check here the value of the object?
I've been trying various combinations in the shell for the past 10 minutes, to no result.
Ok, so here's how I finally did it:
class MyAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
self.object_instance = obj
return super(MyAdmin,self).get_form(request,obj,**kwargs)
After that, everything was easy.