1

Hi everyone I integrate my Django Web app with mail chimp . in my admin panel when I open marketing preference it give me error . and it do not subscribe my users when I click on subscribe my user remain unsubscribe when i hit save .the error I got is

{'type': 'https://mailchimp.com/developer/marketing/docs/errors/', 'title': 'Invalid Resource', 'status': 400, 'detail': "The resource submitted could not be validated. For field-specific details, see the 'errors' array.", 'instance': '2b647b4f-6e58-439f-8c91-31a3223600a9', 'errors': [{'field': 'email_address', 'message': 'This value should not be blank.'}]}

my models.py file is:

class MarketingPreference(models.Model):
    user                    =models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    subscribed              =models.BooleanField(default=True)
    mailchimp_subscribed    = models.NullBooleanField(blank=True)
    mailchimp_msg           =models.TextField(null=True , blank=True)
    timestamp               =models.DateTimeField(auto_now_add=True)
    updated                 =models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.email

def marketing_pref_create_reciever(sender, instance, created, *args, **kwargs):
    if created:
        status_code, response_data = Mailchimp().subscribe(instance.user.email)
        print(status_code, response_data)
post_save.connect(marketing_pref_create_reciever, sender=MarketingPreference)


def marketing_pref_update_reciever(sender, instance, *args, **kwargs):
    if instance.subscribed != instance.mailchimp_subscribed:

        if instance.subscribed:
            #subscribing user
            status_code, response_data = Mailchimp().subscribe(instance.user.email)
        else:
            #unsubscribing user
            status_code, response_data = Mailchimp().unsubscribe(instance.user.email)
        if response_data['status'] =='subscribed':
            instance.subscribed = True
            instance.mailchimp_subscribed = True
            instance.mailchimp_msg = response_data
        else:
            instance.subscribed = False
            instance.mailchimp_subscribed = False
            instance.mailchimp_msg = response_data


pre_save.connect(marketing_pref_update_reciever, sender=MarketingPreference)


def make_marketing_pref_reciever(sender, instance, created, *args, **kwargs):
    if created:
        MarketingPreference.objects.get_or_create(user=instance)

post_save.connect(make_marketing_pref_reciever , sender=settings.AUTH_USER_MODEL)

my utils.py is:

MAILCHIMP_API_KEY = getattr(settings, "MAILCHIMP_API_KEY" , None)
MAILCHIMP_DATA_CENTER = getattr(settings, "MAILCHIMP_DATA_CENTER" , None)
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)

def check_email(email):
    if not re.match(r".+@.+\..+",email):
        raise ValueError("String passed is not a valid email address")
    return email

def get_subscriber_hash(member_email):
    #check email
    check_email(member_email)
    member_email = member_email.lower().encode()
    m = hashlib.md5(member_email)
    return m.hexdigest()


class Mailchimp(object):
    def __init__(self):
        super(Mailchimp, self).__init__()
        self.key = MAILCHIMP_API_KEY
        self.api_url = "https://{dc}.api.mailchimp.com/3.0/".format(dc=MAILCHIMP_DATA_CENTER)
        self.list_id = MAILCHIMP_EMAIL_LIST_ID
        self.list_endpoint = '{api_url}/lists/{list_id}'.format(api_url=self.api_url, list_id=self.list_id)

    def get_members_endpoint(self):
        return self.list_endpoint + "/members"


    def change_subscription_status(self, email, status='unsubscribed'):
        hashed_email = get_subscriber_hash(email)
        endpoint = self.get_members_endpoint() +"/" + hashed_email
        data = {
            "status":self.check_valid_status(status)
        }
        r = requests.put(endpoint, auth=("",self.key), data=json.dumps(data))
        return r.status_code, r.json()

    def check_subscription_status(self,email):
        hashed_email = get_subscriber_hash(email)
        endpoint = self.get_members_endpoint() +"/" + hashed_email
        r = requests.get(endpoint, auth=("", self.key))
        return r.status_code, r.json()


    def check_valid_status(self, status):
        choices = ['subscribed' , 'unsubscribed', 'cleaned' , 'pending']
        if status not in choices:
            raise ValueError("not a valid choice for email status")
        return status

    def add_email(self,email):
       status = "subscribed"
       self.check_valid_status(status)
       data = {
           "email_address":email,
           "status":  status
       }
       endpoint = self.get_members_endpoint()
       r = requests.post(endpoint, auth=("",self.key), data=json.dumps(data))
       return self.change_subscription_status(email, status='subscribed')


    def unsubscribe(self, email):
        return self.change_subscription_status(email, status='unsubscribed')

    def subscribe(self, email):
        return self.change_subscription_status(email, status='subscribed')

    def pending(self, email):
        return self.change_subscription_status(email, status='pending')

