group objects with same field value - python

I've got two models like these:
class Schedule(models.Model):
name = models.CharField(_('name'), blank=True, max_length=15)
class Day(models.Model):
DAYS_OF_THE_WEEK = (
(0, _('Monday')),
(1, _('Tuesday')),
(2, _('Wednesday')),
(3, _('Thursday')),
(4, _('Friday')),
(5, _('Saturday')),
(6, _('Sunday')),
)
schedule = models.ForeignKey(Schedule, blank=True, null=True, verbose_name=_('schedule'))
day = models.SmallIntegerField(_('day'), choices=DAYS_OF_THE_WEEK)
opening = models.TimeField(_('opening'), blank=True)
closing = models.TimeField(_('closing'), blank=True)
It's possible that a schedule can have two Day objects like so:
Day(schedule=1, day=0, opening=datetime.time(7, 30), closing=datetime.time(10, 30))
Day(schedule=1, day=0, opening=datetime.time(12, 30), closing=datetime.time(15, 30))
like different shifts on the same day.
If I iterate them now i'll get two entries of day 0, like so
[day for day in schedule]
[0, 0, 1, 2, 3, 4, 5, 6]
How can I create a queryset so it'll group same days together and keep their attributes?
[day for day in schedule]
[0 (two entries), 1, 3, 4, 5, 6]
Maybe something like
[id: [day], id: [day]]

The code I ended up using is this:
from itertools import groupby
day_set = store.schedule_set.all()[0].day_set.all()
schedule = dict()
for k, v in groupby(day_set, lambda x: x.day):
schedule[k] = list(v)
and sending schedule to the template for rendering, which works like a charm.

You can group them at template level, using {% regroup %} or {% for %}-loop with {% ifchanged %} tag.
In Python code use groupby.

Related

Django annotate + SUM how to get all entries

