I’m trying to add a new action to my OrderAdmin class called "order_shipped", whereby it’ll send an email to the customer informing them that their order has been shipped.
class OrderAdmin(admin.ModelAdmin):
actions = ['order_shipped']
def order_shipped(self, request, obj):
customer_email = obj.customer.email
if request.method == 'POST':
send_mail(
'Order Shipped',
'Your order has been shipped. Please expect it within the next 3-5 days. Thank you for your business.',
EMAIL_HOST_USER,
[customer_email],
fail_silently=False
)
return HttpResponseRedirect('/admin/store/order')
order_shipped.short_description = 'Send Order Shipped Email'
The problem that I’m having is that I keep getting this error:
'QuerySet' object has no attribute 'customer'.
obj <QuerySet [<Order: 19>]>
request <WSGIRequest: POST '/admin/store/order/'>
self <OrderAdmin: model=Order site=AdminSite(name='admin')>
If I add a new column called "order_shipped_btn" to the list_display using the code below, it shows the correct email for each customer.
def order_shipped_btn(self, obj):
return format_html('<a href="mailto:{}">SEND</a>', obj.customer.email)
What I’m super confused about is why does it work for the later, but not the former? What am I doing wrong with the former?
>Solution :
A QuerySet
s a collection of Order
objects, not a single Order
object, hence obj.customer
makes not much sense.
The third parameter of the action is not a single item, but a QuerySet
of items, you thus might want to enumerate. To boost performance, we can use .select_related(…)
[Django-doc] to avoid an N+1 problem:
def order_shipped(self, request, queryset):
for obj in queryset.select_related('customer'):
customer_email = obj.customer.email
if request.method == 'POST':
send_mail(
'Order Shipped',
'Your order has been shipped. Please expect it within the next 3-5 days. Thank you for your business.',
EMAIL_HOST_USER,
[customer_email],
fail_silently=False,
)
return HttpResponseRedirect('/admin/store/order')