Adding/changing logic in send_email method within django-registration-redux - django

My site runs multiple domains. Users can register on each of these domains. As a result, I need to make django-registration-redux:
use the correct email address for sending registration/password reset emails
use the correct email password for sending registration/password reset emails
use the correct domain within registration/password reset emails
I've been digging into the source code for django-registration-redux and believe that I need to update the send_email method within registration/models.py (https://github.com/macropin/django-registration/blob/master/registration/models.py) with my required changes.
I'm assuming the best way to add this cusomtization is as follows:
run 'pip uninstall django-registration-redux==2.2'
run 'pip freeze > requirements.txt'
from the source code, pull the 'registration' folder into my project
go into myproject/registration/models.py and manually update the send_email method so that it includes my changes.
Is there an easier or more correct way to build my custom logic into def send_email without making the changes noted above?
Thanks!

You could subclass the model then override the function that calls send_email() and patch it with your custom function.
from unittest import mock
from django_registration import models
def custom_send_email():
print('sending custom email')
class CustomEmailRegistration(models.Registration):
def function_calling_send_email(self):
with mock.patch('models.send_email', side_effect=custom_send_email):
super().function_calling_send_email()

Solution is to add a context processor that grabs the company name and website based on the current website, which can be accessed via request, and then reference the context variable within the django-registration email templates :)

This is how you add a context processor that gets the current hostname from the request:
Add a file my_ctx_proc.py to your application myapp:
def get_info_email(request):
return {
'info_email': "info#" + request.META['HTTP_HOST'],
}
In your settings.py, add in TEMPLATES OPTIONS context_processors at the end:
'myapp.my_ctx_proc.get_info_email',
Now you can insert {{ info_email }} in your email template, eg. templates/django_registration/activation_body_email.txt

Related

Unit Testing A #verified_email_required View

Im trying to unit test one of my views that is decorated with #verified_email_required. I am unable to find out how to set the user as having their email verified so that it will allow them to view the page and assert that it uses the correct template (creating a superuser doesnt help).
This is the error im getting
AssertionError: False is not true : Template 'enrolment/index.html' was not a template used to render the response. Actual template(s) used: account/verified_email_required.html, account/base.html, base.html
And this is my test
def test_verified_user_uses_correct_template(self):
user = User.objects.create_superuser('username')
self.client.force_login(user)
response = self.client.get('/enrolment/')
self.assertTemplateUsed(response, 'enrolment/index.html')
Thank you.
This is not standard Django authentication but one from django-allauth package.
To verify email address you have to create an EmailAdress object with verified=True
EmailAddress.objects.create(user=user, email="example#example.com", primary=True, verified=True)
You can see following model in source of django-allauth also some of package tests

Easiest strat to add a Subscribe module to a Mezzanine blog

