Dans un formulaire Django, comment puis-je rendre un champ en lecture seule (ou désactivé)?
Lorsque le formulaire est utilisé pour créer une nouvelle entrée, tous les champs doivent être activés - mais lorsque l'enregistrement est en mode de mise à jour, certains champs doivent être en lecture seule.
Par exemple, lors de la création d'un nouveau Item
modèle, tous les champs doivent être modifiables, mais lors de la mise à jour de l'enregistrement, existe-t-il un moyen de désactiver le sku
champ pour qu'il soit visible, mais ne puisse pas être modifié?
class Item(models.Model):
sku = models.CharField(max_length=50)
description = models.CharField(max_length=200)
added_by = models.ForeignKey(User)
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ('added_by')
def new_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Validate and save
else:
form = ItemForm()
# Render the view
La classe peut-elle ItemForm
être réutilisée? Quels changements seraient nécessaires dans la classe ItemForm
ou le Item
modèle? Aurais-je besoin d'écrire une autre classe, " ItemUpdateForm
", pour mettre à jour l'élément?
def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
Réponses:
Comme indiqué dans cette réponse , Django 1.9 a ajouté l' attribut Field.disabled :
Avec Django 1.8 et versions antérieures, pour désactiver l'entrée sur le widget et empêcher les hacks POST malveillants, vous devez nettoyer l'entrée en plus de définir l'
readonly
attribut dans le champ du formulaire:Ou, remplacez
if instance and instance.pk
par une autre condition indiquant que vous modifiez. Vous pouvez également définir l'attributdisabled
dans le champ de saisie au lieu dereadonly
.La
clean_sku
fonction s'assurera que lareadonly
valeur ne sera pas remplacée par aPOST
.Sinon, il n'y a pas de champ de formulaire Django intégré qui rendra une valeur tout en rejetant les données d'entrée liées. Si c'est ce que vous désirez, vous devez plutôt créer un
ModelForm
élément séparé qui exclut le ou les champs non modifiables, et les imprimer simplement dans votre modèle.la source
clean_description
méthode à la classe de formulaire.disabled
est ajouté dans Django 1.9. SiField.disabled
est défini surTrue
, la valeur POST pour celaField
est ignorée. Donc, si vous utilisez 1.9, il n'est pas nécessaire de remplacerclean
, il suffit de définirdisabled = True
. Vérifiez cette réponse.Django 1.9 a ajouté l'attribut Field.disabled: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled
la source
disabled=True
entraînera le crachat du modèle à l'utilisateur avec des erreurs de validation.La définition
readonly
d'un widget ne fait que l'entrée dans le navigateur en lecture seule. L'ajout d'unclean_sku
qui renvoieinstance.sku
garantit que la valeur du champ ne changera pas au niveau du formulaire.De cette façon, vous pouvez utiliser le modèle (sauvegarde non modifiée) et éviter d'obtenir l'erreur de champ requise.
la source
return self.cleaned_data['sku']
ce aussi bon ou meilleur? Les documents semblent suggérer d'utilisercleaned_data
: "La valeur de retour de cette méthode remplace la valeur existante danscleaned_data
, il doit donc s'agir de la valeur du champcleaned_data
(même si cette méthode ne l'a pas modifiée) ou d'une nouvelle valeur nettoyée."réponse de awalker m'a beaucoup aidé!
J'ai changé son exemple pour travailler avec Django 1.3, en utilisant get_readonly_fields .
Habituellement, vous devez déclarer quelque chose comme ceci dans
app/admin.py
:Je me suis adapté de cette façon:
Et ça marche bien. Maintenant, si vous ajoutez un élément, le
url
champ est en lecture-écriture, mais en cas de modification, il devient en lecture seule.la source
Pour que cela fonctionne pour un
ForeignKey
champ, quelques modifications doivent être apportées. Premièrement, laSELECT HTML
balise n'a pas l'attribut readonly. Nous devons utiliserdisabled="disabled"
place. Cependant, le navigateur n'envoie aucune donnée de formulaire pour ce champ. Nous devons donc définir ce champ pour qu'il ne soit pas requis afin que le champ soit validé correctement. Nous devons ensuite réinitialiser la valeur à ce qu'elle était pour qu'elle ne soit pas définie sur vide.Donc, pour les clés étrangères, vous devrez faire quelque chose comme:
De cette façon, le navigateur ne permettra pas à l'utilisateur de modifier le champ et restera toujours
POST
tel qu'il a été laissé vide. Nous remplaçons ensuite laclean
méthode pour définir la valeur du champ comme ce qui était à l'origine dans l'instance.la source
TabularInline
, mais j'ai échoué car ilsattrs
étaient partagés entre leswidget
instances et toutes sauf la première ligne, y compris la nouvelle ligne ajoutée, restituée en lecture seule.Pour Django 1.2+, vous pouvez remplacer le champ comme ceci:
la source
Field
disabled
ne fait pas ce que je veux car il désactive le champ, mais supprime également l'étiquette / le rend invisible.J'ai créé une classe MixIn dont vous pouvez hériter pour pouvoir ajouter un champ itérable en lecture seule qui désactivera et sécurisera les champs lors de la première modification:
(Basé sur les réponses de Daniel et Muhuk)
la source
Je viens de créer le widget le plus simple possible pour un champ en lecture seule - je ne vois pas vraiment pourquoi les formulaires ne l'ont pas déjà:
Sous la forme:
Très simple - et me donne juste une sortie. Pratique dans un jeu de formulaires avec un tas de valeurs en lecture seule. Bien sûr, vous pourriez également être un peu plus intelligent et lui donner une div avec les attrs afin que vous puissiez y ajouter des classes.
la source
unicode(value)
dans le retour à la place peut-être. En supposant que le butin unicode est raisonnable, vous obtiendriez alors cela.J'ai rencontré un problème similaire. Il semble que j'ai pu le résoudre en définissant une méthode "get_readonly_fields" dans ma classe ModelAdmin.
Quelque chose comme ça:
La bonne chose est que
obj
sera Aucun lorsque vous ajoutez un nouvel élément, ou ce sera l'objet en cours de modification lorsque vous modifiez un élément existant.get_readonly_display est documenté ici: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods
la source
Une option simple consiste à taper simplement
form.instance.fieldName
le modèle au lieu deform.fieldName
.la source
verbos_name
oulabel
du champ? Comment afficher `label dans le modèle django? @alzclarkeComment je le fais avec Django 1.11:
la source
Comme ajout utile au message de Humphrey , j'ai eu quelques problèmes avec django-reversion, car il enregistrait toujours les champs désactivés comme «modifiés». Le code suivant résout le problème.
la source
Comme je ne peux pas encore commenter ( la solution de muhuk ), je vais répondre comme une réponse distincte. Ceci est un exemple de code complet, qui a fonctionné pour moi:
la source
Encore une fois, je vais offrir une solution de plus :) J'utilisais le code de Humphrey , c'est donc basé sur cela.
Cependant, j'ai rencontré des problèmes avec le domaine étant un
ModelChoiceField
. Tout fonctionnerait à la première demande. Cependant, si le jeu de formulaires tentait d'ajouter un nouvel élément et échouait à la validation, quelque chose n'allait pas avec les formulaires "existants" où l'SELECTED
option était réinitialisée par défaut---------
.Quoi qu'il en soit, je n'ai pas réussi à résoudre ce problème. Donc à la place, (et je pense que c'est en fait plus propre dans la forme), j'ai fait les champs
HiddenInputField()
. Cela signifie simplement que vous devez faire un peu plus de travail dans le modèle.La solution pour moi a donc été de simplifier le formulaire:
Et puis dans le modèle, vous devrez faire une boucle manuelle du formset .
Donc, dans ce cas, vous feriez quelque chose comme ça dans le modèle:
Cela a fonctionné un peu mieux pour moi et avec moins de manipulation de forme.
la source
J'allais dans le même problème alors j'ai créé un Mixin qui semble fonctionner pour mes cas d'utilisation.
Utilisation, définissez simplement ceux qui doivent être en lecture seule:
la source
'collections.OrderedDict' object has no attribute 'iteritems'
Si vous avez besoin de plusieurs champs en lecture seule, vous pouvez utiliser l'une des méthodes indiquées ci-dessous
méthode 1
méthode 2
méthode d'héritage
la source
Deux autres approches (similaires) avec un exemple généralisé:
1) première approche - suppression du champ dans la méthode save (), par exemple (non testé;)):
2) deuxième approche - réinitialiser le champ à sa valeur initiale dans la méthode propre:
Basé sur la deuxième approche, je l'ai généralisé comme ceci:
la source
Pour la version Admin, je pense que c'est un moyen plus compact si vous avez plusieurs champs:
la source
Sur la base de la réponse de Yamikep , j'ai trouvé une solution meilleure et très simple qui gère également les
ModelMultipleChoiceField
champs.La suppression d'un champ
form.cleaned_data
empêche l'enregistrement des champs:Usage:
la source
Voici une version un peu plus impliquée, basée sur la réponse de christophe31 . Il ne repose pas sur l'attribut "en lecture seule". Cela fait disparaître ses problèmes, comme les zones de sélection toujours modifiables et les sélecteurs de données qui apparaissent encore.
Au lieu de cela, il enveloppe le widget de champs de formulaire dans un widget en lecture seule, ce qui rend le formulaire toujours valide. Le contenu du widget d'origine est affiché à l'intérieur des
<span class="hidden"></span>
balises. Si le widget a unerender_readonly()
méthode, il l'utilise comme texte visible, sinon il analyse le code HTML du widget d'origine et essaie de deviner la meilleure représentation.la source
Est-ce le moyen le plus simple?
Dans un code de vue quelque chose comme ceci:
Ça fonctionne bien!
la source
Pour django 1.9+
Vous pouvez utiliser l'argument Champs désactivés pour désactiver le champ. Par exemple, dans l'extrait de code suivant du fichier forms.py, j'ai désactivé le champ employee_code
Référence https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled
la source
Si vous travaillez avec
Django ver < 1.9
(l' attribut1.9
a ajoutéField.disabled
), vous pouvez essayer d'ajouter le décorateur suivant à votre__init__
méthode de formulaire :L'idée principale est que si le champ est,
readonly
vous n'avez besoin d'aucune autre valeur saufinitial
.PS: N'oubliez pas de régler
yuor_form_field.widget.attrs['readonly'] = True
la source
Si vous utilisez l'administrateur Django, voici la solution la plus simple.
la source
Je pense que votre meilleure option serait simplement d'inclure l'attribut readonly dans votre modèle rendu dans un
<span>
ou<p>
plutôt que de l'inclure dans le formulaire s'il est en lecture seule.Les formulaires servent à collecter des données, pas à les afficher. Cela étant dit, les options à afficher dans un
readonly
widget et à nettoyer les données POST sont de bonnes solutions.la source