mixins.py is:

from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

class CsrfExemptMixin(object):
    @method_decorator(csrf_exempt)
    def dispatch(self, request,*args, **kwargs):
        return super(CsrfExemptMixin, self).dispatch(request,*args, **kwargs)

and my views.py is:

from .mixins import CsrfExemptMixin
from .models import MarketingPreference
from . utils import Mailchimp
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)
# Create your views here.

class MarketingPreferenceUpdateView(SuccessMessageMixin, UpdateView):
    form_class = MarketingPreferenceForm
    template_name = 'base/forms.html'
    success_url = '/settings/email/'   #this is a builtin method and by default it will go to marketig preference
    success_message = 'Your email preferences have been updated. Thank you'

    def dispatch(self, *args, **kwargs): #when user came with incognito email will not show to him it will redirect him back to login page
        user = self.request.user
        if not user.is_authenticated:
            return redirect("/login/?next=/settings/email/") #HttpResponse("not allowed", status=400)
        return super(MarketingPreferenceUpdateView, self).dispatch(*args,**kwargs)#(request, *args...)

    def get_context_data(self, *args, **kwargs):
        context = super(MarketingPreferenceUpdateView, self).get_context_data(*args,**kwargs)
        context['title'] = 'Update Email Preference'
        return context

    def get_object(self):
        user = self.request.user
        obj , created =  MarketingPreference.objects.get_or_create(user=user)
        return obj

class MailchimpWebhookView(CsrfExemptMixin,View): #it will not  work because our web application is not deployes yet and webhook mailchimp do not work with local host
    #def get(self, *args, **kwargs):
     #   return HttpResponse('thank you', status=200)
    def post(self, request, *args, **kwargs):
        data = request.POST
        list_id = data.get('data[list_id]')
        if str(list_id) == str(MAILCHIMP_EMAIL_LIST_ID):  # I CHECK THAT DATA DATA IS THE RIGHT LIST
            hook_type = data.get("type")
            email = data.get('data[email]')
            response_status, response = Mailchimp().check_subscription_status(email)
            sub_status = response['status']
            is_subbed = None
            mailchimp_subbed = None
            if sub_status == "subscribed":
                is_subbed, mailchimp_subbed = (True, True)
            elif sub_status == "unsubscribed":
                is_subbed, mailchimp_subbed = (False, False)
            if is_subbed is not None and mailchimp_subbed is not None:
                qs = MarketingPreference.objects.filter(user__email__iexact=email)
                if qs.exists():
                    qs.update(subscribed=is_subbed, mailchimp_subscribed=mailchimp_subbed, mailchimp_msg=str(data))
        return HttpResponse('thank you', status=200)
Huma Qureshi
  • 121
  • 1
  • 11

1 Answers1

0

Here you have already assigned request.POST to data which is now a dictonary, so to get a value from dictonary you should use the field name of the form widget, as data == request.POST now.
Problem is you are getting wrong key.So your email will always be empty

list_id = data.get('data[list_id]')
email = data.get('data[email]')#You are getting wrong key

It should be like this

list_id = data.get('list_id')
email = data.get('email')
SaGaR
  • 534
  • 4
  • 11