How to auto-generate value from another entity on the same table - python

I have a table that has name and amount entity, and I want to happen is:
this is the sample of my table on models.py:
class Test(models.Model):
test_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50)
amount = models.IntegerField()
timestamp = models.DateTimeField()
if entered amount is 300, the name will be 'hello', else if the entered amount is 500, the name will be 'world'. please help, been trying to figure this out since yesterday, I'm really newbie on python and django.

Create a custom save() method sth like that :
class Test(models.Model):
test_id = models.IntegerField(primary_key=True)
amount = models.IntegerField()
name = models.CharField(max_length=50)
timestamp = models.DateTimeField()
def save(self, *args, **kwargs):
self.name = 'hello' if self.amount == 300 else 'world'
super(Test, self).save(*args, **kwargs)

Either you can handle this on model's custom save method or on serializer level (ModelSerializer) if you're using rest apis. The recommended way is to handle these kind of data manipulation in your model's serializer. Use a custom create/update method in your serializer (The logic on both solutions remains the same):
class TestSerializer(serializers.ModelSerializer):
amount = serilaizers.IntegerField()
name = serilaizers.CharField()
class Meta:
model = Test
fields = [
'test_id',
'amount',
'name',
'timestamp',
]
def create(self, validated_data):
if validated_data['amount'] > 1200:
validated_data['name'] = 'dry'
elif validated_data['amount'] == 0:
validated_data['name'] = 'wet'
elif 900 <= validated_data['amount'] <= 1200:
validated_data['name'] = 'light'
elif 500 <= validated_data['amount'] < 900:
validated_data['name'] = 'medium'
elif 0 < validated_data['amount'] < 500:
validated_data['name'] = 'heavy'
return Test.objects.create(**validated_data)

Related

How to compare two serializer field and show whichever is higher in django rest

