Home > Net >  django UniqueConstraint error in email field (sendgrid verification email)
django UniqueConstraint error in email field (sendgrid verification email)

Time:10-16

I am tryting to implement sendgrid email verification to my django project. I have been following https://www.twilio.com/blog/send-dynamic-emails-python-twilio-sendgrid tutorial. I have models and views setup accordingly however, I am facing UniqueConstraint error when trying to use the same email address twice (although my model has Email Field set to unique = True). Instead of getting UniqueConstraint error, I would like the app just to prevent user from adding the same email twice. I tried to use cleaned_data for form, but I cannot get through this UniqueConstraint error anyway.

My error: Exception Type: IntegrityError Exception Value:
UNIQUE constraint failed: newsletter_newsletteruser.email

My models.py:

from django.db import models

class NewsletterUser (models.Model):
    email= models.EmailField(unique=True)
    date_added = models.DateTimeField(auto_now_add=True)
    conf_num = models.CharField(max_length=15)
    confirmed = models.BooleanField(default=False)

    def __str__(self):
        return self.email   " ("   ("not " if not self.confirmed else "")   "confirmed)"

My admin.py

from .models import NewsletterUser, MailMessage


# Register your models here.
class NewsletterAdmin(admin.ModelAdmin):
    list_display = ('email','date_added','conf_num', 'confirmed')

admin.site.register(NewsletterUser, NewsletterAdmin)
admin.site.register(MailMessage)

My views.py

from django.db.models.fields import EmailField
from django.shortcuts import render, redirect
from sendgrid.helpers.mail.bcc_email import Bcc
from django.http import HttpResponse
from .forms import NewsletterUserForm, MailMessageForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Email, Content, To
from .models import NewsletterUser
from django import forms
import pandas as pd
from django.shortcuts import render
from django.http import HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import random
from sendgrid import SendGridAPIClient

# Create your views here.

#!REGISTER CURRENT HOST!
host = 'http://127.0.0.1:8000/'

# Helper Functions
def random_digits():
    return "%0.12d" % random.randint(0, 999999999999)

@csrf_exempt
def newsletters(request):
    if request.method == 'POST':
        sub = NewsletterUser(email=request.POST['email'], conf_num=random_digits())
        sub.save()
        message = Mail(
            from_email='[email protected]',
            to_emails=sub.email,
            subject='Potwierdzenie Adresu Email w Newsletterze Pasieka Radość',
            html_content= "<h1>Dziękujemy za zapisanie się do newslettera Pasieka Radość!<h1> \
                <h2>Aby potwierdzić rejestrację newslettera</h2> \
                <h2><a href='{}confirm/?email={}&conf_num={}'> Kliknij w poniższy link</a>.<h2>".format(request.build_absolute_uri(host),
                                                    sub.email,
                                                    sub.conf_num))
        sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
        response = sg.send(message)
        return render(request, 'newsletter/newsletter.html', {'email': sub.email, 'action': 'wysłano', 'form': NewsletterUserForm()})
    else:
        return render(request, 'newsletter/newsletter.html', {'form': NewsletterUserForm()})

def confirm(request):
    sub = NewsletterUser.objects.get(email=request.GET['email'])
    if sub.conf_num == request.GET['conf_num']:
        sub.confirmed = True
        sub.save()
        messages.success(request, f'Dziękujemy za zapisanie adresu email do naszego newslettera!')
        return render(request, 'pages/home.html' , {'email': sub.email, 'action': 'confirmed'})

My forms.py

from django import forms
from .models import NewsletterUser, MailMessage


class NewsletterUserForm (forms.ModelForm):
    email = forms.EmailField(label='Twój email',
                             max_length=100,
                             widget=forms.EmailInput(attrs={'class': 'form-control'}))
    
    class Meta:
        model = NewsletterUser
        fields = ['email']

My html template (newsletter.html)

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block content%}

<h1> {{title}} </h1>
<h4>Zapisz się do newslettera i odbieraj najświeższe informacje dotyczące naszej pasieki i oferty.</h4>
<div class="col-12">

    {% if email %}
    <p class="alert alert-info">Na {{ email }} {{ action }} wiadomość weryfikacyjną. Otwórz wiadomość i kliknij w link aby dodać adres do newslettera. </p>
    {% endif %}

<form method="POST" autocomplete="off">
    {% csrf_token %}
        <div class="col-sm-4">
            {{form|crispy}}
        </div>
    <br>
    <div class="col-sm-4">
        <input class="btn btn-info" type='submit' value='Zapisz Się'>
    </div>

</form>
<br>
<div class="row">
    <div class="col-sm-4">
        <p>Spokojnie, nie będziemy wysyłać Ci tony niepotrzebnych wiadomości. Zapisz się do newslettera i dowiedz się pierwszy/pierwsza, kiedy w naszej pasiecie pojawi się nowa dostawa miodów lub innych interesujących produktów.</p>
    </div>

</div>
 
  
    
</div>

{% endblock content %}

I am quite new to django and sendgrid, so it makes me wonder if I need to somehow rebuild my code totally, or there are some steps I could take to make it work as it is above. If any hints, please share. Thank you

CodePudding user response:

Twilio SendGrid developer evangelist here.

I believe Django is behaving as expected here. From the Django documentation:

If you try to save a model with a duplicate value in a unique field, a django.db.IntegrityError will be raised by the model’s save() method.

What you need to do is catch that IntegrityError and then provide an error back to the user.

@csrf_exempt
def newsletters(request):
    if request.method == 'POST':
        sub = NewsletterUser(email=request.POST['email'], conf_num=random_digits())
        try:
            sub.save()
            message = Mail(
                from_email='[email protected]',
                to_emails=sub.email,
                subject='Potwierdzenie Adresu Email w Newsletterze Pasieka Radość',
                html_content= "<h1>Dziękujemy za zapisanie się do newslettera Pasieka Radość!<h1> \
                    <h2>Aby potwierdzić rejestrację newslettera</h2> \
                    <h2><a href='{}confirm/?email={}&conf_num={}'> Kliknij w poniższy link</a>.<h2>".format(request.build_absolute_uri(host),
                                                        sub.email,
                                                        sub.conf_num))
            sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
            response = sg.send(message)
            return render(request, 'newsletter/newsletter.html', {'email': sub.email, 'action': 'wysłano', 'form': NewsletterUserForm()})
      except IntegrityError as ex:
          # Return the error to your user
    else:
        return render(request, 'newsletter/newsletter.html', {'form': NewsletterUserForm()})

I've added the try/except into the code above, you will need to fill in what to do in the except block when the email is not unique.

  • Related