Passing context to django-registration's views

2019-08-19 00:07发布

问题:

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...

回答1:

First create your own view based on the vendor view and overwrite your desired method:

from registration.backends.hmac.views import RegistrationView


class CustomRegistrationView(RegistrationView):
    def get_email_context(self, user):
        context = super().get_email_context(user)
        return RequestContext(self.request, context)

Than have a look at registration.backends.hmac.urls.py (source). They just define a bunch of urls there.

You can easily overwrite just one of them by adding your own before you include the one from the app.

from yourapp import views


urlpatterns = [
    # [...]
    url(r'^accounts/register/$', views.CustomRegistrationView.as_view(), name='registration_register'),
    url(r'^accounts/', include('registration.backends.hmac.urls')),
    # [...]
]

While changing just the necessary things you also get some insight of whats going on in your 3rd party app, which is always an advantage. This applies to most 3rd party apps not just the one you are currently using.



回答2:

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.