My models
class Machine(models.Model):
machineName = models.CharField(verbose_name="Machine Name", max_length=20, blank=False, null=False)
class SalesReport(models.Model):
machine = models.ForeignKey(Machine, on_delete=models.CASCADE, null=False, blank=False)
deviceDate = models.CharField(max_length=200, null=True, blank=True)
serverDate = models.DateTimeField(auto_now_add=True)
totalPrice = models.FloatField()
I have 3 machines, I wanted to get the total sales from each machines for the last 7 days.
my query is
from django.db.models import Sum, Value as V
from django.db.models.functions import Coalesce
SalesReport.objects.values("serverDate__date", "machine__machineName").annotate(
... sales=Coalesce(Sum("totalPrice"),V(0))).filter(
... serverDate__gte=week_start,
... serverDate__lte=week_end)
Which gives the following result,
[{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__1', 'sales': 15.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__1', 'sales': 145.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__2', 'sales': 270.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__3', 'sales': 255.0}]
What i am trying to get is
[{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__1', 'sales': 15.0},
{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__2', 'sales': 0.0},
{'serverDate__date': datetime.date(2020, 7, 22), 'machine__machineName': 'machine__3', 'sales': 0.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__1', 'sales': 145.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__2', 'sales': 270.0},
{'serverDate__date': datetime.date(2020, 7, 28), 'machine__machineName': 'machine__3', 'sales': 255.0}]
I am trying to do it with Coalesce, but i'm getting it wrong .
*I'm using mysql as db. a db specific query is also fine .
Since it is more SQL question I add a more specific answer
SELECT m.machineName, s.price
FROM machine m LEFT OUTER JOIN (
SELECT machine_id id, sum(totalPrice) price
FROM salesreport
WHERE serverDate BETWEEN DATE_SUB(curdate(), INTERVAL 1 WEEK) and curdate()
GROUP BY by machine_id) s on m.id = s.id
If you want the serverDate as outpout you have to apply an aggregate function (Max, Min) since it is located in your SalesReport table.
It depends what serverDate stands for. If it is the date when you bought the machine then it should be in machine table and it can be selected directly from machine table (and the WHERE BETWEEN clause must exist the sub-select and also apply on machine table). If it is a salesDate then it has to be in SalesReport and you must apply an aggregate function on it. ie: You can have potentially 7 dates over a week...
SELECT m.machineName, s.MaxserverDate, s.price
FROM machine m LEFT OUTER JOIN (
SELECT machine_id id, max(serverDate) MaxserverDate, sum(totalPrice) price
FROM salesreport
WHERE serverDate BETWEEN DATE_SUB(curdate(), INTERVAL 1 WEEK) and curdate()
GROUP BY by machine_id) s on m.id = s.id
The thing is that you don't have any sales for some dates. It is more a DB specific issue than a django ORM one. I would suggest to use raw sql with a left outer join on your machine table => take all the machine and list sales when present.
machine = Machine.objects.raw('''
SELECT machine.id, machine.name, sales.sid FROM app_machinelist as machine
LEFT JOIN (select sales_id as sid
from app_sales
where profile_id = {0}) sales
ON sales.sid = machine.id
ORDER BY machine.name ASC
'''.format(myuser.id))
This example works but for security reason, it is better to pass your parameters through a dictionary
machine = Machine.objects.raw(mysql, params)
Where
params = {'profile_id': pk, 'startdate': startdate, 'enddate': enddate}
mysql = '''
SELECT machine.id, machine.name, sales.sid FROM app_machinelist as machine
LEFT JOIN (select sales_id as sid
from app_sales
where profile_id = %(profile_id)s) sales
ON sales.sid = machine.id
ORDER BY machine.name ASC
'''

How to fix 'choices must be an iterable' error in django models?

I'm trying to migrate my app to the database, but the error does not allow me
Where I should make changes?
I have already looked through other Decisions. Nothing could help me
class Movie(models.Model):
NON_RATED = 0
RATED_G = 1
RATED_PG = 2
RATED_R = 3
RATINGS = (
(0, 'NR-not_rated'),
(1, 'G-General_Audiences'),
(2, 'PG-Parental_Guidances', 'Suggested'),
(3, 'R-Restricted')
)
rating = models.IntegerField(
choices=RATINGS,
default=0)
Choices attribute takes a list or tuple of 2 pairs. You cannot have a third value as you have.
class Movie(models.Model):
NON_RATED = 0
RATED_G = 1
RATED_PG = 2
RATED_R = 3
RATINGS = (
(0, 'NR-not_rated'),
(1, 'G-General_Audiences'),
(2, 'PG-Parental_Guidances', 'Suggested'), # you should remove Suggested here.
(3, 'R-Restricted')
)
rating = models.IntegerField(
choices=RATINGS,
default=0)
If you want another one you can try (2, ('PG-Parental_Guidances', 'Suggested')) but this will also give an error in some default values because of the internal structure.

Compare datetime with Django birthday objects

I have a question about my script. I want to know all people who have more than 16 years from my Database. I want to check this when user triggers the function.
I have this function :
def Recensement_array(request) :
date = datetime.now().year
print date # I get year from now
birthday = Identity.objects.values_list('birthday', flat=True) # Return list with all birthday values
for element in birthday :
if date - element < 117 :
print "ok < 117"
else :
print "ok > 117"
From print date I get :
2017
From print birthday I get :
<QuerySet [datetime.date(1991, 12, 23), datetime.date(1900, 9, 12), datetime.date(1900, 9, 12), datetime.date(1900, 9, 12), datetime.date(1900, 9, 12), datetime.date(1089, 9, 22), datetime.date(1900, 9, 12), datetime.date(1900, 9, 12), datetime.date(1089, 9, 22), datetime.date(1089, 9, 22), datetime.date(1089, 9, 22), datetime.date(1089, 9, 22), datetime.date(1990, 12, 12)]>
So my goal is to substract date with birthday and compare if date - birthday = 16 years, I print element, else nothing.
I get two problems :
How extract only year from birthday ?
Then the comparison method is between int and tuple up to now. If I could extract only year from birthday, it should work right ?
Thank you
EDIT :
For example I want to get all people who had 16 years old since the begining of this year or will get 16 years old before the first year :
def Recensement_array(request) :
today = datetime.now()
age_16 = (today - relativedelta(years=16))
result = Identity.objects.filter(birthday__range=[age_16, today]).order_by('lastname')
paginator = Paginator(result, 3)
page = request.GET.get('page', 1)
try:
result = paginator.page(page)
except PageNotAnInteger:
result = paginator.page(1)
except EmptyPage:
result = paginator.page(paginator.num_pages)
context = {
"Identity":Identity,
"age_16":age_16,
"datetime" : datetime,
"result" : result,
"PageNotAnInteger":PageNotAnInteger,
}
return render(request, 'Recensement_resume.html', context)
If you need filter records with some specific year you can just use __year method of date field:
age_16 = (today - relativedelta(years=16))
result = Identity.objects.filter(birthday__year=age_16.year).order_by‌​('last‌​name')

Django models using conditions

My models.py looks like following:
class Exercise(models.Model):
#Field for storing exercise type
EXERCISE_TYPE_CHOICES = (
(1, 'Best stretch'),
(2, 'Butterfly reverse'),
(3, 'Squat row'),
(4, 'Plank'),
(5, 'Push up'),
(6, 'Side plank'),
(7, 'Squat'),
)
exercise_type = models.IntegerField(choices=EXERCISE_TYPE_CHOICES)
#Field for storing exercise name
-- Here comes the logic --
#Field for storing intensity level
INTENSITY_LEVEL_CHOICES = (
(1, 'Really simple'),
(2, 'Rather Simple'),
(3, 'Simple'),
(4, 'Okay'),
(5, 'Difficult'),
(6, 'Rather Difficult'),
(7, 'Really Difficult'),
)
intensity_level = models.IntegerField(choices=INTENSITY_LEVEL_CHOICES)
#Field for storing video url for a particular exercise
video_url = models.URLField()
#Field for storing description of the exercise
description = models.CharField(max_length=500)
I want to have a field called 'exercise_name' for Class Exercise, but in the following way:
For exercise_type=1 it should be 'Best stretch'
For exercise_type=1 it should be 'Butterfly reverse' and so on.
How can I achieve this? Or if not this way, is there a better way to do this?
Bottom line: My Exercise should have following fields- Type, Name, Description, Video_url
If you want to get string representation based on exercise_type, simply use get_exercise_type_display(). It will returns based on EXERCISE_TYPE_CHOICES.
ex = Exercise(exercise_type=1, intensity_level=1)
ex.get_exercise_type_display() # => 'Best stretch'
ex.get_intensity_level_display() # => 'Really simple'

How can I group objects by their date in Django?

I'm trying to select all objects in the articles table, and have them grouped by their date. I'm thinking it would look similar to this:
articles = Article.objects.filter(pub_date__lte=datetime.date.today()).group_by(pub_date.day)
articles = {'2010-01-01': (articleA, articleB, articleC...),
'2010-01-02': (article1, article2, article3...)...}
Here's a working example of ignacio's suggestion to use itertools.groupby.
class Article(object):
def __init__(self, pub_date):
self.pub_date = pub_date
if __name__ == '__main__':
from datetime import date
import itertools
import operator
# You'll use your Article query here instead:
# a_list = Article.objects.filter(pub_date__lte = date.today())
a_list = [
Article(date(2010, 1, 2)),
Article(date(2010, 2, 3)),
Article(date(2010, 1, 2)),
Article(date(2011, 3, 2)),
]
keyfunc = operator.attrgetter('pub_date')
a_list = sorted(a_list, key = keyfunc)
group_list = [{ k.strftime('%Y-%m-%d') : list(g)}
for k, g in itertools.groupby(a_list, keyfunc)]
print group_list
Output:
[{'2010-01-02': [<__main__.Article object at 0xb76c4fec>, <__main__.Article object at 0xb76c604c>]}, {'2010-02-03': [<__main__.Article object at 0xb76c602c>]}, {'2011-03-02': [<__main__.Article object at 0xb76c606c>]}]
itertools.groupby()
MAybe you should do it at template level? If so you only need this : regroup tempalte tag

Categories