How do you change the default widget for all Django date fields in a ModelForm?

前端 未结 6 1007
夕颜
夕颜 2020-11-30 18:48

Given a set of typical models:

# Application A
from django.db import models
class TypicalModelA(models.Model):
    the_date = models.DateField()

 # Applicat         


        
相关标签:
6条回答
  • 2020-11-30 18:56

    You can declare an attribute on your ModelForm class, called formfield_callback. This should be a function which takes a Django model Field instance as an argument, and returns a form Field instance to represent it in the form.

    Then all you have to do is look to see if the model field passed in is an instance of DateField and, if so, return your custom field/widget. If not, the model field will have a method named formfield that you can call to return its default form field.

    So, something like:

    def make_custom_datefield(f):
        if isinstance(f, models.DateField):
            # return form field with your custom widget here...
        else:
            return f.formfield(**kwargs)
    
    class SomeForm(forms.ModelForm)
        formfield_callback = make_custom_datefield
    
        class Meta:
            # normal modelform stuff here...
    
    0 讨论(0)
  • 2020-11-30 18:56

    This article has helped me numerous times.

    The meat of it involves overriding the ModelForm's __init__ method, then calling the super class' __init__ method, then adjusting the fields individually.

    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
    

    This method may seem more complicated than Vasil's, but it offers the additional benefit of being able to precisely override any attribute on a field without resetting any other attributes by re-declaring it.

    UPDATE: Suggested approach could be generalized to change all date fields without typing each name strictly:

    from django.forms import fields as formfields
    from django.contrib.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
    

    That worked for me on python3 and django 1.11

    0 讨论(0)
  • 2020-11-30 19:04

    Some might frown at this but to replace the date picker with your custom widget I would crate a monkeypatch app for your project and patch Django itself at runtime. Benefit of this is any 3rd-party apps will be effected as well and so present a uniform interface to the end user without having to modify the third party code:

    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 '<enter hexdigest fingerprint here>' == \
            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 <app name>*: Replaced django DateInput.widget with my new super  'MyjQueryWidget'" + DateInput.__doc__ 
    
    DateInput.widget = MyjQueryWidget
    

    The above is inspired from my html5monkeypatch I use as part of my projects, take a look at patch_widgets.py and patch_fields.py.

    0 讨论(0)
  • 2020-11-30 19:15

    Well, making a custom model field just to change it's default form widget is not really the obvious place to start.

    You can make your own form widget and override the field in the form, specifying your own widget like in Soviut's answer.

    There's also a shorter way:

    class ArticleForm(ModelForm):
         pub_date = DateField(widget=MyDateWidget())
    
         class Meta:
             model = Article
    

    There is an example of how to write form widgets, it's somewhere in the forms package of Django. It's a datepicker with 3 dropdowns.

    What I usually do when I just want to add some JavaScript to a standard HTML input element is leave it the way it is and modify it by referencing it's id later with JavaScript. You can easily catch the naming convention for the ids of the input fields Django generates.

    You can also just provide the class for the widget when you override it in the form. Then catch them all with jQuery by the class name.

    0 讨论(0)
  • 2020-11-30 19:21

    I use JQuery. You only have to look for the 'id' of the fields you want to associate with the date picker and bind them with JQuery and the right display format:

    models.py

    class ObjectForm(ModelForm):
        class Meta:
            model = Object        
            fields = ['FieldName1','FieldName2']
    

    at the top of the page you render with your view:

    <head>
        <link type="text/css" href="/media/css/ui-darkness/jquery-ui-1.8.2.custom.css" rel="Stylesheet" /> 
        <script type="text/javascript" src="/media/js/jquery-1.4.2.min.js"></script>
        <script type="text/javascript" src="/media/js/jquery-ui-1.8.2.custom.min.js"></script>
    </head>
    <script type="text/javascript">
     $(function() {
            $("#id_FieldName1").datepicker({ dateFormat: 'yy-mm-dd' });
            $("#id_FieldName2").datepicker({ dateFormat: 'yy-mm-dd' });
     });
    </script>
    ...
    {{form}}
    
    0 讨论(0)
  • 2020-11-30 19:22

    You do want to define a custom widget, and use the widget's inner Media class to define the JS (and CSS?) files that have to be included in the page for the widget to work. If you do this right, you can make your widget completely self-contained and reusable. See django-markitup for one example of doing this (it has a reusable widget for the MarkItUp! universal markup editor).

    Then use formfield_callback (see James Bennett's answer) to easily apply that widget to all DateField's in a form.

    0 讨论(0)
提交回复
热议问题