0

I have some models:

class User(AbstractUser):
    cart = models.OneToOneField('cart.Cart', on_delete=models.SET_NULL, null=True, blank=True)

class Cart(models.Model):
    products = models.ManyToManyField('product.Product')
    date_updated = models.DateTimeField(auto_now=True, editable=False)

Now I need to create user cart instance at first call.

   if request.user.cart is None:
        request.user.cart = Cart.objects.create()
        request.user.save()

This method is not good for me, because it leads to code duplication (every time I need to import Cart model and check the user's cart is it None or not).

The best way that I can find is AutoOneToOneField in django-annoying, but unfortunately it absolutely broke current field autocomplete in PyCharm.

What is the right way to do that?

P.S. I really don't need to create user cart object at user creation moment. P.P.S. Sorry for my bad English.

UPDATE: Thank you very much! I came to the first code fragment on my own, but it is noob edition script:

class User(AbstractUser):
    _cart = models.OneToOneField('cart.Cart', on_delete=models.SET_NULL, null=True, blank=True, db_column='cart')

@property
def cart(self):
    return self._cart # just for test

...end in my view i've got error like "object Cart has no property 'products'". But next code works great

class User(AbstractUser):
    _cart = models.OneToOneField('cart.Cart', on_delete=models.SET_NULL, null=True, blank=True, db_column='cart')

@property
def cart(self):
    if not self._cart:
        self._cart = Cart.objects.create()
        self.save(update_fields=('_cart',))
    return self._cart

...except "unresolved attribute reference" warning in view:

if request.user.cart.add(type, pk):
    messages.success(request, 'Added to cart')

but anyway, that warning should be PyCharm bug (do not autocomplete any @property-decorated methods). You are great, thank's a lot!

1
  • You can try adding the cart creation code in manage.py to make sure it gets executed as soon as the server is launched and it'll only get executed once. Commented Oct 3, 2019 at 7:25

1 Answer 1

3

There's a couple different ways I can think of off the cuff:

  • A middleware that does the check on every request – clean, but causes extra database hits even if that request doesn't need the cart on the user
  • A separate function get_cart(request) -> Cart that does the check
  • An accessor on your custom User:
class User(AbstractUser):
    _cart = models.OneToOneField('cart.Cart', on_delete=models.SET_NULL, null=True, blank=True, db_column='cart')

    @property
    def cart(self):
        if not self._cart_id: 
             self._cart = ...
             self.save(update_fields=('_cart',))
        return self._cart

Also, you might want to consider

class Cart(models.Model):
    user = models.OneToOneField(User)
    products = models.ManyToManyField('product.Product')
    date_updated = models.DateTimeField(auto_now=True, editable=False)

# and

class User(AbstractUser):

    @cached_property
    def cart(self):
        cart, created = Cart.objects.get_or_create(user=self)
        return cart

instead.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.