Quiero que mis usuarios ingresen su año de nacimiento. No quiero que escriban lo mismo en el formulario, sino que seleccionen el año entre las opciones disponibles. Sabía que podía hacer algo como esto en mi modelo si necesitaba una fecha en lugar de un año:
class MyModel(models.Model): birthday = models.DateField(null=True, blank=True)
Puedo hacer esto en formularios para permitir que el usuario elija la fecha del selector de fechas.
birthday = forms.fields.DateField(widget=forms.widgets.DateInput(attrs={'type': 'date'}))
Por año, puedo usar CharField/IntegerField
con choices
similares a las que se han hecho en esta respuesta SO .
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)
El problema, sin embargo, es que el cambio del año actual de, digamos, 2018 a 2019, no cambiará las opciones disponibles.
¿Pueden ayudarme o darme pistas para lograr lo que quiero hacer?
Mi pensamiento inicial fue escribir un invocable que devuelva las opciones, que se evaluarán para cada solicitud.
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)
Sin embargo, esto no funciona, porque el marco de verificación de Django no permite que year_choices se use por defecto. Incluso si pudiera piratear las opciones para que se generen dinámicamente, tendría la desventaja de que Django intentaría crear una migración cada año cuando cambien las opciones.
Puede evitar esto generando las opciones en el nivel de formulario. Puede utilizar validadores en el modelo para evitar datos no válidos. Tenga en cuenta que MaxValueValidator
está envuelto en una función max_value_current_year
para evitar una nueva migración cada año.
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)
Puede poner las opciones (años) en el formulario, usando IntegerField min_value y max_value . En el modelo, puede usar IntegerField sin opciones.
Así, no te preocuparás por el cambio de año, porque solo cambiarás las opciones del formulario.
Si desea cambiar el año automáticamente, esto podría ayudarlo: Django Forms integerField establece max_value en tiempo de ejecución
Para aquellos que no prefieren tener campos de elección largos, es decir, en la solución @Alasdair, imaginen tener un campo de elección desde 1984 hasta 2019, una lista de elección tan larga. ¿Qué tal tener un campo en el que puede escribir el año y aún así tener una forma de incrementar o disminuir? Para resolver esto a mi manera, use models.PositiveIntegerField con MinValueValidator y MaxValueValidator como se muestra a continuación:
import datetime from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models def current_year(): return datetime.date.today().year def max_value_current_year(value): return MaxValueValidator(current_year())(value) class EmployeesTrainings(models.Model): name = models.ForeignKey(employee, default='', blank=True, null=True, on_delete=models.CASCADE) training_name = models.TextField(max_length=200, default='', blank=False, null=False) training_year = models.PositiveIntegerField( default=current_year(), validators=[MinValueValidator(1984), max_value_current_year])