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)