use __gte for string date django - python

Date is in string format in database
class A(models.Model):
date = models.CharField(max_length=50, blank=True, null=True)
Yah i know it should be date type.
but now we have many records and we will soon change it to date type.
For current status i want get the objects greater than particular date.
So how can i use date__gte
example
objs = A.objects.filter(date__gte=datetime.now())
Is there any way to achieve this without converting date to datetime field.

I am not sure if that works. What you can try is a custom manager. So you can create a manager for your model, add something like date_gte and then convert the string to a datetime. Then you can user those operators as usual. That's a quick fix for now, but the best solution is to use a DateTimeField, which you want to do as far as I understood.
Example Manager:
from django.db import models
class MyManager(models.Manager):
def date_gte(self, date=datetime.now()):
items = []
for obj in self.all():
if datetime(obj.date) < date:
items.append(obj)
return items
Then you could call it like MyModel.objects.date_gte(date=datetime.now()).
Note: This is an expensive query and you may need to convert the simple list into QuerySet object. I haven't tested it, so this example should only help you get started.

There is no way to do this without a conversion (either in Django or the database during the query) to a proper DateTime type. You're trying to compare a datetime.datetime to a str. That won't work in normal Python and it won't work here.

What kind of string? if it is formatted to %Y%m%d, you can use .extra() method to do the query.
A.objects.extra(where=['date >= date_of_today'])

Related

How to keep a property value in django.values()?

I have my model as:
class Subs(models.Model):
...
created_at = models.DateTimeField(auto_now_add=True, db_column="order_date", null=True, blank=True)
#property
def created_date(self):
return self.created_at.strftime('%B %d %Y')
I want to get created_Date in my views.py
data = Subs.objects.values('somevalues','created_date')
It throws an error. How to access created_date so that I can use it here?
Although your approach works, it's not best practice performance-wise. generally iterating whole Model.objects.all() is a bad idea because it loads all rows in memory.
In such cases you have several options:
if you just need some simple python logic on your data (like formatting here) do this on the presentation layer (e.g. filter tags)
if you need to apply some heavy business logic, it's better to have them in create/update time (e.g. overriding .save()) or have some cronjobs for it in off-peak time and save them in an extra column in DB.
if your manipulation needs some DB layer query and depends on several columns or tables use .annotate() to add it into your queryset.
As values didnot work i used for loop.
instead of doing this:
data = Subs.objects.values('somevalues','created_date')
I did this :
newarr = [{'created_date': i.created_date} for i in Subs.objects.all()]

Formatting DateTimeField in Django

When saving timestamp in Django's DateTimeField using auto_now_add this way:
creation_timestamp = models.DateTimeField(auto_now_add=True)
the field is saved with miliseconds:
2018-11-20T15:58:44.767594-06:00
I want to format this to be displayed without miliseconds:
2018-11-20T15:58:44-06:00
But the only option I could come up with does not exactly show what I need:
format="%Y.%m.%dT%H:%M:%S%z" gives me 2018.11.20T15:58:44-0600
How do I format this field the way I need?
Alternatively I'd rather save DateTimeField without milliseconds at all but does auto_now_add allow to do this sort of thing?
If you want to format it on when displaying it, you can use: creation_timestamp.strftime("%Y-%m-%d%H:%M:%S")
You can also make DateTimeField to save it in that format, but this would request a set of changes which will apply system wide:
In your settings file set the follwing:
DATETIME_FORMAT="%Y-%m-%d%H:%M:%S"
L10N=False to make sore localization data doesn't take precedent when it comes to dates format.
USE_TZ=False
But, consider the fact that this changes will apply by default to all date time objects from your project.
You can override DateTimeField's value_to_string method and add the changes there. For example:
class CustomDateTimeField(models.DateTimeField):
def value_to_string(self, obj):
val = self.value_from_object(obj)
if val:
val.replace(microsecond=0)
return val.isoformat()
return ''
And use it in model:
created = CustomDateTimeField(auto_now_add=True)
I think you will have to use isoformat. Look at that answer:
Show the : character in the timezone offset using datetime.strftime
I use pandas to_datetime() function to convert the time to string format.
import pandas as pd
record_list = myModel.objects.all()
for idx, record in enumerate(record_list ):
st = str(pd.to_datetime(record.start_time))
record_list[idx].start_time = st

How to get django queryset results with formatted datetime field

