Django - Extra context in django-registration activation email - django

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.

Related

How to trigger allauth.account.signals.user_signed_up signal using pytest?

I have a OneToOneField between my UserTrust model and django.contrib.auth.models.User that I would like to create whenever a new User registers. I thought on creating the UserTrust instance using the user_signed_up signal.
I have the following code in my AppConfig
def ready(self):
# importing model classes
from .models import UserTrust
#receiver(user_signed_up)
def create_usertrust(sender, **kwargs):
u = kwargs['user']
UserTrust.objects.create(user=u)
... and this is in my pytest test
#pytest.fixture
def test_password():
return 'strong-test-pass'
#pytest.fixture
def create_user(db, django_user_model, test_password):
def make_user(**kwargs):
kwargs['password'] = test_password
if 'username' not in kwargs:
kwargs['username'] = str(uuid.uuid4())
return django_user_model.objects.create_user(**kwargs)
return make_user
#pytest.mark.django_db
def test_my_user(create_user):
user = create_user()
assert user.usertrust.available == 1000
Still, the test fails with
django.contrib.auth.models.User.usertrust.RelatedObjectDoesNotExist: User has no usertrust.
What did I miss?
The problem with you creating the user via the django_user_model is that it doesn't actually pass through the allauth code that actually sends that signal. You've got two options:
Use a client (since I'm assuming you're using pytest-django) and fill out a registration via the allauth provided link for registering new users. That way, a signal is sent, and you can assert attributes and model instances like that.
You can simply ignore the signal and unittest the function itself. That's it. You put your trust in that single registration view not changing at all and put your trust in the package that the API will not change. I can still recommend this option, but you've been warned.
You can send the signal yourself. Not recommended in case allauth's API changes, but you could just import the signal from allauth and send it like this: user_signed_up.send(sender=self.__class__, toppings=toppings, size=size) where user_signed_up is the signal. Ref the docs: https://docs.djangoproject.com/en/dev/topics/signals/#sending-signals
Again, definitely recommend the first one in case of API changes. I can also recommend the second option just because allauth is pretty reputable and you know what going to happen without too huge of an package change, but you never know.

django User registration and authentication by email

