Here I have a simple function for sending leave request and accepting by the admin.This code works for now but I want to add some feature here.For example if the user enter day = 2 which is IntegerField then it get stores into databse then after the leave has been accepted by the function below def accept_leave(request,pk): I want to display the remaining days of leave(Example:1 day 12 hours and 30 sec. remaining to complete leave ).After 2 days completed it should display some message like you leave has been completed.
I got no idea for starting this .How can I do it ?Any help would be great.
Is there any mistake in my approach ?
EDIT: Now I removed the day(Integer Field) and added start_day and end_day as DateTimeField. Now how can I display the remaining days and time of leave after the leave is accepted ?
models.py
class Leave(models.Model):
staff = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,related_name='staff_leave')
organization = models.ForeignKey(Organization,on_delete=models.CASCADE,related_name='staff_leave')
sub = models.CharField(max_length=300)
msg = models.TextField()
start_day = models.DateTimeField()
end_day = models.DateTimeField()
#day = models.IntegerField(default=0)
is_accepted = models.BooleanField(default=False)
is_rejected = models.BooleanField(default=False)
sent_on = models.DateTimeField(auto_now_add=True)
views.py
def send_leave_request(request):
form = MakeLeaveForm()
if request.method == 'POST':
form = MakeLeaveForm(request.POST)
if form.is_valid():
leave_days = form.cleaned_data['day']
org = form.cleaned_data['organization']
start_day = form.cleaned_data['start_day']
end_day = form.cleaned_data['end_day']
diff = end_day - start_day
leave_days = diff.days
print('org',org)
if leave_days > request.user.staff.organization.max_leave_days:
messages.error(request,'Sorry can not be sent.Your leave days is greater than {}.'.format(request.user.staff.organization.max_leave_days))
return redirect('organization:view_leaves')
else:
leave = form.save(commit=False)
leave.staff = request.user
leave.organization = org
leave.save()
return redirect('organization:view_leaves')
return render(request,'organization/send_leaves.html',{'form':form})
def accept_leave(request,pk):
leave = get_object_or_404(Leave, pk=pk)
leave.is_accepted = True
leave.is_rejected = False
leave.day = ?
leave.save()
return redirect('organization:leave_detail',leave.pk)
For your leave request, why don't you store something like :
start_date and end_date
The idea is (ideally) to store only things you can't compute.
Then you can make a python property that computes the number of days between start_date and end_date (a "fget" would be enough for the property). A python property won't be stored in your database but it's not a big deal because you can compute it ! So you don't have to store it.
days = property(fget=_get_days, doc="type: Integer")
That means whenever the "days" attribute of an object "Leave" is accessed, the function "_get_days" is called to retrieve what you want.
If self represents a Leave object and you do print(self.days) it will print the result of _get_days finally.
The "doc" part is just here to indicate your property returns an Integer. It is not mandatory but a good practice in order not to forget it.
Then you must write that method "_get_days" (it must be above your property definition or Python won't know what is "_get_days"
def _get_days(self):
return self.end_date - self.start_date
(something like that, that you convert into an int somehow)
Moreover, for your additional functionality, you must know how much leaves your user can have. Just store that on the user, on your user team or whatever you want.
Then to check if the user has remaining leaves he can take, just browse a queryset with all his accepted leaves and use the property mentioned above.
Then you substract the result to the total number of leaves the user can take.
Related
How do I check if a time is in the range of two timefields?
#appointmentApp.models Appointment
#Appointments
class Appointment(models.Model):
...
date_selected = models.DateField(blank=True, default='2001-12-1')
time_start = models.TimeField(blank=True)
total_time = models.IntegerField(blank=False, null=False, default=0)
end_time = models.TimeField(blank=True, null=True)
appointment_accepted = models.BooleanField(blank=False, default=False)
Total_time and end_time are calculated after the object is created because it requires an attribute from a many to many field
In my views where I want to check if there is an object that exists
#appointmentApp.views def appointment_view
def appointment_view(request):
...
#form is submitted
else:
form = AppointmentForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
date = cd.get('date_selected') #this is a datetime field
start = cd.get('time_start')
#appointments = Appointment.objects.filter(date_selected=date, start_time__range=())
#form needs to save first because total time and end time don't have values until after the object is saved
form.save()
new_appointment = Appointment.objects.filter(date_selected=date, time_start=start, appointment_accepted=False)
for apps in new_appointment:
new_app_starttime = apps.time_start
new_app_endtime = apps.end_time
appointments = Appointment.objects.filter(date_selected=date, appointment_accepted=True)
#if the start time
#my question is here
for app in appointments:
if app.time_start__range(new_app_starttime, new_app_endtime):
print('another appointment begins/ends during this time slot!')
return render(request, 'home.html')
How can it check if its time falls in between two timefields?
Edit:
Been looking around these question have been a bit helpful
Django filter when current time falls between two TimeField values
Django After Midnight Business Hours TimeField Comparison Error
the current method I am trying is
#appointmentApp.views appointment_view
if Appointment.objects.filter(date_selected=date, time_start__lte=F('end_time')).exists():
print('Y E S')
Appointment.objects.filter(date_selected=date, time_start=start, appointment_accepted=False).delete()
This line hasn't returned what I am looking for and Im not sure where Im going wrong with it
Its not a pretty solution but at the end of the day it works
this answer solved my question
https://stackoverflow.com/a/65572546/10485812
#gets the appointment that is being checked
n_a = Appointment.objects.get(date_selected=date, time_start=start, appointment_accepted=False)
#filters for all appointments that have been accepted and share the same date as n_a
appointments = Appointment.objects.filter(date_selected=date, appointment_accepted=True)
#checks each appointment on that day and sees if the times overlap
for apps in appointments:
if (apps.time_start < n_a.time_start < apps.end_time) or (apps.time_start < n_a.end_time < apps.end_time) or (n_a.time_start <= apps.time_start and n_a.end_time >= apps.end_time):
I have the following method in one of my model classes. It's designed to let my views know that an JournalEntry can be edited. It should return true if the entry is not more than 90 days old and does not have any related LineItems that have a value in the LineItems reconciled_date field. Ie all the related LineItems must have NULL in their reconciled_date field.
The method works but it iterates thru the LineItems which seems very inefficient. Is there a better way?
Models.py
class JournalEntry(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False, blank=False)
date = models.DateField(null=False, blank=False)
def can_edit(self):
"""Checks if logged user can edit"""
is_reconciled = 0
for lineitem in self.lineitem_set.all():
if lineitem.reconciliation_date != None:
is_reconciled = 1
if (datetime.date.today() < (self.date + datetime.timedelta(90))) and is_reconciled == 0:
return True
else:
return False
Thank you
You can perform a single query that determines if the JournalEntry has any related line items that have a non-null reconciliation_date. It could be handy to have this in a property if you would like to re-use it in other methods
#property
def is_reconciled(self):
"""Returns True if any related line items have a non-null reconciliation_date"""
return self.lineitem_set.filter(reconciliation_date__isnull=False).exists()
Then you can use this in your method
def can_edit(self):
"""Checks if logged user can edit"""
# First check if the entry is less than 90 days old as it does not require
# a call to the DB
if self.date > datetime.date.today() - datetime.timedelta(days=90):
# If the entry is less than 90 days old then we return the inverse of is_reconciled
return not self.is_reconciled
return False
I have a list of client records in my database. Every year, we generate a single work order for each client. Then, for each work order record, the user should be able to create a note that is specific to the work order. However, not all work orders need a note, just some.
Now, I can't simply add a note field to the work order because some times, we need to create the note before the work order is even generated. Sometimes this note is specific to a work order that won't happen for 2-3 years. Thus, the notes and the work order must be independent, although they will "find" each other when they both exist.
OK, so here's the situation. I want the user to be able to fill out a very simple note form, where they have two fields: noteYear and note. Thus, all they do is pick a year, and then write the note. The kicker is that the user should not be able to create two notes for the same year for the same client.
What I'm trying to get as is validating the note by ensuring that there isn't already a note for that year for that client. I'm assuming this would be achieved by a custom is_valid method within the form, but I can't figure out how to go about doing that.
This is what I tried so far (note that I know it's wrong, it doesn't work, but it's my attempt so far):
Note that systemID is my client record
My model:
class su_note(models.Model):
YEAR_CHOICES = (
('2013', 2013),
('2014', 2014),
('2015', 2015),
('2016', 2016),
('2017', 2017),
('2018', 2018),
('2019', 2019),
('2020', 2020),
('2021', 2021),
('2022', 2022),
('2023', 2023),
)
noteYear = models.CharField(choices = YEAR_CHOICES, max_length = 4, verbose_name = 'Relevant Year')
systemID = models.ForeignKey(System, verbose_name = 'System ID')
note = models.TextField(verbose_name = "Note")
def __unicode__(self):
return u'%s | %s | %s' % (self.systemID.systemID, self.noteYear, self.noteType)
And my form:
class SU_Note_Form(ModelForm):
class Meta:
model = su_note
fields = ('noteYear', 'noteType', 'note')
def is_valid(self):
valid = super (SU_Note_Form, self).is_valid()
#If it is not valid, we're done -- send it back to the user to correct errors
if not valid:
return valid
# now to check that there is only one record of SU for the system
sysID = self.cleaned_data['systemID']
sysID = sysID.systemID
snotes = su_note.objects.filter(noteYear = self.cleaned_data['noteYear'])
for s in snotes:
if s.systemID == self.systemID:
self._errors['Validation_Error'] = 'There is already a startup note for this year'
return False
return True
EDIT -- Here's my solution (thanks to janos for sending me in the right direction)
My final form looks like this:
class SU_Note_Form(ModelForm):
class Meta:
model = su_note
fields = ('systemID', 'noteYear', 'noteType', 'note')
def clean(self):
cleaned_data = super(SU_Note_Form, self).clean()
sysID = cleaned_data['systemID']
sysID = sysID.systemID
try:
s = su_note.objects.get(noteYear = cleaned_data['noteYear'], systemID__systemID = sysID)
print(s)
self.errors['noteYear'] = "There is already a note for this year."
except:
pass
return cleaned_data
For anyone else looking at this code, the only confusing part is the line that has: sysID = sysID.systemID. The systemID is actually a field of another model - even though systemID is also a field of this model -- poor design, probably, but it works.
See this page in the Django docs:
https://docs.djangoproject.com/en/dev/ref/forms/validation/
Since your validation logic depends on two fields (the year and the systemID), you need to implement this using a custom cleaning method on the form, for example:
def clean(self):
cleaned_data = super(SU_Note_Form, self).clean()
sysID = cleaned_data['systemID']
sysID = sysID.systemID
try:
su_note.objects.get(noteYear=cleaned_data['noteYear'], systemID=systemID)
raise forms.ValidationError('There is already a startup note for this year')
except su_note.DoesNotExist:
pass
# Always return the full collection of cleaned data.
return cleaned_data
This is a hard question for me to describe, but I will do my best here.
I have a model that is for a calendar event:
class Event(models.Model):
account = models.ForeignKey(Account, related_name="event_account")
location = models.ForeignKey(Location, related_name="event_location")
patient = models.ManyToManyField(Patient)
datetime_start = models.DateTimeField()
datetime_end = models.DateTimeField()
last_update = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True)
event_series = models.ForeignKey(EventSeries, related_name="event_series", null=True, blank=True)
is_original_event = models.BooleanField(default=True)
When this is saved I am overriding the save() method to check and see if the event_series (recurring events) is set. If it is, then I need to iteratively create another event object for each recurring date.
The following seems to work, though it may not be the best approach:
def save(self, *args, **kwargs):
if self.pk is None:
if self.event_series is not None and self.is_original_event is True :
recurrence_rules = EventSeries.objects.get(pk=self.event_series.pk)
rr_freq = DAILY
if recurrence_rules.frequency == "DAILY":
rr_freq = DAILY
elif recurrence_rules.frequency == "WEEKLY":
rr_freq = WEEKLY
elif recurrence_rules.frequency == "MONTHLY":
rr_freq = MONTHLY
elif recurrence_rules.frequency == "YEARLY":
rr_freq = YEARLY
rlist = list(rrule(rr_freq, count=recurrence_rules.recurrences, dtstart=self.datetime_start))
for revent in rlist:
evnt = Event.objects.create(account = self.account, location = self.location, datetime_start = revent, datetime_end = revent, is_original_event = False, event_series = self.event_series)
super(Event, evnt).save(*args, **kwargs)
super(Event, self).save(*args, **kwargs)
However, the real problem I am finding is that using this methodology and saving from the Admin forms, it is creating the recurring events, but if I try to get self.patient which is a M2M field, I keep getting this error:
'Event' instance needs to have a primary key value before a many-to-many relationship can be used
My main question is about this m2m error, but also if you have any feedback on the nested saving for recurring events, that would be great as well.
Thanks much!
If the code trying to access self.patient is in the save method and happens before the instance has been saved then it's clearly the expected behaviour. Remember that Model objects are just a thin (well...) wrapper over a SQL database... Also, even if you first save your new instance then try to access self.patient from the save method you'll still have an empty queryset since the m2m won't have been saved by the admin form yet.
IOW, if you have something to do that depends on m2m being set, you'll have to put it in a distinct method and ensure that method get called when appropriate
About your code snippet:
1/ the recurrence_rules = EventSeries.objects.get(pk=self.event_series.pk) is just redundant, since you alreay have the very same object under the name self.event_series
2/ there's no need to call save on the events you create with Event.objects.create - the ModelManager.create method really create an instance (that is: save it to the database).
Dear django gurus,
please grab my nose and stick it in where my silly mistake glows.
I was about to proceed some simple math operation based on existing field values and store it in a separate field (subtotal) of current instance.
My question is actually included in the very last comment of code.
class Element(models.Model):
name = models.CharField(max_length=128)
kids = models.ManyToManyField('self', null=True, blank=True, symmetrical=False)
price = models.IntegerField('unit price', null=True, blank=True)
amount = models.IntegerField('amount', null=True, blank=True)
subtotal = models.IntegerField(null=True, blank=True)
def counter(self):
output = 0
# Check if this is the lowest hierarchy level (where the intention is to
# store both values for price and amount) and proceed calculations
# based on input values. Also check if values are set to avoid operations
# with incompatible types.
# Else aggregate Sum() on subtotal values of all kids.
if self.kids.count() == 0:
if self.price == None or self.amount == None:
output = 0
else:
output = self.price * self.amount
else:
output = self.kids.aggregate(Sum('subtotal'))['subtotal__sum']
self.subtotal = output
def __unicode__(self):
return self.name
This is how my sample data look like (I am sorry if I am missing some convention of how to show it).
element.name = son
element.kids = # None
element.price = 100
element.amount = 1
element.subtotal = 100 # Calculates and stores correct value.
element.name = daughter
element.kids = # None
element.price = 200
element.amount = 5
element.subtotal = 1000 # Calculates and stores correct value.
element.name = father
element.kids = son, daughter
element.price = # None. Use this field for overriding the calculated
# price at this level.
element.amount = # None.
element.subtotal = 25 # Calculates completely irrelevant value.
# The result seems to be some previous state
# (first maybe) of subtotal of one of the kids.
# This is where my cache part of question comes from.
While solving this simple task I have started with clean() class, but the results were calculated after second save only (maybe a good topic for another question). I switched then to custom method. But now after a night spent on this I would admiteddly use Mr. Owl's words to Winnie the Pooh: "You sir are stuck!". At this point I am using only native django admin forms. Any help will be most appreciated.
Without seeing the code you are using to invoke all these calculations, I can only guess what the problem might be. It may be helpful to show your view code (or other), which 'kicks off' the calculations of subtotal.
There are two potential issues I can see here.
The first is in your counter method. Are you saving the model instance after calculating the total?
def counter(self):
output = 0
if self.kids.count() == 0:
if self.price == None or self.amount == None:
output = 0
else:
output = self.price * self.amount
else:
output = self.kids.aggregate(Sum('subtotal'))['subtotal__sum']
self.subtotal = output
self.save() # save the calculation
Without saving the instance, when you query the children from the parent, you will get the current database value rather than the newly calculated value. This may or may not be an issue, depending on the code that calls counter to begin with.
The other potential issue I see here is cache invalidation. If a child updates their price or amount, is the parent notified so it can also update its subtotal? You may be able to override the save method to do your calculations at the last minute.
def save(self, *args, **kwargs):
self.counter() # calculate subtotal
super(Element, self).save(*args, **kwargs) # Call the "real" save() method.
for parent in self.element_set.all(): # use a related name instead
parent.save() # force recalculation of each parent
You'll note that this will only force the correct values of subtotal to be valid after saving only. Be aware that overriding the save method in this way is directly contradictory to my first solution of saving the instance when calculating counter. If you use both, together, you will get a StackOverflow as counter is calculated then saved, then during saving counter is calculated, which will trigger another save. Bad news.