I've Django model which has foreign keys associated with other models. Each model is having same field names(attributes) created_at and updated_at
In every django queryset results I'll be getting datetime values.
Model.objects.all().values('created_at')
But I want to format the datetime field to "DD-MM-YYYY HH:MM:SS" and trim down the milliseconds in the django query results.
If I use "extra" and and date_trunc_sql like the following command
dt = connection.ops.date_trunc_sql('day','created_date')
objects.extra({'date':dt}).values('date')
Which works fine. But If I query like the following, its raising ambiguous statement error.
objects.extra({'date':dt}).values('date', 'x', 'y', 'z')
How to overcome this problem?
Solved it via #Yannics answer at: https://stackoverflow.com/a/60924664/5804947
This also avoids using extra which should be "a last resort" due to Django docs.
from django.db.models import F, Func, Value, CharField
qs.annotate(
formatted_date=Func(
F('created_at'),
Value('DD-MM-YYYY HH:MM:SS'),
function='to_char',
output_field=CharField()
)
)
Got the solution.
data = list(Model.objects.extra(select={'date':"to_char(<DATABASENAME>_<TableName>.created_at, 'YYYY-MM-DD hh:mi AM')"}).values_list('date', flat='true')
It's not just tablename.attribute, it should be dbname_tablename.attribute when we have multiple databases(ambiguous)
which will result list of created_at datetime values trimmed to 'YYYY-MM-DD HH:MM' format.
I don't think values() function would anything related to formatting datetime result. But why does that bother you? Can't you convert them to proper format when you try to display them? If you try to render them in the template, django has template filter date for formatting your datetime value: https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#date

Ordering a list of objects by the Year element of a DateTimeField

I have the following model defined in models.py
class Schoolclass(models.Model):
...
date_created = models.DateTimeField('date created')
In my views.py I want to order the list of Schoolclass objects by the year only associated with date_created. I specifically don't want ordering to take account of any other element of the DateTimeField. The reason being that I wish secondary ordering to occur through a different field.
This is what I can come up with but it doesn't work.
def index(request):
class_list = SchoolClass.objects.order_by('date_created__year')
In case it helps I get the following error when I run the above code:
Join on field 'date_created' not permitted. Did you misspell 'year' for the lookup type?
The problem is that the year field only exists in Python, not in the database. I guess you'll have to use extra() and make it explicit to the database that it should order by the year part of the datetime field. How exactly to do that will depend on your database.
This should work for MySQL:
def index(request):
class_list = SchoolClass.objects.extra(select={'year_created': 'YEAR(date_created)'},
order_by=['year_created'])
If you're using other database, you'll have to replace YEAR(date_created) with the equivalent operation to extract the year from a datetime field.

How do I get each distinct 'year' value out of a Django model with a datetime field?

I can successfully filter by a given year in my Django model, but I'm having trouble finding a way to list valid years so a user can access them.
I have a django model with a defined 'datetime' field, oh-so-originally named 'date'. In my templates, I can successfully access the 'bar.date.date.year' field, so I know it exists, but when I try the following function...
blog_years=[]
for entry in blog_entries:
if entry.date.date.year not in blog_years:
blog_years.append(entry.date.date.year)
I'm told that "'builtin_function_or_method' object has no attribute 'year'"
I can only assume I"m tripping over some aspect of Python I'm not familiar with, but I can't figure out what it is. I'm quite certain it has to be syntactical, but past that...
Django has an elegant and efficient way of doing this. You can check from their docs https://docs.djangoproject.com/en/3.0/ref/models/querysets/#dates
But to go over it
Entry.objects.dates('pub_date', 'year')
This will bring out distinct year values in the query.
if you are using postgres, you can do
BlogEntry.objects.extra(select={"year": "EXTRACT(YEAR FROM date)"}).distinct().values_list("year", flat=True)
A Python set does not allow duplicates, so if you wanted a list of distinct years:
blog_years = list(set([entry.date.year for entry in blog_entries]))
Or, you could use distinct():
blog_years = blog_entries.distinct(entry__date__year).values(entry__date__year)
Of course, adjust the above based on your model.
The first .date accesses a datetime object.
The second .date is accessing a method on datetime objects that returns a date object but not calling it (this step is unneccessary).
The last part (the way you wrote it) is trying to access the year attribute of the date method, instead of accessing the year attribute of the result of the date method call.
Correcting the code to see the difference, it would look like this...
blog_years=[]
for entry in blog_entries:
if entry.date.date().year not in blog_years:
blog_years.append(entry.date.date().year)
But what you should do is more like this...
blog_years=[]
for entry in blog_entries:
if entry.date.year not in blog_years:
blog_years.append(entry.date.year)
since datetime objects have the date attribute as well.
date() is a method of datetime, use
blog_years=[]
for entry in blog_entries:
if entry.date.date().year not in blog_years:
blog_years.append(entry.date.date().year)
from django 1.10 it has become very simple
from django.db.models.functions import ExtractYear
blog_years= blog_entries.annotate(
year=ExtractYear('created_on')
).values_list('year', flat=True)
blog_years = sorted(set(blog_years), reverse=True)
It might not be what you exactly expects (it can return years without blog posts):
from datetime import date
from django.db.models import Min
def blog_years():
current_year = date.today().year
queryset = Entry.objects.annotate(Min('date')).order_by('date')
if queryset:
oldest_year = queryset[0].date.date().year
else:
oldest_year = current_year
return range(oldest_year, current_year + 1)
ModelName.objects.dates('column_name', 'year')

Categories

Resources