2

I'm on my second Django project. On my first project I used all generic views, with only the most basic forms tied directly to a custom user model using UpdateView.

In this project I'm trying to implement user profile functionality. My custom user model has some extra dummy fields just so I can manipulate the data. This I refer so as the CustomUser model. I also have a UserAddress model containing addresses since a user can have more than one address. I have tried looking for other questions, and I get similar questions, but there is always something missing:

Django class based views - UpdateView with two model forms - one submit

Using Multiple ModelForms with Class-Based Views

Multiple Models in a single django ModelForm?

Django: multiple models in one template using forms

I've spent the last day or two looking for the "Django way" of doing what I want to do and have had no luck. My initial thought was that I could use a single template, fed with two ModelForms wrapped in a single <form> tag. The view would then process each form and update the CustomUser model and create or update the UserAddress models. I have figured out how to mash together the functionality using the base View CBV, but I suspect I'm duplicating a lot of functionality that I could probably find already done in Django. This is my view, where I handle the form instantiating manually, and feed the context.

class UserDetailsView(View):

    def get(self, request):
        user = request.user
        user_basic = CustomUser.objects.get(pk=user.pk)
        basic_form = UserBasicForm(instance=user_basic)

        user_address = UserAddress.objects.get(user=user.pk)   
        billing_address_form = UserAddressForm(instance = user_address)

        context = {'basic_form':basic_form,'billing_address_form':billing_address_form}

        return render(request, 'mainapp/profile.html', context)

My post is the same at this point, as I haven't done the actual validation and saving yet.

class UserBasicForm(forms.ModelForm):

    class Meta(forms.ModelForm):
        model = CustomUser
        fields = (
            'username',
            'first_name',
            'last_name',
            )
        labels = {
            'username':'Username',
            'first_name':'First Name',
            'last_name':'Last Name',
            }

class UserAddressForm(forms.ModelForm):

    class Meta(forms.ModelForm):
        model = UserAddress
        fields = (
            'description',
            'addressee',
            'company',
            'address_1',
            'address_2',
            'city',
            'prov_state',
            'post_zip',
            'country',
            )
        labels = {
            'description':'Address Description',
            'addressee':'Addressee',
            'company':'Company Name',
            'address_1':'Address',
            'address_2':'Address 2',
            'city':'City',
            'prov_state':'Province or State',
            'post_zip':'Postal or Zip Code',
            'country':'Country',
            }

class CustomUser(AbstractUser):
    objects = CustomUserManager()

    def __str__(self):
        return self.email

class UserAddress(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)

    description = models.CharField(max_length = 256, default='Description')

    addressee = models.CharField(max_length = 256,)
    company = models.CharField(max_length = 256, default='Self')
    address_1 = models.CharField(max_length = 256,)
    address_2 = models.CharField(max_length = 256,)
    city = models.CharField(max_length = 256,)
    prov_state = models.CharField(max_length = 256,)
    post_zip = models.CharField(max_length = 256,)
    country = models.CharField(max_length = 256,)

    def __str__(self):
        return self.description

Please go easy on me, I'll take most advice offered.

Edit After reviewing some other SO questions and Django form examples, it appears that the final answer probably isn't SO material. That said, my observation is that for the Django built-in CBVs, the "best" base view is that which you can minimize or simplify the code you add. Using a TemplateView or FormView for my project in this case just depends on which methods I choose to re-write or override and for that, I'm still open to suggestions.

5
  • did you try to just extend the basic form from the address form class? Commented Mar 9, 2019 at 14:49
  • Hmm, I'm not sure I understand what you mean. A ModelForm and associated methods should work well as the data is tightly coupled to the model (I think). Am I misinterpreting? Commented Mar 9, 2019 at 14:52
  • The view logic is what I feel should likely exist built into Django...the forms and templates I think are project dependent. Commented Mar 9, 2019 at 15:01
  • Yeah should. Try this: django-betterforms.readthedocs.io/en/latest/… Commented Mar 9, 2019 at 15:02
  • I see...I saw another SO answer where they used composite forms, but I couldn't figure out how to make a view work with that. At the time, I felt like a bit of duct tape solution. Commented Mar 9, 2019 at 15:06

1 Answer 1

1

I'd do something like this (with betterforms):

class UserCreationMultiForm(MultiModelForm):
    form_classes = {
        'basic': UserBasicForm,
        'address': UserAddressForm,
    }


class UserDetailsView(View):
    template = "mainapp/profile.html"
    form_class = UserCreationMultiForm
    success_url = reverse_lazy('home')

    def form_valid(self, form):
        # Save the user first, because the profile needs a user before it
        # can be saved.
        user = form['basic'].save()
        address = form['address'].save(commit=False)
        address.user = user
        address.save()
        return redirect(self.get_success_url())

Then rename your forms in your template to form.basic and form.address

Sign up to request clarification or add additional context in comments.

3 Comments

So this would use the generic View because it contains data from multiple models?
After reviewing many more SO questions and answers, I realized I was making a few stupid mistakes that didn't make things any easier. My initial idea to use Django built in views matches other solutions where a main model and form is used in UpdateView, and other data is managed manually. For now, I'm looking to avoid multi-form solutions, to keep dependencies down. Your approach uses the generic view which is pretty much what I was doing on my previous project. I'll leave this open for now for further input.
django-betterforms is no longer maintained. Last update was in 2022

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.