1

Here is my first project in which I am experimenting with the Django documentation: a Person model. and two featured views (one to create a person and the other to view the list of created persons). So far a person has only two CharFields, first_name and last_name.

What I'd like to do now is to implement a BooleanField adult, which returns True if the difference between today's date and the date of birth is greater than or equal to, say, 18 (or 16, it doesn't matter).

Possibly I would also implement an attribute age based on the same principles. Obviously, it would be perfectly ok to derive adult from age.

How can this be implemented in Django? Where should I write the piece of code that makes the math to derive adult/age? Inside models.py, forms.py, views.py or maybe inside the template?

P.S. I know it looks weird to see the age attribute been declared as a DurationField, but as I was saying I am trying to experiment with different attribute fields. If the answer to my question requires it to be changed into, say, PositiveInteger, I don't mind changing it.

My models.py looks like this

from django.db import models


# Create your models here.
class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=30)
    adult = models.BooleanField(default=False)
    date_of_birth = models.DateField(default=None)
    created_on = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated_on = models.DateTimeField(auto_now_add=False, auto_now=True)
    age = models.DurationField(default=None)

my forms.py is the following

from django import forms
from .models import Person


class CreatePersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = [
            'first_name',
            'last_name',
            'adult',
            'date_of_birth',
            'age',

        ]

And here is my views.py

from django.shortcuts import render
from .models import Person
from .forms import CreatePersonForm


# Create your views here.
def home_view(request):
    return render(request, 'home.html')


def persons_list_view(request):
    persons = Person.objects.all()
    context = {
        'persons': persons
    }
    return render(request, 'persons_list.html', context)


def create_person_view(request):
    if request.method == 'POST':
        form = CreatePersonForm(request.POST)
        persons = Person.objects.all()
        context = {
            'persons': persons
        }
        if form.is_valid():
            instance = form.save(commit=False)
            instance.save()
            return render(request, 'persons_list.html', context)
    else:
        form = CreatePersonForm()
    context = {'form': form}
    return render(request, 'create_person.html', context)

Thanks for any help in advance

EDIT

I had a go at writing the subtraction in views.py (as suggested), and ran all the migrations, but when I try to create a new person on the localhost I get the following error:

Exception Type: TypeError
Exception Value:    
unsupported operand type(s) for -: 'DeferredAttribute' and 'DeferredAttribute'

which seems to be caused by this line of code if (Person.date_today - Person.date_of_birth) >= 18:. I have also tried this solution, which involves models.py rather than views.py, but I get the same error. I am also, trying to find something on the Django documentation, but I have to say it's not really 'beginners friendly'.

Probably I should mention that I only have a basic Python knowledge, and I might be pushing things a bit too far.

I forgot to upload the code I have written:

def create_person_view(request):
    if (Person.date_today - Person.date_of_birth) >= 18:
        Person.adult=True
    if request.method == 'POST':
        form = CreatePersonForm(request.POST)
        persons = Person.objects.all()
        context = {
            'persons': persons
        }
        if form.is_valid():
            instance = form.save(commit=False)
            instance.save()
            return render(request, 'persons_list.html', context)
    else:
        form = CreatePersonForm()
    context = {'form': form}
    return render(request, 'create_person.html', context)

And this is what I assed in models.py

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=30)
    adult = models.BooleanField(default=False)
    date_of_birth = models.DateField(default=None)
    date_today = models.DateField(auto_now=True)

EDIT 2

I am now trying this solution from the python documentation (in the methods section, rearranging the baby boomer status), which, however, does the calculation inside models.py rather than views.py (as suggested in the comments to this question).

So I have tried something like:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=30)
    adult = models.BooleanField(default=False)
    date_of_birth = models.DateField(default=None)

    def __str__(self):
        return '%s %s' % (self.first_name, self.last_name)

    def is_adult(self):
        import datetime
        if (datetime.date.today() - self.date_of_birth) > datetime.timedelta(days=18*365):
            self.adult = True

This time I don't get any error, but when I try to create a person born on 1985-04-28 with the admin and save it, adult remains false. Does anyone have any idea of how to actually implement this?

Techoplite
  • 605
  • 9
  • 23

2 Answers2

2

To answer your revised question, the reason adult remains false is because you are not saving the updated model object instance at the end of your method. What you probably want to do is update the adult field at the time of save, or use a post_save signal.

Presuming your age calculation logic in your is_adult() method is correct, all you should have to do is override the save method on the model like so:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=30)
    adult = models.BooleanField(default=False)
    date_of_birth = models.DateField(default=None)

    def __str__(self):
        return '%s %s' % (self.first_name, self.last_name)

    def is_adult(self):
        import datetime
        if (datetime.date.today() - self.date_of_birth) > datetime.timedelta(days=18*365):
            self.adult = True

    def save(self, *args, **kwargs):
        self.is_adult()
        super(MyModel, self).save(*args, **kwargs)
Rob
  • 1,656
  • 2
  • 17
  • 33
  • Thank you for your answer. I think that apart for the save method you mentioned (save), the problem here is that I can't make the calculation working, possibly due to the data field and the type of data it returns. Do you have any idea of how to do it? I have tried different solutions found on satckoverflow and other places, such as int(datetime.now().date() - self.date_of_birth).days > datetime.timedelta(days=18*365) but they don't work. Is there any definitive source I can look at? beacuse as I said in the post the official documentation is a bit difficult to follow – Techoplite Dec 02 '19 at 00:54
  • I’d like to help you, but you can’t do sprawling questions like this on SO. You can create another question focused on calculating date. – Rob Dec 02 '19 at 03:55
  • Ok, I will post a new question, sorry about that. – Techoplite Dec 02 '19 at 09:40
  • It is okay. If, after you figure this out and find my answer is correct please mark it so. – Rob Dec 02 '19 at 17:05
1

My take would be to put such calculations into your views.py where the user inputs his date_of_birth into the form. After he submits the form it then calculates if the user is > or < 18/21 and return true/false for adult accordingly. Then push this to your model.
Alternatively you could implement a logic that automatically displays adult or no adult within the view's form upon user input for date_of_birth. Really depends on which solution you want. Should the user see the adult or no adult field right after his input or should it be some backend process?

JSRB
  • 2,492
  • 1
  • 17
  • 48
  • Thanks for your answer. As I am practising with Django I'd rather see everything is going on, so I would go for the second hint. In both cases anyway, the calculation is made up in views.py, right? – Techoplite Dec 01 '19 at 08:31
  • This is common practice, yes. – JSRB Dec 01 '19 at 08:35
  • I have been on it all day, but I can't really figure out how to actually write the code... – Techoplite Dec 01 '19 at 14:21