diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index 7a60943..56b4f56 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -126,7 +126,7 @@ class InlineAdminForm(AdminForm):
         super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
 
     def pk_field(self):
-        return AdminField(self.form, self.formset._pk_field_name, False)
+        return AdminField(self.form, self.formset._pk_field.name, False)
 
     def deletion_field(self):
         from django.forms.formsets import DELETION_FIELD_NAME
diff --git a/django/forms/models.py b/django/forms/models.py
index bd9597d..3e3d56c 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -21,7 +21,7 @@ __all__ = (
 )
 
 def save_instance(form, instance, fields=None, fail_message='saved',
-                  commit=True):
+                  commit=True, exclude=None):
     """
     Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
 
@@ -36,10 +36,13 @@ def save_instance(form, instance, fields=None, fail_message='saved',
     cleaned_data = form.cleaned_data
     for f in opts.fields:
         if not f.editable or isinstance(f, models.AutoField) \
+                or (isinstance(f, models.OneToOneField) and f.rel.parent_link) \
                 or not f.name in cleaned_data:
             continue
         if fields and f.name not in fields:
             continue
+        if exclude and f.name in exclude:
+            continue
         f.save_form_data(instance, cleaned_data[f.name])
     # Wrap up the saving of m2m data as a function.
     def save_m2m():
@@ -115,8 +118,6 @@ def model_to_dict(instance, fields=None, exclude=None):
             else:
                 # MultipleChoiceWidget needs a list of pks, not object instances.
                 data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
-        elif isinstance(f, OneToOneField):
-            data[f.attname] = f.value_from_object(instance)
         else:
             data[f.name] = f.value_from_object(instance)
     return data
@@ -291,7 +292,11 @@ class BaseModelFormSet(BaseFormSet):
             existing_objects[obj.pk] = obj
         saved_instances = []
         for form in self.initial_forms:
-            obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]]
+            cleaned_pk = form.cleaned_data[self._pk_field.name]
+            if hasattr(cleaned_pk, "pk"):
+                obj = existing_objects[cleaned_pk.pk]
+            else:
+                obj = existing_objects[cleaned_pk]
             if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
                 self.deleted_objects.append(obj)
                 obj.delete()
@@ -319,9 +324,13 @@ class BaseModelFormSet(BaseFormSet):
 
     def add_fields(self, form, index):
         """Add a hidden field for the object's primary key."""
-        if self.model._meta.pk.auto_created:
-            self._pk_field_name = self.model._meta.pk.attname
-            form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
+        from django.db.models import OneToOneField
+        pk = self.model._meta.pk
+        while isinstance(pk, OneToOneField):
+            pk = pk.rel.to._meta.pk # drill down until we find a "real" pk
+        if pk.auto_created:
+            self._pk_field = self.model._meta.pk
+            form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
         super(BaseModelFormSet, self).add_fields(form, index)
 
 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
@@ -369,7 +378,18 @@ class BaseInlineFormSet(BaseModelFormSet):
     def save_new(self, form, commit=True):
         kwargs = {self.fk.get_attname(): self.instance.pk}
         new_obj = self.model(**kwargs)
-        return save_instance(form, new_obj, commit=commit)
+        exclude = []
+        if self.fk == self._pk_field:
+            exclude.append(self.fk.name)
+        return save_instance(form, new_obj, exclude=exclude, commit=commit)
+    
+    def add_fields(self, form, index):
+        from django.db.models import OneToOneField
+        super(BaseInlineFormSet, self).add_fields(form, index)
+        if isinstance(self._pk_field, OneToOneField):
+            queryset = self.instance.__class__._default_manager.all()
+            form.fields[self._pk_field.name] = ModelChoiceField(queryset,
+                required=False, widget=HiddenInput)
 
 def _get_foreign_key(parent_model, model, fk_name=None):
     """
@@ -419,13 +439,21 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
     """
     fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
     # let the formset handle object deletion by default
