django-salesman icon indicating copy to clipboard operation
django-salesman copied to clipboard

Store custom data on Order

Open Timusan opened this issue 1 year ago • 1 comments

Currently, when overriding and expanding your Order model with custom fields (let's say I want to store first_name and last_name on my order besides the default email), everything has to go through the extras object during checkout. I was wondering if there is (or could be in the future) a more robust way of doing this, as it currently feels like twisting the frameworks arm a bit. Let me clarify.

Say I expand my order model with these fields:

from salesman.orders.models import BaseOrder

class Order(BaseOrder):
    first_name = TextField(_("First name"), null=True, blank=True)
    last_name = TextField(_("Last name"), null=True, blank=True)

And, after creating a basket I initiate a checkout with this JSON:

{
  "email": "[email protected]",
  "first_name": "Tim",
  "last_name": "van der Linden",
  "payment_method": "pay-later",
}

To get the extra fields first_name and last_name stored on my order I need to override the populate_from_basket() method on the Order and add in these two fields:

@transaction.atomic
    def populate_from_basket(
        self,
        basket: BaseBasket,
        request: HttpRequest,
        **kwargs: Any,
    ) -> None:
        """
        Populate order with items from basket.

        Args:
            basket (Basket): Basket instance
            request (HttpRequest): Django request
        """
        from salesman.basket.serializers import ExtraRowsField

        if not hasattr(basket, "total"):
            basket.update(request)

        self.user = basket.user
        self.email = basket.extra.pop("email", "")

        self.first_name = basket.extra.pop("first_name", "") # <- added in
        self.last_name = basket.extra.pop("last_name", "")  # <- added in
     
        ...

As you can see I need to pop them from the extras object of the basket to store them on the final Order.

I understand that the checkout itself is not a model and is more of a process, your Basket simply gets "transformed" into an Order with the above method. But still it feels strange to pull all of our custom data we wish to capture from the extras object of the basket.

Is this the intended way? Or should the Basket model be overridden as well and contain mirror fields of the data we want to store on the final Order (eg. a custom Basket model which would also contain a first_name and last_name field)?

Timusan avatar Mar 22 '23 07:03 Timusan