How can I change django model field value automatically with method? - python

class AssignedTask(Task):
deadline = models.DateField()
amount = models.IntegerField(blank=True)
priority = models.FloatField()
def priority_set(self):
delta = self.deadline - timezone.now().date()
self.priority = delta.days / self.importance
self.save()
def __str__(self):
return self.title
I want to set a value of the 'priority' field when data AssignedTask is created, but the priority field always returns null when I create data with a mutation in graphQL API.

Overwrite the save method
def save(self):
self.priority_set()
super().save()

Related

Django DateTimeField() casuing TypeError ('datetime.timedelta' object is not callable)

I am trying to get differenc of two Datetiemfield in Djago. I have tried overriding the default save() but still getting error.
models.py
class Sample(models.Model):
ad_start = models.DateTimeField()
ad_end = models.DateTimeField()
ad_duration = models.IntegerField()
#property
def get_time_diff(self):
timediff = self.ad_end - self.ad_start
return timediff
#return relativedelta(self.ad_end, self.ad_start)
def save(self, *args, **kwargs):
self.ad_duration = self.get_time_diff()
super(Sample, self).save(*args, **kwargs)
forms.py
class SampleForm(forms.ModelForm):
class Meta:
model = Sample
exclude = ("submitted", 'ad_duration', "list_date" )
widgets = {
'ad_start': DatePickerInput(),
'ad_end': DatePickerInput(),
}
Error
Django Version: 2.1.7
Exception Type: TypeError
Exception Value:'datetime.timedelta' object is not callable
There are two changes required in your code.
First, you need to remove #property from method get_time_diff. Because you can't call a property method via (). Or, you can still keep the property method ,but don't call it in save function, for example like this: self.ad_duration = self.get_time_diff
Second, you need to update the model field to DurationField to store the time delta object created in get_time_diff. Like this:
class Sample(models.Model):
ad_start = models.DateTimeField()
ad_end = models.DateTimeField()
ad_duration = models.DurationField()
def get_time_diff(self):
timediff = self.ad_end - self.ad_start
return timediff
def save(self, *args, **kwargs):
self.ad_duration = self.get_time_diff()
super(Sample, self).save(*args, **kwargs)
Or you can get the total seconds from get_time_diff and store it in ad_duration field(which will be a float field).
class Sample(models.Model):
ad_start = models.DateTimeField()
ad_end = models.DateTimeField()
ad_duration = models.FloatField()
def get_time_diff(self):
timediff = self.ad_end - self.ad_start
return timediff.total_seconds() # returns value in seconds

Django generate custom ID

I saw this answer but there is no specific answer yet. I want to create custom id that starts with letter. When a new record comes into database I want to change the id to A00001, .... A00002, .... A00010, ...A10000 etc. The id will be always in range 99999- 00001 so how can I do that?
my model is simple:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
The AutoField field is a kind of IntegerField field, so you can't use PKs as A00001 .
So, the possible way to achieve the requirement is to change the AutoField to CharField.
Technically you can use "String PK Field" But, you should be aware of the problems/performance issues if you are going to use that.
Here I found one nice SO post that explains the same - Strings as Primary Keys in SQL Database========================================================================
If you still really wish to migrate to String PKs, read the following
First you need to use the CharField instead of AutoField and override the save() method of model
from django.db.models import Max
class Custom(models.Model):
id = models.CharField(primary_key=True, editable=False, max_length=10)
name = models.CharField(max_length=100)
def save(self, **kwargs):
if not self.id:
max = Custom.objects.aggregate(id_max=Max('id'))['id_max']
self.id = "{}{:05d}".format('A', max if max is not None else 1)
super().save(*kwargs)
string as Primary Key not good idea if you plan to do references to the table, so i recommend you to add a property, for example:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
#property
def sid(self):
return "A%05d" % self.id
and to do queries you can do processing the input values, for example:
s_input = "A%05d" % 231 # 'A00231'
number = s_input[1:] # '00231'
input_id = int(number) # 231
I also have another way, That i use in my django project. Here are some code
def ids():
no = Employee.objects.count()
if no == None:
return 1
else:
return no + 1
emp_id = models.IntegerField(('Code'), default=ids, unique=True, editable=False)
id = models.CharField(primary_key=True, editable=False, max_length=30)
def save(self, **kwargs):
if not self.id:
self.id = "{}{:08d}".format('ABC', self.emp_id)
super().save(*kwargs)
It's better to create a new field for the custom id in the models and the process in the backend. You can set that as primary_key with unique=True and editable=False:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.set_uid() # calling the set_uid function
def set_uid(self):
if not self.uid: # if uid of the instance is blank
uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid
customer= Custom.objects.get(id=self.id) # getting the instance
customer.uid = uid # allocating the value
customer.save() # saving the instance
def __str__(self):
return self.uid
Can also merge the set_uid() inside the save() where the function is called:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.uid: # if uid of the instance is blank
self.uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid and allocating the value
self.save() # saving the instance
def __str__(self):
return self.uid
I tried to use answer of #JPG, but it has a bug.
The bug is becasue it can't auto increment.
I fixed the bug, and this my resultant code:
def save(self, **kwargs):
if not self.id:
max = YourModel.objects.aggregate(
id_max=models.Max('id'))['id_max']
if max is not None:
max += 1
else:
max = 100
self.id = "{:08d}".format(
max) # id from 100 to start
super().save(*kwargs)

