Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store custom data on Order #29

Open
Timusan opened this issue Mar 22, 2023 · 1 comment
Open

Store custom data on Order #29

Timusan opened this issue Mar 22, 2023 · 1 comment
Assignees
Labels
enhancement New feature or request

Comments

@Timusan
Copy link

Timusan commented Mar 22, 2023

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": "tim.vanderlinden@example.com",
  "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)?

@dinoperovic
Copy link
Owner

Hi @Timusan, this is how I initially designed it. The basket and orders intentionally have the same "extra" concept of storing data so that it can be easily extended (and validated using the SALESMAN_EXTRA_VALIDATOR) without having to override the models.

The basket should store all data that will end up in the order, and vice versa. This makes it so that we can theoretically re-create baskets from orders as well.

I could introduce an additional method for assigning "extra" data during the populate_from_basket, which would make it easier to override for such cases.

As you've mentioned in the #30, adding SALESMAN_CHECKOUT_SERIALIZER option would make it easier to implement the case where you really don't want to use the "extra" dict -- and enable some other customisations. I would like to add that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants