I have a Django class to convert the date of birth (dob) field in my model to age and annotate the result to a queryset.
class CalculateAge(Case):
def __init__(self, model, field, condition=None, then=None, **lookups):
today = date.today()
obj = model.objects.first()
field_object = model._meta.get_field(field)
field_value = field_object.value_from_object(obj)
bornyear = field_value.year
bornmonth = field_value.month
bornday = field_value.day
# something is wrong with the next two lines
age = [today.year - bornyear - ((today.month, today.day) < (bornmonth, bornday))]
return super().__init__(*age, output_field=IntegerField())
however when I try to pass the result to my queryset
queryset = Person.objects.all().annotate(age=CalculateAge(Person, 'dob')
I get the error
Positional arguments must all be When objects.
How can I get this to work?
There is an easier way. Just add a function on your model to get the age like this:
class ModelName(models.Model):
birth_date = models.DateField()
#other fields
def get_age(self):
age = datetime.date.today()-self.birth_date
return int((age).days/365.25)
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 write this python function for calculate age.
def age(birthdate):
today = date.today()
age = today.year - birthdate.year - ((today.month, today.day) < (birthdate.month, birthdate.day))
return age
result:
>>> print(age(date(1980, 1, 1)))
42
here is my code:
models.py
class CalculateAge(models.Model):
age = models.IntegerField(max_length=100)
date_of_birth = models.DateField()
user only pick the date of birth and I want age will be automatically calculate when submitting forms.
views.py
def CalculateAge(request):
if request.method == "POST":
patient_from = AddPatientFrom(request.POST or None)
if patient_from.is_valid():
patient_from.save()
how to implement this age function in my views.py and models.py?
I tried this in my views.py but didn't work.
if patient_from.is_valid():
pick_date = request.POST['date_of_birth']
find_age = age(date(pick_date))
print(find_age)
getting this error:
TypeError at /add-patient/ an integer is required (got type str)
You should work with the .cleaned_data [Django-doc] of the form: this will contain the data as date object:
if patient_from.is_valid():
pick_date = form.cleaned_data['date_of_birth']
find_age = age(age_y)
print(find_age)
I want to create an error message for following form:
class ExaminationCreateForm(forms.ModelForm):
class Meta:
model = Examination
fields = ['patient', 'number_of_examination', 'date_of_examination']
Models:
class Patient(models.Model):
patientID = models.CharField(max_length=200, unique=True, help_text='Insert PatientID')
birth_date = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
gender = models.CharField(max_length=200,choices=Gender_Choice, default='UNDEFINED')
class Examination(models.Model):
number_of_examination = models.IntegerField(choices=EXA_Choices)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date_of_examination = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
Every Patient has 2 Examinations (number of examination = Choices 1 or 2) and the error message should be activated when the date of the second examination < date of the first examination. Something like this:
Solution: `
def clean_date_of_examination(self):
new_exam = self.cleaned_data.get('date_of_examination')
try:
old_exam = Examination.objects.get(patient=self.cleaned_data.get('patient'))
except Examination.DoesNotExist:
return new_exam
if old_exam:
if old_exam.date_of_examination > new_exam:
raise forms.ValidationError("Second examination should take place after first examination")
return new_exam`
def clean_date_of_examination(self):
new_exam = self.cleaned_data.get('date_of_examination')
old_exam = Examination.objects.get(patient = self.cleaned_data.get('Patient'))
if old_exam:
if old_exam.date_of_examination > new_exam.date_of_examination:
raise forms.ValidationError("Second examination should take place after first examination")
return data
def clean_date_of_examination(self):
# Where 'data' is used?
date_of_exam = self.cleaned_data['date_of_examination']
try:
pat1 = Patient.object.get(examination__number_of_examination=1, date_of_examination=date_of_exam)
except Patiens.DoesNotExist:
# Patient 1 with given query doesn't exist. Handle it!
try:
pat2 = Patient.object.get(examination__number_of_examination=2, date_of_examination=date_of_exam)
except Patiens.DoesNotExist:
# Patient 2 with given query doesn't exist. Handle it!
if pat2.date_of_examination < pat1.date_of_examination:
raise forms.ValidationError("Second examination should take place after first examination")`
return data`
I can't sort table by it's models property. I know that I should set accessor in the column so django-tables2 knows what field to process but it does not work.
This is the table:
class ScansTable(tables.Table):
site = tables.columns.Column(accessor='occurence.site', verbose_name='Site')
url = tables.columns.TemplateColumn("""{{ record.occurence.url|truncatechars:20 }}""",
accessor='occurence.url', verbose_name='Url')
price = tables.columns.TemplateColumn(u"""{{ record.price }} €""")
date = tables.columns.Column(accessor='date',order_by='date')
time = tables.columns.Column(accessor='time',order_by='time')
class Meta:
model = Scan
fields = ('date', 'time', 'site', 'url', 'valid', 'price')
attrs = {'id': 'cans_table',
'class': 'table',}
This is the Scan model:
class Scan(models.Model):
occurence = models.ForeignKey('Occurence', related_name='scans')
datetime = models.DateTimeField()
price = models.DecimalField(max_digits=20,decimal_places=2,null=True,blank=True,verbose_name='Price')
valid = models.BooleanField(default=True,verbose_name='Valid')
def __unicode__(self):
return u'{} {} {} {}'.format(self.occurence, self.datetime, self.price, u'OK' if self.valid else 'NOK')
#property
def date(self):
return self.datetime.date()
#property
def time(self):
return self.datetime.time()
The view:
def scans(request):
...
scans = Scan.objects.filter(occurence__product=product)
scans_table = ScansTable(scans)
RequestConfig(request).configure(scans_table)
scans_table.paginate(page=request.GET.get('page', 1), per_page=50)
return render(request,"dashboard_app/scans.html",context={'scans_table':scans_table})
The table is being properly renderd when I don't want to sort it. When I click on time (for example), it returns:
Cannot resolve keyword u'time' into field. Choices are: datetime,
groups, id, occurence, occurence_id, price, valid
Do you know where is the problem?
it's strange what the type product ?? you show the Occurence model and what value it in the view
It appears that defined properties/methods of the model are not available for sorting/filtering within the queryset. I don't fully understand why that is the case. A solution would be to NOT define date and time as properties on the Scan model, but instead annotate them to the queryset used to populate the data.
from django.db import models
def scans(request):
...
scans = Scan.objects.filter(occurence__product=product).annotate(
date=models.F('datetime__date'),
time=models.F('datetime__time')
)
...
See the documentation here on field lookups. Also you could use the tables specific columns for those fields - note that you don't need to define the accessors now the results are already in the queryset:
class ScansTable(tables.Table):
...
date = tables.DateColumn()
time = tables.TimeColumn()
...
I am working on an basic application and I`m stuck at a displaying some info.
Please take a look:
Models:
class Companies(models.Model):
name = models.CharField()
address = models.CharField()
def __unicode__(self):
return self.name
class Payments(models.Model):
company = models.ForeignKey(Companies)
year = models.CharField(choices=YEAR)
month = models.CharField(choices=MONTHS)
date = models.DateField(auto_now_add=True)
I want a view in which to display ONLY the companies that did not pay the monthly fee.
So I`ve started like this:
def checks(request):
i = datetime.datetime.now()
an_c = i.strftime('%Y')
comp = Companies.objects.all()
pay1 = Payments.objects.filter(an=an_c, month='01')
But in the template I do not know how to filter the "comp" list.
I want to display in the template all the records from "comp" except that ones with the id/pk which can be find in the "pay1.company"
You wouldn't do that in the template. Do the whole thing in the view:
pay1 = Payments.objects.filter(an=an_c, month='01')
comp = Companies.objects.exclude(payments__in=pay1)
(Style note: Django model classes are usually named in the singular, not the plural.)