Je vais garder des données sur les compétitions dans ma base de données. Je veux pouvoir rechercher les compétitions selon certains critères - type de compétition en particulier.
Les types de compétition sont conservés dans un tuple. Un exemple légèrement raccourci:
COMPETITION_TYPE_CHOICES = (
(1, 'Olympic Games'),
(2, 'ISU Championships'),
(3, 'Grand Prix Series'),
)
Celles-ci sont utilisées dans le modèle comme suit (encore une fois - il s'agit d'une version abrégée/simplifiée du modèle):
class Competition(models.Model):
name = models.CharField(max_length=256)
type = models.IntegerField(choices=COMPETITION_TYPE_CHOICES)
Je ne veux pas que les champs soient obligatoires dans le formulaire de recherche, le formulaire est donc défini comme suit:
class CompetitionSearchForm(forms.Form):
name = forms.CharField(required=False)
type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)
J'aimerais que le widget sélectionné dans ChoiceField affiche une étiquette vide, mais je n'en ai pas. Toute aide à ce sujet serait très appréciée :)
J'ai trouvé une solution qui fonctionne comme je le souhaite sans violer le principe DRY. Pas très propre, mais il faudra que je suppose.
Selon la documentation choix ne doit pas nécessairement être un tuple:
Enfin, notez que les choix peuvent être n’importe lequel objet itérable - pas nécessairement un liste ou tuple. Cela vous permet de construire choix dynamiquement. Mais si vous trouvez vous-même le piratage des choix pour être dynamique, vous êtes probablement mieux lotis en utilisant une table de base de données appropriée avec un Clé étrangère. choice est destiné à données statiques qui ne changent pas beaucoup, si jamais.
Donc, la solution que je vais pour le moment est la suivante:
COMPETITION_TYPE_CHOICES = [
(1, 'Olympic Games'),
(2, 'ISU Championships'),
(3, 'Grand Prix Series'),
]
COMP_TYPE_CHOICES_AND_EMPTY = [('','All')] + COMPETITION_TYPE_CHOICES
Et alors:
class CompetitionSearchForm(forms.Form):
name = forms.CharField(required=False)
type = forms.ChoiceField(choices=COMP_TYPE_CHOICES_AND_EMPTY, required=False)
Le modèle reste le même.
J'ai essayé les solutions de Monika et d'Evgeniy sans succès, mais Monika a un bon point en ce sens que les choix ne doivent pas nécessairement être des tuples. Par conséquent, la solution la plus simple (et la plus sèche) consiste simplement à faire ce que Django fait déjà dans le champ Modèle. Ajoutez simplement le choix vide et les n-uplets ensemble après les avoir convertis en une liste:
from Django.db.models.fields import BLANK_CHOICE_DASH
...
type = forms.ChoiceField(choices=BLANK_CHOICE_DASH + list(COMPETITION_TYPE_CHOICES), required=False)
Un meilleur choix consiste à mettre à jour les choix de champs sous la forme init method
COMPETITION_TYPE_CHOICES = (
(1, 'Olympic Games'),
(2, 'ISU Championships'),
(3, 'Grand Prix Series'),
)
class CompetitionSearchForm(forms.Form):
name = forms.CharField(required=False)
type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)
def __init__(self, *args, **kwargs):
super(CompetitionSearchForm, self).__init__(*args, **kwargs)
self.fields['type'].choices.insert(0, ('','---------' ) )
Selon la documentation:
Soit un élément itérable (par exemple, une liste ou un nuplet) de 2 tuples à utiliser comme choix pour ce champ, ou un objet appelable qui renvoie un tel élément itérable ( https://docs.djangoproject.com/en/dev/ref/forms/fields/ )
Donc, vous pouvez simple:
sample_field = forms.ChoiceField(choices=(('', '---'),) + Model.YOUR_CHOICES)
Juste un petit changement dans la réponse d'Evgeniy qui vérifie si l'alternative vide n'est pas déjà ajoutée.
Sans la vérification (au moins lors de l'exécution du serveur d'exécution intégré), une étiquette vide supplémentaire est ajoutée pour chaque rechargement de page.
COMPETITION_TYPE_CHOICES = (
(1, 'Olympic Games'),
(2, 'ISU Championships'),
(3, 'Grand Prix Series'),
)
class CompetitionSearchForm(forms.Form):
name = forms.CharField(required=False)
type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)
def __init__(self, *args, **kwargs):
super(CompetitionSearchForm, self).__init__(*args, **kwargs)
if not self.fields['type'].choices[0][0] == '':
self.fields['type'].choices.insert(0, ('','---------' ) )
Essayez d’ajouter blank=True
aux champs du modèle (en supposant que ce soit le comportement souhaité), puis de remplacer le formulaire par un ModelForm et de supprimer les définitions de champ. Notez que les champs pour lesquels vous définissez blank=True
ne seront pas obligatoires lors de la validation ou de l'enregistrement du modèle. Encore une fois, ce n'est peut-être pas ce que vous voulez, mais si c'est le cas, cela permettra à Django de prendre en charge automatiquement certaines tâches.
Sinon changez simplement votre COMPETITION_TYPE_CHOICES
en:
COMPETITION_TYPE_CHOICES = (
('', '---------'),
('1', 'Olympic Games'),
('2', 'ISU Championships'),
('3', 'Grand Prix Series'),
)
Pourquoi n'utilisez-vous pas ModelForm si vous avez déjà une classe de modèle?
Meilleure solution:
forms.py
class CompetitionSearchForm(ModelForm):
class Meta:
model = Competition
models.py
class Competition(models.Model):
name = models.CharField(max_length=256)
type = models.IntegerField(choices=COMPETITION_TYPE_CHOICES, default=COMPETITION_TYPE_CHOICES[0][0], blank=True)
Vous pouvez définir blank = False pour supprimer empty_label de la liste
Un peu tard pour la fête ..
Pourquoi ne pas modifier les choix du tout et simplement le manipuler avec un widget?
from Django.db.models import BLANK_CHOICE_DASH
class EmptySelect(Select):
empty_value = BLANK_CHOICE_DASH[0]
empty_label = BLANK_CHOICE_DASH[1]
@property
def choices(self):
yield (self.empty_value, self.empty_label,)
for choice in self._choices:
yield choice
@choices.setter
def choices(self, val):
self._choices = val
Alors appelez ça:
class CompetitionSearchForm(forms.Form):
name = forms.CharField(required=False)
type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False, widget=EmptySelect)
Voici ce avec quoi vous vous retrouvez:
print(CompetitionSearchForm().as_p())
<p>
<label for="id_name">Name:</label>
<input id="id_name" name="name" type="text" />
</p>
<p>
<label for="id_type">Type:</label>
<select id="id_type" name="type">
<option value="" selected="selected">------</option>
<option value="1">Olympic Games</option>
<option value="2">ISU Championships</option>
<option value="3">Grand Prix Series</option>
</select>
</p>