Django QuerySet Object Has No Attribute

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')

Leave a Reply