Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Django Admin Actions to send bulk emails

I'm looking for a way to send bulk emails to users from a Django Admin Action. This is what I have thus far:

class MyUserAdmin(UserAdmin):
    list_display = ['username', 'email', 'first_name', 'last_name', 'is_active', staff]
    list_filter = ['groups', 'is_staff', 'is_superuser', 'is_active']
    actions = ['send_EMAIL']


    def send_EMAIL(self, request, queryset):
        from django.core.mail import send_mail
        for i in queryset:
            if i.email:
                send_mail('Subject here', 'Here is the message.', '[email protected]',[i.email], fail_silently=False)
            else:
        self.message_user(request, "Mail sent successfully ") 
    send_EMAIL.short_description = "Send an email to selected users"

This is fine but! I have to hardcode the actual message every single time. What if I could make it Dynamic? Instead of changing the message from the admin.py every single time I need to send a bulk email, why not create an intermediate Django admin action page that has a empty Text input field where I can write a new message to send every single time?

How can this be done? I'm looking for a well detailed answer that is not open ended and generic.

like image 716
deadlock Avatar asked Sep 04 '13 16:09

deadlock


People also ask

How do I send multiple emails in django?

How to send multiple mass emails django. We need to create a Tuple of messages and send them using send mass mail. In this tutorial, we create a project which sends email using Django. We fill the data in the form and send it using Django Email.

Can django send emails?

Django ships with several email sending backends. With the exception of the SMTP backend (which is the default), these backends are only useful during testing and development. If you have special email sending requirements, you can write your own email backend.

How do I send and receive emails in django?

Use the get_new_mail method to collect new messages from the server. Go to Django Admin, then to 'Mailboxes' page, check all the mailboxes you need to receive emails from. At the top of the list with mailboxes, choose the action selector 'Get new mail' and click 'Go'.


2 Answers

You are on the right track. Here is my implementation of a django admin action that allows you to write a message to the selected users. (I know this is super late but might help someone else).

send_email function:

def send_email(self, request, queryset):
    form = SendEmailForm(initial={'users': queryset})
    return render(request, 'users/send_email.html', {'form': form})

send_email.html template (I borrowed the markup from the django confirm delete view for this you may want to do something different here):

{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}


{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}

{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label='users' %}">{% trans "Users" %}</a>
&rsaquo; <a href="{% url 'admin:users_user_changelist' %}">{% trans "Users" %}</a>
&rsaquo; <span>Send email</span>
</div>
{% endblock %}

{% block content %}
<p>{% blocktrans %}Write your message here{% endblocktrans %}</p>
<form method="POST" action="{% url 'users:email' %}">{% csrf_token %}
    <div>
        <div>
            <p>{{ form.users.errors }}</p>
            <p>{{ form.users.label_tag }}</p>
            <p>
                {% for user in form.users.initial %}
                    {{ user.email }}{% if not forloop.last %},&nbsp;{% endif %}
                {% endfor %}
            </p>
            <select name="users" multiple style="display: none">
                {% for user in form.users.initial %}
                    <option value="{{ user.id }}" selected>{{ user }}</option>
                {% endfor %}
            </select>
        </div>
        <div>
            <p>{{ form.subject.errors }}</p>
            <p>{{ form.subject.label_tag }}</p>
            <p>{{ form.subject }}</p>
        </div>
        <div>
            <p>{{ form.message.errors }}</p>
            <p>{{ form.message.label_tag }}</p>
            <p>{{ form.message }}</p>
        </div>
        <input type="submit" value="{% trans 'Send message' %}" />
        <a href="{% url 'admin:users_user_changelist' %}" class="button cancel-link">{% trans "No, take me back" %}</a>
    </div>
</form>
{% endblock %}

send email form class:

class SendEmailForm(forms.Form):
    subject = forms.CharField(
        widget=forms.TextInput(attrs={'placeholder': _('Subject')}))
    message = forms.CharField(widget=forms.Textarea)
    users = forms.ModelMultipleChoiceField(label="To",
                                           queryset=User.objects.all(),
                                           widget=forms.SelectMultiple())

And finally the send email view + url conf:

# url pattern
url(
    regex=r'^email-users/$',
    view=views.SendUserEmails.as_view(),
    name='email'
),


# SendUserEmails view class
class SendUserEmails(IsStaff, FormView):
    template_name = 'users/send_email.html'
    form_class = SendEmailForm
    success_url = reverse_lazy('admin:users_user_changelist')

    def form_valid(self, form):
        users = form.cleaned_data['users']
        subject = form.cleaned_data['subject']
        message = form.cleaned_data['message']
        email_users.delay(users, subject, message)
        user_message = '{0} users emailed successfully!'.format(form.cleaned_data['users'].count())
        messages.success(self.request, user_message)
        return super(SendUserEmails, self).form_valid(form)

This implementation worked fine for me. Here is what the intermediate view looks like:

Email users

You might have to change a couple of things in the template where I build out the breadcrumbs or the reverse url for the view in case you don't have an app called users or a model called User.

like image 60
manncito Avatar answered Oct 21 '22 08:10

manncito


You need to write a custom admin view in Your admin class that will render the form You want. To define URL for this view You need to overwrite the ModelAdmin.get_urls() method. Django documentation mentions this briefly, but for more details I suggest You to look into source code of Django Admin (file django/contrib/admin/sites.py).

like image 20
HankMoody Avatar answered Oct 21 '22 08:10

HankMoody