bmons
bmons

Reputation: 3392

is there a way to convert a view function to a celery task?

task.py

@task(name="send_mail_to_custormer")
def order_created(order_id):
    order = Order.objects.get(id=order_id)
    subject = 'Order nr. {}'.format(order.id)
    message = 'Dear {} {},\n\nYou have successfully placed an order. Your order id is {}'.format(order.first_name,
                                                        order.last_name, order.id)
    from_email = settings.EMAIL_HOST_USER
    to_email = [order.email]
    mail_sent = send_mail(
                            subject,
                            message,
                            from_email,
                            to_email,
                            fail_silently=False
                          )
    return mail_sent

views.py

def order_create(request):
cart = Cart(request)
if request.method == 'POST':
    form = OrderCreateForm(request.POST)
    if form.is_valid():
        order = form.save()
        for item in cart:
            try:
                OrderItem.objects.create(order=order,
                                         product=item['product'],
                                         price=item['price'],
                                         quantity=item['quantity'])
            except:
                pass
            cart.clear()
            order_created.delay(order.id)
        return render(request,'orders/order_created.html', {'cart': cart, 'order': order})
else:
    form = OrderCreateForm()
return render(request, 'orders/order_create.html', {'cart': cart, 'form': form})

cart.py

class Cart(object):

def __init__(self, request):
    self.session = request.session
    cart = self.session.get(settings.CART_SESSION_ID)
    if not cart:
        cart = self.session[settings.CART_SESSION_ID] = {}
    self.cart = cart

def add(self, product, quantity=1, update_quantity=False):
    product_id = str(product.id)
    if product_id not in self.cart:
        self.cart[product_id] = {'quantity': 0,
                                 'price': str(product.price)}
    if update_quantity:
        self.cart[product_id]['quantity'] = quantity
    else:
        self.cart[product_id]['quantity'] += quantity
    self.save()

def save(self):
    self.session[settings.CART_SESSION_ID] = self.cart
    self.session.modified = True

Now the celery task sends mail and view function creates order after taking values from cart and order form. How I can change the task to create order? Is it a good practice doing so. Hope somebody can help me, thank you. Also is it possible to do a success message using django message framework as celery task?

Upvotes: 2

Views: 266

Answers (1)

user2390182
user2390182

Reputation: 73470

I would not and do not (in my company we run a couple of web shops) create the order asynchronously. When the user sees your thank-you page, you best be sure that an order has been created. It is only a couple of db operations after all, and the page-speed won't be an issue. Mail sending, however, is a classic example of a task that should not stretch your request-response cycle. One thing though: You might want to consider giving your task a safety delay of a couple of seconds:

order_created.apply_async(args=[order.id], countdown=5)

The order has just been created and - particularly if you have atomic requests set for your database - the task worker could query for the order before the order creating transaction has been committed and becomes visible to the worker. Alternatively, you can use the transaction.on_commit hook to avoid that race condition.

Upvotes: 2

Related Questions