Cancel a save from save method in django models - django

So I have django model and I want to override save so that it only saves on certain instances. Is there a way to avoid a save from happening if a condition is met? The idea is if certain conditions defined with an if statement aren't met the instance fails to be saved. so for instance if there is not enough waiters we cancel the save, or if there is not enough tables we do the same.
Here's my code:
class Service(models.Model):
id = models.AutoField(primary_key=True)
arrival = models.DateTimeField(auto_now_add=True)
exit = models.DateTimeField(null=True, blank=True)
waiter = models.ForeignKey('Waiter', on_delete=models.CASCADE)
table = models.ForeignKey('Table', on_delete=models.CASCADE)
total_ammount= models.DecimalField(max_digits=15, decimal_places=2)
def save(self, *args, **kwargs):
if self.id == None:
time = datetime.datetime.now()
# check for waiters
waiters = Waiter.objects.select_related().annotate(num_Service=Count('service', filter=Q(service__exit__gt=time))).all()
available_waiters = waiters.filter(num_Service__lt=4)
avalable_waiters_length = len(available_waiters)
# check for tables
tables = Table.objects.select_related().annotate(num_Service=Count('service', filter=Q(service__exit__gt=time))).all()
available_tables = tables.filter(num_Service__lt=1)
avalable_tables_length = len(available_tables)
# return exception if a problem arises
if avalable_tables_length == 0 and avalable_waiters_length == 0:
print("not enough waiters or tables")
if avalable_waiters_length == 0:
print("not enough waiters")
return
if avalable_tables_length == 0:
print("not enough tables")
return
# assign waiter and table
waiter_obj = random.choice(available_waiters)
self.waiter = waiter_obj
table_obj = random.choice(available_tables)
self.table = table_obj
print(time.time())
# check if current time is open
if datetime.time(9,0) < time.time() and time.time()> datetime.time(21, 30):
print("The restaurant is closed")
return
print(time.time())
# add timedelta to init_time
if time.time() < datetime.time(17,0):
print(time + datetime.timedelta(minutes=90))
self.exit = time + datetime.timedelta(minutes=90)
if time.time() > datetime.time(17,0):
self.exit = time + datetime.timedelta(minutes=120)
#finalize pre_save
return super(Service, self).save(*args, **kwargs)
Thank you in advance :)

you should raise an error, like ValidationError for example

Related

Custom serializer.py Connection closed by server

What i am trying to do - is to save post to the model with custom conditions. But each time im trying to save - i receive this error django.db.utils.IntegrityError: null value in column violates not-null constraint
So far, i have tried editing my models, and adding null=True in parameters, but it doesn't seem to help. So what is the best way to solve the problem here? Also have to mention that it doesnt save event_code at all. But even if i will enter that code by hand - it still will give me "Connection closed by server." Also i dont know what this exactly means: Exception Location: /usr/local/lib/python3.11/site-packages/redis/asyncio/connection.py,
serialier.py:
class EventSerializers(serializers.ModelSerializer):
class Meta:
model = Events
fields = ["cam_id", "vehicle_plate", "is_recognition", "is_confirmation", "vehicle_plate", "transit_block_time_min"]
def save(self, **kwargs):
instance = super().save(**kwargs)
is_recognition = instance.is_recognition
is_confirmation = instance.is_confirmation
vehicle_plate = instance.vehicle_plate
if CarParkItem.objects.filter(vehicle_plate=vehicle_plate).exists():
instance.event_code = 1008
elif BlackListItem.objects.filter(vehicle_plate=vehicle_plate).exists():
instance.event_code = 1004
elif is_recognition == False:
instance.event_code = 1003
elif is_recognition == True and is_confirmation == False:
instance.event_code = 1006
elif is_recognition == True and is_confirmation == True:
instance.event_code = 1007
instance.save()
return instance
I have read about different approaches, but still dont know where and how to do it the best.
models that are used for it:
class BlackListItem(models.Model):
vehicle_plate = models.CharField(max_length=999, db_index=True)
valid_until = models.DateTimeField(db_index=True)
description = models.CharField(max_length=999)
def __str__(self):
return self.vehicle_plate
class CarParkItem(models.Model):
vehicle_plate = models.CharField(max_length=999, db_index=True)
valid_until = models.DateTimeField(db_index=True)
description = models.CharField(max_length=999)
def __str__(self):
return self.vehicle_plate
class WorkingMode(models.Model):
PASSMODE_CHOICES = [
('pay_by_hour', 'pay_by_hour'),
('pay_by_interval', 'pay_by_interval'),
('closed', 'closed'),
]
time_lte_hour = models.IntegerField() # <
time_gte_hour = models.IntegerField() # > от
time_lte_min = models.IntegerField() # < до
time_gte_min = models.IntegerField() # >
pass_mode = models.CharField(max_length=999, choices=PASSMODE_CHOICES) # pay_by_hour | pay_by_interval | closed
free_time_min = models.IntegerField(default=0)
interval = models.IntegerField(default=0)
transit_block_time_min = models.IntegerField(default=0, null=True)
price = models.IntegerField(default=0)
entry_fee = models.IntegerField(default=0)
description = models.CharField(max_length=999)
def __str__(self):
return self.pass_mode
and views.py:
class EventModeSet(viewsets.ModelViewSet):
queryset = Events.objects.all()
serializer_class = EventSerializers
it appears that the method def save should have been async as well, because my consumers.py code is also async. So i just added this:
async def save(self, **kwargs):
instance = super().save(**kwargs)
is_recognition = instance.is_recognition
is_confirmation = instance.is_confirmation
vehicle_plate = instance.vehicle_plate
if CarParkItem.objects.filter(vehicle_plate=vehicle_plate).exists():
instance.event_code = 1008
elif BlackListItem.objects.filter(vehicle_plate=vehicle_plate).exists():
instance.event_code = 1004
elif is_recognition == False:
instance.event_code = 1003
elif is_recognition == True and is_confirmation == False:
instance.event_code = 1006
elif is_recognition == True and is_confirmation == True:
instance.event_code = 1007
await asyncio.sleep(0)
instance.save()
As i understand asyncio.sleep(0) is letting other coroutines (from consumers.py) to finish their job before going to next sten instance.save. The fact that other coroutines were not awaited and were not finished - was causing disconnect issue

