0

I have my django app and I want to pass from url to view years in format, for example: 2017-18. Below I got an error that my date have to be in date format - YYYY-MM-DD.

Here is my url:

url(r'^(?P<branch_slug>/production/(?P<year1>[0-9]{4})-(?P<year2>[0-9]{2})$', Events.as_view()),

Here is my view:

def get_queryset(self):
    season = Events.objects.get(start=self.kwargs['year1'], end=self.kwargs['year2'])
    filter_['start__gte'] = season.start
    filter_['start__lte'] = season.end
    return self.model.objects.filter(**filter_)
Mark
  • 15
  • 1
  • 6
  • Some answers here suggest converting the input dates to a datetime and then matching a season which starts exactly on 1 January of year1 and ends on 31 December of year2. Either this is what you want, in which case the `season = ` bit of your code is superfluous, or you want to match a season which starts and ends in the given years, in which case I recommend the `start__year=` lookup instead. – nimasmi Apr 04 '17 at 09:23

4 Answers4

2

The start and end attributes of your Event object are probably datetime.date instances (if you are using a DateField). So you need to convert the year1 and year2 variables from your url to a date before using them in your view.

some_date = datetime.date(YYYY, 1, 1)
dentemm
  • 6,231
  • 3
  • 31
  • 43
  • I know this, but how do this? @dentemm – Mark Apr 04 '17 at 07:57
  • Either use the `__year` lookup, or knowing that the dates must be *within* year, use `start__gte=datetime.date(kwargs['year1'], 1, 1), start__lte=datetimedate(kwargs['year1'], 12, 31)` – nimasmi Apr 04 '17 at 07:59
  • I'm not sure what you're expecting, but in Python 2 and 3, I get `ValueError: month must be in 1..12` when I try `datetime.date(YYYY, 0, 0)`. Where does the zero, zero format come from? – nimasmi Apr 04 '17 at 08:03
  • My bad, month and day can not be 0, they should be 1 (or a different number). I case of a time instance 0 is a possible value, but not for dates. – dentemm Apr 04 '17 at 08:06
  • Here is python2 and I have `filter_['start__gte'] = datetime.date(self.kwargs['year1'], 1, 1), filter_['start__lte'] = datetime.date(self.kwargs['year2'], 12, 31),` and I get error: `an integer is required` – Mark Apr 04 '17 at 08:15
  • 2
    As the error suggests, `self.kwargs['year1']` is a string, (e.g. `'2017'`). You need to use `int(self.kwargs['year1'])` instead. Since `(?P[0-9]{2})` only matches two digits, you'll have to convert that to a 4 digit year as well. – Alasdair Apr 04 '17 at 08:31
2

Your original question, and others' answers here, match a season that begins on 1 January of year1 and ends on 31 December of year2. I suspect that this is not what you want, and that instead you want a season which starts sometime in year1 and ends some time in year2, and then you want to look up events between those dates.

Django has a special lookup for matching only the year part of a date, using __year=. See https://docs.djangoproject.com/en/1.10/ref/models/querysets/#year for more on this. You don't need to convert the input values to dates to use this; it works with integers.

def get_queryset(self):
    # Get a season starting any time in year1 and ending in year2
    season = Events.objects.get(
        start__year=int(self.kwargs['year1']),
        end__year=int(self.kwargs['year2']),
    )

As with others have commented you should change the URL regex to match 4-digit years to avoid ambiguity.

url(r'^(?P<branch_slug>/production/(?P<year1>[0-9]{4})-(?P<year2>[0-9]{4})$', Events.as_view()),
nimasmi
  • 3,978
  • 1
  • 25
  • 41
0

As @dentemm pointed out, to do filtering on datetime fields, strings representing date must be converted to datetime objects. My suggestion is to use datetime field. Pass year string as you are already doing and then in the views convert date string to a datetime object to do filtering

import datetime
def get_queryset(self):
    start=datetime.strptime('1-1-'+self.kwargs['year1'], '%m-%d-%Y')
    end=datetime.strptime('12-31-'+self.kwargs['year2'], '%m-%d-%Y')
    season = Events.objects.get(start=start, end=end)
    filter_['start__gte'] = season.start
    filter_['start__lte'] = season.end
    return self.model.objects.filter(**filter_)

datetime.strptime('1-1-'+self.kwargs['year1'], '%m-%d-%Y') will create datetime object for start date with date as 1st of january for given start date string and datetime.strptime('12-31-'+self.kwargs['year2'], '%m-%d-%Y') will create datetime object for end date with date as 31st of december for given end date string. To make sure all objects created within end date is returned by query you may do something like

`end_date = datetime.strptime('31-12-'+self.kwargs['year1']+'T23:59:59.999999', '%m-%d-%YT%H:%M:%S.%f')` 

As @Alasdair mnetioned in one of the comments change (?P<year2>[0-9]{2}) to (?P<year2>[0-9]{4}) to accept 4 digit year string from url

Community
  • 1
  • 1
cutteeth
  • 2,148
  • 3
  • 25
  • 45
0

If you have a datetime/date field in your model, use the year lookup from Django: https://docs.djangoproject.com/en/1.10/ref/models/querysets/#year

As it says:

Entry.objects.filter(pub_date__year=2005)

is equal in sql to:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';