4

Currently, I have a Cart model for each registered CustomUser (OneToOne relation):

class Cart(models.Model):
    created_at = models.DateTimeField(auto_now_add = True)
    user = models.OneToOneField(CustomUser, related_name='cart')

But now I have some requirement for anonymous user (guest). after looking for some articles, session seems to be required?

  1. Simple database-based session.
  2. How to let guest has a related temporary GuestCart model when they need:
    • Because I don't expect even a robot has a cart to waste resource.
    • So...may I GuestCart.objects.create(session = request.session['cart']) #just pseudo-code, I don't sure how to do this when a view is called by the guest?
  3. GuestCart would live for exactly 1 week (count from the GuestCart is established. Time won't updated for guest's activity), and should be deleted automatically when expired.

** Update @ 2015/3/21: currently, I add a new key cart_id into request.session, and not save session info into DB.

kuanyui
  • 782
  • 13
  • 23

1 Answers1

8

In order to support anonymous users i believe your Cart model class should looks like this;

class Cart(models.Model):
    created_at = models.DateTimeField(auto_now_add = True)
    user = models.OneToOneField(CustomUser, blank=True, null=True, related_name='cart')
    session_key = models.CharField(max_length=40)

  class Meta:
    unique_together = ('user', 'session_key',)

Then you can retrieve the user cart with something like this:

if request.user.is_authenticated():
    cart, created = Cart.objects.update_or_create(
        user = request.user,
        defaults = {'session_key': request.session.session_key}
    )
else:
    cart, created = Cart.objects.get_or_create(
        session_key = request.session.session_key,
        defaults = {'user': None}
    )

and later on if the checkout process required registration you can always alter the cart and add the user.

Todor
  • 15,307
  • 5
  • 55
  • 62
  • 1
    The code means that when the user is authenticated you use `request.user`, when the user is anonymous you use `session_key`. About the 3rd requirement you can create a cron job executing `Cart.objects.filter(user=None, created_at__lt=week_ago).delete()` to remove the old carts. You should be aware that this approach is session based for the guest carts, which means that when a anonymous user start a new session, he will receive a new `cart`. – Todor Mar 19 '15 at 08:06
  • `Cannot assign None: "Cart.user" does not allow null values.` – kuanyui Mar 19 '15 at 08:40
  • Even if add a `null=True` to `Cart.user`, it still raise `IntegrityError carts_cart.session_key may not be NULL` at `defaults = {'user': None}` – kuanyui Mar 19 '15 at 08:57
  • Your old schema had `unique=True` on the `user` field. I guess this can be the reason for the errors. Run `show create table myapp_cart` and check what's going on with the fields *(are the fields consistent with the current state of the model)*. – Todor Mar 19 '15 at 09:18
  • sorry, I just forgot to say that I've removed `unique=True`, but still got error. (BTW, http://stackoverflow.com/questions/9949077/difference-between-foreignkeyuser-unique-true-and-onetoonefield ) – kuanyui Mar 19 '15 at 09:34
  • Removing `unique=True` from the field do not update the database schema. Have you tried running `makemigration` and `migrate` after that? – Todor Mar 19 '15 at 09:41
  • I guess it would be better to make another `GuestCart` model? – kuanyui Mar 19 '15 at 10:02
  • I don't think its better, that's why i suggested you such structure :). However later on I will try to make a simple demo to test the code. – Todor Mar 19 '15 at 10:13
  • Oops, `request.session` is `{'modified': False, 'serializer': , '_session_cache': {}, '_session_key': None, 'accessed': True}`, without `session_key` – kuanyui Mar 19 '15 at 10:28
  • 1
    I saw you accepted the answer, did you managed to make it work ? I wasn't able to spend more time on the problem these days? Let me know if the answer need some corrections. – Todor Mar 21 '15 at 07:59
  • Yes, now it can work, but I use `request.session['cart_id']` to save `cart.pk`, instead of add field into `Cart` due to some strange problems as I've described in these comments. – kuanyui Mar 21 '15 at 08:44