Django ModelForm not saving data to database
Question: A Django beginner here having a lot of trouble getting forms working. Yes I've
worked through the tutorial and browsed the web a lot - what I have is mix of
what I'm finding here and at other sites. I'm using Python 2.7 and Django 1.5.
(although the official documentation is extensive it tends to assume you know
most of it already - not good as a beginners reference or even an advanced
tutorial)

I am trying to create a form for "extended" user details - eg. company name,
street address, etc but the form data is not being saved to the database.

Initially I tried to create a model extending the standard `User` model, but I
gave up on that - too much needed modifying and it was getting into nitty
gritty that was way beyond me at this stage. So instead I have created a new
model called `UserProfile`:

    
    
    class UserProfile(models.Model):
        user = models.ForeignKey(settings.AUTH_USER_MODEL, unique=True) 
        company = models.CharField(max_length=50, blank=True)
        address1 = models.CharField(max_length=50, blank=True)
        address2 = models.CharField(max_length=50, blank=True)
        city = models.CharField(max_length=20, blank=True)
        region = models.CharField(max_length=20, blank=True)
        postcode = models.CharField(max_length=10, blank=True)
        country = models.CharField(max_length=20, blank=True)
        phone = models.CharField(max_length=30, blank = True)
    

I have seen different references online as to whether I should link to the
User model with a `ForeignKey` (as above) or with a OneToOne.

I am trying to use a ModelForm (keep it simple for what should be a simple
form). Here is my forms.py

    
    
    from django.forms import ModelForm
    from .models import UserProfile   
    
    class UserDetailsForm(ModelForm):
        class Meta:
            model = UserProfile
            fields = ['company','address1','address2','city','region', 'postcode','country','phone']
    

Here is my view:

    
    
    def UserDetailsView(request):
        #f = 0
        if request.method == 'POST':
            f = UserDetailsForm(request.POST, instance = request.user)
            if f.is_valid():
                f.save()
        else:
            f = UserDetailsForm(request.POST , instance = request.user)
    
        print "UserDetails objects: ", (UserProfile.objects.all()) 
    
        return render_to_response('plagweb/console/profile.html', 
                                  { 'form': f}, 
                                  context_instance=RequestContext(request))     
    

(yes there is an inconsistency in UserProfile vs UserDetail - this is a
product of all my hacking and will be fixed once I get it working)

Diagnostics show `f.is_valid()` returning True. Similarly, diagnostics show
`UserProfile.objects.all()` as being empty. I tried this in the above view
after the save(), and also at the Django console.

Here is my template:

    
    
     <form method="POST" action="">
     <table>{{ form }}</table>
     <input type="submit" value="Update" />
     {% csrf_token %}
     </form>
    

At the moment the main problem is that form data is not being saved to the
database. I do not know if is being read yet or not (once I have some data in
the database...)

One thought is that the `User` relationship might be causing a problem?

* * *

_**Addenda, following on from Daniel Roseman's useful comment/help:_**

Now the form is saving correctly (confirmed with diagnostics and command line
checks. However when I go back to the form, it is not displaying the existing
data. A form with empty data fields is displayed. As far as I can tell, I'm
passing the instance data correctly.

Is there a ModelForm setting that needs to change?

Here's the modified View:

    
    
    def UserDetailsView(request):
        #print "request co:", request.user.profile.company
        f = UserDetailsForm(request.POST, instance = request.user.profile )
    
        if request.method == 'POST':
            if f.is_valid():
                profile = f.save(commit=False)
                profile.user = request.user
                profile.save()
    
        return render_to_response('plagweb/console/profile.html', 
                                  { 'form': f}, 
                                  context_instance=RequestContext(request))     
    

The diagnostics show that `request.user.profile` is set correctly
(specifically the company field). However the form is displayed with empty
fields. The HTML source doesn't show any data values, either. As an extra
check, I also tried some template diagnostics:

    
    
    <table border='1'>
        {% for field in form%}
            <tr>
                <td>{{field.label}}</td>
                <td>{{field.value}}</td>
            </tr>
        {% endfor%}
    </table>
    

This lists the field labels correctly, but the values are all reported as
`None`.

For completeness, the UserProfile's user field is now defined as `user =
models.OneToOneField(settings.AUTH_USER_MODEL)` and the User.profile lambda
expression is unchanged.


Answer: I'm not sure exactly what the problem is here, but one issue certainly is that
you are passing `instance=request.user` when instantiating the form. That's
definitely wrong: `request.user` is an instance of User, whereas the form is
based on `UserProfile`.

Instead, you want to do something like this:

    
    
    f = UserDetailsForm(request.POST)
    if f.is_valid():
        profile = f.save(commit=False)
        profile.user = request.user
        profile.save()
    

As regards ForeignKey vs OneToOne, you should absolutely be using OneToOne.
The advice on using ForeignKeys was given for a while in the run-up to the
release of Django 1 - almost five years ago now - because the original
implementation of OneToOne was poor and it was due to be rewritten. You
certainly won't find any up-to-date documentation advising the use of FK here.

**Edit after comments** The problem now is that you're instantiating the form
with the first parameter, data, even when the request is not a POST and
therefore `request.POST` is an empty dictionary. But the data parameter, even
if it's empty, takes precedence over whatever is passed as initial data.

You should go back to the original pattern of instantiating the form within
the `if` statement - but be sure not to pass `request.POST` when doing it in
the `else` clause.