I want to make users active by sending them an activation email to click. I guess it is currently not incorporated in Django 1.6.The user-registration app coded in Django seems to serve this purpose. But I have some doubts with regard to the DefaultForm it provides in forms.py. I want to have more fields included in it. How can I achieve that in class RegistrationForm(forms.Form) implemented there. If I install this app, is it a good idea to change include more fields directly there, is there a better way to achieve the same.
In the views.py, I see some methods such as the following are not implemented. I dont have a clear picture of what these methods need to do. should I redirect the url here to the pages?
def register(self, request, **cleaned_data):
raise NotImplementedError
def activate(self, request, *args, **kwargs):
raise NotImplementedError
def get_success_url(self, request, user):
raise NotImplementedError
You need to first let them sign up and mark them as is_active=False for the time being. Something like this:
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
def signup(request):
# form to sign up is valid
user = User.objects.create_user('username', 'email', 'password')
user.is_active=False
user.save()
# now send them an email with a link in order to activate their user account
# you can also use an html django email template to send the email instead
# if you want
send_mail('subject', 'msg [include activation link to View here to activate account]', 'from_email', ['to_email'], fail_silently=False)
return HttpResponseRedirect('register_success_view')
Then once they click the link in the email it takes them to the next view (note: you need to put a link in the email so that you know which user it is. This may be 16-digit salt or something. The below view uses the user.pk:
def activate_view(request, pk):
user = User.objects.get(pk=pk)
user.is_active=True
user.save()
return HttpResponseRedirect('activation_success_view')
Hope that helps. Good Luck!
Basically you can use django's user model(https://docs.djangoproject.com/en/1.9/ref/contrib/auth/). However, in user model email is not a required field. You need to modify the model to make email an required field.
In your views, you might need the following method:
1) Sign up: after sign up, set user.is_active=False and call function send_email to include an activation link in the email. In the link, you might want to include the user's information (for example, user.id) so when the user click the link, you know which user to activate.
2) send_email: send a link to user's email address for verification. The link includes user's id. For example:
http://127.0.0.1:8000/activation/?id=4
3) Activate: get the id information from the URL using id=request.GET.get('id'). Query user=user whose id is id. set user.is_active=True.
Actually I implemented an reusable application like your request, check this(https://github.com/JunyiJ/django-register-activate) if you are interested.
Hope that helps. Good luck!
check this out... i Hope it helps out with not only the solution u need but also the explanation.. Because i think django-registration app is meant for default Django User. So if u want to have extra fields in your registration form, Start thinking of customizing ur Django User and Its authentication yourself. You dont need the django-registration app here..
Here are some tutorials thats will help
http://www.caktusgroup.com/blog/2013/08/07/migrating-custom-user-model-django/
and many more...

Use proxy class instead of User for request.user

I have a meta class for the Django User model that I use to add extra methods (overly simplified version):
# project.models.pie_lover.py
from django.contrib.auth.models import User
class PieLover(User):
class Meta:
app_label = "core"
proxy = True
def likes_pie(self):
return True
In my view, I wish to get the logged in PieLover and see if he likes pie (silly thing to do because a PieLover always loves pie, but in a real world situation this may not be the case). My problem lies in the way Django logs in the users, I use the built-in login(request) function for this and as a result the object stored in request.user is a User object, not a PieLover.
# project.views.like_pie.py
from ..models.pie_lover import PieLover
def index(request):
pie_lover = request.user
if pie_lover.likes_pie():
print "Try pie, try!"
else:
print "BLASPHEMER!"
If I try to do this Django tells me that the User object has no method likes_pie which is to be expected as request.user is not a PieLover instance.
As a quick workaround I just get the PieLover which has the same ID as the User but that means an extra DB hit.
How can I make Django use PieLover by default in the request?
I was thinking that instead of making another database query to get the proper PieLover object to create a new PieLover object and pass request.user to it at initialization but I don't know what the implications of this are.
After poking around I found, what seems to me, the easiest and non-hackish way to access the PieLover methods for a given User instance. I have added this to a custom middleware:
from models.pie_lover import PieLover
class PieLoverMiddleware(object):
def process_request(self, request):
request.pie_lover = PieLover(request.user)
How can I make Django use PieLover by default in the request?
You don't.
Read this before you do anything else: https://docs.djangoproject.com/en/1.3/topics/auth/#storing-additional-information-about-users
Your "extension" to user should be a separate model with all of your extension methods in that separate model.
You can then navigate from User (in the request) to your extension trivially using the get_profile() method already provided.
from django.contrib.auth import models as auth_models
def _my_func(self):
return True
auth_models.User.my_func = _my_func

Error when wrapping the view of a 3rd-party Django app? (Facebook, django-socialregistration, django-profiles)

I'm using django-socialregistration to manage my site's connection with Facebook.
When a user clicks the "Connect with Facebook" button, I am able to automatically create a new Django user and log them in. However, I also need to create a UserProfile (my AUTH_PROFILE_MODULE) record for them which contains their Facebook profile information (email, name, location).
I believe I need to override socialregistration's "setup" view so I can do what I need to do with UserProfile. I've added the following to my project's urls.py file:
url( r'^social/setup/$', 'myapp.views.socialreg.pre_setup', name='socialregistration_setup'),
My custom view is here "/myapp/views/socialreg.py" and looks like:
from socialregistration.forms import UserForm
def pre_setup(request, template='socialregistration/setup.html',
form_class=UserForm, extra_context=dict()):
# will add UserProfile storage here...
return socialregistration.views.setup(request, template, form_class, extra_context)
The socialregistration view signature I'm overriding looks like this:
def setup(request, template='socialregistration/setup.html',
form_class=UserForm, extra_context=dict()):
...
I'm getting the error "ViewDoesNotExist at /social/setup/: Could not import myapp.views.socialreg. Error was: No module named socialregistration.views" when I try the solution above.
The socialregistration app is working fine when I don't try to override the view, so it is likely installed correctly in site-packages. Anyone know what I'm doing wrong?
OK, as Tim noted, this particular problem was path related.
Bigger picture, the way to accomplish what I wanted (creating a linked UserProfile when django-socialregistration creates a user) is best done by passing in a custom form into socialregistration's "setup" view, as the author suggested here: http://github.com/flashingpumpkin/django-socialregistration/issues/issue/36/#comment_482137
Intercept the appropriate url in your urls.py file:
from myapp.forms import UserForm
url('^social/setup/$', 'socialregistration.views.setup',
{ 'form_class': UserForm }, name='socialregistration_setup'),
(r'^social/', include('socialregistration.urls')),
You can base your UserForm off socialregistration's own UserForm, adding in code to populate and save the UserProfile.

Non-destructively handling Django user messages

I'm displaying user messages through templates using RequestContext in Django, which gives access to user messages through {{messages}} template variable - that's convenient.
I'd like user him/herself delete the messages - is there a way to do it in Django without rewriting much code? Unfortunately Django automatically deletes messages at each request - not very useful in this case.
Django doc says:
"Note that RequestContext calls get_and_delete_messages() behind the scenes"
Would be perfect if there were a way to simply turn off the automatic deletion of messages!
NOTE: Unfortunately solution below makes admin interface unusable. I don't know how to get around this, really annoying.
EDIT - found a solution - use custom auth context processor that calls user.message_set.all() as Alex Martelli suggested. There's no need to change the application code at all with this solution. (context processor is a component in django that injects variables into templates.)
create file myapp/context_processors.py
and replace in settings.py in TEMPLATE_CONTEXT_PROCESSORS tuple
django.core.context_processors.auth with myapp.context_processors.auth_processor
put into myapp/context_processors.py:
def auth_processor(request):
"""
this function is mostly copy-pasted from django.core.context_processors.auth
it does everything the same way except keeps the messages
"""
messages = None
if hasattr(request, 'user'):
user = request.user
if user.is_authenticated():
messages = user.message_set.all()
else:
from django.contrib.auth.models import AnonymousUser
user = AnonymousUser()
from django.core.context_processors import PermWrapper
return {
'user': user,
'messages': messages,
'perms': PermWrapper(user),
}
I know it sounds like a strange approach, but you could copy into your own list
request.user.message_set.all()
before instantiating RequestContext, and later put them back in..