308

I have a Model as follows:

class TankJournal(models.Model):
    user = models.ForeignKey(User)
    tank = models.ForeignKey(TankProfile)
    ts = models.IntegerField(max_length=15)
    title = models.CharField(max_length=50)
    body = models.TextField()

I also have a model form for the above model as follows:

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput()) 

    class Meta:
        model = TankJournal
        exclude = ('user','ts')

I want to know how to set the default value for that tank hidden field. Here is my function to show/save the form so far:

def addJournal(request, id=0):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/')

    # checking if they own the tank
    from django.contrib.auth.models import User
    user = User.objects.get(pk=request.session['id'])

    if request.method == 'POST':
        form = JournalForm(request.POST)
        if form.is_valid():
            obj = form.save(commit=False)

            # setting the user and ts
            from time import time
            obj.ts = int(time())
            obj.user = user

            obj.tank = TankProfile.objects.get(pk=form.cleaned_data['tank_id'])

            # saving the test
            obj.save()

    else:
        form = JournalForm()

    try:
        tank = TankProfile.objects.get(user=user, id=id)
    except TankProfile.DoesNotExist:
        return HttpResponseRedirect('/error/')
daaawx
  • 3,273
  • 2
  • 17
  • 16
Mike
  • 7,769
  • 13
  • 57
  • 81

9 Answers9

497

You can use Form.initial, which is explained here.

You have two options either populate the value when calling form constructor:

form = JournalForm(initial={'tank': 123})

or set the value in the form definition:

tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 
Sergey Golovchenko
  • 18,203
  • 15
  • 55
  • 72
  • And how is that variable `initial` passed to the actual form? In the actual form model, do we have to write as a parameter/argument? – mgPePe May 04 '11 at 12:03
  • wait, wut? Wut does that mean? Do you mean that, somewhere in the _init_ of the form i'd need something like: tank=initial.tank? Or something? Or would it just happen magically? – bharal Sep 25 '12 at 17:36
  • 2
    @bharal It's magic. ;-) (Or rather, Django form constructors understand the special "initial" variable and iterate through its contents without you needing to do anything extra). Personally, I prefer to specify default values in the form definition and only use the initial=... mechanism if the desired default is a dynamic value (e.g. something entered by a user, or based on their login, geographical location, etc.) – Richard J Foster Sep 26 '12 at 19:43
  • 82
    initial is not default. The initial value does not apply if the form has been bound:(. – clime Jun 22 '13 at 22:28
  • @umnik700 , Is it secure to use forms.IntegerField(widget=forms.HiddenInput(), initial=123) because hackers send other values ? – madhu131313 Jul 22 '13 at 18:09
  • 1
    I swear there was a `default` keyword argument on the `Field` constructor... I can't find the documentation for it now... EDIT: I mistook it for the ModelField reference. FormFields have no `defaul` argument – tutuca Jan 18 '14 at 19:54
  • 5
    Commenting here to mention another usecase where this solution of initial param will help: While sending a registration link to someone if you want to prepopulate the email/id or any other credential on the registration form then in that case you can't write that in the model itself as its dynamic rather you need to call the constructor with _initial_ param as mentioned by @umnik700. Works great! – Keshav Agrawal May 27 '14 at 06:45
  • @bharal might refer to Views using the `form_class=` shortcut, without an explicit constructor. This throws a monkeywrench into the simpler form example code. Hours of fun overriding `View.get_form` just to work around this "shortcut" – Phlip Feb 22 '15 at 16:19
  • 2
    @Phlip: Don't override `View.get_form` to set initial data. Any CBV with a FormMixin (basically all CBVs that create or edit data) can either use a class-level `initial` variable (for unchanging defaults) or you can override `FormMixin.get_initial()` to return a dict of initial values to be passed to the form's constructor (for dynamic defaults.) – GDorn Oct 22 '15 at 20:00
  • `initial` value is passed from the Form to its fields when the form processes its data. But it will not work if the `initial` value is None, because the field checks it like this `if parsed_data is None and initial: ...`. – Hải Phạm Lê Dec 31 '18 at 07:12
  • This doesn't work for me when you pass in form data to the form class. If the field is missing from the form, it doesn't use the default defined in the initial dictionary. – Cerin Sep 03 '21 at 23:28
57

Other solution: Set initial after creating the form:

form.fields['tank'].initial = 123
Andreas Zwerger
  • 713
  • 5
  • 6
  • This is the solution I needed. I have a form with default initial values, but sometimes the user comes back to a page to edit something. In that case the initial values are the previously saved values (dynamic). – Dr Phil Sep 08 '22 at 21:32
25

If you are creating modelform from POST values initial can be assigned this way:

form = SomeModelForm(request.POST, initial={"option": "10"})

https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#providing-initial-values

smunk
  • 314
  • 3
  • 8
quux
  • 900
  • 2
  • 14
  • 24
  • 6
    That `or None` part is really not needed. – smido Sep 09 '18 at 19:59
  • @smido, yes the `or None` part is needed. If the request is a GET request, request.POST will be an empty dictionary which is treated differently than `None` in the Form init method. Please review Django documentation. – Bobort Jul 23 '21 at 16:34
19

I had this other solution (I'm posting it in case someone else as me is using the following method from the model):

class onlyUserIsActiveField(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(onlyUserIsActiveField, self).__init__(*args, **kwargs)
        self.fields['is_active'].initial = False

    class Meta:
        model = User
        fields = ['is_active']
        labels = {'is_active': 'Is Active'}
        widgets = {
            'is_active': forms.CheckboxInput( attrs={
                            'class':          'form-control bootstrap-switch',
                            'data-size':      'mini',
                            'data-on-color':  'success',
                            'data-on-text':   'Active',
                            'data-off-color': 'danger',
                            'data-off-text':  'Inactive',
                            'name':           'is_active',

            })
        }

The initial is definded on the __init__ function as self.fields['is_active'].initial = False

Rafael Ortega
  • 434
  • 5
  • 15
  • thanks! this works! I tried the method in the post above by adding initial while creating the form, somehow it did not work for me. – hehe Jul 22 '22 at 15:29
  • Actually there is an easier way: Just set default value in models. For example: alpha=models.FloatField(max_length=150, verbose_name="Enter an alpha level", default=0.05) – hehe Jul 22 '22 at 15:52
18

As explained in Django docs, initial is not default.

  • The initial value of a field is intended to be displayed in an HTML . But if the user delete this value, and finally send back a blank value for this field, the initial value is lost. So you do not obtain what is expected by a default behaviour.

  • The default behaviour is : the value that validation process will take if data argument do not contain any value for the field.

To implement that, a straightforward way is to combine initial and clean_<field>():

class JournalForm(ModelForm):
    tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123) 

    (...)

    def clean_tank(self):
        if not self['tank'].html_name in self.data:
            return self.fields['tank'].initial
        return self.cleaned_data['tank']
Flimm
  • 136,138
  • 45
  • 251
  • 267
Timothé Delion
  • 1,310
  • 12
  • 17
2

If you want to add initial value and post other value you have to add the following :

or None after request.POST

form = JournalForm(request.POST or None,initial={'tank': 123})

If you want to add files or images also

form = JournalForm(request.POST or None,request.FILES or None,initial={'tank': 123})
Amira Bedhiafi
  • 8,088
  • 6
  • 24
  • 60
1

I hope this can help you:

form.instance.updatedby = form.cleaned_data['updatedby'] = request.user.id
Valerii Boldakov
  • 1,751
  • 9
  • 19
Code47
  • 11
  • 1
0

I also encountered the need to set default values in the form during development. My solution is

initial={"":""}
form=ArticleModel(request.POST)
if form.has_changed():
    data = {i: form.cleaned_data[i] for i in form.changed_data}
    data.update({key: val for key, val in init_praram.items() if key not in form.changed_data})

use form.has_changed ,if form.fields is required you can use this method

xin.chen
  • 964
  • 2
  • 8
  • 24
0

How I added the initial to the form: I read @Sergey Golovchenko answer.

So I just added it to the form in if request.method == 'POST':. But that's not where you place it, if you want to see what value it got before posting the form. You need to put it in the form where the else is.

Example here from views.py

def myForm(request):
    kontext = {}

    if request.method == 'POST':
        # You might want to use clean_data instead of initial here. I found something on a stack overflow question, and you add clean data to the Forms.py, if you want to change the post data. https://stackoverflow.com/questions/36711229/django-forms-clean-data
        form = myModelForm(request.POST, initial={'user': request.user})
        if form.is_valid():
            form.save()
            return redirect('/')
    else:
        # you need to put initial here, if you want to see the value before you post it
        form = myModelForm(initial={'user': request.user})
    kontext['form'] = form
    return render(request, 'app1/my_form.html', kontext)
AnonymousUser
  • 690
  • 7
  • 26