Applying a Django form to multiple pages

So I have a feedback form.

This form is to be applied to multiple pages, however, in the interests of the DRY concept of coding, I have created a template which just contains the form and used the {% include 'snippets/legal-feedback-form.html' %}’ to insert this into every HTML template with which I want to include the form.

The legal-feedback-form.html as the following:

<section class="page">
    <div class="page-content">
        <h1>Feedback</h1>
        <h2>Was this page helpful and informative? Feel free to submit feedback.</h2>
        <form method="POST"> 
            {% csrf_token %}

            {% for hidden in form.hidden_fields %}
            {{ hidden }}
            {% endfor %}

            {% for field in form.visible_fields %}
            <div class="form-group">
                {{ field.errors }}
                <label>{{ field.label_tag }}</label>
                <p>{{ field }}</p>
            </div>
            {% endfor %}
            <div class="disclaimer">
                {% include 'snippets/privacy-notice.html' %}
            </div>
            <button type="submit" class="btn btn--pink">Submit Feedback</button>
        </form>
    </div>
</section> 

In the legal.forms.py, I have the following:


def should_be_empty(value):
    if value:
        raise forms.ValidationError('Field is empty')


class FeedbackForm(forms.Form):
    name        =   forms.CharField(required=True,max_length=80, widget=forms.TextInput(attrs={'placeholder': 'Enter Your Name'}))
    message     =   forms.CharField(required=True,widget=forms.Textarea(attrs={'placeholder': 'Enter Your Message'}))
    email       =   forms.EmailField(widget=forms.EmailInput(attrs={'placeholder': 'Enter your Email'}))
    number      =   forms.CharField(max_length=12, required=True, widget=forms.TextInput(attrs={'placeholder' : 'Your Contact Number'}))
    Feedback    =   forms.URLField(widget=forms.HiddenInput)
    forcefield  =   forms.CharField(required=False, widget=forms.HiddenInput, label="Leave Empty", validators=[should_be_empty])

In the legal.views.py file, I have the following:

from .forms import FeedbackForm

def legal__privacy_statement(request):

    form = FeedbackForm()

    if request.method == 'POST':
        form = FeedbackForm(request.POST)
        if form.is_valid():
            subject     = form.cleaned_data["name"]
            message     = form.cleaned_data["message"]
            email       = form.cleaned_data["email"]
            number      = form.cleaned_data["number"]
            name        = form.cleaned_data["name"]
            body        = "Message:  " + message + "\n\nEmail: " + email + "\n\nContact Number:  " +number
            sender      = name + " <from_email>"
            recipients  = ["<to_email>"] 
            try:
                send_mail(subject, body, sender, recipients, fail_silently=True)
                return render (request, 'success.html')
            except:
                return HttpResponse('There has been an error in submitting this form')

    context = {
        'form'          : form
    }
    return render (request, 'privacy-statement.html', context)

All, very well, however, I’m having to apply this to every app.views.<method> with which I wish to include the form. I don’t really want to do that, so how would I go about creating a separate def function with which ANY webpage from within the app can draw this from.

In this, example, this is tied to the privacy-statement.html template but - as expected - you include the template in another webpage with the {% include 'snippets/legal-feedback-form.html' %} and - as expected - it doesn’t populate the form fields from the for loop.

Thank you.

Hey there.
If you want this form to be available on every page, then it would be a good idea to have this form on a context_processor, instead of creating on each view.
Here’s the doc on how to create your own context processor.
Hope that helps.

Howdy!

OK, I will have a look into this and see how I get on.

Many thanks,

Do all these other pages contain forms or are they just displaying data?

If any of the other pages also contain forms, you’ll first want to use a prefix on this form to ensure there’s no confusion within Django between this form and the forms being used on that page.

You’ll also need to decide how you want to handle errors with two forms. You’ve got four cases to account for:

  • Both forms good
  • Base form good, privacy form bad
  • Base form bad, privacy form good
  • Base form bad, privacy form bad

You need to define how you want to handle each of these combinations. (It will affect how you create the view for this.)

If this is the only form on a page, then the process is a lot easier.

To most easily make this view a function that you can combine with other views, you’ll want to change this such that what the function returns is a rendered html fragment and not a complete HttpResponse.
In other words, instead of using the render() shortcut, you can use the render_to_string function to produce a string that can be put into a context to be included in a page elsewhere.

Example:

def statement(request):
...
    context = { 'form': form }
    return render_to_string('statement.html', context=context, request=request)

Then, your view can do something like this:

def a_view(request):
...
    context = { ... }
    statement_html = statement(request)
    context['statement'] = statement_html
...

and then, within your template:

{{ statement|safe }}

If you’re only using this in “display” pages (pages not containing any other form), there are other ways of handling this - but those other ways could require more substantial changes to the code.

1 Like

Looks good, so just to clarify, the def statement is in the forms.py and the def a_view is in your views.py document, with the statement.html is the template fragment which contains the form?

Thanks

Could be. There’s no requirement for it to be there though, and since it’s not a form, I’d be more inclined to place it in my views.py file. (Or, if I had a separate file for utility functions, I might place it in there, too.)

With a few key exceptions (models being the most notable), where functions are placed in your project is a matter of individual choice. Django places very few restrictions on the internal organization of your project. Most everything you’ll read on the topic are expressing “common convention” or “typical usage” and not system requirements.

You are correct on the other two parts. You’ll usually have your views in views.py and the template in your templates directory. (And statement.html is the template fragment for rendering the form - and only that form.)