Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crispy forms to render multiple forms #144

Closed
tarasdi opened this issue Jan 14, 2013 · 20 comments
Closed

Crispy forms to render multiple forms #144

tarasdi opened this issue Jan 14, 2013 · 20 comments

Comments

@tarasdi
Copy link

tarasdi commented Jan 14, 2013

Hi,

I don't think this is possible, but I thought I'd check. Does crispy forms support rendering and processing of multiple forms combined into one larger layout, and if not, has it ever been considered? EG: consider a 'Person' crispy form for a 'Person' model with name/age/etc.. attributes. Now imagine your app wants to display a form to allow a user to enter 'Parents', that is, two 'Person' models. For the layout you could render the two forms separately using two crispy tags in the template, or what if you could create a crispy form that doesn't map to a Django model, but whose layout is composed of the layouts of the two constituent forms? This would allow you to use the 'Parents' form as part of an even larger form, and also reuse the 'Parents' form in other parts of the app.

You could achieve this reuse by having separate templates for each form composition (in this case a 'parents' template) and including them via 'includes', but having it in the code seems neater and more flexible.

One thing you'd have to consider in addition to HTML generation is form validation (possibly by calling is_valid on each of the constituent forms).

Post on multiple form models per view: http://stackoverflow.com/questions/569468/django-multiple-models-in-one-template-using-forms.

@migajek
Copy link

migajek commented Jan 30, 2013

I'm also looking for functionality like this.
I have a form and it's layout - first the fields, then the submit button. Now I'd like to place inline formset "between" the "parent" (main) form and the FormActions layout element.
Currently what I have to do is to remove FormActions from the main form layout and code it manually in the HTML, right after crispy-rendering formset.
Coding HTML is what we wanted to avoid, isn't it? :)

@joekode
Copy link

joekode commented Mar 14, 2013

+1 needed.

@keithio
Copy link

keithio commented Mar 24, 2013

Here's a method that should work: django-crispy-forms docs

With this in mind, you should be able to have one template, say form_wizard.html, that contains the necessary template tags and helpers for the form wizard based on Django FormWizard docs

Then, just replace {{ form }} with {% crispy form %}. Make sure that you set self.helper.form_tag = False in your form definition.

The form processing in the view might be the easiest way to handle the data (at the moment). You can use get_all_cleaned_data() method, or you can loop over each form and collect the respective cleaned_data from each form (optional conditional step skipping) into a superset.

@maraujop
Copy link
Contributor

Hi @tarasdi, @migajek and @joekode

I totally understand what you want, the ability to merge form fields or different kind of forms using layouts. That's currently not supported in crispy-forms as you know. It's been requested several times and once I got a PR for this that I rejected. It's currently one of the missing features more requested, so It's time to put in the right way.

I've started development locally on a branch, that I will hopefully upload before a week in a beta usable state. Then I will send you a request for feedback, so you can say if it fits your needs or anything else.

Cheers,
Miguel

@joekode
Copy link

joekode commented Mar 24, 2013

That would be awesome

Would love to give feedback

Thanks for taking the time to do it.

@tarasdi
Copy link
Author

tarasdi commented Mar 24, 2013

Thanks @maraujop , let me know when you've checked it in and I'll check it out :)

@maraujop
Copy link
Contributor

maraujop commented Apr 7, 2013

Well, it's taken a bit longer, but I have some preliminary work that illustrates how I've thought resolving this problem. You can see implementation within branch named multiple_form_renderer, that you can see here

A code example probably speaks better than words:

from crispy_forms.helper import MultipleFormRenderer

renderer = MultipleFormRenderer()
renderer.add_form("test_form", TestForm())
renderer.add_form("checkboxes_form", CheckboxesTestForm())
self.renderer.layout = Layout(
    "test_form",
    Field("checkboxes_form.checkboxes", autocomplete="off"),
    "checkboxes_form.alphacheckboxes",
)
html = self.renderer.render(Context())

It's quite explained within the commit and tests included in the branch. This would render the whole test_form, then checkboxes field from checkboxes_form setting autocomplete to off in its input and finally alphacheckboxes field from checkboxes_form.

There is currently no templatetag support for MultipleFormRenderer, I'm considering using the same {% crispy %} tag but passing different parameters. It would be like:

{% crispy renderer "test_form" test_form "checkboxes_form" checkboxes_form %}

That way the mapping would be created in the template, and views would have way less logic.

  • Only Field layout object has been adapted to work with this new rendering behavior, others could be easily adapted if the implementation is worth the effort, but as a demonstration is good enough.
  • Finally, although this has not been implemented, MultipleFormRenderer will behave mainly as a FormHelper, I've been tempted to call it MultipleFormHelper but I believe that would be confusing. My understanding is that FormHelper attributes would be applicable to the whole layout, it doesn't really matter if any of those forms have helpers, because within a MultipleFormRenderer behavior should change.

