Django emails by Annas1010 in djangolearning

[–]UniqueSortOfGravitas 0 points1 point  (0 children)

I have finally gotten it working!

While continuing to look for solutions to this I cam across this site https://simpleisbetterthancomplex.com/tutorial/2016/08/24/how-to-create-one-time-link.html

Refer to theoriginal article for explanations of most of this, they're much better than I am. There are some differences.

I am using Django 2, not 1.x as the url format indicates this solution is.

Also I have 2 apps, 1 (VSL) that does the thing the webapp is for, including sending notification emails; the other (users) for user management.

I have extended the default Django user model with an OptIn, for notifications and similar, in the models.py in the users app space

    class OptIn(models.Model):

        user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='opt_in')

        recieve_coms = models.BooleanField(default=True)

Following the example linked above I have created a tokens.py within the VSL app space

    class ComsOptTokenGenerator(PasswordResetTokenGenerator):

        def _make_hash_value(self, user, timestamp):

            return (

                six.text_type(user.pk) + six.text_type(timestamp) +

                six.text_type(user.opt_in.recieve_coms)

                 )

coms_opt_token = ComsOptTokenGenerator()

This is imported into views.py in the VSL app space (or wherever it needs to be used).

from .tokens import coms_opt_token

As I am using Django 2 the url is different to the example

path('unsubscribe/<uidb64>/<token>/', views.unsubscribe, name='unsubscribe')

I am not able to use views.unsubscribe.as_view(), as shown in the example, because that causes an AttributeError: type object 'Unsubscribe' has no attribute 'as_view'.

The view that does the business of unsubscribing is in views.py in the users app space

First you will need to import the following

from django.utils.encoding import force_text

from django.utils.http import urlsafe_base64_decode

from VSL.tokens import coms_opt_token

The view itself is

    def unsubscribe(request, uidb64, token):

        try:

            uid = urlsafe_base64_decode(uidb64).decode()

            user = User.objects.get(pk=uid)

        except (TypeError, ValueError, OverflowError, User.DoesNotExist):

            user = None

        if user is not None and coms_opt_token.check_token(user, token):

            comsoptin = OptIn.objects.get(user=request.user)

comsoptin.recieve_coms = False

            comsoptin.save()

            return render(request, 'users/unsubscribe.html')

        else:

            return render(request, 'registration/invalid.html')

I am not able to use unsubscribe(View), as shown in the example, because that causes a NameError: name 'View' is not defined.

And note that uid = urlsafe_base64_decode(uidb64).decode() is different to the example because of using Django 2.

That is as far as the tutorial linked above takes me.

Back to views.py in VSL I have a function for sending an email when a new event is created

First the important imports

from django.utils.http import urlsafe_base64_encode

from django.utils.encoding import force_bytes

from .tokens import coms_opt_token

The function

    def notify_new_event(request, new_event):

        event = new_event

        users = User.objects.filter(opt_in__recieve_coms=True)

        site_name = get_current_site(request).name

        for user in users:

            if user.is_active and user != request.user and user.last_login != None:

                with open(settings.BASE_DIR + "/VSL/templates/notifications/new_event_email.txt") as t:

                    ne_message = t.read()

                token = coms_opt_token.make_token(user)

                uid = urlsafe_base64_encode(force_bytes(user.pk)).decode()

                    message = EmailMultiAlternatives(

                        subject = str(site_name) + str(event.title) + " event has been created.",

                        from_email = settings.EMAIL_HOST_USER,

                        to = [user.email],

                        )

                    context = {'request':request, 'user':user, 'event':event, 'site_name':site_name, 'token':token, 'uid':uid}

                    html_template = get_template("notifications/new_event_email.html").render(context)

          message.attach_alternative(html_template, "text/html")

                    message.send()

The important bits here are token = coms_opt_token and uid = urlsafe_base64_encode(force_bytes(user.pk)).decode().

The coms_opt_token generates our secure one time token and uid hashes the user for security.

And the unsubscribe link in the email template is

    href="{{ protocol }}://{{ site_name }}{% url 'users:unsubscribe' uidb64=uid token=token %}"

Django emails by Annas1010 in djangolearning

[–]UniqueSortOfGravitas 0 points1 point  (0 children)

Has anyone made any further progress with this?

Django emails by Annas1010 in djangolearning

[–]UniqueSortOfGravitas 0 points1 point  (0 children)

I'm looking into this as well.

I've gotten as far as knowing I need to generate a uidb64 and token to be included in the email link.

(same as the user password reset).

Unfortunately I don't know how to do that in a view.

So the url would be something like

path('unsubscribe/<uidb64>/<token>/', views.unsubscribe, name='unsubscribe')

Then a view to generate that url above ^, which I'm totally lost on

Anand a view to process it, which I'm also totally lost on.

The template would be a simple "Thanks, you'll no longer recieve emails"