How to sort a query based on another model in Django

If I had:
class Assets(models.Model):
assetMake = models.CharField(max_length=50,blank=True,)
assetModel = models.CharField(max_length=50,blank=True,)
class Maintenance(models.Model):
assetID = models.ForeignKey(Assets, on_delete=models.CASCADE)
dateRequested = models.DateTimeField(null=True, blank=True)
dateCompleted = models.DateTimeField(null=True, blank=True)
complete = models.BooleanField(default = False)
I want to list all of the assets on a view, and sort them by the last time they were maintained (dateCompleted).
I have been trying to get this working below, but I feel like there is much easier way of accomplishing this, Any help is appreciated
class MaintenanceAgeList(LoginRequiredMixin,TemplateView):
template_name = 'maintenance_list.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
maintenanceList = []
assets = Assets.objects.filter(retire = False)
today = timezone.now().date()
for asset in assets:
maintenance = Maintenance.objects.filter(assetID_id = asset.id, complete =True,).order_by('-dateCompleted')
if maintenance.count() > 0:
asset['maintDate'] = (maintenance[0].dateCompleted).date()
maintenanceList.append(asset)
asset['days'] = (today - lastMaintenance[0].dateCompleted.date()).days
else:
asset['maintDate'] = today - timedelta(days=365)
asset['days'] = 365
maintenanceList.append(asset)
maintenanceList = maintenanceList.sort(key=lambda r: r.maintDate)
context['items'] = maintenanceList
return context
Try
from django.db.models import Max
Assets.objects.annotate(
last_modified=Max('maintenance__dateCompleted')
).order_by('-last_modified')
Note: Optionally, you can set the position of null values by nulls_first or nulls_last args.

Cannot save record from Django Model

