Django - How to randomly pick objects based on input from form? - django

I'm trying to build a basic workout planner, where you have to input in a form some info, and based on the info a personalized planner will be generated. I created a model where I inputted all the exercises, and which movement they are focusing on.
If the user choose to exercise 2x week, 2 days will be generated with 6 specific movements each day. Each movement has a number of exercises, but only one exercise will be randomly picked and showed on the planner. So you basically have 6 different exercises each day. If the user pick 3x week there will be 3 days with 6 other exercises, and so on.
I tried to find some way to randomly pick and generate those exercises, but I can't. I can't just use random, because I have the condition where each frequency picked on the form, has to follow a specific movement schedule.
So I decided to hard code it, and write each condition, line by line. But that will lead to many lines of code.
And that is not the only issue. By doing so, every time I reload the page, the exercises name change. It keeps randomly picking new exercises. Maybe I should have made the class Exercise in a different way, or I don't know. I think I' missing something and it could be much easier.
Here are my models:
class Generator(models.Model):
GOAL = (
('Strength', 'Strength'),
('Hypertophy', 'Hypertophy'),
('Endurance', 'Endurance')
)
FREQUENCY = (
('2X week', '2X week'),
('3X week', '3X week'),
('4X week', '4X week'),
('5X week', '5X week'),
('6X week', '6X week')
)
PERIOD = (
('10 weeks', '10 weeks'),
('12 weeks', '12 weeks')
)
goal = models.CharField(max_length=12, choices=GOAL)
frequency = models.CharField(max_length=12, choices=FREQUENCY)
period = models.CharField(max_length=12, choices=PERIOD, default='12 weeks')
objects = models.Manager()
def __str__(self):
return self.frequency
class Exercise(models.Model):
name = models.CharField(max_length=150)
movement = models.CharField(max_length=150)
Focus = models.CharField(max_length=150)
objects = models.Manager()
def __str__(self):
return self.name
And here are my view functions at the moment. I only did a small example of what my idea would be to code each exercise to each day (list_1 function). If I follow that idea, only the '6x week' would have 36 lines of code alone.
def create(request):
generator = GeneratorForm()
if request.method == 'POST':
generator = GeneratorForm(request.POST)
if generator.is_valid():
generator.save()
return redirect('/planner/')
context = {'generator': generator}
return render(request, 'planner/create.html', context)
def list_1(request):
generator = Generator.objects.last()
if generator.frequency == '2X week':
days = Day.objects.filter(id__in=[1, 2])
exercises_1 = Exercise.objects.filter(movement='DeadLift Pattern').order_by['?']
exercises_2 = Exercise.objects.filter(movement='Horizontal Pushing').order_by['?']
exercises = [exercises_1, exercises_2]
if generator.frequency == '3X week':
days = Day.objects.filter(id__range=(1, 3))
if generator.frequency == '4X week':
days = Day.objects.filter(id__range=(1, 4))
if generator.frequency == '5X week':
days = Day.objects.filter(id__range=(1, 5))
if generator.frequency == '6X week':
days = Day.objects.filter(id__range=(1, 6))
if generator.period == '10 weeks':
weeks = Week.objects.exclude(id__in=[11, 12])
elif generator.period == '12 weeks':
weeks = Week.objects.all()
context = {'exercises': exercises, 'days': days, 'weeks': weeks}
return render(request, 'planner/list_1.html', context)
Maybe I should make the model classes in some other way. Perhaps I need a model class for each frequency (2x wee, 3x week, etc.). I feel like there should be an easier way that I'm just missing it.
Even if the only way is to code every line like this, how to solve the issue that it picks new exercises every time I reload the page?

Related

How could you make this really reaaally complicated raw SQL query with django's ORM?

Good day, everyone. Hope you're doing well. I'm a Django newbie, trying to learn the basics of RESTful development while helping in a small app project. Currently, there's a really difficult query that I must do to create a calculated field that updates my student's status accordingly to the time interval the classes are in. First, let me explain the models:
class StudentReport(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE,)
headroom_teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE,)
upload = models.ForeignKey(Upload, on_delete=models.CASCADE, related_name='reports', blank=True, null=True,)
exams_date = models.DateTimeField(null=True, blank=True)
#Other fields that don't matter
class ExamCycle(models.Model):
student = models.ForeignKey(student, on_delete=models.CASCADE,)
headroom_teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE,)
#Other fields that don't matter
class RecommendedClasses(models.Model):
report = models.ForeignKey(Report, on_delete=models.CASCADE,)
range_start = models.DateField(null=True)
range_end = models.DateField(null=True)
# Other fields that don't matter
class StudentStatus(models.TextChoices):
enrolled = 'enrolled' #started class
anxious_for_exams = 'anxious_for_exams'
sticked_with_it = 'sticked_with_it' #already passed one cycle
So this app will help the management of a Cram school. We first do an initial report of the student and its best/worst subjects in StudentReport. Then a RecommendedClasses object is created that tells him which clases he should enroll in. Finally, we have a cycle of exams (let's say 4 times a year). After he completes each exam, another report is created and he can be recommended a new class or to move on the next level of its previous class.
I'll use the choices in StudentStatus to calculate an annotated field that I will call status on my RecommendedClasses report model. I'm having issues with the sticked_with_it status because it's a query that it's done after one cycle is completed and two reports have been made (Two because this query must be done in StudentStatus, after 2nd Report is created). A 'sticked_with_it' student has a report created after exams_date where RecommendedClasses was created and the future exams_date time value falls within the 30 days before range_start and 60 days after the range_end values of the recommendation (Don't question this, it's just the way the higherups want the status)
I have already come up with two ways to do it, but one is with a RAW SQL query and the other is waaay to complicated and slow. Here it is:
SELECT rec.id AS rec_id FROM
school_recommendedclasses rec LEFT JOIN
school_report original_report
ON rec.report_id = original_report.id
AND rec.teacher_id = original_report.teacher_id
JOIN reports_report2 future_report
ON future_report.exams_date > original_report.exams_date
AND future_report.student_id = original_report.student_id
AND future_report.`exams_date` > (rec.`range_start` - INTERVAL 30 DAY)
AND future_report.`exams_date` <
(rec.`range_end` + INTERVAL 60 DAY)
AND original_report.student_id = future_report.student_id
How can I transfer this to a proper DJANGO ORM that is not so painfully unoptimized? I'll show you the other way in the comments.
FWIW, I find this easier to read, but there's very little wrong with your query.
Transforming this to your ORM should be straightforward, and any further optimisations are down to indexes...
SELECT r.id rec_id
FROM reports_recommendation r
JOIN reports_report2 o
ON o.id = r.report_id
AND o.provider_id = r.provider_id
JOIN reports_report2 f
ON f.initial_exam_date > o.initial_exam_date
AND f.patient_id = o.patient_id
AND f.initial_exam_date > r.range_start - INTERVAL 30 DAY
AND f.initial_exam_date < r.range_end + INTERVAL 60 DAY
AND f.provider_id = o.provider_id

How to display remaining days and hours in django from DateTimeField?

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.

Using property in Django

I need help with the following situation.
My app has the following models:
class Person(models.Model):
person_sequential_nr = models.IntegerField(primary_key=true)
person_id = models.CharField(max_length=10)
person_name = models.CharField(max_length=200)
year = models.CharField(max_length=4)
#property
def _summarize_goods(self):
return self.goods_set.all().aggregate(Sum('good_value')).values()
patrimony = property(_summarize_goods)
class Goods:
person_sequential_nr = models.Foreignkey()
good_description = models.CharField(max_length=200)
good_value = models.DecimalField(max_digits=12, decimal_places=2)
year = models.CharField(max_length=4)
Year is a string like 2012, 2010, 2008, etc
person_sequential_nr is specific (different) for each year.
person_id is the same for all years.
The intention of _summarize_goods is to totalize all goods of a person in a specific year.
1) How can I get the top ten people with the highest patrimonies?
When I call Person.patrimony it says "TypeError: 'property' object is not callable"
Person._summarize_goods works, but I have no idea how to order it.
2) How can I calculate the patrimony variation from of a person from one year to another (in the past)?
I would like to have something like: variation = (patrimony(year='2012')/patrimony(year='2010') - 1) * 100
I suppose that variation should be also a property, because I would like to use it to order some records.
An additional problem is the the person data may exist in 2012, but may not exist in a year in the past (e.g. 2010). So I need to handle this situation.
3) How can I create a view to show the patrimony of a person?
self.goods_set.all().aggregate(Sum('good_value')) was returning a dictionary, so I added .values() to extract only the values of it, then I got a list.
But wen I use str(Person._summarize_goods) it seemns that I still have a list.
Because when I call:
all_people = Person.objects.all()
people_list = [[p.person_name, str(p._summarize_goods)] for p in all_people]
output = ','.join(people_list)
return HttpResponse(output)
It shows an error referring the line output =
TypeError at /
sequence item 0: expected string, list found
EDITING...
Find some answers:
After removing the decoration (thanks Daniel) Person.patrimony is working so:
1) How can I get the top ten people with the highest patrimonies?
This was solved with the code below:
def ten_highest_pat():
people_2012 = Person.objects.all().filter(year=2012)
return sorted(people_2012, key=lambda person: person.patrimony, reverse=True)[:10]
2) How can I calculate the patrimony variation from of a person from one year to another (in the past)?
I tried the code below, which works, but is too slow. So I would thank you if someone has a sugestion how can I improve it.
def _calc_pat_variation(self):
c = Person.objects.all().filter(person_id = self.person_id, year__lt=self.year).order_by('-year')
if c.count() >= 1 and c[0].patrimony != 0:
return ((self.patrimony / c[0].patrimony) - 1) * 100
else:
return 'Not available'
pat_variation = property(_calc_pat_variation)