Formsets

This is currently only working with forms, formsets will come. This is what I have in mind for formsets:

    Formset(
        'formset.field_name',
        'form.field_name'
    )

Most of the times, someone wants to merge a form field within a formset field, it wants all formset forms to behave the same way. We need a way to let crispy-forms know where a formset starts and ends, and that probably will need an extra layout object.

I'm willing to hear any crispy-forms' user comments, questions or feedback.

Thanks, cheers,
Miguel

@budlight
Copy link

Your formset example is going to need a concrete example for the documentation. I actually wouldn't want to merge a form field to a formset field, but the reverse, I'd like to show as formset almost as if it were a regular field (as far as where it goes in the layout is concerned at least). Here's an example of one type of many to many relationship that I think would be a common pattern for formsets:

STATUS = [
    (-1, 'Deleted'),
    (0, 'Paused'),
    (1, 'Active'),
]
class Country(models.Model):
    code = models.CharField(max_length=2, blank=True, default="", unique=True)
    name = models.CharField(max_length=60, blank=True, default="")

class MyObject(models.Model):
    countries = models.ManyToManyField(
        Country, through=ObjectCountry,)

class ObjectCountry(models.Model):
    offer = models.ForeignKey(MyObject, on_delete=models.CASCADE)
    country = models.ForeignKey(Country)
    enabled = models.BooleanField(default=True)
    status = models.IntegerField(default=1, choices=STATUS)

@hxu
Copy link

hxu commented Apr 23, 2013

This open ticket in the Django tracker may be of interest to you guys. The attachment on the ticket has a basically working FormContainer class that can take Forms and FormSets as subforms.

I'm currently using it in a project, and it works OK for most things. You can basically get it to work with crispy forms by separately rendering each form with form_tag = False.

@gregarmer
Copy link

@maraujop I also need this for an app. The branch is working well, any idea when it's going to be merged to master and released ?

@maraujop
Copy link
Contributor

@budlight I think I could detect that one of you fields is a formset and render it using formset structure. I will work on the branch when I have some free time, for adding this feature.

@hxu Thanks for letting us know.

@gregarmer I was considering adding this to future version 1.3.0, but I was expecting some feedback on it. It's quite a big change and I'm not sure the way I've solved it follows the philosophy of the rest of the project.

@qris
Copy link

qris commented Nov 6, 2014

I too have a working implementation of including a Formset in a form as though it were any other field.

@durdenk
Copy link

durdenk commented Jun 22, 2015

qris , is this method still working. I get KeyError: 'wrapper_class'

@samkuehn
Copy link
Contributor

samkuehn commented Sep 9, 2015

@durdenk I was getting the same error. I opened an issue #511 and have submitted a pull request.

@sLesage
Copy link

sLesage commented Nov 19, 2015

I have been looking for something similar for a while now. So ... have there been any updates ?

@J20S
Copy link

J20S commented Oct 16, 2016

+1 for any updates?

@joewandy
Copy link

Still waiting for an update too here ..

@carltongibson
Copy link
Collaborator

carltongibson commented Jan 21, 2017

It's unlikely we'll now add anything here.

Rendering multiple forms settIng form_tag=False on the Helper gives you 90% of what you need.

I'm sceptical that anything else would be generic enough and simple enough to include in the library. (It doesn't strike me as a problem that you can't just solve with a list in userland, view, code.)

I'll leave this open, and discussion open too, for the now. Please don't comment with "Any updates?" or similar.

Happy to consider serious suggestions. Pull requests are welcome. (Although see above.)

@bobort
Copy link

bobort commented May 8, 2017

It looks like the work that maraujop did is not in the master branch, but it is exactly what we need for this feature to work. Was there any particular reason why this feature did not make it into master? I know this question is similar to "any updates?", but why should anyone do a pull request if the code is already complete and ready to use?

@carltongibson
Copy link
Collaborator

carltongibson commented May 9, 2017

@bobort At this point it's mainly a combination of age vs the trade-off between utility and added-complexity and maintenance overhead.

I'm in favour of users just solving this in their own code by setting form_tag=False on the Helper and rendering the list of forms they want. (And yes, that will involve user involvement in their own software.)

As I said, I'm happy to review concrete suggestions — part of that is why it would be better as a library provided solution. I'm going to close the discussion on this ticket: I would prefer to start discussion afresh on a new ticket if there's a sensible proposal.

@django-crispy-forms django-crispy-forms locked and limited conversation to collaborators May 9, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests