1

I have several different profiles I want people to be able to create (let's call them special types of Apples). I am rendering the base Apple form, which contains a drop-down for the type of Apple, Red Apple or Green Apple. Based on this drop-down, I'd like to also display the RedAppleProfileForm, or the GreenAppleProfileForm, ModelForms based off the profiles.

How do I conditionally render these forms?

  • I have tried rendering both of them with display: none, and changing the display based on selected element value, but because the fields of the rendered forms are mandatory, I can't ever submit, even if the form has display: none. If there is a way around this, this is a solution.

Relevant Stack: How to render django form differently based on what user selects?

Notes on this stack: The solution with 4 upvotes seems to work for 2 profiles, such as Red and Green apples, but in my scenario I happen to have 5 different types of Apples, and I don't know how I'd make it work then. The accepted solution could work maybe but I don't fully grasp how? Would I just a BaseForm inherit all 5 of my ProfileForms?

Python3.8, Django 3.0

David Jay Brady
  • 1,034
  • 8
  • 20

2 Answers2

1

I think the best way is to separate your form into two forms: the button as a form itself (to distinguish which form to render) and the "actual" Apple form as your second form. As long as you can find out a way to get the styles aligned, I don't see any problem doing so. Implementation:

<!-- you might want to change the styles -->
<form action="..." method="post">
  <input type="submit" name="red_apple" value="Red Apple" />
  <input type="submit" name="green_apple" value="Green Apple" />
</form>

<form action="..." method="post">
  <!-- your main form from the view -->
</form>

views.py

if 'red_apple' in request.POST:
    # return the right form
else :
    # return the other form

As a note, you might want to choose one of these as a default, otherwise, it will look weird having only the choice button but not the form.

Reference

crimsonpython24
  • 2,223
  • 2
  • 11
  • 27
  • Hmm I like where this might go. I'll give it a shot and come back and drop another comment! Thank you – David Jay Brady Jul 18 '20 at 01:28
  • 1
    Ok.. 10 hours of working later... Ended up just having everything in 1 form, since I have the type of Apple as part of the base model. If there is a good way to access the profile without knowing it's type then I would ABSOLUTELY go this route.. but I think I'd need to have stupid try catches to figure out the related_name.. and I couldn't make all the related_name s the same, so I ended up needing to make it work with just 1 form – David Jay Brady Jul 19 '20 at 19:41
1

EDIT: I ended up overhauling my code to use abstract inheritance and it worked out much nicer. I recommend exploring this before trying to use Profiles. With abstract inheritance, you can stick with things like UpdateView/CreateView much more easily

Freaking got it. If you don't need the dropdown as part of your model, go with crimsonpython24's answer. If you do, the trick is posting back to your View with an onchange dropdown.

class AppleForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["color"].widget.attrs.update({"onchange": "this.form.submit()"})

    class Meta:
        ...

Now when the user selects the type of Pawn (or Apple) they want, it posts.

Here's my View:

class AppleCreate(LoginRequiredMixin, View):
    login_url = "/login/"

    @staticmethod
    def _create_apple(pawn_form, profile_form):
        apple = apple_form.save()
        profile_obj = profile_form.save(commit=False)
        profile_obj.apple = apple
        profile_obj.save()      
        return redirect(reverse("list_apples"))

    def get(self, request):
        context = {"apple_form": AppleForm()}
        return render(request, "my/template.html", context=context)

    def post(self, request):
        apple_form = AppleForm(request.POST)

        apple_color = request.POST.get("color", "")
        profile_form_selector = {"Red": RedAppleProfileForm(request.POST), "Green": GreenAppleProfileForm(request.POST)}
        profile_form = profile_form_selector.get(apple_color, "")

        if apple_color and apple_form.is_valid() and profile_form.is_valid():
            return AppleCreate._create_apple(apple_form, profile_form)
        else:
            return render(request, "my/template.html", context={"apple_form": pawn_form, "profile_form": profile_form})

Essentially in your post method, if you have all data, you can finish up. If you don't, you need to rerender the form with all the fields you want to be shown.

David Jay Brady
  • 1,034
  • 8
  • 20