prepopulate form with model instance values using UpdateView and ModelForm

2,998 views
Skip to first unread message

dauer.benjamin

unread,
Jan 18, 2017, 7:46:15 AM1/18/17
to Django users
Hey folks,

Question for you all. Appreciate any insight you can give. So I have a class-based view (UpdateView) and a ModelForm. I want the form to pre-populate with data from a model instance. I've googled around a lot, couldn't find a plug n' play solution. I have a partial solution but it feels really hacky. It's also getting to the point where it's not working so well. I figure there must be a more djangoistic way to get this done. Here's the view from views.py. selected_task is from a hidden input where I'm storing the ID. I think there's probably a better way to do that too, but this was working for me.

class ModifyTaskView(LoginRequiredMixin, generic.UpdateView):
   
"""
    Update an existing task.
    """

    form_class
=ModifyTaskForm
    template_name
= 'tracktasks/modifytask.html'




   
def form_valid(self, form):
        form
.instance.user = self.request.user


       
if form.instance.is_timed:
           
# convert from seconds to minutes
            form
.instance.total_time *=60
            form
.instance.remaining_time = form.instance.total_time


       
return super(ModifyTaskView, self).form_valid(form)


   
def get_initial(self):


        initial
= super(ModifyTaskView, self).get_initial()
        task
= Task.objects.get(pk=self.request.POST['selected_task'])
        initial
['name'] = task.name
        initial
['date_type'] = task.date_type
        initial
['is_timed'] = task.is_timed
        initial
['date'] = task.date
        initial
['total_time'] = task.total_time
        initial
['recurring'] = task.recurring
       
return initial


   
def get_object(self):
       
print(self.request.POST)
        obj
= Task.objects.get(pk=self.request.POST['selected_task'])
       
return obj;


   
def get_success_url(self):
       
return reverse_lazy('tracktasks:index')


Here's the form from forms.py, with the leading function being my current solution and the source of much headache, very nowhere going.

def display_field_values(fields, kwargs):
   
for field in fields:
        widget
= fields[field].widget
        field_type
= widget.__class__.__name__
        initial_value
= kwargs['initial'][field]


       
if any(item in field_type for item in ["TimeInput", "TextInput"]):
            widget
.attrs['value'] = initial_value


       
elif field_type == "CheckboxInput":
           
if fields[field].widget.check_test(initial_value):
                widget
.attrs['checked'] = ''
       
elif field_type == "Select":
           
print(field_type)
           
for choice in widget.choices:
               
if initial_value in choice:
                   
print(choice)
                   
print(initial_value)
                   
# print(widget.render_option(selected_choices=initial_value, option_value=choice[0], option_label=choice[1]))


       
elif field_type == "SelectDateWidget":
           
print(widget.data)


class ModifyTaskForm(forms.ModelForm):




   
def __init__(self, *args, **kwargs):
       
super(ModifyTaskForm, self).__init__(*args, **kwargs)


        display_field_values
(self.fields, kwargs)




   
class Meta:
        model
= Task
        fields
= ['id','name', 'date_type', 'date',
                 
'is_timed', 'total_time', 'recurring']
        widgets
= {
               
'date':  forms.SelectDateWidget(),
               
'total_time': forms.TimeInput(),
       
}
        help_texts
= {
           
'total_time': ('minutes.'),
   
}



So yeah, any insight, guidance or free advice would be a huge help. Someday I hope to return the favor by helping somebody else out. Just one person though. Don't want to go overboard.

Thanks again!

Ben



Melvyn Sopacua

unread,
Jan 18, 2017, 8:51:23 AM1/18/17
to django...@googlegroups.com

On Wednesday 18 January 2017 04:44:01 dauer.benjamin wrote:

 

> Question for you all. Appreciate any insight you can give. So I have a

> class-based view (UpdateView) and a ModelForm. I want the form to

> pre-populate with data from a model instance.

 

It feels like you're doing work that's already done properly when you setup your URLs right.

The operations work like this:

  • Create view: doesn't get an instance ID (but creates one) and fills form with values set by defaults keyword argument on model fields.
  • Update view: gets an instance ID and fills it with field values from the instance. Any missing values get assigned defaults.
  • Delete view: gets an instance ID and removes it. Field values typically do not come into play.

Does this conflict with what you're trying to accomplish?

--

Melvyn Sopacua

Benjamin Dauer

unread,
Jan 18, 2017, 9:28:00 AM1/18/17
to django...@googlegroups.com
Hi Melvyn,

That's exactly what I'm trying to do, but prior to making the changes it wasn't prepopulating. At each stage I tested and was getting blank fields albeit based off of the correct model.

Maybe it's because I don't provide the pk in the URL? I was trying to avoid that.

--


You received this message because you are subscribed to the Google Groups "Django users" group.


To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.


To post to this group, send email to django...@googlegroups.com.


Visit this group at https://groups.google.com/group/django-users.


To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/2825798.hbvF57IJau%40devstation.


For more options, visit https://groups.google.com/d/optout.


--
Sent from Gmail Mobile

Melvyn Sopacua

unread,
Jan 18, 2017, 8:22:54 PM1/18/17
to django...@googlegroups.com

On Wednesday 18 January 2017 14:26:52 Benjamin Dauer wrote:

> Hi Melvyn,

>

> That's exactly what I'm trying to do, but prior to making the changes

> it wasn't prepopulating. At each stage I tested and was getting blank

> fields albeit based off of the correct model.

>

> Maybe it's because I don't provide the pk in the URL? I was trying to

> avoid that.

 

Gotcha! By default the class-based generic views provide two ways to identify the instance:

- pk

- slug

 

Look at slug_field and slug_url_kwarg here.

 

But in the end, this is passed to get_object() (method also shown on that handy page) and this shall return the instance to be shown.

The tricky part about passing this in a form, is that you're fighting the order: the form is processed after the instance is loaded.

 

The best way to go is to put something in the URL that uniquely identifies the object and then override get_object() to work with that. Note that little added security is gained from hiding the PK, but if user-friendly URLs is what you're after, slugs (or some other field(s) with unique=True) are the way to go.

 

--

Melvyn Sopacua

Sergiy Khohlov

unread,
Jan 19, 2017, 2:57:32 AM1/19/17
to django-users
Hello, 
 I had a similar problem  (but I'm using DetailView for sending some additional data) and I hope I  can help
 Usually editing page  is  using GET for receiving  default data and POST for sending  data from  form to  view ( we are skipping ajax way)
1) You would like to set initial data to the page but  default get is not customized 
Could you please  add    next code to view 
def get(self, request, *args, **kwargs):
    """  Default  GET view"""
    print ("Default view is calliable")
    return super(ModifyTaskView, self).get(request, *args, **kwargs) 


2) Also  have you seen any printing of your form ? 


 I would like to separate task in two simplest task 
 1) Verify if view is preparing data for form 
2) Verifing populating form by data 

Many thanks,

Serge


+380 636150445
skype: skhohlov

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
Message has been deleted

Benjamin Dauer

unread,
Jan 19, 2017, 8:05:37 AM1/19/17
to django...@googlegroups.com
Hi Sergiy,

Thanks for your help!

1) I tried overriding the GET request as you suggested and the print statement never went through. It looks like by default it calls `get_object()`, and my `get_object()` code is running, so I'm not sure why it doesn't appear.

2) The form is appearing with the correct fields but it wasn't populating. All of the weird overrides I had in `display_field_values()` were populating the fields but it looks like for some widgets that's not a good solution (I mean, probably a bad solution even when I could get it to work.) Those were running in the form's `__init__`.

I think at this point I may just take Melvyn's advice and include the pk or a slug in the url. Appreciate the help though. If you're able to share the solution that you found to your problem I'd be very interested to see it.

On Thu, Jan 19, 2017 at 8:02 AM, Benjamin Dauer <dauer.b...@gmail.com> wrote:
Hi Sergiy,

Thanks for your help!

1) I tried overriding the GET request as you suggested and the print statement never went through. It looks like by default it calls `get_object()`, and my `get_object()` code is running, so I'm not sure why it doesn't appear.

2) The form is appearing with the correct fields but it wasn't populating. All of the weird overrides I had in `display_field_values()` were populating the fields but it looks like for some widgets that's not a good solution (I mean, probably a bad solution even when I could get it to work.) Those were running in the form's `__init__`.

I think at this point I may just take Martin's advice and include the pk or a slug in the url. Appreciate the help though. If you're able to share the solution that you found to your problem I'd be very interested to see it.



Reply all
Reply to author
Forward
0 new messages