1

Well, it may actually be a simple case but I'm having a tough time figuring it out.

I have two user registration funnels (corporate & everyone else). When a corporate user creates a User instance through my registration form, I would also like them to input secondary forms that create related instances (based on the Website and Organization_Contact models).

I know how to solve this by making additional synchronous or asynchronous requests, but I'd like the user to fill out the three forms and submit with one click.

My issue here is that the other two forms rely on a User foreign key as a field. I've made that field "null=True, blank=True" so that I can validate and save the forms without the foreign key, but I ultimately want to add that key to both model instances.

I thought that I could validate the three forms, save the UserForm, and then use a model queryset to return the new User.id (all in one view). I would then add that user_id value to the other two form dictionaries (WebsiteForm and Organization_ContactForm).

It would work as so:

def register_company(request):
    if request.method=='POST'
       uform=UserCreationForm(request.POST, prefix='user')
       sform=SiteForm(request.POST, prefix='site')
       oform=OrgForm(request.POST, prefix='site')
       if uform.is_valid() and sform.is_valid() and oform.is_valid():
            uform.save()
            user=User.objects.get(email__iexact=uform.cleaned_data['email'])
            uid=unicode(user.id)
       #now I'd add uid back into the SiteForm and Orgform dictionaries and then save both instances              

Problems: 1) Not sure if I can save a modelform and then return that model instance as a queryset in a single view

2)I say that I'm not sure because I couldn't get passed the problem of trying to pass a variable to the queryset.

The get manager method seems to not accept a variable there. I assume as much because I passed an equivalent hardcoded string and it worked.

Ok, so I was thinking about creating a new manager method (email) which accepted a variable argument (the cleaned email field) and then retrying the process of saving one modelform, retrieving the model id data, and saving the other modelforms.

I also thought that I might be able to handle this issue through a post save signal.

Just general direction here would be really helpful. I need a starting point.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Ben
  • 15,010
  • 11
  • 58
  • 90

2 Answers2

2

Are these all ModelForms?

if request.method=='POST'
   uform=UserCreationForm(request.POST, prefix='user')
   sform=SiteForm(request.POST, prefix='site')
   oform=OrgForm(request.POST, prefix='site')
   if uform.is_valid() and sform.is_valid() and oform.is_valid():
        # save returns the object 
        user = uform.save()

        # commit = false doesn't save to DB.
        site = sform.save(commit=False)
        site.user = user
        site.save()

        org = oform.save(commit=False)
        org.user = user
        org.save()

Update regarding comments: Why spread this form saving logic around into multiple areas and files (signals, form save) when you can do it in one place?

The code is more readable, and you even save lines of code. Unless it's going to be used globally.

Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
  • sorry, I took so long explaining the general problem that I skimped on the code. Yes, those are all ModelForms and the view in question obviously extends beyond org.save(). – Ben Feb 17 '11 at 18:50
  • I'm sorry, does this not solve your problem? You said `#now I'd add uid back into the SiteForm and Orgform dictionaries and then save both instances`. This is a better way of doing just that :) Let the form do what it can, then set the user directly instead of modifying the POST dictionary you pass into the 2 forms. – Yuji 'Tomita' Tomita Feb 17 '11 at 18:54
  • I'm thinking I should just save all three modelforms and then send a post save signal with a callback that takes the user_id from the new User instance and updates the other two new model instances (Org and Site). Is that reasonable? Or can I save and query in the same view (and is that preferable)? – Ben Feb 17 '11 at 18:55
  • That sounds like a complicated solution for a simple problem! I'd definitely prefer copy and pasting the code above to spreading this logic outside the view. You already have a user instance returned from saving your `UserCreationModelForm`, so use it! Why use user ids if you'd have to query the user table to get the instance again anyways? – Yuji 'Tomita' Tomita Feb 17 '11 at 19:01
1

This is how I would do it:

def register_company(request):
    uform=UserCreationForm(request.POST or None, prefix='user')
    sform=SiteForm(request.POST or None, prefix='site')
    oform=OrgForm(request.POST or None, prefix='site')

    if request.POST and all((uform.is_valid(), sform.is_valid(), oform.is_valid()):
        user = uform.save()
        sform.save(user)
        oform.save(user)

    ruturn ...

class UserCreateionForm(ModelForm):
    Meta:
        model = User

class SiteForm(ModelForm):
    Meta:
        model=Site
        exclude=['user', ]

    def save(self, user, commit=True):
        self.instance.user = user
        return super(SiteForm, self).save(commit=commit)
Ski
  • 14,197
  • 3
  • 54
  • 64
  • good lord Skir, you just made me want to jump out of my window. I never even considered that save can take an instance as a parameter. I've only seen "commit=False" argument. Oh boy, if true, that's too easy. – Ben Feb 17 '11 at 18:59
  • It cannot, but you can define your save method which does that. And in Meta you use `exclude=['user',]`, because without this line, your form will not validate and will draw user selection box. – Ski Feb 17 '11 at 19:02
  • right, so I knew about exclude and I actually got so excited by your solution that I rushed to try it without looking down at the overriden save method. That I'm familiar with. I'm going to give it a shot now. – Ben Feb 17 '11 at 19:05
  • Btw, take a note that I've used `all(...)` instead of `and` in if statement. Because in your example, if first form will not validate, others will not be validated, user will see errors for first form only. – Ski Feb 17 '11 at 19:11
  • So your solution makes sense. But when I submit, get the following error Exception Value: save() takes exactly 1 argument (2 given) – Ben Feb 17 '11 at 19:20
  • 1
    set it to def `save(self, user):` – Yuji 'Tomita' Tomita Feb 17 '11 at 19:24
  • it worked. You guys were amazingly helpful. Whom should I give answer credit to? – Ben Feb 17 '11 at 19:27