I have a Mezzanine blog and I would like to add a little form to every page so users can type their email addresses and click 'subscribe' so, from that moment, an email will be sent to announce any new post to the blog.
I don't see that built in or any existing module for that purpose... Should I program that from scratch? Any ideas?
Since there are no answers, I will try to offer my best guess as to the easiest strategy in this situation. I don't know of any built-in functions in Mezzanine that perform this specific function, so here's how I would approach it:
python manage.py startapp subscriber
Build out basic model - email = models.EmailField(unique=True), etc.
Create basic admin.py
Update settings.py -> INSTALLED_APPS and ADMIN_MENU_ORDER
ADMIN_MENU_ORDER = (
(_("Web Content"), ((_("Pages"), "pages.Page"),
(_("Subscribers"), "subscriber.Subscriber"),
Create a forms.py - something like this:
class SubscriberForm(forms.ModelForm):
class Meta:
model = Subscriber
fields = ['email']
Setup a views.py for GET/POST of above form
Reconfigure urls.py to redirect to the new view function
Then, perhaps the only interesting part of my response, I would copy the Mezzanine Blog app directory into the project directory, and create a signals.py. Maybe add something like this:
#receiver(pre_save, sender=BlogPost, dispatch_uid="save_blogpost")
def save_blogpost(sender, instance, **kwargs):
""" Every time a new blog post is created, notify all subscribers."""
if instance.published:
subs = Subscriber.objects.all()
for sub in subs:
email = EmailMultiAlternatives(
subject="New Blog Post!",
body="A new blog post has been added!",
from_email="example#email.com",
to=[sub.email]
)
email.attach_alternative(self.body, "text/html")
email.send()
Update app.py in the blog app directory to contain this under the Config class:
def ready(self):
import blog.signals # noqa
If you've got Django configured to send mail through an external SMTP mail server, that's easier. If not, you'll likely want to setup Postfix with OpenDKIM and configure SPF to reduce probability the outgoing mail ends up in spam.
(Also, you'll obviously need to handle situations where an admin changes a draft post to a published post, etc.)
Not sure if this helps, but hopefully!

how to restrict a view function to be executed from website in django?

i have a website developed using django 1.6.0 with python 2.7.5 . In my view.py file i have a method defined which i want to be executed only when request for that view is redirected request from some where. I want to restrict user from executing that view by typing the url.
suppose view.py:
def online_test(request):
return buy_test_final(request)
urls.py:
url(r'^test$',online_test),
i need to restrict access of online_test method from url.
For making that view accessible for only redirected requests, you can check if request.META has HTTP_REFERER or not.
def online_test(request):
if 'HTTP_REFERER' not in request.META:
raise Http404
return buy_test_final(request)
Edit
As Andrew Gorcester has pointed out, in a comment below, that HTTP headers can be manipulated manually. Not only that, someone can simply add a link on any of your website's page by using Chrome's Developer Tools. Like this: Test. If he clicks this link, request.META will have HTTP_REFERER, thereby executing that view.
Use the above piece of code carefully, if you must.

Django - Extra context in django-registration activation email

I'm using django-registration for a project of mine.
I'd like to add some extra contextual data to the template used for email activation.
Looking into the register view source, I cannot figure out how to do it.
Any idea ?
From what I remember, you need to write your own registration backend object (easier then is sounds) as well as your own profile model that inherits from RegistrationProfile and make the backend use your custom RegistrationProfile instead (This model is where the email templates are rendered and there is no way to extend the context, so they need to be overwritten)
A simple solution is to rewrite the send_activation_email
So instead of
registration_profile.send_activation_email(site)
I wrote this in my Users model
def send_activation_email(self, registration_profile):
ctx_dict = {
'activation_key': registration_profile.activation_key,
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
'OTHER_CONTEXT': 'your own context'
}
subject = render_to_string('registration/activation_email_subject.txt',
ctx_dict)
subject = ''.join(subject.splitlines())
message = render_to_string('registration/activation_email.txt',
ctx_dict)
self.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
And I call it like this
user.send_activation_email(registration_profile)
I don't get what it is your problem but the parameter is just in the code you link (the last one):
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None)
That means you can do it from wherever you are calling the method. Let's say your urls.py:
from registration.views import register
(...)
url(r'/registration/^$', register(extra_context={'value-1':'foo', 'value-2':'boo'})), name='registration_access')
That's in urls.py, where usually people ask more, but, of course, it could be from any other file you are calling the method.

Why Django 1.4 per-site cache does not work correctly with CACHE_MIDDLEWARE_ANONYMOUS_ONLY?

I am working on a Django 1.4 project and writing one simple application using per-site cache as described here:
https://docs.djangoproject.com/en/dev/topics/cache/#the-per-site-cache
I have correctly setup a local Memcached server and confirmed the pages are being cached.
Then I set CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True because I don't want to cache pages for authenticated users.
I'm testing with a simple view that returns a template with render_to_response and RequestContext to be able to access user information from the template and the caching works well so far, meaning it caches pages just for anonymous users.
And here's my problem. I created another view using a different template that doesn't access user information and noticed that the page was being cached even if the user was authenticated. After testing many things I found that authenticated users were getting a cached page if the template didn't print something from the user context variable. It's very simple to test: print the user on the template and the page won't be cached for an authenticated user, remove the user on the template, refresh the page while authenticated and check the HTTP headers and you will notice you're getting a cached page. You should clear the cache between changes to see the problem more clearly.
I tested a little more and found that I could get rid of the user in the template and print request.user right on the view (which prints to the development server console) and that also fixed the problem of showing a cached page to an authenticated user but that's an ugly hack.
A similar problem was reported here but never got an answer:
https://groups.google.com/d/topic/django-users/FyWmz9csy5g/discussion
I can probably write a conditional decorator to check if user.is_authenticated() and based on that use #never_cache on my view but it seems like that defeats the purpose of using per-site cache, doesn't it?
"""
A decorator to bypass per-site cache if the user is authenticated. Based on django.views.decorators.cache.never_cache.
See: http://stackoverflow.com/questions/12060036/why-django-1-4-per-site-cache-does-not-work-correctly-with-cache-middleware-anon
"""
from django.utils.decorators import available_attrs
from django.utils.cache import add_never_cache_headers
from functools import wraps
def conditional_cache(view_func):
"""
Checks the user and if it's authenticated pass it through never_cache.
This version uses functools.wraps for the wrapper function.
"""
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view_func(request, *args, **kwargs):
response = view_func(request, *args, **kwargs)
if request.user.is_authenticated():
add_never_cache_headers(response)
return response
return _wrapped_view_func
Any suggestions to avoid the need of an extra decorator will be appreciated.
Thanks!
Ok, I just confirmed my "problem" was caused by Django lazy loading the User object.
To confirm it, I just added something like this to my view:
test_var = "some text" + request.user
And I got an error message telling me I couldn't concatenate an str to a SimpleLazyObject. At this point the lazy loading logic hasn't got a real User object yet.
To bypass the lazy loading, hence return a non-cache view for authenticated users, I just needed to access some method or attribute to triggers an actual query on the User object. I ended up with this, which I think it's the simplest way:
bypass_lazyload = request.user.is_authenticated()
My conditional_cache decorator is no longer needed, although it was an interesting exercise.
I may not need to do this when I finish working with my views as I'll access some user methods and attributes on my templates anyway but it's good to know what was going on.
Regards.