I want to create a constraint of the form code = <min_weight> - <max_weight> <weight_unit>
how do i do that i just learned it
class Size(models.Model):
code = models.CharField(max_length=200, primary_key=True)
uni = [("GC", "Gram/Con"), ("KC", "Kg/Con")]
min_weight = models.IntegerField(blank=False, null=False)
max_weight = models.IntegerField(blank=False, null=False)
weight_uni = models.CharField(max_length=10, choices=uni, default="KC")
def __str__(self):
return self.code
Since code is 100% determined by values of other fields, there is no need to store it as separate field in db. Rather, make it a property, like this:
class Size(models.Model):
uni = [("GC", "Gram/Con"), ("KC", "Kg/Con")]
min_weight = models.IntegerField(blank=False, null=False)
max_weight = models.IntegerField(blank=False, null=False)
weight_uni = models.CharField(max_length=10, choices=uni, default="KC")
#property
def code(self):
return '%s - %s %s' % (self.min_weight, self.max_weight, self.weight_uni)
def __str__(self):
return self.code
You can override the save() method and configure the following:
def save(self, *args, **kwargs):
self.code = self.min_weight - self.max_weight*self.weight_unit
return super(Size, self).save(*args, **kwargs)
That way each time a save is called(it is called when a value is changed) it will reevaluate the "code" field and update it if necessary.
Here is an example how to use constraints but I don't think that you would be able to achieve the same result:
class Meta:
constraints = [
models.CheckConstraint(
name='SimpleConstaint',
check=(models.Q(sold_units__gte=0)&models.Q(sold_units__lte=100))
)
]
Related
I'm implementing a soft delete for class Animal. Following the example in the docs I created a custom manager for it, but the query field, 'Inactive_Date' is undefined. I tried putting the AnimalManager class def inside the Animal class def; no help.
Code from models.py:
class AnimalManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(Inactive_Date == None)
class Animal(models.Model):
Name = models.CharField(max_length=64, unique=True)
Inactive_Date = models.DateField(null=True, blank=True)
Animal_Type = models.ForeignKey(Animal_Type, null=True, on_delete=models.SET_NULL, default=None)
Comments = models.CharField(max_length=255, blank=True)
def __str__(self) -> str:
return (self.Name)
def delete(self):
self.Inactive_Date = datetime.datetime.today()
self.save()
objects = AnimalManager() # omits inactive animals
Seems like you have double equal sign on the filtering
return super().get_queryset().filter(Inactive_Date == None)
^^
But regardless, checking for NULL value can be done via __isnull
return super().get_queryset().filter(Inactive_Date__isnull=True)
I have this model in my app which is meant to auto-generate it's primary Key based on a method added in the save().
However, for each object, I will be expected to make updates of certain fields. Right now, anytime I make an update on the admin side (testing use cases) it instead creates a new record of the PK instead of updating the existing one. Any thoughts on how to remedy this?
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])
def save(self):
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
self.deployment_id = new_task_id
super(DeploymentTask, self).save()
enter image description here
You are always setting self.deployment_id to a new value in the save method.
Django tries to do UPDATE ... WHERE deployment_id = %, but there are no records with this id yet (at least if you are saving "old" object on different date or having different ticket_count)
If you want to update fields other than deployment_id, then simply do not set self.deployment_id if it's already set. If you want to update deployment_id then there is not straightforward way to do this, because it's used as primary key (but you can remember old pk and delete that object after you have created a new one during save)
Read more in the Django docs.
This is my updated code, which worked for me...
def make_id():
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
return new_task_id
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False, default=make_id)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])
I have two models in Django and I want to automatically create an object of History when an object of Food is created or updated and set the food_price attribute of History to the price attribute of the created Food object. My purpose is to have a history of food change price. How can I achieve that?
My models.py is:
class Food(models.Model):
food_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30, null=False, blank=False)
desc = models.TextField(max_length=200)
price = models.IntegerField(null=False, blank=False)
f_thumbnail = models.ImageField(upload_to=get_food_t_image_name)
DDD_data = models.ImageField(upload_to=get_food_d_image_name)
availability = models.BooleanField(default=True)
discount = models.IntegerField(default=0)
def __str__(self):
return '%s %s %s' % (self.name, self.category_id, self.price)
class History(models.Model):
id = models.AutoField(primary_key=True)
food_id = models.IntegerField()
food_price = models.IntegerField()
history_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s %s %s' % (self.id, self.food_id.name, self.food_id.price)
Thanks in advance.
I think you have two ways.
First, use __save__() method in model.
In django model, (models.Model), there's __save__ method. When model object saves, you can do an additional feature that inherits this method.
For more information, please check official docs save()
Second, use signals
Django supports many signals including django.db.models.signals. It includes pre_save, post_save, and so on. So before(after) saving model object, you can do sth in signals.
Please check official docs signals
I think __save__() method is more fit to your purpose. So your code will be...
class Food(models.Model):
food_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30, null=False, blank=False)
desc = models.TextField(max_length=200)
price = models.IntegerField(null=False, blank=False)
f_thumbnail = models.ImageField(upload_to=get_food_t_image_name)
DDD_data = models.ImageField(upload_to=get_food_d_image_name)
availability = models.BooleanField(default=True)
discount = models.IntegerField(default=0)
def __str__(self):
return '%s %s %s' % (self.name, self.category_id, self.price)
def save(self, force_insert=False, force_update=False, *args, **kwargs):
# you can add this for only existing model object
if self.pk:
# You can check if only 'price' field changed
_original_food = Food.objects.get(id=self.pk)
if _original_food.price != self.price:
# do somthing here like saving history...
History.objects.create(food_id=self.id, food_price=self.price)
super(Food, self).save(force_insert, force_update, *args, **kwargs)
super(Food, self).save(force_insert, force_update, *args, **kwargs)
This is just example. You can add & modify your code. I hope it helps you.
I have a simple django model with an integer field status:
from django.db import models
class MyObject(models.Model):
dateDownloaded = models.DateField(null=True)
dateShared = models.DateTimeField(null=False)
sharedBy = models.CharField(max_length=100, null=False)
sharedTo = models.CharField(max_length=100, null=False)
token = models.CharField(max_length=200, null=False)
status = models.IntegerField(null=False, default=0)
def save(self, *args, **kwargs):
if not self.pk: # First time saved
self.status = 0
self.token = get_random_string(length=32)
super(MyObject, self).save(*args, **kwargs)
I can add objects to my model and I have a simple helper that counts the number of created objects. Now I also have a call that does the following:
def resetStatus(request):
myObjects = MyObject.objects.all()
for myObject in myObjects:
myObject.status = 0
myObject.save()
return HttpResponse("reset done")
Issue is, after calling this, from time to time all the objects from my database have disappeared. Maybe I have done something wrong with my objects in between but I have no idea what it could be. How can I go about debugging this ?
How to set default charfield in lowercase?
This is my model:
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=64)
name = models.CharField(max_length=200)
phone = models.CharField(max_length=20)
email = models.CharField(max_length=200)
def __init__(self, *args, **kwargs):
self.username = self.username.lower()
I tried the __init__ but it doesn't work. I want to make the username in lowercase every time new record saved. Thanks.
While overwriting save() method is a valid solution. I found it useful to deal with this on a Field level as opposed to the Model level by overwriting get_prep_value() method.
This way if you ever want to reuse this field in a different model, you can adopt the same consistent strategy. Also the logic is separated from the save method, which you may also want to overwrite for different purposes.
For this case you would do this:
class NameField(models.CharField):
def get_prep_value(self, value):
return str(value).lower()
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=64)
name = NameField(max_length=200)
phone = models.CharField(max_length=20)
email = models.CharField(max_length=200)
Just do it in the save method. ie, override the save method of Model class.
def save(self, *args, **kwargs):
self.username = self.username.lower()
return super(User, self).save(*args, **kwargs)
signals also works
from django.db.models.signals import pre_save
#receiver(pre_save, sender=YourModel)
def to_lower(sender, instance=None, **kwargs):
instance.text = instance.text.lower() if \
isinstance(instance.text, str) else ''
In my case I had a recipient_name field that I needed to make all lower case when it is stored on DB
class LowerField(models.CharField):
def get_prep_value(self, value):
return str(value).lower()
class Recipients(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='recipients', on_delete=models.CASCADE, )
recipient_account_number = models.IntegerField()
recipient_name = LowerField(max_length=30)
recipient_bank_name = models.CharField(max_length=30)
date = models.DateTimeField(auto_now=True, verbose_name='Transaction Date')
class Meta:
ordering = ['-date']
def __str__(self):
return self.recipient_name
def get_absolute_url(self):
return reverse('recipient-detail', kwargs={'pk': self.pk})
Similarly, you can apply to another table called Transactions in your app, like this
class Transactions(models.Model):
transaction_type = (
('transfer', 'Transfer'),
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='transactions', on_delete=models.CASCADE, )
bank_name = LowerField(max_length=50)
def save(self, force_insert=False, force_update=False):
self.YourFildName = self.YourFildName.upper()
super(YourFomrName, self).save(force_insert, force_update)