Django: Creating editable default values for model instance based on foreignkey existance

I'm playing around in Django, and wondering if there is a way to loop through instances of two different models I have created?
/ models.py:
class Tran(models.Model):
name = models.CharField(max_length=300)
description = models.CharField(max_length=2000)
type = models.ForeignKey(TransactionType)
def __str__(self):
return self.name
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
What I want to do:
Look through each of the Tran instances and create a default value for the links/displays in the DocLink table instead of doing it manually.
Is there anyway I can be pointed in the right direction?
If you want to set links/displays default value in DocLink instance based on trann field you can override model's save method.
For example following code shows how to set t_link if it doesn't have a value:
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
def save(self, *args, **kwargs):
if not self.t_link:
pass # TODO set self.t_link based on trann
super(DocLink, self).save(*args, **kwargs)
Also you can change model's trann field to:
trann = models.ForeignKey(Transaction, related_name="doclinks")
And then access to all DocLinks of a Tran with:
# t is an instance of Tran class
t.doclinks.all()
So you can loop through this list and do what you want.

Django Admin - Unable to change Foreign Key field

I have Django version 1.4.5
Here are the relevant parts of my model
class Product (models.Model):
name=models.CharField(max_length=200)
description=models.TextField()
label=models.ForeignKey('Label')
pub_date = models.DateTimeField(editable=False)
def save(self):
#item will not have id if this is the first save
if not self.id:
self.pub_date = datetime.date.today()
super(Product, self).save()
def __unicode__(self):
return self.name
class Label(models.Model):
"""
A clothing label, e.g. Kate Spade
"""
name=models.CharField(max_length=100)
def __unicode__(self):
return self.name
When I attempt to publish a Product, selecting a Label works fine. Publishing the item works as expected, and the label field remains populated upon returning to the Product in the admin console. However, if I attempt to change the value of the label field, I am taken to the default list of Products page, with the message "he product "Prod 1" was changed successfully" but returning to the Prod 1 page reveals that the field wasn't actually saved properly.
Any ideas here?
super(Product, self).save() is within the if block, so it isn't being called on edits. Also, why not just use auto_now_add on the pub_date field?
In your case, no need to set the date & time explicitly. You can use 'auto_now_add' Please fer this link for more details.
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField.auto_now_add
class Product (models.Model):
name=models.CharField(max_length=200)
description=models.TextField()
label=models.ForeignKey('Label')
pub_date = models.DateTimeField(editable=False, auto_now_add = True)
def __unicode__(self):
return self.name
If you need to set it manually, Use the following snippet. It calls super class for change also.
def save(self):
#item will not have id if this is the first save
if not self.id:
self.pub_date = datetime.date.today()
super(Product, self).save()

Adding dynamic fields to Django models

How do I create a dynamic field on a model?
Let's say I'm writing an application related to the stock market. I make a purchase on one day and sometime later I want to check the gain (or loss) based on today's price. I'd have a model like this:
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
What I'd like to do is define a model something like this:
class PurchaseGain(Purchase):
gain = models.DecimalField(max_digits=20, decimal_places=3)
class Meta:
proxy = True
So that I could do this:
todays_price = get_price_from_webservice(ticker)
for p in PurchaseGain.objects.get_purchase_gain(todays_price):
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain)
Where p.gain is dynamically computed based on the input to get_purchase_gain. Rather than just constructing dictionaries on the fly I want to use a model, because I'd like to pass this around and generate forms, save changes, etc from the instance.
I tried creating a derived QuerySet, but that led to a circular dependency, because Purchase needed to know about the QuerySet (through a custom manager) and the QuerySet returned an iterator that needed to instantiate a PurchaseGain, which was derived from Purchase.
What options do I have?
Thanks,
Craig
Why not add a gain() method to your model?
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
def gain(self, todays_price=None):
if not todays_price:
todays_price = get_price_from_webservice(self.ticker)
result_gain = todays_price - self.price
return result_gain
Then you can pretty much do what you want:
for p in Purchase.objects.all():
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain())
Creating a proxy class is what confused me. By just adding attributes to a Purchase, I was able to accomplish what I wanted.
class PurchaseQuerySet(QuerySet):
def __init__(self, *args, **kwargs):
super(PurchaseQuerySet, self).__init__(*args, **kwargs)
self.todays_price = None
def get_with_todays_price(self, todays_price):
self.todays_price = todays_price
cloned = self.all()
cloned.todays_price = todays_price
return cloned
def iterator(self):
for p in super(PurchaseQuerySet, self).iterator():
p.todays_price = self.todays_price
yield p
class PurchaseManager(models.Manager):
def get_query_set(self):
return PurchaseQuerySet(self.model)
def __getattr__(self, name)
return getattr(self.get_query_set(), name)
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
objects = PurchaseManager()
#property
def gain(self):
return self.todays_price - self.price
Now I can do:
for p in Purchase.objects.filter(ticker=ticker).get_with_todays_price(100):
print p
print p.gain

Categories