Étant donné un ensemble de modèles typiques:
# Application A from django.db import models class TypicalModelA(models.Model): the_date = models.DateField() # Application B from django.db import models class TypicalModelB(models.Model): another_date = models.DateField() ...
Comment peut-on changer le widget par défaut pour tous les DateFields en un MyDateWidget personnalisé?
Je demande parce que je veux que mon application ait un datepicker jQueryUI pour la saisie des dates.
J’ai considéré un champ personnalisé qui étend django.db.models.DateField avec mon widget personnalisé. Est-ce la meilleure façon de mettre en œuvre ce type de changement généralisé? Un tel changement nécessitera d’importer spécifiquement un champ spécial MyDateField dans chaque modèle, ce qui demande beaucoup de travail, est sujet aux erreurs de développement (quelques modèles vont disparaître) et dans mon esprit, cela semble une duplication inutile des efforts. Par contre, je n’aime pas modifier ce qui pourrait être considéré comme la version canonique de models.DateField.
Les pensées et les commentaires sont appréciés.
Vous pouvez déclarer un atsortingbut sur votre classe ModelForm
, appelée formfield_callback
. Ce devrait être une fonction qui prend une instance de Field
modèle de Django comme argument et renvoie une instance de Field
formulaire pour la représenter dans le formulaire.
Il suffit ensuite de voir si le champ du modèle transmis est une instance de DateField
et, si tel est le cas, renvoie votre champ / widget personnalisé. Dans le cas contraire, le champ du modèle aura une méthode nommée formfield
que vous pouvez appeler pour renvoyer son champ de formulaire par défaut.
Donc, quelque chose comme:
def make_custom_datefield(f): if isinstance(f, models.DateField): # return form field with your custom widget here... else: return f.formfield() class SomeForm(forms.ModelForm) formfield_callback = make_custom_datefield class Meta: # normal modelform stuff here...
Bien, créer un champ de modèle personnalisé juste pour changer le widget de formulaire par défaut n’est pas vraiment l’endroit idéal pour commencer.
Vous pouvez créer votre propre widget de formulaire et remplacer le champ du formulaire, en spécifiant votre propre widget, comme dans la réponse de Soviut.
Il y a aussi un moyen plus court:
class ArticleForm(ModelForm): pub_date = DateField(widget=MyDateWidget()) class Meta: model = Article
Il y a un exemple de la façon d’écrire des widgets de formulaire, c’est quelque part dans le paquet de formulaires de Django. C’est un datepicker avec 3 menus déroulants.
Ce que je fais habituellement lorsque je veux simplement append du JavaScript à un élément d’entrée HTML standard est de le laisser tel quel et de le modifier en référençant son identifiant ultérieurement avec JavaScript. Vous pouvez facilement saisir la convention de dénomination des identifiants des champs de saisie générés par Django.
Vous pouvez également fournir la classe du widget lorsque vous la remplacez dans le formulaire. Ensuite, attrapez-les tous avec jQuery par le nom de la classe.
Cet article m’a aidé à plusieurs resockets.
Le principe consiste à remplacer la méthode __init__ de __init__
, puis à appeler la méthode __init__
la super classe, puis à ajuster les champs individuellement.
class PollForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(PollForm, self).__init__(*args, **kwargs) self.fields['question'].widget = forms.Textarea() class Meta: model = Poll
Cette méthode peut sembler plus compliquée que celle de Vasil, mais elle offre l’avantage supplémentaire de pouvoir remplacer avec précision tout atsortingbut sur un champ sans réinitialiser d’autres atsortingbuts en le déclarant à nouveau.
UPDATE: L’ approche suggérée pourrait être généralisée pour changer tous les champs de date sans taper chaque nom ssortingctement:
from django.forms import fields as formfields from django.consortingb.admin import widgets class PollForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(PollForm, self).__init__(*args, **kwargs) for field_name in self.fields: field = self.fields[field_name] if isinstance(field, formfields.DateField): field.widget = widgets.AdminDateWidget() class Meta: model = Poll
Cela a fonctionné pour moi sur python3
et django 1.11
J’utilise JQuery. Il suffit de rechercher l’identifiant des champs que vous souhaitez associer au sélecteur de date et de les lier avec JQuery et le bon format d’affichage:
models.py
class ObjectForm(ModelForm): class Meta: model = Object fields = ['FieldName1','FieldName2']
en haut de la page que vous affichez avec votre vue:
... {{form}}
Vous voulez définir un widget personnalisé et utiliser la classe Media interne du widget pour définir les fichiers JS (et CSS?) À inclure dans la page pour que le widget fonctionne. Si vous le faites correctement, vous pouvez rendre votre widget complètement autonome et réutilisable. Voir django-markitup pour un exemple de la façon de procéder (il contient un widget réutilisable pour l’ éditeur de balisage universel MarkItUp! ).
Utilisez ensuite formfield_callback (voir la réponse de James Bennett) pour appliquer facilement ce widget à tous les DateField dans un formulaire.
Certains pourraient se plaindre à ce sujet, mais pour remplacer le sélecteur de date par votre widget personnalisé, je créerais une application Monkeypatch pour votre projet et patcherais Django lui-même au moment de l’exécution. L’avantage de ceci est que toutes les applications tierces seront également effectuées et présenteront donc une interface uniforme à l’utilisateur final sans avoir à modifier le code tiers:
from django.forms.widgets import DateInput , DateTimeInput, TimeInput from FOO.widgets import MyjQueryWidget # be nice and tell you are patching logger.info("Patching 'DateInput.widget = MyjQueryWidget': Replaces django DateInput to use my new super 'MyjQueryWidget'") # be nicer and confirm signature of code we are patching and warn if it has changed - uncomment below to get the current hash fingerprint # raise Exception(hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest()) # uncommet to find latest hash if not '' == \ hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest(): logger.warn("md5 signature of 'DateInput.widget' does not match Django 1.5. There is a slight chance patch " "might be broken so please compare and update this monkeypatch.") # be nicest and also update __doc__ DateInput.__doc__ = "*Monkeypatched by *: Replaced django DateInput.widget with my new super 'MyjQueryWidget'" + DateInput.__doc__ DateInput.widget = MyjQueryWidget
Ce qui précède est inspiré de mon html5monkeypatch que j’utilise dans le cadre de mes projets, jetez un oeil à patch_widgets.py et patch_fields.py .