I need to perform a filter by date with a form where if one of the date parameters is suppressed the upper/lower border of the date range is not being set in filter (which means min possible date/max possible date, respectively)
My code is:
#forms.py
...
start_date = forms.DateField(
required=False,
)
end_date = forms.DateField(
required=False
)
...
# views.py
def get(self, *args, **kwargs):
form = self.request.GET
if form.is_valid():
cd = form.cleaned_data
if not cd['start_date']:
start_date = datetime.date(1,1,1)
else:
start_date = cd['start_date']
if not cd['end_date']:
end_date = datetime.date(3000,1,1)
else:
start_date = cd['end_date']
MyC.objects.filter(date__range=(start_date,end_date))
This code works but looks very cludgy to me (Many senseless if clauses, duplicate code, etc).
Maybe there is a Filter function for this case (if start date is None -> don't filter)?
You can apply the filters as needed. The query won't happen until you use the queryset.
objects = MyC.objects.all()
if cd['start_date']:
objects = objects.filter(date__gt=cd['start_date'])
if cd['end_date']:
objects = objects.filter(date__lt=cd['end_date'])
return objects
if you need default dates you can do:
start_date = self.cleaned_data.get('start_date', datetime.date(1,1,1))
end_date = self.cleaned_data.get('end_date', datetime.date(3000,1,1))
MyC.objects.filter(date__range=(start_date, end_date))
Related
I have a bit of a challenge with the way a date filter is working:
Django Class based view, starting here https://github.com/varlenthegray/wcadmin/blob/master/customer/views.py#L61
class CustomersCustomReport(generic.ListView):
model = Customer
template_name = 'customer/reports/custom_report.html'
def get_queryset(self):
from_date = self.request.GET.get('fromDate')
to_date = self.request.GET.get('toDate')
self.queryset = Customer.objects.filter(is_active=True)
if from_date:
from_date = datetime.strptime(from_date, '%m-%d-%Y').strftime('%Y-%m-%d')
print("Checking date from " + from_date)
self.queryset.filter(next_service__gte=from_date)
if to_date:
to_date = datetime.strptime(to_date, '%m-%d-%Y').strftime('%Y-%m-%d')
print("Checking date to " + to_date)
self.queryset.filter(next_service__lte=to_date)
return self.queryset
I'm expecting this to return a filtered query based on the date that is a form field.
https://wcadmin.innovated.tech/customer/report/custom_report?fromDate=04-01-2022&toDate=04-30-2022
I know this data isn't filtered because the entire customer list is 521 entries of mock data that are active. I was following information from this question: How Can I Filter By Date Range Using Djangos Built in ListView?
I know it's getting data from the database, I know it's getting the date range I want from the URL due to the print, and the model is set to DateField for next_service, so I'm not quite sure what's going wrong here?
you need only a little changes:
def get_queryset(self):
from_date = self.request.GET.get('fromDate')
to_date = self.request.GET.get('toDate')
queryset = Customer.objects.filter(is_active=True) # change
if from_date:
from_date = datetime.strptime(from_date, '%m-%d-%Y').strftime('%Y-%m-%d')
print("Checking date from " + from_date)
queryset = queryset.filter(next_service__gte=from_date) # change
if to_date:
to_date = datetime.strptime(to_date, '%m-%d-%Y').strftime('%Y-%m-%d')
print("Checking date to " + to_date)
queryset = queryset.filter(next_service__lte=to_date) # change
return queryset # change
I wrote the following code:
date = self.request.query_params.get('date')
queryset = Fixture.objects.all().order_by('-date')
if(date):
date = pytz.utc.localize(datetime.strptime(date, "%Y-%m-%d")).astimezone(pytz.UTC)
queryset = Fixture.objects.filter(date__date=date).order_by('date')
Upon excuting this with date = "2020-09-02" the queryset returns values containing the date "2020-09-03". How come this happens and how can this be solved?
If you want to work with a date, why make it a datetime first? Your parsing could be simplified.
date = self.request.query_params.get('date')
queryset = Fixture.objects.all().order_by('-date')
if date:
date = datetime.strptime(date, "%Y-%m-%d").date()
queryset = Fixture.objects.filter(date__date=date).order_by('date')
But this parsing is also is sensitive for wrongly inserted data, and you'll get an error. Best practice imo would be creating a simple form with a DateField.
class ParseDateForm(forms.Form):
date = forms.DateField()
# This somewhere in a method
form = ParseDateForm(data=self.request.query_params)
queryset = Fixture.objects.all().order_by('-date')
if form.is_valid():
date = form.cleaned_data['date']
queryset = Fixture.objects.filter(date__date=date).order_by('date')
I am trying to join two querysets in Django with different key values, but from the same model, is there any way to do so?
Here is my code:
models.py
class CustomerInformation(models.Model):
status = (
('lead', 'Lead'),
('client', 'Client'),
)
customer_id = models.AutoField(primary_key=True)
customer_name = models.CharField(max_length=100)
status = models.CharField(max_length=100, choices=status, default='lead')
conversion_date = models.DateField(null=True, blank=True)
created_date = models.DateField(default=timezone.localdate)
def save(self, *args, **kwargs):
if self.customer_id:
if self.status != CustomerInformation.objects.get(customer_id=self.customer_id).status and self.status == 'client':
self.conversion_date = timezone.now()
elif self.status != CustomerInformation.objects.get(customer_id=self.customer_id).status and self.status == 'lead':
self.conversion_date = None
super(CustomerInformation, self).save(*args, **kwargs)
here is my filtering
start = date.today() + relativedelta(days=-30)
client_qs = CustomerInformation.objects.filter(conversion_date__gte=start).values(date=F('conversion_date')).annotate(client_count=Count('date'))
lead_qs = CustomerInformation.objects.filter(created_date__gte=start).values(date=F('created_date')).annotate(lead_count=Count('date'))
Basically what I am trying to achieve is to get the count of CustomerInformation instances created in the past 30 days (by annotating the count of the field 'created_date'). Also, I want to get the count of CustomerInformation instances that have converted to 'client' status within the past 30 days (by annotating the count of the field 'conversion_date'). Is there any way for me to do so and receive them in a single queryset, preferably with a single date field?
For example, my desired output would be
[ {'date': '170620', 'lead_count': 2, 'client_count': 1}, {'date': '180620', 'lead_count': 1, 'client_count': 0 }, ... ]
All help is appreciated, thanks all!
I think you can achieve the same by doing:
combined_qs = CustomerInformation.objects.filter(created_date__gte=start, conversion_date__gte=start).annotate(lead_count=Count('created_date'), client_count=Count('conversion_date')
Note that the above will use AND the filter. Roughly that means it will only return customer information that have both created_date and conversion_date greater than date.( I might be wrong but I think that's what you want in this case).
Otherwise you can use the Q object for a more complex query.
from django.db.models import Q
combined_qs = CustomerInformation.objects.filter(Q(created_date__gte=start)|Q(conversion_date__gte=start)).annotate(lead_count=Count('created_date'), client_count=Count('conversion_date')
The official doc for using the Q objects is here
I will say try both and compare the results you get.
Hope that helps.
Hi i have some Django 11 project, my model look like
class Event(models.Model):
name = models.CharField(max_length=100, unique=True)
title = models.CharField(max_length=100)
info = models.CharField(max_length=100)
image = models.ImageField(upload_to='events/%Y/%m/%d')
start_date = models.DateField(default=timezone.now)
start_time = models.TimeField(default=timezone.now)
stop_date = models.DateField(default=timezone.now)
stop_time = models.TimeField(default=timezone.now)
place = models.ForeignKey('places.Place', on_delete=models.CASCADE)
company = models.ForeignKey('companies.Company', on_delete=models.CASCADE)
and my view look like
def place_website(request, place_id):
place_template = get_template('room.html')
place_obj = Place.objects.filter(id=place_id)
# filter for event obejts only for requested place, filtered for now and next events
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
place_events_next = Event.objects.filter(place=place_id, start_date=timezone.now(), stop_date__gte=timezone.now()).order_by('start_time')
place_context = {
'place_obj': place_obj,
'place_event_now': place_event_now,
'place_events_next': place_events_next,
}
return HttpResponse(place_template.render(place_context))
the thing i want to manage is to pass to template the list of filtered Event objects based on time.
Lets pick this line
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
it couse error "expected string or bytes-like object" but when i remove ".date" from "timezone.now()" error disappear (then filter do nothing) but i want to compare date to date and time to time.
How to do this properly ?
This approach to filter objects in view rather than in template is proper?
###### UPDATE ########
Its werid because after correction now i have no error but queryset is not filtered properly, look like only two first parameter is filtering ok and the another two is causing empty queryset.
place_event_now = Event.objects.filter(place=place_id, start_date=timezone.now().strftime('%Y-%m-%d'), start_time__lte=timezone.now().strftime('%H:%M:%S'), stop_time__gte=timezone.now().strftime('%H:%M:%S'))
I my database time is saved in format H:M:S and timezone.now().time() has different format so i modified filter with .strftime this didnt help, what i wont is to limit "place_event_now" queryset to particular object/objects that come true with condition start_time < currenttime < stop_time.
Another case is with place_event_next
place_events_next = Event.objects.filter(place=place_id, start_date=timezone.now().strftime('%Y-%m-%d'), stop_date__gte=timezone.now().strftime('%Y-%m-%d'), start_time__gt=timezone.now().strftime('%H:%M:%S')).order_by('start_time')
Event when i filter objects that start_time is greater than timezone.now() they still are in queryset.
Am I doing something wrong ?
I figured it out that timezone.now() return time not in my current timezone, i change it to timezone.localtime() and everything working perfect !!!
May be you need call date to date()
replace
filter(place=place_id, start_date=timezone.now().date, stop_date__gte=timezone.now().date)
# ^^^ ^^^
to
filter(place=place_id, start_date=timezone.now().date(), stop_date__gte=timezone.now().date())
# ^^^ ^^^
I have managed to create a very simple model which allows me to subtract 2 DateTime fields, like so:
class Log(models.Model):
date = models.DateField()
take = models.DateTimeField()
land = models.DateTimeField()
tot = models.CharField(max_length=200, blank=True, default='00000')
def __str__(self):
return str(self.date)
def time_delta(self):
tdelta = self.land - self.take
return str(tdelta)
def save(self, *args, **kwargs):
self.tot = self.time_delta()
super(Log, self).save(*args, **kwargs)
My problem is the user would have to specify the date on every field. How could I make the fields take and land refer to date once and for all?
I don’t know how to do that in your Django model. I think you have to calculate the dates and times in your controller instead, and then register the values in your database.
You can do something like that with datetime.datetime.combine() function:
Return a new datetime object whose date components are equal to the given date object’s, and whose time components are equal to the given time object’s.
You have a reference date, for instance: today.
import datetime
date = datetime.date.today()
The user enter the takeoff time, you can combine this time with the reference date.
takeoff_time = datetime.time(8, 12)
takeoff_datetime = datetime.datetime.combine(date, takeoff_time)
print(takeoff_datetime.isoformat(" "))
# -> 2016-12-21 08:12:00
If the landing date is the same as the takeoff date, you can calculate the landing date/time with the same date reference:
landing_time = datetime.time(12, 37)
landing_datetime = datetime.datetime.combine(date, landing_time)
print(landing_datetime.isoformat(" "))
# -> 2016-12-21 12:37:00
Then, you can register the date, _takeoff_datetime_ and _landing_datetime_ in your database.
Note: you can do the same with the flight duration
I eventually managed to find a solution largely based on Laurent's answer so here it is,if that can ever help someone else:
from datetime import datetime
def calculation(self):
calc_take_off = datetime.combine(self.date, self.take)
calc_land = datetime.combine(self.date, self.land)
result = calc_land - calc_take_off
return str(result)
and then to save this in models:
def save(self, *args, **kwargs):
self.tot = self.calculation()
super(Log, self).save(*args, **kwargs)