How to Render contact-from in every view (Django)

Viewed 144

Contact-form is placed footer. And most of the different views has footer section.

def about-page(request):
return render(request, 'about.html')

def faq-page(request):
    return render(request, 'faq.html')

def home-page(request):
    return render(request, 'home.html')

All above template have "included" footer section:

{% include 'footer.html' %}

And this has further included contact-form:

{% include 'contact.html' %}

So, now if I want to render contact form on all pages, I have to pass it in context. Like:

def about-page(request):
form = ContactForm()
return render(request, 'about.html',{'form':form})

def faq-page(request):
form = ContactForm()
return render(request, 'faq.html',{'form':form})

def home-page(request):
form = ContactForm()
return render(request, 'homeabout.html',{'form':form})

Is there any universal function I can do to add form to all pages by not repeating. Because I have way to many pages in whole site.

3 Answers

If it's a function, I can't see any ways of extending. Have you considered switching to class-based views? In that way, you can do something like:

class BaseView(FormView):
    form = ContactForm()

class HomePage(BaseView):
    template_name = 'home.html'

class FAQPage(BaseView):
    template_name = 'faq.html'

In this case, BaseView will be the parent view that contains a form, and you can use that in all child classes. The FormView thing which the BaseView is inheriting is one of Django's default generic view purposed for rendering forms.

Hint: when using class-based views in URLs, remember to add the .as_view() function:

urlpatterns = [
    path('', views.HomePage.as_view(), name='home'),
]

You can use class based views to accomplish this:

from django.views import View

class BaseView(TemplateView):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['contact_form'] = ContactForm()
        return context

Now in your other views that inherit from BaseView, contact_form will be added to the context automatically.

class AboutView(BaseView):
    template_name = 'about.html'

class FaqView(BaseView):
    template_name = 'faq.html'

class HomeView(BaseView):
    template_name = 'home.html'

If you really want to use function based views, you can write your own context processor (docs).

In the file myapp/context.py:

def my_context_processor(request):
    return {'contact_form': ContactForm()}

Now in your settings.py, add the context processor to the TEMPLATES option list, which looks something like this:

TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',

                'myapp.context.my_context_processor'
            ],
        },
    },
]

The only downside to this approach is that the contact_form will be inserted in every view's context, which probably doesn't matter in your case.

If you want to stick to functions, use partial:

from functools import partial

def template_with_contact_form(template, request):
    return render(request, template, {"form": ContactForm()})


about = partial(template_with_contact_form, "about.html")
Related