I have product serializer which return category_offer_price & product_offer_price,
before getting this response I want to compare both price and only return whichever is highest price.
#Serilaizer.py
class ProductSerializer(ModelSerializer):
category = CategorySerializer()
product_offer_price = SerializerMethodField()
category_offer_price = SerializerMethodField()
class Meta:
model = Products
fields = [
"id",
"product_name",
"slug",
"category",
"description",
"category_offer_price",
"product_offer_price",
"base_price",
"stock",
"is_available",
"created_date",
"images",
"images_two",
"images_three",
]
def get_product_offer_price(self, obj):
try:
product_offer = ProductOffer.objects.get(product=obj)
if product_offer.is_active:
offer_price = product_offer.product_offer_price()
return offer_price
except Exception:
pass
return None
def get_category_offer_price(self, obj):
try:
category_offer = CategoryOffer.objects.get(category=obj.category)
if category_offer.is_active:
offer_price = category_offer.category_offer_price(obj)
return offer_price
except Exception:
pass
return None
#Models.py
class Products(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
product_name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(max_length=100, unique=True)
description = models.TextField(max_length=500)
base_price = models.IntegerField()
images = models.ImageField(upload_to="photos/products")
images_two = models.ImageField(upload_to="photos/products")
images_three = models.ImageField(upload_to="photos/products")
stock = models.IntegerField()
is_available = models.BooleanField(default=True)
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Products"
def __str__(self):
return self.product_name
I'd like to know is it possible to compare serializer fields in a serializer class?
You can move into one method, to validate your field. Also, substitute your try:except with get-object-or-404 method and your serializer fields with all value since you are using everything, to have a much cleaner code.
from django.shortcuts import get_object_or_404
class ProductSerializer(ModelSerializer):
category = CategorySerializer()
price = SerializerMethodField()
class Meta:
model = Products
fields = '__all__'
def get_price(self, obj):
product_offer = get_object_or_404(ProductOffer, product=obj)
category_offer = get_object_or_404(CategoryOffer, category=obj.category)
if product_offer.is_active and category_offer.is_active:
if product_offer.product_offer_price() > category_offer.category_offer_price(obj):
return product_offer.product_offer_price()
else:
return category_offer.category_offer_price(obj)
elif product_offer.is_active and not category_offer.is_active:
return product_offer.product_offer_price()
elif category_offer.is_active and not product_offer.is_active:
return category_offer.category_offer_price(obj)
EDIT: As you can see I used the classic if/else in this solution, although since Python 3.10 you can use the Match case statement to substitute these conditions chain.
In case of objects do not exist:
class ProductSerializer(ModelSerializer):
category = CategorySerializer()
price = SerializerMethodField()
class Meta:
model = Products
fields = '__all__'
def get_price(self, obj):
try:
product_offer = ProductOffer.objects.filter(product=obj).first()
category_offer = CategoryOffer.objects.filter(category=obj.category).first()
if not product_offer and not category_offer:
return obj.base_price
elif not category_offer:
return product_offer.product_offer_price()
elif not product_offer:
return category_offer.category_offer_price(obj)
elif category_offer and product_offer:
if category_offer.is_active and not product_offer.is_active:
return category_offer.category_offer_price(obj)
elif product_offer.is_active and not category_offer.is_active:
return product_offer.product_offer_price()
elif category_offer.is_active and product_offer.is_active:
if category_offer.category_offer_price(obj) > product_offer.product_offer_price():
return category_offer.category_offer_price(obj)
else:
return product_offer.product_offer_price()
except:
return obj.base_price
Although, to be honest, if there could be no objects then the is_active field is redundant.
You can override to_representation()
Example:
class ProductSerializer(ModelSerializer):
category = CategorySerializer()
product_offer_price = SerializerMethodField()
category_offer_price = SerializerMethodField()
...
...
def to_representation(self, instance):
data = super().to_representation(instance)
# access required fields like this
product_offer_price = data['product_offer_price']
category_offer_price = data['category_offer_price']
# do calculations here and returning the desired field as `calculated_price`
if category_offer_price > product_offer_price:
data['calculated_price'] = category_offer_price
else:
data['calculated_price'] = product_offer_price
return data
Not sure it s what you want but you could use a field of type SerializerMethodField which allow you to add a computed field that you could call category_offer_higher_price. Its value is computed by a function that return the highest one. See following link : https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield

How to validate a django modelform if the form has some other fields

I have made a Django model form but the problem is in my logic I am using something else and now I want to figure out a way to validate it by either defining a Meta class and choosing the fields that I want to display to the user but of course this won't validate the form.
Now I want to know if there is a way to validate the form without touching the models and pass the data required for the logic and after take care of the information needed for the data of the model to be saved.
Here is the models:
from django.db import models
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class RoomCategory(models.Model):
name = models.CharField(max_length=59)
price = models.IntegerField()
beds = models.PositiveIntegerField()
capacity = models.PositiveIntegerField()
size = models.CharField(max_length=59)
def __str__(self):
return self.name
class Room(models.Model):
room_number = models.CharField(max_length=60)
room_category = models.ForeignKey(RoomCategory, on_delete=models.CASCADE)
def __str__(self):
return f"The room {self.room_number} {self.room_category} has a maximum of {self.room_category.capacity} person and cost {self.room_category.price}/night "
class Booking(models.Model):
customer = models.ForeignKey(User, on_delete=models.CASCADE)
room = models.ForeignKey(RoomCategory, on_delete=models.CASCADE)
check_in = models.DateField()
check_out = models.DateField()
adults = models.PositiveSmallIntegerField()
children = models.PositiveSmallIntegerField()
def __str__(self):
return f"{self.customer} has booked for {self.room} from {self.check_in} to {self.check_out}"
Here is the form:
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields = ['room', 'check_in', 'check_out', 'adults', 'children']
here is the views.py
data = form.cleaned_data
roomlist = Room.objects.filter(room_category__name=data['room'])
available_rooms = []
for room in roomlist:
if data['adults'] + data['children'] > room.room_category.capacity:
return HttpResponse(f'Sorry !! But this category of room cannot handle more than {room.room_category.capacity}')
else:
if check_availability(room.room_category.name, data['check_in'], data['check_out'], data['adults'], data['children']):
available_rooms.append(room)
if len(available_rooms) > 0:
room = available_rooms[0]
new_booking = Booking.objects.create(
customer=self.request.user,
room=room,
check_in=data['check_in'],
check_out=data['check_out'],
adults=data['adults'],
children=data['children']
)
new_booking.save()
return HttpResponse(new_booking)
else:
return HttpResponse('All the rooms of this type are not available')
It is not printing the data means that the form is not valid and it fall down to the else statement.
You can validate any field in the form by writing a method in this way : def clean_(field_name) i.e def clean_room(self) read more:
https://docs.djangoproject.com/en/3.1/ref/forms/validation/#cleaning-a-specific-field-attribute

Django : Updating model by overriding save method

This is my first model
from django.db import models
from django.contrib.auth.models import User
CHOICES = (('Earned Leave','Earned Leave'),('Casual Leave','Casual Leave'),('Sick Leave','Sick Leave'),('Paid Leave','Paid Leave'))
STATUS_CHOICES = (('0', 'Rejected'),('1', 'Accepted'),)
class Leave(models.Model):
employee_ID = models.CharField(max_length = 20)
name = models.CharField(max_length = 50)
user = models.ForeignKey(User, on_delete = models.CASCADE, null =True)
type_of_leave = models.CharField(max_length = 15, choices = CHOICES)
from_date = models.DateField()
to_date = models.DateField()
status = models.CharField(max_length = 15, choices = STATUS_CHOICES)
#property
def date_diff(self):
return (self.to_date - self.from_date).days
This is my second model
class History(models.Model):
first_name = models.CharField(max_length = 50)
last_name = models.CharField(max_length = 50)
employee_ID = models.CharField(max_length = 20)
earned_leave = models.IntegerField()
casual_leave = models.IntegerField()
sick_leave = models.IntegerField()
paid_leave =models.IntegerField()
Here upon saving the first model Leave, I have written to override save method like,
def save(self, *args, **kwargs):
super(Leave, self).save()
if self.employee_ID == History.employee_ID:
if self.status == '1':
if self.type_of_leave == 'Earned Leave':
history = History.objects.update(
earned_leave = self.date_diff,
)
But upon saving the first model, all the entries in the History model are getting updated. Where in the history table every user have a separate entry with user's details(first_name, last_name, employee_ID) and default values as 10 for the rest. Upon saving the Leave model only the entry that is associated with the employee_ID of Leave model should be updated in the History model. For that purpose i have given as if self.employee_ID == History.employee_ID: but it isn't working.
I've even tried as -
def save(self, *args, **kwargs):
super(Leave, self).save()
if self.employee_ID == History.employee_ID:
if self.status == '1':
if self.type_of_leave == 'Earned Leave':
history = History.objects.update(
earned_leave = History.earned_leave - self.date_diff,
)
But this is not working, nothing gets updated and get'a an error unsupported operand types
So, the base of the project is employee-leave-management. As the user applies for the leave and is accepted the number of days left should get updated in History table or model.
If there's any alternate method, share.
History.objects.update(...) does in fact update all the History objects. You should specify which ones you want to update in your query:
from django.db.models import F
def save(self, *args, **kwargs):
super(Leave, self).save()
if self.status == '1':
if self.type_of_leave == 'Earned Leave':
history = History.objects.filter(employee_id=self.employee_id).update(
earned_leave = F('earned_leave') - self.date_diff,
)
The F() expression here refers to the value of the column and will be computed by the database rather than in Python.

Multiple django model values depend on choice field value

My Django model has a pricing_plan choice field. There are 12 fields whose value depends on the pricing_plan value.
class Organisation(models.Model):
PRICING_CHOICES = (
('basic', 'Basic'),
('team', 'Team'),
('super', 'Super')
)
# some fields...
pricing_plan = models.CharField(max_length=50, default='basic')
max_host_accounts = models.IntegerField(default=1)
max_admin_accounts = models.IntegerField(default=1)
max_guests = models.IntegerField(default=10)
# more fields whose value depends on pricing_plan value
For every different pricing_plan these fields get specific values. In code that could be described like:
if pricing_plan == 'basic':
max_host_accounts = 1
max_admin_accounts = 1
max_guests = 10
...
elif pricing_plan == 'team':
max_host_accounts = 10
max_admin_accounts = 3
max_guests = 25
...
However, there might be more pricing plans in the future and more options and I am afraid that an if/elif/else statement will be huge and not-easily readable.
What would be the best/idiomatic way to implement that in a Django model?
Use more CHOICE tuples with constant values for every pricing plan?
Use Enum classes with constant values for every pricing plan?
Use Organisation as parent class and create subclasses
like:
.
class BasicOrganisation(Organisation):
max_host_accounts = models.IntegerField(default=1)
max_admin_accounts = models.IntegerField(default=1)
max_guests = models.IntegerField(default=10)
class TeamOrganisation(Organisation):
max_host_accounts = models.IntegerField(default=10)
max_admin_accounts = models.IntegerField(default=3)
max_guests = models.IntegerField(default=25)
Anything else?
I would do it like that (I've used django-choices package for the pseudo-Enum emulation):
from django.db import models
from djchoices import ChoiceItem, DjangoChoices
def get_max_admin_accounts(pricing_plan):
if pricing_plan == Organization.Pricing.BASIC:
return 1
# other code
class Organization(models.Model):
class Pricing(DjangoChoices):
BASIC = ChoiceItem('basic', 'Basic')
TEAM = ChoiceItem('team', 'Team')
SUPER = ChoiceItem('super', 'Super')
pricing_plan = models.CharField(max_length=50, default=Pricing.BASIC)
max_host_accounts = models.IntegerField()
max_admin_accounts = models.IntegerField()
max_guests = models.IntegerField()
def save(self, **kwargs):
if not self.max_admin_accounts:
self.max_admin_accounts = get_max_admin_accounts(self.pricing_plan)
# other fields filled
super().save(**kwargs)
I would do like
class Organisation(models.Model):
PRICING_CHOICES = {
"basic": ("Basic", (1, 1, 10)),
"team": ("Team", (10, 3, 25)),
}
# some fields...
pricing_plan = models.CharField(choices=tuple([(i,j[0]) for i, j in PRICING_CHOICES.items()]), max_length=50, default='basic')
#other fields
def set_plan(self, max_host_accounts, max_admin_accounts, max_guests):
self.max_host_accounts = max_host_accounts
self.max_admin_accounts = max_admin_accounts
self.max_guests = max_guests
def save(self, *args, **kwargs):
if getattr(self, "plan_changed", ""): #you need to set this attribute whenever updating a plan. like model_obj.plan_changed = True
#otherwise you need to check db whether plan is changed or not.
self.set_plan(*self.PRICING_CHOICES[self.pricing_plan][1])
super(Organisation, self).save(*args, **kwargs)

Updata data based on the status django model

I am currently creating a model and one of the field is the percentage. I wanted to update the percentage field depending on the status.
this is my models.py
class Contact(models.Model):
STATUS = (
('NY','Not Yet'),
('RN','Running'),
('CO','Completed'),
)
status = models.CharField(max_length=2, choices=STATUS, default='NY')
percentage = models.FloatField()
if status == 'CO':
percentage = 100
else:
percentage = 0
and this does not work or does not save.
If you're just using the percentage field for display purposes, you don't need to make it a real field. I would do it like this:
class Contact(models.Model):
STATUS = (
('NY','Not Yet'),
('RN','Running'),
('CO','Completed'),
)
status = models.CharField(max_length=2, choices=STATUS, default='NY')
#property
def percentage(self):
if self.status == 'CO':
return 100
else:
return 0
But if you really do want it to be a field, you could do something like what Kapil Sachdev's answer has.
Override Model's save method and put your logic in there.
class Contact(models.Model):
STATUS = (
('NY','Not Yet'),
('RN','Running'),
('CO','Completed'),
)
status = models.CharField(max_length=2, choices=STATUS, default='NY')
percentage = models.FloatField()
def save(self, *args, **kwargs):
if self.status == 'CO':
self.percentage = 100
else:
self.percentage = 0
super(Contact, self).save(*args, **kwargs) # Call the "real" save() method.

Categories