I need to store start and ending dates to save holidays.
I don't care about the year part of dates, as a holiday will repeat every year.
Also I need to be able to query this dates to see if a random datetime is in the range. And even when I don't want years stored, the query must understand that a holiday can start in one year and end next year (i.e. Dec 25th to Jan 4th).
Previously I was storing the dates as DateTimeFields, then iterating over each stored holiday and checking if a given target date was inside the dates range. This was made in Python and forced me to evaluate the QuerySets into lists and finally add the value using something like getattr(result, 'is_a_holiday', value)
As performance issues have arise I want to move this into an annotation (so I can keep the queryset with select_related and prefetch_related data) and make the database (Postgresql) do the query part, but then I run into the problem that the database considers the year, and thus a date inside a holiday for this year is not considered inside a holiday the previous year or next year.
I've already tried django-yearlessdate and annotating with conditional expressions and F expressions (to check when a year changes) but it is not working as expected: when there are several holidays some of the condition cases don't match and I just get the default return value.
What are other/different approaches to this problem?
If you don't need year, you can create two separate fields in your model to hold month and day separately:
class Holiday(...):
month = models.PositiveSmallIntegerField()
day = models.PositiveSmallIntegerField()
Then to find holidays between a given range, you can do this:
from_date = <datetime object>
to_date = <datetime object>
Holiday.objects.filter(month__range[from_date.month, to_date.month],
day__range[from_date.day, to_date.day]
)
Related
I am making a Django app to print the expenses made each month
I have been searching since a long time how to print the objects(expenses) created in a specific month(or may be last month)
I checked across many stack overflow questions similar to this but was not able to find an appropriate answer.
Many people use
datetime.timedelta(days=30)
But how can this method be proper to print the expenses of a particular month.
I want to use a method like
datetime.timedelta(previous month)
Is there any possible way to do this?
Here is a good way to do that
First of all, in your views.py file :
Start with declaring today's date
today = datetime.date.today()
You need to filter out the objects created last month (taking May month as an example here)
may_month_expenses =
Expenses.objects.filter(created__month='05',created__year=today.year)
Explanation:
Expenses is the name of the model
objects.filter() is used to print the objects which satisfy the
conditions in the brackets.
created is a DateTimeField in Expenses
created__month='05' specifies that the object should have been created
in May(month number 5)
created__year = today.year is important as you want to print the objects
created in month May of this year(2021) only.
This prints the objects created in month May of year 2021
how can I translate query like this in django orm?
select id, year, month
where (year*100 + month) between 201703 and 201801
Thanks in advance for any help
You can first create an annotation, and then filter on that:
from django.db.models import F
(Modelname.objects
.annotate(yearmonth=F('year')*100+F('month'))
.filter(yearmonth__range=(201703, 201801)))
So here we construct an annotation yearmonth (you can use another name if you like), and make it equal to the year column times 100 plus the month column. Next we can filter on that annotation, and do so by specifying a __range here with two bounds.
Normally this will work for any database system that performs the operations you here perform (multiplying a column with a constant number, adding two values together), as well as do the __range filter (in MySQL this is translated into <var> BETWEEN <min> AND <max>). Since we use Django ORM however, if we later decide to use another database, the query will be translated in the other database query language (given of course that is possible).
How about using something similar to this.
Did you try filter and __range
Created_at will be the field in your DB
ModelName.objects.filter(created_at__range=(start_date, end_date))
Later you can do calculation in your view this is just a workaround.
If you want to run the exactly same query then probably you can run using.
ModelName.objects.raw("select id, year, month
where (year*100 + month) between 201703 and 201801")
If I use something like
year_published = models.DateField()
then I have to enter in a day and month, in addition to a year. But in this field I only want to enter a year. Likewise, I only want to enter a month in
month_published = models.DateField()
Any idea how to do this?
If you only store the year, how will you sort on Month and Date? Instead, store the full datetime object or split out the full date into three fields. As already mentioned, these would normally be Char fields, but can be Int and can be converted back to date to sort and compare/diff. See:
How to convert integer into date object python?
Or simply store the full date natively and then get the chunks you need from it as you need it. You might check out the Pendulum Package which opens up a lot more options on date operations.
For a QuerySet of blog entries, I want to create a DateQuerySet of the months in which those posts were made. The query is:
dates = Entry.published.all().dates('pub_date', 'month')
According to the docs
"month" returns a list of all distinct year/month values for the field.
If I have 4 entries with dates :
(2012, Feb, 3rd)
(2012, Feb, 2nd)
(2012, Jan, 24th)
(2011, Dec, 28th)
I expect to get 3 datetime objects returned; one for Dec, Jan, Feb, instead I get 4 returned, one for each of the original dates
Is this expected behaviour? I've tried adding distinct() to the query, but it still returns every date.
UPDATE
A simple way to fix this is make a Set from the DateQuerySet:
dates = Entry.published.all().dates('pub_date', 'month')
return set(dates)
This removes the duplicates datetime objects but I still don't understand why this is happening (or if I am misunderstanding how dates() works)
Your symptoms sound exactly like a GROUP BY query with ordering.
You can print query.query to see if there's ordering being applied to a field that would be added to the SELECT, thus making all of those distinct as well.
Entry.published.dates('pub_date', 'month').order_by()
datetimes() instead of dates(), is a good idea in datetime-type objects situation.
My Environment:
win10 + python3.6.8
django2.2.12
FYI:
datetimes: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#datetimes
dates: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#dates
I have this model:
class Person(models.Model):
city = models.CharField(max_length=20, blank=True)
added_date = models.DateField(default=datetime.date.today)
I want to create a template/view that has a table of months and the number of people added that month (ie, 5 in january, 10 in february, 8 in march, etc.). I have a similar table for all the people from a each city using:
cities = Patient.objects.values('city').annotate(city_count=Count('city')).order_by('-city_count')
I don't know how to do that for my months table. I could filter for a particular month, then count all. But then I'd need to run that through a loop over every month, which would be multiple database hits. I don't want to do that.
Is there a way to do this without writing sql and just using django's api?
Its a very old thread, but i guess I'll answer in case someone else ended up here looking for a solution.
The solution is for Django 1.10+ using the ExtractMonth function, for more detail visit official documentation
First you have to import ExtractMonth, like
from django.db.models.functions import ExtractMonth
Then using your Persons model, the code will be like this
personsMonthlyData = Person.objects.annotate(month=ExtractMonth('added_date')).values('month').annotate(count=Count('id')).order_by('month')
personsMonthlyData will output something like this
[{month: 1, count: 3}, {month: 2: count: 1}]
where month represent the month number e.g. 1 for January and 2 for February and the count against each month is assigned to the count item.
I hope this helps.
The fact that most DBs have such a perfect way of doing this with a GROUP BY query that has no analog in Django AFAIK would lead me to drop into SQL to do this. I did a google search for "django sql" and turned up this post by Doug Hellman on this exact problem: http://blog.doughellmann.com/2007/12/using-raw-sql-in-django.html. I would use that as a starting point for getting your month counts into Django.