I want to create a pre_save signal which will update the already created model instance whenever the sender is updated. I want to do it in pre_save signal.
The previous instance of the model will be either deleted and new instance will be created or the previous instance will be updated.
My models:
class Sales(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
company = models.ForeignKey(Company,on_delete=models.CASCADE,null=True,blank=True)
party_ac = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='partyledgersales')
sales = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='saleledger')
date = models.DateField(default=datetime.date.today,blank=False, null=True)
sub_total = models.DecimalField(max_digits=10,decimal_places=2,blank=True, null=True)
class Journal(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
company = models.ForeignKey(Company,on_delete=models.CASCADE,null=True,blank=True,related_name='Companyname')
voucher_id = models.PositiveIntegerField(blank=True,null=True)
date = models.DateField(default=datetime.date.today)
by = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='Debitledgers')
to = models.ForeignKey(Ledger1,on_delete=models.CASCADE,related_name='Creditledgers')
debit = models.DecimalField(max_digits=10,decimal_places=2,null=True)
credit = models.DecimalField(max_digits=10,decimal_places=2,null=True)
My pre_save signal for creation:
#receiver(pre_save, sender=Sales)
def user_created_sales(sender,instance,*args,**kwargs):
if instance.sub_total != None:
Journal.objects.update_or_create(user=instance.user,company=instance.company,date=instance.date,voucher_id=instance.id,by=instance.party_ac,to=instance.sales,debit=instance.sub_total,credit=instance.sub_total)
I want to create a signal which will update the Journal instance whenever the sender model or Sales model is update or delete the previous instance or create a new one.
Any idea how to perform this?
Thank you
update_or_create takes two sets of parameters:
kwargs which are the parameters that uniquely identify the row to fetch from the database
defaults which are the parameters that should be updated
Right now, you're never updating existing instances of Journal because you haven't specified defaults. You're looking for instances that match all the parameters, if one exists, you don't do anything, if it doesn't exist you create it.
You haven't told us what makes a Journal entry unique (and how it relates to a Sales entry, since there is no ForeignKey relationship between them).
update: with voucher_id, you now have way of looking up the corresponding Journal entries.
But something like:
Journal.objects.update_or_create(
user=instance.user,
company=instance.company,
voucher_id=instance.id,
defaults={
'date': instance.date,
'debit': instance.sub_total,
'credit': instance.sub_total,
'by': instance.party_ac,
'to': instance.sales}
)
would look for existing instance with the same user, company, and voucher_id and update the date, by, to, credit and debit.
Related
I have a catalog and the price is a one to many relationship, so that I can track the price of a catalog item over time. The model looks like:
class CatalogItem(db.Model)
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(250))
price = db.relationship("Price", back_populates="catalogitem")
class Price(db.Model):
id = db.Column(db.Integer(), primary_key=True)
price = db.Column(db.Float())
timestamp = db.Column(db.DateTime(), server_default=func.now(), nullable=False)
catalogitem_id = db.Column(db.Integer(), db.ForeignKey("catalogitem.id"))
catalogitem = db.relationship("CatalogItem", back_populates="material_einheitspreis_verkauf")
And this is my View. At least I managed to only show the price.
class CatalogItemView(ModelView):
inline_models = [(Price, dict(form_columns=["id","price"]))]
There are two issues:
When I render a catalog item and set price as inline model, I can do that just fine, but the default behavior would allow me to add multiple prices. What I would actually like to do is to have just a price field. I'm looking for a way to limit the form to just one entity (and also leaving away the button "add price".
When editing a catalogitem, it shouldn't edit the price, but actually create a new relationship -> basically when I edit the price it will create a new Price entity.
For 1 I have not idea on how to achieve this. For 2 I guess I could maybe do this by adding some kind of additional form field outside of the model and then create the "magic" with some kind of listeners.
Any other ideas?
i need to display previous balance of users , i have used pre save signal to assign to the old balance before the transaction happens
class Payment(models.Model):
admin = models.ForeignKey(User,on_delete=models.CASCADE)
client_seller = models.ForeignKey(ClientSeller,on_delete=models.CASCADE,blank=True)
type_of_payment = models.CharField(choices=type_of_payment,max_length=60,default=retrieve)
price = models.DecimalField(max_digits=20,decimal_places=2)
previous_balance = models.DecimalField(max_digits=20,decimal_places=3,blank=True,default=0)
class ClientSeller(models.Model):
admin = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=40,unique=True)
balance = models.DecimalField(decimal_places=3,max_digits=30,default=0)
i need to assign previous_balance in Payment to balance in ClientSeller , balance in ClientSeller i change every time , but i need to show the users what was previous balance when a transaction happens
def pre_save_balance(sender,instance,*args,**kwargs):
if not instance.pk:
Payment.objects.update(
previous_balance = Subquery(
Payment.objects.filter(client_seller__name=instance.client_seller.name).annotate(
pre_balance=F('client_seller__balance')
).values('pre_balance')[:1]
)
)
pre_save.connect(pre_save_balance,sender=Payment)
but it only show the default previous_balance value which is 0 !?
is there something i have missed ? or not understanding in pre_save signal good ?!
thank you for helping
Every time you're getting the first payment you made and maybe then the seller had 0 balance. You need to order your queryset and use the latest() method in order to gey the latest payment.
So you need to add something like .latest('payment_date') in your Subquery and lose the [:1] at the end.
Assuming the following models schema,
Parent model:
class Batch(models.Model):
start = models.DateTimeField()
end = models.DateTimeField()
One of many child models:
class Data(models.Model):
batch = models.ForeignKey(Batch, on_delete=models.ON_CASCADE)
timestamp = models.DateTimeField()
My goals is the following: to have a start field of parent model that is always updated when any child model is modified.
Basically, if the timestamp of a newly data instance is older than the start field I want the start field to be updated to that instance timestamp value. In the case of deletion of the data instance which is the oldest time reference point I want batch start field to be updated to the second oldest. Vice-versa for the end field.
One of the possible way to do this is to add post or pre-save signal of relative models and Update your necessary fields according to this. Django official documentation for signal, link. I want to add another link, one of the best blog post i have seen regarding django signal.
Edit for André Guerra response
One of easiest way to do a get call and bring Batch instance. What i want to say
#receiver(post_save,sender=Data)
def on_batch_child_saving(sender,instance,**kwargs):
batch_instance = Batch.objects.get(pk=instance.batch)
if (instance.timestamp < batch_instance.start):
batch_instance.start = instance.timestamp
batch_instance.save()
elif (instance.timestamp > batch_instance.end):
batch_instance.end = instance.timestamp
batch_instance.save()
Based on Shakil suggestion, I come up with this: (my doubt here was on how to save the parent model)
#receiver(post_save,sender=Data)
def on_batch_child_saving(sender,instance,**kwargs):
if (instance.timestamp < instance.batch.start):
instance.batch.start = instance.timestamp
instance.batch.save()
elif (instance.timestamp > instance.batch.end):
instance.batch.end = instance.timestamp
instance.batch.save()
Say I have a model like
class Product(models.Model):
name = models.CharField(max_length=64)
[...]
and another like
class Price(models.Model):
product = models.ForeignKey('Product')
date = models.DateField()
value = models.FloatField()
[...]
and I want to display products in a modelAdmin, with the last price registered in a column.
So far, the only way I have found is to add the following method to the product object:
#property
def last_price(self):
price = Price.objects.filter(product=self.pk).order_by("-date")
return price[0].value if price else None
and then adding last_price to the list_display in Product's ModelAdmin. I don't like this approach because it ends up doing a query for each row displayed.
Is there a better way to do this in code?. Or do I have to create a column in the Product table and cache the last price there?.
Thanks!
to reduce the the queries for each entry use the following:
Price.objects.filter(product=self.pk).order_by("-date").select_related('product')
this will decrease the product query at each object, hope it is helpful, vote up please
A cleaner version of what you have would be:
def last_price(self):
latest_price = self.price_set.latest('date')
return latest_price.value if latest_price else None
But this still involves queries for each item.
You if you want to avoid this I would suggest adding a latest_price column to Product. Then you could set up a post_save signal for Price that then updates the related Product latest_price (This could be a ForiegnKey or the value itself.)
Update
Here is a receiver that would update the products latest price value when you save a Price. Obviously this assumes that you are saving Price models in chronological order so the lastest one saved is the latest_value
#receiver(post_save, sender=Price)
def update_product_latest_value(sender, instance, created, **kwargs):
if created:
instance.product.latest_value = instance.value
instance.product.save()
I am reading Excel using xlrd. One of the columns has the Bank name, which is linked to vehicle model via Foreign Key. When xlrd finishes reading a row, it should save that record to vehicle table. However getting the actual pk value and error that Vehicles.bank must a Banks instance.
After checking dozens of questions related to this issue, I found this one the most similar one, but still I am not getting the expected result.
The relevant Vehicle model section is as follows:
class Vehicles(models.Model):
stock = models.CharField(max_length=10, blank=False, db_index=True)
vin = models.CharField(max_length=17, blank=False, db_index=True)
sold = models.DateField(blank=True, null=True, db_index=True)
origin = models.CharField(max_length=10, blank=False, db_index=True)
bank = models.ForeignKey('banks.Banks', db_column='bank', null=True)
I am using python 2.7, django 1.5.4 and Postgresql 9.2.5. Dbshell utility does show that banks table has a Foreign contraint referring to vehicles table, via banks(id).
Since I am not using a form for this particular part, I think it does not matter whether I use a ModelForm or not.
Current scenario: Excel file has FBANK as the cell value. There is an existing record in banks table that contains FBANK in its name column, id=2. The python line is:
def bank(value):
return Banks.objects.get(name=value).id
With the above line, error is:
Cannot assign "2": "Vehicles.bank" must be a "Banks" instance.
If I remove the ".id" at the end, error is then:
Banks matching query does not exist.
Appreciate your help.
Ricardo
When saving Vehicle you need to pass Banks instance with corresponding bank name. See example, I suppose that you have all data in corresponding cells from 0 to 4, replace with your own cells numbers:
def get_bank_instance(bank_name):
try:
bank = Banks.objects.get(name=bank_name)
except Banks.DoesNotExist:
return None
return bank
# reading excel file here, we have list of cells in a row
for cell in cells:
bank = get_bank_instance(cell[4])
if bank:
# get other cells values to be saved in Vehicles
stock, vin, sold, origin = cell[0], cell[1], cell[2], cell[3]
Vehicles.create(bank=bank, stock=stock, vin=vin, sold=sold, origin=origin)
You also can create save instance of Vehicles passing bank id directly:
b_id = Banks.objects.get(name=bank_name).id
Vehicles.create(bank_id=b_id, stock=stock, vin=vin, sold=sold, origin=origin)
Update:
create() is a built-in model method to create and save into database model instance. If you are asking about "Add a classmethod on the model class" in Django docs, this is not the case, because you are just using built-in method for the model. For some cases you can use custom method for creating new models, but I would do so if I had to pass a lot of default attributes for the new instance.
Also, it's possible to create and save new model instance by using save():
bank_instance = Banks.objects.get(name=bank_name)
vehicle = Vehicles()
vehicle.bank = bank_instance
vehicle.stock = stock
vehicle.vin = vin
vehicle.sold = sold
vehicle.origin = origin
# without save() data will not be saved to db!
vehicle.save()
It's quite long and you always need to remember to call .save(), so it's a good idea to use .create()
You should be returning a Banks instance when you want to assign it to a Vehicle model instance; so you should not have the .id part at the end of the return value for your bank() method.
Secondly, if it says that it isn't finding the Banks instance, then you should check the value of your value parameter to see what it is and try to manually do a Banks.objects.get from your database. If it can't be found then there is probably another reason for this other than using the Django ORM incorrectly.
When you are assigning instances to other instances in Django, for example setting the Bank for a Vehicle it must be an instance of the model and not the id or pk value of model; this is stated in the other StackOverflow question that you reference in your question.