Trying to do a calculation on django models - django

i have two integer fields that i want to divide to get the value of 3rd field.
#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
but the result in the database always shows null .

#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
#property will not save anything into your model field. It works like a method. You can call in in your template like a model field mypost.Pallets_Count.
If you want to put the result into a database field, you need to override save method. But this might not be necessary. Property most likely is enough.
class MyModel(models.Model):
# your other fields
fieldname = models.FloatField()
def save(self, *args, **kwargs):
self.fieldname = self.CASES/self.CasesPerPallet
super(MyModel, self).save(*args, **kwargs)

Related

How does Django model save data that is cleaned?

I've seen bits and pieces of this, but am having trouble putting it all together. Let's say I have a model with a field:
class MyModel(models.Model):
my_field = models.CharField(...)
And I want to remove all of the "x's" from my_field and save it:
def clean(self):
x_less = self.my_field.replace('x', '')
How do I get x_less to the save method? or how does the cleaned data get saved?
def save(self, *args, **kwargs):
self.my_field = x_less #??????
super().save(*args, **kwargs)
If there is a good tutorial that I missed somewhere, please let me know. Thanks.
Update:
You have to return from your clean method:
def remove_x(self):
x_less = self.my_field.replace('x', '')
return x_less
And assign the returned value in the save method:
def save(self, *args, **kwargs):
self.my_field = self.remove_x()
super().save(*args, **kwargs)
Old answer, but still useful
Letting the user provide some input and later change that input without a previous warning it will result in bad user experience.
Following your example use case: If you don't want xs to be in your input, provide a validator for that field:
def x_validator(value):
if 'x' in value:
raise ValidationError(
"x character is no allowed.",
params={'value': value}
)
class MyModel(models.Model):
my_field = models.CharField(validators=[x_validator], ...)
Said that, if you want to modify a model instance just before or after being saved. Take a look in to pre_save and post_save signals.

Populating field values in django forms

I want to know the best way to assign a generated value for model field in a django form. Here is the code for what I need to implement the logic.
Model:
class Booking:
number = models.CharField(max_length=8)
category = models.CharField(max_length=2)
ref = models.CharField(max_length=10)
What I need to do is, store the combination of number + category in ref field when model is saved. I know there are two methods called save() and clean() available for this. But I'm not sure which one is the best to use.
Thanks.
You can do this with a custom save function in the Booking model.
class Booking(models.Model):
...
def save(self, *args, **kwargs):
self.ref = "%s%s" % (self.number, self.category)
super(Booking, self).save(*args, **kwargs)

How to auto fill SlugField instead of overriding save()?

Hello Awesome People
I wonder if there is a way to generate slug, rather than overriding the save() methods of my models.
Here's how I used to do:
def save(self, *args, **kwargs):
if self.pk is None:
self.slug = create_slug(self)
super(ModelName, self).save(*args, **kwargs)
create_slug is a function that generates the slug with slugify
def create_slug(instance,new_slug=None,field="name"):
''' long stuff to ensure the slug is unique '''
return slug
As django can do in Django Admin by automatically filling the slug field. Possible that we can simply add
an additional argument models.SlugField(field='name'). by adding this argument, django will make sure to take the value of field = name before saving to generate the slug without always overriding the save() method by copy/paste a lot of code to generate a slug for each model that We have.
I'm a little tired of copying this for all models, any hint will help!
Thank you in advance!
No, there is no such argument on model level. You can do it on admin level using prepopulated_fields:
class ArticleAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
On the model level you can write custom mixin class and use it as parent for all models:
class SlugMixin:
def create_slug(instance,new_slug=None,field="name"):
''' long stuff to ensure the slug is unique '''
return slug
def save(self, *args, **kwargs):
if self.pk is None:
self.slug = create_slug(self)
super().save(*args, **kwargs)
class SomeModel(SlugMixin, models.Model):
...
class OtherModel(SlugMixin, models.Model):
...
In this case you don't need to implement create_slug and override save in each models.

Before save model check some field

I need to check if other models already created, have a field filled .
If another model has the field with any value, the current model that attempts to create should not happen. And if possible send an error message.
This is my current code:
class Video(models.Model):
#####
# Fields of model
#####
def save(self, force_insert=False, force_update=False, *args, **kwargs):
some_video = Video.objects.all().filter(field_boolean=True).first()
if not some_video:
# Save current model
super(Video, self).save(force_insert, force_update, *args, **kwargs)
else:
# avoid save method for the current model created and send error message
What am I doing wrong or what I'm missing? What is the correct way to do this?
Firstly, you do not need to use all() and filter() together. Secondly, use exists() instead of first(). It returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible.
class Video(models.Model):
name = models.CharField(max_length=30)
field_boolean = models.BooleanField()
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if Video.objects.filter(field_boolean=True).exists():
print('Video with field_boolean=True exists')
else:
super(Video, self).save(*args, **kwargs)

Behaviour of Django's forms.ModelForm.save(): Why does models.save_instance not work via parent?

I am using class based views, but the forms have two underlying models, instead of one. So the "main" form (the Employee, the view knows about) has another form object.
The model of the Shift has a pkey, referencing one element of the Employees.
I would like to know, why this is not working:
class ShiftSubForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super( ShiftSubForm, self).__init__(*args, **kwargs)
... some adjustment of widgets, deleting and adding form fields
class Meta:
model=Shift
class EmployeeForm(forms.ModelForm):
second_form = None
def __init___(self, *args, **kwargs):
kwargs.update({'instance': kwargs['shift']})
del kwargs['shift']
self.second_form = ShiftSubForm(*args, **kwargs)
def save(self):
employee = super(ShiftSubForm, self).save()
self.second_form.cleaned_data.update({'employee': employee})
self.second_form.save()
class Meta:
model = Employee
I'd expect the save() in the Parents of ShiftSubForm to call models.save_instance(..) and to save the data. But it fails, with an integrity error because employee_id is Null. So the employee object didn't make it into the save.
But, if i call it directly, it works:
class ShiftSubForm(forms.ModelForm):
... as above ...
def save(self):
return models.save_instance(self, self.instance, self._meta.fields,
fail_message, True, exclude=self._meta.exclude)
What am i missing?
EDIT: Can't answer myself, so here ...
Think that this might be the best way?
class ShiftSubForm(forms.ModelForm):
... as above ...
def createInstanceWith(self,employee):
self.save(commit=False) # prepares self.instance
self.instance.employee = employee
class EmployeeForm(forms.ModelForm):
...
def save(self):
employee = super(ShiftSubForm, self).save()
self.second_form.full_clean() # populates its self.cleaned_data
self.second_form.createInstanceWith(employee)
self.second_form.save()
PS: Ignore typos - this is not the real code. But it has everything, that fumbles around with the forms
Don't update the cleaned_data. Call save() with commit=False, set the employee then save it to the db.
class EmployeeForm(forms.ModelForm):
second_form = None
def save(self):
employee = super(ShiftSubForm, self).save()
shift = self.second_form.save(commit=False)
shift.employee = employee
shift.save()
return employee
class Meta:
model = Employee