views:

262

answers:

4

I'm working on an online store in Django (just a basic shopping cart right now), and I'm planning to add functionality for users to mark items as favorite (just like in stackoverflow). Models for the cart look something like this:

class Cart(models.Model):
    user = models.OneToOneField(User)

class CartItem(models.Model):
    cart = models.ForeignKey(Cart)
    product = models.ForeignKey(Product, verbose_name="produs")

The favorites model would be just a table with two rows: user and product.

The problem is that this would only work for registered users, as I need a user object. How can I also let unregistered users use these features, saving the data in cookies/sessions, and when and if they decides to register, moving the data to their user?

I guess one option would be some kind of generic relations, but I think that's a little to complicated. Maybe having an extra row after user that's a session object (I haven't really used sessions in django until now), and if the User is set to None, use that?

So basically, what I want to ask, is if you've had this problem before, how did you solve it, what would be the best approach?

+7  A: 

I haven't done this before but from reading your description I would simply create a user object when someone needs to do something that requires it. You then send the user a cookie which links to this user object, so if someone comes back (without clearing their cookies) they get the same skeleton user object.

This means that you can use your current code with minimal changes and when they want to migrate to a full registered user you can just populate the skeleton user object with their details.

If you wanted to keep your DB tidy-ish you could add a task that deletes all skeleton Users that haven't been used in say the last 30 days.

Dave Webb
+1: anonymous users still have IP addresses that can be used to create a necessary temp userid with no password. When they register you're actually upgrading them to have a proper name and password.
S.Lott
Nice idea, I didn't think of that. thanks!
Adrian Mester
The IP address can't be used as a unique key. Think multiple users behind firewalled NAT.
ΤΖΩΤΖΙΟΥ
+1 I've used this approach and it works fine. As @ΤΖΩΤΖΙΟΥ says, you can't use IP address as identifier, you either have to generate a GUID or ask them for an email address or something.
Carl Meyer
+1  A: 

Just save the user data the user table and don't populate then userid/password tables.

if a user registers then you just have to populate those fields.

You will have to have some "cleanup" script run periodically to clear out any users who haven't visited in some arbitrary period. I'd make this cleanup optional. and have a script that can be run serverside (or via a web admin interface) to clear out in case your client wants to do it manually.

remember to deleted all related entries as well as the user entry.

Omar Kooheji
+2  A: 

Seems to me that the easiest way to do this would be to store both the user id or the session id:

class Cart(models.Model):
    user = models.ForeignKey(User, null=True)
    session = models.CharField(max_length=32, null=True)

Then, when a user registers, you can take their request.session.session_key and update all rows with their new user id.

Better yet, you could define a "UserProxy" model:

class Cart(models.Model):
    user = models.ForeignKey(UserProxy)

class UserProxy(models.Model):
    user = models.ForeignKey(User, unique=True, null=True)
    session = models.CharField(max_length=32, null=True)

So then you just have to update the UserProxy table when they register, and nothing about the cart has to change.

tghw
A: 

I think you were on the right track thinking about using sessions. I would store a list of Product id's in the users session and then when the user registers, create a cart as you have defined and then add the items. Check out the session docs.

You could allow people that are either not logged in or don't have an account to add items to a 'temp' cart. When the person logs in to either account or creates a new account, add those items to their 'real' cart. Then by just adding a few lines to your 'add item to cart' and login functions, you can use your existing models.

Gerry