I am making a change to a record in a sqlite3 database using Django, and the save() function doesn't seem to work.
I am able to save changes using the graphical admin app however.
Here is the code to the specific model I'm trying to do the save in:
def pickup(self,item):
room_items = Item.objects.filter(roomID = self.room.id)
items = [ri.item_name for ri in room_items]
if item in items:
i = Item.objects.filter(item_name = item)
i[0].roomID = 0
i[0].playerID = self.id
i[0].save()
return f'{self.name} picked up the {item} from {self.room}'
else:
return f"{item} is not in the room. can't pick it up."
the pickup function is in a class called Player. I am updating an Item record. Any solutions?
Here is the entire models file for those who want more context:
from django.db import models
import string,random
class Room(models.Model):
### Field Columns in Room Table ###
room_name = models.CharField(max_length = 64)
description = models.CharField(max_length=500, default=f"No Room description'" )
up = models.CharField(max_length = 64, default="")
down = models.CharField(max_length = 64, default="")
left = models.CharField(max_length = 64, default="")
right = models.CharField(max_length = 64, default="")
def items(self):
items = Item.objects.filter(roomID = self.id)
return [i.item_name for i in items]
def __str__(self):
return self.room_name
class Player(models.Model):
# uuid = models.UUIDField(default=uuid.uuid4, unique=True)
HP = models.IntegerField(default=10)
name = models.CharField(max_length=64, default=f"Room {random.choice(string.ascii_letters)}")#attempting to generate a random room name using ascii_letters from string library and random.choice()
room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True)
# inventory = models.ForeignKey(Inventory)
def inventory(self):
inventory = Item.objects.filter(playerID = self.room.id)
return [i.item_name for i in inventory]
def pickup(self,item):
print(self.room.id)
room_items = Item.objects.filter(roomID = self.room.id)
items = [ri.item_name for ri in room_items]
if item in items:
i = Item.objects.filter(item_name = item)
i[0].roomID = 0
i[0].playerID = self.id
i[0].save()
i[0].persist()
return f'{self.name} picked up the {item} from {self.room}'
else:
return f"{item} is not in the room. can't pick it up."
def drop_item(self,item):
pass
def initialize(self,start):
# start = input(f"{self.name}, you are outside the PyTower. It is a 10 story tower. There is a treasure chest on the top floor. Do you have what it takes to reach the top??? type 'y' to enter Pytower: ")
if start == 'y':
self.room = Room.objects.get(room_name = "Foyer")
print(f"{self.name}, you have now entered the {self.room.room_name}")
return f"{self.name}, you have now entered the {self.room.room_name}"
else:
print(f"{self.name}, when you're ready for Pytower, you may enter!")
return f"{self.name}, when you're ready for Pytower, you may enter!"
print(self.room.description)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
def move(self,way=""):
# print(self.room[way]) #causes error, Room object not subscriptable
# print(way)
# if self.room[way]:
# pass
if way == 'up':
if not self.room.up:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.up)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
elif way == 'down':
if not self.room.down:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.down)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
elif way == 'left':
if not self.room.left:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.left)
print('in room-', self.room, 'up-',self.room.up, 'down-',self.room.down, 'left-',self.room.left, 'right-', self.room.right)
return self.room
elif way == 'right':
if not self.room.right:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.right)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
else:
print('you have entered an invalid direction')
return 'you have entered an invalid direction'
def __str__(self):
if not self.room:
return f"{self.name} is outside."
else:
return f"{self.name} in {self.room}"
class Item(models.Model):
item_name = models.CharField(max_length=64)
strength = models.IntegerField(default=5)
item_type = models.CharField(max_length=64,default="weapon")
# playerID = models.ForeignKey(Player, on_delete=models.CASCADE, null=True)
# roomID = models.ForeignKey(Room, on_delete=models.CASCADE, null=True)
playerID = models.IntegerField(blank=True,null=True)
roomID = models.IntegerField(default=1,null=True,blank=True)
def persist(self):
self.save()
def __str__(self):
return self.item_name
To understand why your model isn't saving, you must first understand how querysets are evaluated. Essentially, anytime you iterate over them, or slice them, they will hit the database, however there are caveats to this.
Consider the following abstract example:
def MyModel(models.Model):
column = models.IntegerField()
>>> MyModel.objects.create(column=1)
<MyModel: MyModel object (1)>
>>> queryset = MyModel.objects.all()
Slicing:
>>> queryset[0].column = 2
>>> queryset[0].save()
>>> queryset[0].column
1
In the example above, I took a slice, eg. queryset[0], which hits the database, then immediately took a second slice, to try and save the changes made, which hits the database a second time. Finally, I took a third slice, which hits the database again.
Since the first slice is not the same object as the object I called .save() on, the changes are not reflected in the database. To fix this, simply save a reference to the slice as a variable:
>>> instance = queryset[0]
>>> instance.column = 2
>>> instance.save()
>>> instance.column
2
In this example, I only hit the database twice: once when I call instance = queryset[0], and a second time in instance.save().
Here is the optimized version of your code:
def pickup(self, item_name):
items = Item.objects.filter(item_name=item_name, roomID=self.room.id)
if items:
item = items[0]
item.roomID = 0
item.playerID = self.id
item.save()
return 'message'
return 'no item'

