1

Is there a way to display User fields under a form that adds/edits a UserProfile model? I am extending default Django User model like this:

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True)
    about = models.TextField(blank=True)

I know that it is possible to make a:

class UserProfileInlineAdmin(admin.TabularInline):

and then inline this in User ModelAdmin but I want to achieve the opposite effect, something like inverse inlining, displaying the fields of the model pointed by the OneToOne Relationship (User) in the page of the model defining the relationship (UserProfile). I don't care if it would be in the admin or in a custom view/template. I just need to know how to achieve this.

I've been struggling with ModelForms and Formsets, I know the answer is somewhere there, but my little experience in Django doesn't allow me to come up with the solution yet. A little example would be really helpful!

  • Do you mean that you want to offer one page with all relevant fields to create a User and UserProfile at the same time? – dokkaebi Oct 11 '12 at 20:56
  • Exactly! I want to add/edit all the fields at the same time and create the appropriate instances in add case – Jack Sparrow Oct 11 '12 at 21:03

1 Answers1

2

This has been brought up before.

Here's a blog post with what I think is my favorite solution. The gist is to use two ModelForms, and render them into a single <form> tag in the template making use of the prefix kwarg:

http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/

Here's another method which I like a bit less, but is also valid. They use two separate <form>s on the page, with different actions and two submit buttons:

Proper way to handle multiple forms on one page in Django

This one talks more specifically about Users and UserProfiles:

How to create a UserProfile form in Django with first_name, last_name modifications?

Update

Here is what I ended up with

# models.py
class UserProfile(models.Model):

    favorite_color = models.CharField(max_length=30)
    user = models.OneToOneField(User)


# forms.py
class UserProfileForm(forms.ModelForm):

    class Meta:
        model = UserProfile
        # we fill the 'user' value in UserCreateView.form_valid
        exclude = ('user',)


# views.py
from django.contrib.auth.forms import UserCreationForm
class UserCreateView(FormView):

    # url to redirect to after successful form submission
    success_url = reverse_lazy('user_list')
    template_name = "userform.html"

    def get_context_data(self, *args, **kwargs):
        data = super(UserCreateView, self).get_context_data(*args, **kwargs)
        data['userform'] = self.get_form(UserCreationForm, 'user')
        data['userprofileform'] = self.get_form(UserProfileForm, 'userprofile')
        return data

    def post(self, request, *args, **kwargs):
        forms = dict((
            ('userform', self.get_form(UserCreationForm, 'user')),
            ('userprofileform', self.get_form(UserProfileForm, 'userprofile')),
        ))
        if all([f.is_valid() for f in forms.values()]):
            return self.form_valid(forms)
        else:
            return self.form_invalid(forms)

    def get_form(self, form_class, prefix):
        return form_class(**self.get_form_kwargs(prefix))

    def get_form_kwargs(self, prefix):
        kwargs = super(UserCreateView, self).get_form_kwargs()
        kwargs.update({'prefix': prefix})
        return kwargs

    def form_valid(self, forms):
        user = forms['userform'].save()
        userprofile = forms['userprofileform'].save(commit=False)
        userprofile.user_id = user.id
        userprofile.save()
        return HttpResponseRedirect(self.get_success_url())

    def get(self, request, *args, **kwargs):
        return self.render_to_response(self.get_context_data())


# userform.html
<form action="" method="POST" class="form">
    {% csrf_token %}
    {{ userform.as_p }}
    {{ userprofileform.as_p }}
    <button type="submit">Submit</button>
</form>

# urls.py
...
url(r'^create/$', UserCreateView.as_view(), name='user_create'),
...
Community
  • 1
  • 1
dokkaebi
  • 8,294
  • 3
  • 38
  • 58
  • Wow!That's a really exhaustive answer. I've already tried your recommendation in the last link but I got a DoesNotExist exception by loading add/edit page when trying @Natim 's answer and also a DoesNotExist exception when trying wunki 's answer further down in this conversation. I'll get back soon to report my results from your other two links. Thank you very much! – Jack Sparrow Oct 11 '12 at 21:25
  • Thank you very much for your effort. I have three questions though. Do I have to add something to my urls.py (probably yes, but what?)? What is `UserCreationForm` (probably a ModelForm for User...)? Do I have to instantiate `UserCreateView`? – Jack Sparrow Oct 12 '12 at 10:58
  • Added the `UserCreationForm` import, and you can see how the view class is used in the url conf. [More on class based views here.](https://docs.djangoproject.com/en/dev/topics/class-based-views/) – dokkaebi Oct 12 '12 at 17:21