Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
429 views
in Technique[技术] by (71.8m points)

python - Year Field in Django

I want my users to enter their birth year. I don't want them to type the same in the form rather select the year from available options. I known that I can do something like this in my model if I needed to date instead of year:

class MyModel(models.Model):

    birthday = models.DateField(null=True, blank=True)

I can do this in forms to let the user choose date from datepicker.

    birthday = forms.fields.DateField(widget=forms.widgets.DateInput(attrs={'type': 'date'}))

For year, I can use a CharField/IntegerField with choices similar to what has been done in this SO answer.

import datetime
YEAR_CHOICES = [(r,r) for r in range(1984, datetime.date.today().year+1)]

year = models.IntegerField(_('year'), choices=YEAR_CHOICES, default=datetime.datetime.now().year)

The problem, however, is that change of current year from say, 2018 to 2019, will not change the available options.

Can you help or provide hints to achieve what I want to do?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

My initial thought was to write a callable that returns the choices, that will be evaluated for each request.

import datetime

def year_choices():
    return [(r,r) for r in range(1984, datetime.date.today().year+1)]

def current_year():
    return datetime.date.today().year

class MyModel(models.Model):
    year = models.IntegerField(_('year'), choices=year_choices, default=current_year)

However this doesn't work, because Django's check framework doesn't allow the year_choices to be used as the default. Even if you could hack the choices to be generated dynamically, it would have the disadvantage that Django would try to create a migration each year when the choices change.

You can avoid this by generating the choices at the form level instead. You can use validators in the model to prevent invalid data. Note that MaxValueValidator is wrapped in a function max_value_current_year to avoid a new migration every year.

import datetime
from django.core.validators import MaxValueValidator, MinValueValidator

def current_year():
    return datetime.date.today().year

def max_value_current_year(value):
    return MaxValueValidator(current_year())(value)    

class MyModel(models.Model):
    year = models.IntegerField(_('year'), validators=[MinValueValidator(1984), max_value_current_year])

def year_choices():
    return [(r,r) for r in range(1984, datetime.date.today().year+1)]

class MyForm(forms.ModelForm):
    year = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=current_year)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...