Search code examples
pythondjangodjango-registration

Passing context to django-registration's views


I'm utilizing django-registration with a set of premade templates I found on Github for doing a two-step (registration-activation) workflow using HMAC.

I want to pass global variables (defined in context-processors) like my website's name to the emails sent by django-registration. the activation email sent to a new registrant, for example, or the password change one.

The "problem" is I don't directly have access to those views. That's kinda the point of django-registration, you include its path in the urls.py file, and everything works:

urlpatterns = [
    url(r'^', include('core.urls')),
    url(r'^admin/', admin.site.urls),
    url(r'^accounts/', include('registration.backends.hmac.urls')),
]

What's the minimum effort way of adding context to those views? I've already created and am successfully passing context to emails in my own views (using context processors):

def send_some_email_view(request):

    msg_plain = render_to_string('email_change_email.txt', context, request=request)
   msg_html = render_to_string('email_change_email.html', context, request=request)

But what about views I didn't create?

Edit: So I made some progress, finding django-registration's registration view, and this method inside of it:

def send_activation_email(self, user):
    """
    Send the activation email. The activation key is simply the
    username, signed using TimestampSigner.

    """
    activation_key = self.get_activation_key(user)
    context = self.get_email_context(activation_key)
    context.update({
        'user': user
    })
    subject = render_to_string(self.email_subject_template,
                               context)
    # Force subject to a single line to avoid header-injection
    # issues.
    subject = ''.join(subject.splitlines())
    message = render_to_string(self.email_body_template,
                               context)
    user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)

I don't want to override it inside the source code because that would prevent me from updating. So now the question becomes: Is my only way out writing a view that subclasses this view, and overriding the method? This means I'm writing separate views for every view provided by django-registartion that needs to send an email...


Solution

  • Here's what I ended up doing, Thanks to the direction dahrens' answer sent me to:

    # myapp/processors.py
    def get_website_name(request):
        website_name = 'ExcitingWebsiteThatsComingSoon'
        return {'mysite_name': website_name}
    
    # some views.py file
    from myapp.processors import get_website_name
    
    class RegistrationViewWithContext(RegistrationView):
        def get_email_context(self, user):
            context = super().get_email_context(user)
            context['req'] = get_website_name(self.request)
            return context
    

    Basically, I'm simply using my custom processor to inject the website's name. It isn't as clean as I hoped it would be: While in my templates I can simply use {{ mysite_name}}, in the email template I have to use {{req.mysite_name}}. But this does have the DRY-ness I aimed for: all templates updating accordingly if the variable in the function changes.

    I'll mark my answer as correct for now and will update accordingly if any new answers come in.