-
-    if exclude is not None:
-        exclude.append(fk.name)
-    else:
-        exclude = [fk.name]
+    
+    def _formfield_callback(f, **kwargs):
+        if fk == model._meta.pk:
+            if f.primary_key:
+                return None
+        return formfield_callback(f, **kwargs)
+    
+    if fk != model._meta.pk:
+        if exclude is not None:
+            exclude.append(fk.name)
+        else:
+            exclude = [fk.name]
+    
     FormSet = modelformset_factory(model, form=form,
-                                    formfield_callback=formfield_callback,
+                                    formfield_callback=_formfield_callback,
                                     formset=formset,
                                     extra=extra, can_delete=can_delete, can_order=can_order,
                                     fields=fields, exclude=exclude, max_num=max_num)
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index b8d3f93..bff869f 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -66,6 +66,12 @@ class ImprovedArticle(models.Model):
 class ImprovedArticleWithParentLink(models.Model):
     article = models.OneToOneField(Article, parent_link=True)
 
+class ImprovedArticleWithOneToOnePK(models.Model):
+    article = models.OneToOneField(Article, primary_key=True)
+    
+    def __unicode__(self):
+        return self.article.headline
+
 class BetterWriter(Writer):
     pass
 
@@ -811,7 +817,35 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
 >>> bw = BetterWriter(name=u'Joe Better')
 >>> bw.save()
 >>> sorted(model_to_dict(bw).keys())
-['id', 'name', 'writer_ptr_id']
+['id', 'name', 'writer_ptr']
+
+>>> class ImprovedArticleWithOneToOnePKForm(ModelForm):
+...     class Meta:
+...         model = ImprovedArticleWithOneToOnePK
+
+>>> print ImprovedArticleWithOneToOnePKForm().as_p()
+<p><label for="id_article">Article:</label> <select name="article" id="id_article">
+<option value="" selected="selected">---------</option>
+<option value="1">New headline</option>
+<option value="2">The walrus was Paul</option>
+<option value="3">The walrus was Paul</option>
+<option value="4">The walrus was Paul</option>
+</select></p>
+
+>>> form = ImprovedArticleWithOneToOnePKForm(data={'article': u'1'})
+>>> instance = form.save()
+>>> instance
+<ImprovedArticleWithOneToOnePK: New headline>
+
+>>> form = ImprovedArticleWithOneToOnePKForm(instance=instance)
+>>> print form.as_p()
+<p><label for="id_article">Article:</label> <select name="article" id="id_article">
+<option value="">---------</option>
+<option value="1" selected="selected">New headline</option>
+<option value="2">The walrus was Paul</option>
+<option value="3">The walrus was Paul</option>
+<option value="4">The walrus was Paul</option>
+</select></p>
 
 # PhoneNumberField ############################################################
 
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
index 65987c1..746cfbb 100644
--- a/tests/modeltests/model_formsets/models.py
+++ b/tests/modeltests/model_formsets/models.py
@@ -262,12 +262,12 @@ used.
 >>> for form in formset.forms:
 ...     print form.as_p()
 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p>
-<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" id="id_form-0-author_ptr_id" /></p>
+<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr" /></p>
 
 >>> data = {
 ...     'form-TOTAL_FORMS': '1', # the number of forms rendered
 ...     'form-INITIAL_FORMS': '0', # the number of forms with initial data
-...     'form-0-author_ptr_id': '',
+...     'form-0-author_ptr': '',
 ...     'form-0-name': 'Ernest Hemingway',
 ...     'form-0-write_speed': '10',
 ... }
@@ -283,17 +283,17 @@ True
 >>> for form in formset.forms:
 ...     print form.as_p()
 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p>
-<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr_id" value="..." id="id_form-0-author_ptr_id" /></p>
+<p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" value="..." id="id_form-0-author_ptr" /></p>
 <p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p>
-<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr_id" id="id_form-1-author_ptr_id" /></p>
+<p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr" /></p>
 
 >>> data = {
 ...     'form-TOTAL_FORMS': '2', # the number of forms rendered
 ...     'form-INITIAL_FORMS': '1', # the number of forms with initial data
-...     'form-0-author_ptr_id': hemingway_id,
+...     'form-0-author_ptr': hemingway_id,
 ...     'form-0-name': 'Ernest Hemingway',
 ...     'form-0-write_speed': '10',
-...     'form-1-author_ptr_id': '',
+...     'form-1-author_ptr': '',
 ...     'form-1-name': '',
 ...     'form-1-write_speed': '',
 ... }