Django - Check if newer value exist for each item in queryset

I have a model that hold the users reactions and I have few types of reactions - 1,2,3,4.
The user can react few times with different values and I want to keep them in the DB.
This is my model (I'm using Generic Foreign Key, not sure if it matters).
class Reaction(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.SET_NULL, null=True)
date = models.DateTimeField("Reaction date", auto_now_add=True)
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'),
related_name="content_type_for_%(class)s", on_delete=models.CASCADE)
object_pk = models.TextField(_('object ID'))
root_obj = GenericForeignKey(ct_field="content_type", fk_field="object_pk")
react = models.CharField('Reaction', max_length=10)
I want to get all the the root_obj (distinct if possible) that the user reacted with specific reaction (e.g. 2), but not with a reaction of type 3,4 afterwards.
Is it possible?
Thanks
R
I hope I did this correctly. See if it works:
def view(request, object_pk):
query = Reaction.objects.filter(user=request.user, object_pk=object_pk) \
.only('react', 'date', 'root_obj')
try:
react2 = query.get(react='2')
except:
react2 = None
try:
react3 = query.get(react='3')
except:
react3 = None
try:
react4 = query.get(react='4')
except:
react4 = None
# Is there a reaction = 2 and not a reaction = 3 or 4?
if react2 and not react3 or not react4:
return react2.root_obj
# Is there a reaction = 3 or 4 along with 2?
elif react2 and react3 or react4:
if react3:
if react2.date < react3.date:
return react2.root_obj
return None
elif react4:
if react2.date < react4.date::
return react2.root_obj
return None
return None

correctly aggregating model objects and simple view calculation

if "allotted_pto" (paid time off) is an integer field (expressing number of days) in a UserProfile model:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fullname = models.CharField(max_length=64, unique=False)
company = models.CharField(max_length=50, choices=CLIENT_CHOICES)
...
allotted_pto = models.IntegerField(max_length=2, blank=True, null=True)
...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
and "total_days" returns an integer from a vacation request model:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
...
total_days = models.IntegerField(max_length=2, blank=True, null=True)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
fromdate = self.start_date
todate = self.return_date
daygenerator = (fromdate + timedelta(x + 1) for x in xrange((todate - fromdate).days))
self.total_days = sum(1 for day in daygenerator if day.weekday() < 5)
super(LeaveRequest, self).save(*args, **kwargs)
...
how can I construct a view that gives me the sum of "total_days" from a filter set of records and subtract that sum from the "allotted_pto" in the user profile? The simple view I wrote (see below) produces the number of "total_days" objects (in dictionary form) as opposed to counting the actual days, and the request for "allotted_pto" is apparently incorrectly constructed because it returns nothing at all...
#views.py
def leave_screen(request, id):
profile = UserProfile.objects.get(user=id)
records = LeaveRequest.objects.filter(employee=id)
agg_pto = LeaveRequest.objects.aggregate(Count('total_days'))
if profile.allotted_pto: #if the allotted_pto field in UserProfile is not empty
allotted_pto = profile.allotted_pto
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
return render_to_response("vacation/leave_request.html", {'records': records, 'agg_pto': agg_pto, 'remaining_pto': remaining_pto})
ok, figured out calculation:
def leave_screen(request, id):
...
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
just have to figure out how to pull the allotted_pto integer from the User Profile model.
ok, so this wasn't as difficult as I thought. The first challenge was to get an aggregate sum of objects. My first attempt was close but I should have just used "Sum" as opposed to "Count":
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
then I just used the python method for extracting the value from a dictionary:
agg_pto = agg_pto['total_days__sum']
finally:
def leave_screen(request, id):
user = request.user.id
profile = request.user.get_profile()
records = LeaveRequest.objects.filter(employee=id).order_by('-submit_date')
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
allotted_pto = profile.allotted_pto
if allotted_pto: #if the allotted_pto field in UserProfile is not empty
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
supervised_records = LeaveRequest.objects.filter(supervisor=id).order_by('-submit_date')
return render_to_response("vacation/leave_request.html", {'records': records, 'supervised_records': supervised_records, 'agg_pto': agg_pto, 'allotted_pto': allotted_pto, 'remaining_pto': remaining_pto, 'profile': profile })
I don't know why it was so hard for me to figure out the syntax for pulling objects from the UserProfile. But I do know that the django-debug-toolbar is very helpful.