How to create proper NEXT & PREV buttons for retrieving next & prev objects in Django

I would like to put NEXT & PREV links to my detail page to get easily to Previous or Next item. I dont want to do it with DATE or ID, because it is totally impractical.
I dont want to use:
Model.get_next_by_FOO(**kwargs)
Model.get_previous_by_FOO(**kwargs)
How can I setup a column with for example numbers and sort by that. If I do integers it will sort like:
1 12 13 14 2 21 24 3 etc....doesnt work either.
What would be the simplest way for the user in CMS specify order and it will sort it and then I can use the NEXT and PREV.
models.py
class Art(models.Model):
pub_date = models.DateTimeField('date published')
title = models.CharField(max_length=200)
title_url = models.SlugField(max_length=200)
image = models.ImageField(blank=True, null=True, upload_to='uploaded_images')
graphics = models.ImageField(upload_to='images/art/',blank=True)
def image_thumbnail(self):
return '<img width="160px" src="/media/%s"/>' % (self.graphics, self.graphics)
image_thumbnail.allow_tags = True
image_thumbnail.short_description = 'graphics'
description = models.TextField(blank=True)
def __unicode__(self):
return self.title
views.py
def artdetail(request, art_title_url):
art = Art.objects.all().filter(title_url=art_title_url)
return render_to_response('nart-detail.html', {'art':art)
How can I on this example make next & prev buttons in the template to get next ot previous object and if I got to the end it will hide the next button etc....
My major concern is to make it proper way maybe by sort (number column) I am thinking wrong, I would like to know the best way how to achieve this that user is happy with easy customization.
I hope this will be of some help:
http://www.dajaxproject.com/pagination/

django - weird results (cached?) obtained while storing calculated values in fields at model level

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.